My friend Jackson made the 3 songs used after I gave him some small prompts, I was happy to get to include a piece of him in this odd little project.
The initial idea was a barber shop that caught on fire, not sure how well that was reflected but I was happy with the mood of the finished piece.
Electronics and Structure:
Some of the fabrication:
Some early sketches and plans:
Electronics:
arduino uno, 7408 quad AND gate, 7404 NOT gate, L293D motor driver, DFPlayer mini, 2 hobby gear motors, 1 continuous rotation servo, micro sd card, micro sd card reader/writer, 3 pushbuttons, 2 potentiometers, speakers, neo pixel ring, 4 yellow LEDs, 8 red LEDs, wires, resistor, breadboard, 5v power source
Materials:
wood, cardstock, cereal boxes, sewing pins, paperclips, wire, plastic bags, old clothing/fabric, thread, tissues, electrical tape, gaffer tape, duct tape, staples, boba straw, pebbles, Styrofoam
Tools:
soldering iron and fan, scissors, wire cutters, needle, pliers, precision knife, compass (drawing tool), stapler, pencils/pens, ruler, cutting mat
Code:
Everything but music:
#include "Arduino.h" #include "SoftwareSerial.h" #include "DFRobotDFPlayerMini.h" #include <Adafruit_NeoPixel.h> #include <Servo.h> #define PIN 9 #define NUMPIXELS 12 SoftwareSerial mySoftwareSerial(10, 11); // RX, TX DFRobotDFPlayerMini myDFPlayer; void printDetail(uint8_t type, int value); bool stage1 = false; bool stage2 = false; bool stage3 = false; bool button1 = false; bool button2 = false; bool button3 = false; bool song1 = false; bool song2 = false; bool song3 = false; int value; int angle; Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); uint32_t red = pixels.Color(250,0,0); Servo myservo; void setup() { pixels.begin(); //backlight pixels.setBrightness(0); pixels.show(); pinMode(5, OUTPUT); //m1 pinMode(6, OUTPUT); //m2 myservo.attach(3); //m3 pinMode(7, INPUT); //button1 pinMode(8, INPUT); //button2 pinMode(4, INPUT); //button3 pinMode(A0, INPUT); //p1 pinMode(A1, INPUT); //p2 mySoftwareSerial.begin(9600); Serial.begin(115200); Serial.println(); Serial.println(F("DFRobot DFPlayer Mini Demo")); Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)")); if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3. Serial.println(F("Unable to begin:")); Serial.println(F("1.Please recheck the connection!")); Serial.println(F("2.Please insert the SD card!")); while(true); } Serial.println(F("DFPlayer Mini online.")); myDFPlayer.volume(30); //Set volume value. From 0 to 30 } void loop() { //part 1 if (digitalRead(7) == HIGH) { analogWrite(5, analogRead(A0)/4); button1 = true; } else { button1 = false; } //part 2 if (digitalRead(8) == HIGH) { analogWrite(6, analogRead(A0)/4); button2 = true; } else { button2 = false; } //part 3 if (digitalRead(4) == HIGH) { //m3 value = analogRead(A1); angle = map(value, 0, 1023, 10, 170); myservo.write(angle); //backlight pixels.setBrightness(150); pixels.fill(red); pixels.show(); button3 = true; } else { myservo.write(10); pixels.setBrightness(0); pixels.show(); button3 = false; } //dfmini stage1 = (button1 && !button2); stage2 = (button1 && button2 && !button3); stage3 = (button1 && button2 && button3); if (stage1 && !stage2 && !stage3 && !song1) { myDFPlayer.play(3); song1 = true; song2 = false; song3 = false; } else if (stage2 && !stage3 && !song2){ myDFPlayer.play(1); song2 = true; song1 = false; song3 = false; } else if (stage3 && !song3){ myDFPlayer.play(2); song3 = true; song1 = false; song2 = false; } if (!stage1 && !stage2 && !stage3) { myDFPlayer.stop(); song1 = false; song2 = false; song3 = false; } }
dfmini controls:
/*************************************************** DFPlayer - A Mini MP3 Player For Arduino <https://www.dfrobot.com/index.php?route=product/product&product_id=1121> *************************************************** This example shows the basic function of library for DFPlayer. Created 2016-12-07 By [Angelo qiao](Angelo.qiao@dfrobot.com) GNU Lesser General Public License. See <http://www.gnu.org/licenses/> for details. All above must be included in any redistribution ****************************************************/ /***********Notice and Trouble shooting*************** 1.Connection and Diagram can be found here <https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram> 2.This code is tested on Arduino Uno, Leonardo, Mega boards. ****************************************************/ #include "Arduino.h" #include "SoftwareSerial.h" #include "DFRobotDFPlayerMini.h" SoftwareSerial mySoftwareSerial(10, 11); // RX, TX DFRobotDFPlayerMini myDFPlayer; void printDetail(uint8_t type, int value); int counter = 1; void setup() { pinMode(7, INPUT); mySoftwareSerial.begin(9600); Serial.begin(115200); Serial.println(); Serial.println(F("DFRobot DFPlayer Mini Demo")); Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)")); if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3. Serial.println(F("Unable to begin:")); Serial.println(F("1.Please recheck the connection!")); Serial.println(F("2.Please insert the SD card!")); while(true); } Serial.println(F("DFPlayer Mini online.")); myDFPlayer.volume(30); //Set volume value. From 0 to 30 } void loop() { if (digitalRead(7) == HIGH) { if (counter == 1) { myDFPlayer.play(3); } else if (counter == 2) { myDFPlayer.play(1); } else if (counter == 3) { myDFPlayer.play(2); } else { myDFPlayer.stop(); } counter = counter + 1; if (counter == 5) { counter = 1; } delay(200); } if (myDFPlayer.available()) { printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states. } } void printDetail(uint8_t type, int value){ switch (type) { case TimeOut: Serial.println(F("Time Out!")); break; case WrongStack: Serial.println(F("Stack Wrong!")); break; case DFPlayerCardInserted: Serial.println(F("Card Inserted!")); break; case DFPlayerCardRemoved: Serial.println(F("Card Removed!")); break; case DFPlayerCardOnline: Serial.println(F("Card Online!")); break; case DFPlayerPlayFinished: Serial.print(F("Number:")); Serial.print(value); Serial.println(F(" Play Finished!")); break; case DFPlayerError: Serial.print(F("DFPlayerError:")); switch (value) { case Busy: Serial.println(F("Card not found")); break; case Sleeping: Serial.println(F("Sleeping")); break; case SerialWrongStack: Serial.println(F("Get Wrong Stack")); break; case CheckSumNotMatch: Serial.println(F("Check Sum Not Match")); break; case FileIndexOut: Serial.println(F("File Index Out of Bound")); break; case FileMismatch: Serial.println(F("Cannot Find File")); break; case Advertise: Serial.println(F("In Advertise")); break; default: break; } break; default: break; } }
Useful references:
DFPlayermini
https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299
https://www.instructables.com/Tutorial-of-MP3-TF-16P/
L293d
https://www.tinkercad.com/things/gigwXYoyaDb
Neopixel_ring
https://www.instructables.com/3D-Printed-NeoPixel-Ring-Butterfly-With-Tinkercad/
https://www.tinkercad.com/things/c4nyZ9UpWer
Libraries found on Github:
https://github.com/adafruit/Adafruit_NeoPixel
https://github.com/DFRobot/DFRobotDFPlayerMini
TinkerCad Circuits I used to troubleshoot:
https://www.tinkercad.com/things/0evKMIHM0Ai-fantabulous-inari/editel?sharecode=NX5CYihf8vkyRuLa-rzRwghpmeox1-OVk11WzY99hrU
https://www.tinkercad.com/things/7uIwm5bLp6N-copy-of-project-1/editel?sharecode=YA4F9Lz2rxujlDz2OgQx0Xv3PKQZNSbunOs2S8PjWWM
]]>Project Statement:
In order to capture and enhance my existing rountine of dancing in my living room, this dance space has been created. It is meant to be used as a place to let go of any stress, it may be added to a daily routine- or not. As you enjoy tea and dance, the space around you reacts to your actions. The idea is to move freely and unpredictably through the living space until the knit yarn pieces are lifted. I plan to complete this project with a final piece of documentation which will be a video of me dancing to music.
How to Use:
In order to turn the light on, the tea kettle must be lifted. in order to make the “worms” move up, three or more buttons one the ground must be pressed. Enjoy the space, move freely, and dance!
Process and Issues:
I ran into some issues while constructing the pieces. I didn’t predict well enough, how many things I would need into order to complete the project successfully. I think, for the time we were given I may have created a project that was a bit too big/complex in the manufacturing process. Another issue I ran into was not prdicting how long the clay was going to take to dry. Once dry, it will be spray painted the same shade as the chair.
The electronics/programming portion of the project ran pretty smoothly except for the uploading the program into the arduino. I didn’t have the library I needed for the code to run, so I downloaded the right library for the motor.
Looking back, I am happy with the direction this project has gone. If I were to change anything, it would most likely be the construction of my pulley system. Although it works, it is very fragile and I would have liked it to be more rigid so that there would be no risk in the whole system falling apart. I’m satisfied with the interactivity and tangibility of this project. I feel that I have achieved a very intuitive system as to how to operate the piece. Perhaps I could have have used less fragile floor pieces, however I enjoy how delicate they are- even the possibility of the clay cracking as I step on it.
The main apparatus…
This device was referred to as the “Workaholic’s Calendar” during development, but this has since been changed to “Workaholic’s Clock” since it really is just a clock that tells you whether or not you can go to work.
In order to be able to go to work, the following criteria must be met:
The device has 3 main inputs and is controlled by an Arduino and a hard-wired logic circuit. The first input is an infrared break-beam sensor, which is there to detect whether or not you have taken the tissue–doing so would indicate that you are sick. You will get fired if you decide to come in anyway. The second input is a real-time clock, which the Arduino polls in order to determine whether or not Hunt Library (my workplace) is open. If it is open, then pressing * on the keypad will tell the Arduino to probe the logic circuit. If Hunt is closed, then the Arduino will probe the logic circuit once a 5-digit code is entered . If the output of that circuit is high (true), then you can go to work because the necessary conditions were met. If the output is low (false), then you get fired, and a certain message will be displayed, along with an accompanying sound depending on the reason for which you were fired.
In the case where Hunt is open, there is no need to enter a code, and the only thing that would cause you to get fired is sickness.
In the case where Hunt is closed, there are a few more possibilities. You must enter a 5-digit code. If the code is incorrect and you are sick, then you reach the “critical failure” output, where you not only infect everyone but also get arrested by “The Crops.” If the code is incorrect, but you aren’t sick, then you are arrested by “The Crops.” If the code is correct, but you are sick, then you infect everyone. In any of these cases, you get fired.
If Hunt is closed, but you enter the correct code to disarm security and you aren’t sick, then you can come in to work (sneakily, you devil).
Here are some shots of the device working under certain circumstances…
When Hunt is open, but you’re sick…
When you enter the correct code to disarm security, but you’re sick…
When you aren’t sick, but you enter the wrong code to disarm security…
When you are sick AND you enter the wrong code to disarm security…
When you aren’t sick and you properly disarm security…
When Hunt is open and you aren’t sick…
Here is the hard-wired logic circuit after completion. When I first wired it up, I accidentally pulled the outputs low, rather than the inputs. This picture is from after I remedied that. From right to left, 7408 quad-AND gate, 7432 quad-OR gate, 7404 hex inverter (six NOT gates). All inputs are pulled low using 10k ohm resistors.
Here’s the device after initially wiring up the Arduino. The only components missing are the LCD and keypad. As you can see, the wiring is quite messy, but more alarmingly, the power is all coming from the Arduino. This is not enough to power everything properly and is ultimately what caused the problem shown in the video below… pardon my French…
Here’s the device after I got the keypad and LCD working. This was the final major hurdle in the build process. I actually had to start using pins 0 and 1 on the Arduino for communicating with the DFPlayer Mini so that I would have enough pins that weren’t A4 and A5 (which you can’t use when using I2C devices like the RTC). To get around problems when uploading code to the Arduino, I just unplug pins 0 and 1 and then plug them back in once the Arduino is ready.
And this was the most satisfying part–getting to come in to work with all parts working correctly.
The hardest part, after all was said and done, was getting everything to look somewhat neat, while still remaining in the realm of breadboards and jumpers. I had originally planned to enclose parts within their own custom-fabricated enclosures to tidy the device up, but due to my own poor time management and the challenging circumstances brought about by the COVID-19 pandemic, obtaining such parts would prove impossible. As a result, breadboards and jumpers ended up being the final aesthetic. Another issue (closely related to the previous one) was that I never managed to separate the break-beam sensor from the breadboard, as I didn’t have a suitable enclosure that would allow me to thread a tissue through it. I still plan on addressing this at some point when things are a little less frantic, but for now, it is what it is.
I ended up learning (or in some cases, re-learning) quite a lot about the limitations of the Arduino Uno platform, mostly relating to the pinout of its microcontroller. For instance, I re-learned that you can’t use pins A4/A5 and SDA/SCL at the same time, as they conflict. I also learned that you CAN use pins 0 and 1, so long as you unplug whatever device is connected to them whenever you need to upload code to the Arduino, and you don’t use the built-in serial monitor/plotter. Lastly, I learned that quite a few components are picky about how much power they receive and just won’t work right if you don’t give them enough. This ultimately stopped me from using the Elegoo Power Supply Module, as it wouldn’t supply enough power to the DFPlayer Mini given the audio setup I wanted to use. This was the case even when it was plugged into a 12V, 2A DC power supply, which should’ve been more than enough.
Despite the issues encountered, I’m actually pretty happy not just with the outcome, but also with what I learned about working with dedicated logic ICs and re-learned about working with the Arduino.
Main program:
/* Workaholic's Clock (62-362 Project 1) Seth Geiser (sgeiser) Collaboration: Reference code and the guides I used for setting and accessing the RTC, setting, storing, and reading the passcode and keypad input, as well as outputting to the LCD can be found at these links: RTC: https://create.arduino.cc/projecthub/MisterBotBreak/how-to-use-a-real-time-clock-module-ds3231-bc90fe Keypad and passcode: https://www.instructables.com/Arduino-password-lock/ LCD: Used guide from the Elegoo Super Starter Kit UNO R3 Project. Pin mapping: pin | mode | description ---------|------------|------------ SDA/SCL I2C Real-time Clock 0 RX DFPlayer Mini TX 1 TX DFPlayer Mini RX 2-8 I/O Keypad 9 INPUT_PULLUP Read output of main hard-wired logic... 10 OUTPUT Send hi/lo to main logic based on RTC 11 INPUT Read state of sick sensor for determining cause of firing (in part to determine which sound to play) 12 LCD LCD RS 13 LCD LCD Enable A0 LCD LCD D4 A1 LCD LCD D5 A2 LCD LCD D6 A3 LCD LCD D7 */ #include <ds3231.h> #include <Wire.h> #include <Keypad.h> #include <LiquidCrystal.h> #include <EEPROM.h> #include <SoftwareSerial.h> #include <DFRobotDFPlayerMini.h> const int passLen = 5; const int readOutput1 = 9; const int huntPin = 10; const int sickPin = 11; LiquidCrystal lcd(12, 13, A3, A2, A1, A0); char password[passLen]; char pass[passLen], pass1[passLen]; int i = 0; char customKey = 0; bool arrested = false; bool infected = false; const byte ROWS = 4; // four rows const byte COLS = 3; // four columns char hexaKeys[ROWS][COLS] = { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}, {'*', '0', '#'} }; byte rowPins[ROWS] = {5, 3, 2, 7}; byte colPins[COLS] = {4, 8, 6}; // initialize a new instance of class Keypad Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); struct ts t; // Be sure to set the RTC using setRTC.ino // RX, TX, disconnect when uploading sketch!!! SoftwareSerial mySoftwareSerial(0, 1); DFRobotDFPlayerMini myDFPlayer; void setup() { mySoftwareSerial.begin(9600); Wire.begin(); lcd.begin(16, 2); DS3231_init(DS3231_INTCN); // Initialize connection to RTC... pinMode(huntPin, OUTPUT); pinMode(sickPin, INPUT); pinMode(readOutput1, INPUT_PULLUP); if (!myDFPlayer.begin(mySoftwareSerial)) { // Use softwareSerial to communicate with DFPlayer Mini. while (true); } // Set serial communictaion time out 500ms myDFPlayer.setTimeOut(500); // Set volume value (range 0-30). myDFPlayer.volume(15); myDFPlayer.EQ(DFPLAYER_EQ_NORMAL); myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD); lcd.print("CMU IDeATe"); lcd.setCursor(0, 1); delay(1000); lcd.print("BOOTING DEVICE"); delay(2000); lcd.clear(); DS3231_get(&t); // Probe RTC if (t.hour >= 19 || t.hour <= 8) { digitalWrite(huntPin, HIGH); // Hunt is closed, Q3=T lcd.setCursor(0, 0); lcd.print("Hunt is closed."); lcd.setCursor(0, 1); lcd.print("Code: "); for (int j = 0; j < passLen; j++) { pass[j] = EEPROM.read(j); // Read password from built-in EEPROM } } else { digitalWrite(huntPin, LOW); // Hunt is open, Q3=F lcd.setCursor(0, 0); lcd.print("Welcome to Hunt."); lcd.setCursor(0, 1); lcd.print("Press '*'"); } } void loop() { customKey = customKeypad.getKey(); // Read keypad... if (digitalRead(sickPin)) { infected = true; } else if (!digitalRead(sickPin)) { infected = false; } if (t.hour >= 19 || t.hour <= 8) { // Hunt closed... if (customKey) { password[i++] = customKey; lcd.print(customKey); } if (i == passLen) { i = 0; delay(200); for (int j = 0; j < passLen; j++) pass[j] = EEPROM.read(j); if (!strncmp(password, pass, passLen)) { i = 0; lcd.setCursor(0, 0); lcd.print("Passkey Accepted,"); lcd.setCursor(0, 1); lcd.print("Welcome 2 IDeATe"); delay(3000); digitalWrite(huntPin, LOW); // Open Hunt Library, disarm security... submit(); } else { i = 0; lcd.clear(); lcd.print("Access Denied..."); delay(3000); arrested = true; submit(); } } } else if (customKey == '*') { submit(); } } void submit() { lcd.clear(); if (digitalRead(readOutput1)) { // Logic is outputting LOW into a NOT-gate. Since the pin is being pulled up, pin 9 is connected to the output of the NOT-gate... lcd.setCursor(0, 0); lcd.print("You're fired!"); lcd.setCursor(0, 1); if (arrested && infected) { lcd.print("Busted AND sick."); myDFPlayer.play(2); // 0002_get_out.mp3 } else if (infected) { lcd.print("You're sick."); myDFPlayer.play(4); // 0004_bruh.mp3 } else if (arrested) { lcd.print("Busted!"); myDFPlayer.play(1); // 0001_crops.mp3 } } else { // Logic is outputting HIGH into a NOT-gate... lcd.setCursor(0, 0); lcd.print("You can come to"); lcd.setCursor(0, 1); lcd.print("work today!"); myDFPlayer.play(3); // 0003_scatman_world.mp3 } }
Set real-time clock:
#include <Wire.h> #include <ds3231.h> struct ts t; void setup() { Serial.begin(9600); Wire.begin(); DS3231_init(DS3231_INTCN); /*---------------------------------------------------------------------------- In order to synchronise your clock module, insert timetable values below ! ----------------------------------------------------------------------------*/ t.hour=12; t.min=7; t.sec=0; t.mday=9; t.mon=10; t.year=2020; DS3231_set(t); } void loop() { DS3231_get(&t); Serial.print("Date : "); Serial.print(t.mday); Serial.print("/"); Serial.print(t.mon); Serial.print("/"); Serial.print(t.year); Serial.print("\t Hour : "); Serial.print(t.hour); Serial.print(":"); Serial.print(t.min); Serial.print("."); Serial.println(t.sec); delay(1000); }
Initialize EEPROM with default password of “12345”:
#include<EEPROM.h> void setup() { // Passcode will be 5 digits long for (int j=0;j<5;j++) { EEPROM.write(j, j+49); } } void loop() { }
Test password storage + retrieval and optionally change password stored in EEPROM:
/* +------------------------------------------------------------------------+ | | | ARDUINO LOCK SYSTEM FIRMWARE v1.1.0 | | BY: DIY MECHANICS | | -------------------------------------------------------------- | | written_by: suhail project:HOME AUTOMATION:-passcode lock sytem v1.1 | | facebook.com/suhail jr. twitter.com/suhail jr. | | visit more @ www.diymechanics.wordpress.com | +------------------------------------------------------------------------+ */ /////////////////////////////////////////////////////////////////////////////// #include <Keypad.h> #include <LiquidCrystal.h> #include <EEPROM.h> ////////////////////////////////////////////////////////////////////////////// LiquidCrystal lcd(12, 13, A3, A2, A1, A0); const int passLen = 5; char password[passLen]; char pass[passLen], pass1[passLen]; int i = 0; char customKey = 0; ////////////////////////////////////////////////////////////////////////////// const byte ROWS = 4; // four rows const byte COLS = 3; // four columns char hexaKeys[ROWS][COLS] = { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}, {'*', '0', '#'} }; /////////////////////////////////////////////////////////////////////////////// byte rowPins[ROWS] = {5,3,2,7}; // connect to the row pinouts of the keypad byte colPins[COLS] = {4,8,6}; // connect to the column pinouts of the keypad //initialize an instance of class NewKeypad Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); //////////////////////////////////////////////////////////////////////////////////// void(* resetFunc) (void) = 0; void setup() { lcd.begin(16, 2); lcd.print("STARK-TECHNOLOGY"); lcd.setCursor(0, 1); delay(4000); lcd.print("BOOTING DEVICE"); delay(1000); lcd.clear(); lcd.print("KEYPAD-LOCKED"); lcd.clear(); lcd.print(" Passcode Please:"); lcd.setCursor(0, 1); for (int j = 0; j < passLen; j++) { pass[j] = EEPROM.read(j); } //////////////////////////////////////////////////////////////////////////////////// } void loop() { customKey = customKeypad.getKey(); if (customKey == '#') { change(); } if (customKey == '*') { resetFunc(); } if (customKey) { password[i++] = customKey; lcd.print(customKey); } if (i == passLen) { i = 0; delay(200); for (int j = 0; j < passLen; j++) { pass[j] = EEPROM.read(j); } if (!(strncmp(password, pass, passLen))) { i = 0; lcd.clear(); lcd.print("Passkey Accepted"); lcd.setCursor(0, 1); lcd.print("#-Change"); lcd.print("*-Lock"); } else { lcd.clear(); lcd.print("Access Denied..."); lcd.clear(); lcd.print("restarting..."); delay(5000); i = 0; resetFunc(); } } } //////////////////////////////////////////////////////////////////////////////////// void change() { int j = 0; lcd.clear(); lcd.print("Current Passcode:"); lcd.setCursor(0, 1); while (j < passLen) { char key = customKeypad.getKey(); if (key) { pass1[j++] = key; lcd.print(key); } key = 0; } delay(500); if ((strncmp(pass1, pass, passLen))) { lcd.clear(); lcd.print("Wrong Passkey..."); lcd.setCursor(1, 1); lcd.print("DENIED"); delay(2000); customKey = 0; resetFunc(); } else { j = 0; lcd.clear(); lcd.print("New Passcode:"); lcd.setCursor(0, 1); while (j < passLen) { char key = customKeypad.getKey(); if (key) { pass[j] = key; lcd.print(key); EEPROM.write(j, key); j++; } } lcd.print(" Done......"); delay(1000); resetFunc(); } }
]]>
By William Lamkin
(Name changed from the prototype title Cut. )
/* Warp Field * William Lamkin * * This code takes inputs from the external Logic Gates and sends * predetermined voltage values out of the 4 outputs of the * Adafruit MCP4728 Quad DAC. These voltage signals are then * sent into a Eurorack synthesizer to control sounds. */ #include <Adafruit_MCP4728.h> #include <Wire.h> //assign pin names const int pinA = 9; const int pinB = 2; const int pinC = 3; const int pinD = 4; const int pinE = 5; const int pinF = 6; const int pinG = 7; const int pinH = 8; //defining variables for the values of pins int A = 0; int B = 0; int C = 0; int D = 0; int E = 0; int F = 0; int G = 0; int H = 0; Adafruit_MCP4728 mcp; void setup() { //activating pins pinMode(pinA, INPUT); pinMode(pinB, INPUT); pinMode(pinC, INPUT); pinMode(pinD, INPUT); pinMode(pinE, INPUT); pinMode(pinF, INPUT); pinMode(pinG, INPUT); pinMode(pinH, INPUT); // checks for I2C connection to the MCP4728 (code from Adafruit) Serial.begin(115200); while (!Serial) delay(10); // will pause Zero, Leonardo, etc until serial console opens Serial.println("Adafruit MCP4728 test!"); // Try to initialize! if (!mcp.begin()) { Serial.println("Failed to find MCP4728 chip"); while (1) { delay(10); } } //default values for the outputs mcp.setChannelValue(MCP4728_CHANNEL_A, 0); mcp.setChannelValue(MCP4728_CHANNEL_B, 0); mcp.setChannelValue(MCP4728_CHANNEL_C, 0); mcp.setChannelValue(MCP4728_CHANNEL_D, 0); } void loop() { //update pin state variables A = digitalRead(pinA); B = digitalRead(pinB); C = digitalRead(pinC); D = digitalRead(pinD); E = digitalRead(pinE); F = digitalRead(pinF); G = digitalRead(pinG); H = digitalRead(pinH); // Had to mirror the logic of the gates in here since only one // value can be sent to an output at a time // Channel B controlled the lowpass filter cutoff on the drone if (A == HIGH) { if (H == HIGH) { mcp.setChannelValue(MCP4728_CHANNEL_B, 0); } if (H == LOW) { if (B == LOW) { mcp.setChannelValue(MCP4728_CHANNEL_B, 1024); } else if (B == HIGH) { if (C == LOW) { mcp.setChannelValue(MCP4728_CHANNEL_B, 2048); } else if (C == HIGH) { mcp.setChannelValue(MCP4728_CHANNEL_B, 4095); } } } } // Channel C controlled the amount of resonance on that filter if (D == HIGH) { if (F == HIGH) { mcp.setChannelValue(MCP4728_CHANNEL_C, 4095); } else if (F == LOW) { mcp.setChannelValue(MCP4728_CHANNEL_C, 2048); } } // Channel D controlled the volume of the random pitch granular if (E == HIGH) { if (H == HIGH) { mcp.setChannelValue(MCP4728_CHANNEL_D, 0); } else if (H == LOW) { if (F == LOW) { mcp.setChannelValue(MCP4728_CHANNEL_D, 2048); } else if (F == HIGH) { mcp.setChannelValue(MCP4728_CHANNEL_D, 4095); } } } // Channel A checks if the scissors are touching the circuit if (G == HIGH) { mcp.setChannelValue(MCP4728_CHANNEL_A, 4095); } else if (G == LOW) { mcp.setChannelValue(MCP4728_CHANNEL_A, 0); } }
Group Check-In 2: 9-30-20
By William Lamkin
Made significant progress from Monday in that I discovered how logic chips function! Got an AND gate to function with the scissors, showing its ability to be conductive, and also how that could successfully route through the human body. Video below:
Video of conductive scissors working with logic chip
I am still waiting on my Adafruit Quad DAC to come in, from which I could complete my circuit and be able to control my synthesizer, since it would allow my Arduino to send out a full range of voltages.
Process Blog Check-In 9-28-20
By William Lamkin
In reflecting on how to utilize ambient sound that adaptively changes, I’ve decided to use a modular synthesizer to generate sounds. I had toyed around with the idea of using an mp3 playing device that would work with an Arduino, but I realized that the technology involved with those wasn’t as fully interactive as I would like it to be. My goals for this project are for the actions of the user to have a significant effect on the sound.
I will be using logic chips for all of connections, and these will be connected to the board to be cut via a alligator clips, and the output signals of these logic chips will then be sent into the inputs of the Arduino. The Arduino will send out voltages through an Adafruit Quad DAC which I have ordered and I will be able to connect to the synthesizer via these audio jack parts. In the link is a demo of the kinds of sounds that can be made and changed.
Here is a diagram of what I want the setup to be like! I do want it to be a dark and isolated experience so the user can focus on what they are doing. For documentation purposes, I will be recording a video of myself at home working on this.
Here is an example logic problem for how a parameter of music could be changed.
In Process Critique: 9-23-20
By William Lamkin
Draft version of Cut, made using cardstock and conductive tape to create circuits. In essence, all circuits start ON (1), except one involving the scissors, and breaking them (0) triggers a reaction in ambient sound created from the experience interactively.
The side which starts the experience is labeled at the top “START”, and will be cut using specially created scissors to interact with it. Breaking the start circuit will begin the the output of music. Cutting along the dashed lines, at forks in the path, the player will be given prompts to choose a direction to go down cutting. Certain circuits are cut (turned off) to trigger an effect. Others are created through the act of cutting itself, because the scissors also have conductive tape on them that connects the blade of the scissors to the hand. The body is then used as a connection in itself to the other end of circuit. Therefore, circuits are actively created throughout the piece. Eventually the other end of the sheet is breached (labeled “END”) and this stops music playback.
Connections on scissors labeled below.
Multiple logic operations will be used through combinations of the different circuits. Here is one example of how one could work.