diff --git a/README.md b/README.md index f79bf41..c229ee5 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ Installation The Clyde firmware is divided into three parts: bootloader, library, and sketch. -You can find the Clyde library in /software/arduino/libraries/. Place the "clyde" library folder inside your Arduino "librairies" folder. This contains the majority of the code that controls Clyde. If you are not sure about how to install an Arduino library, you can find instructions here: http://arduino.cc/en/Guide/Libraries +You can find the Clyde library in /software/arduino/libraries/. Place the "clyde" library folder inside your Arduino "libraries" folder. This contains the majority of the code that controls Clyde. If you are not sure about how to install an Arduino library, you can find instructions here: http://arduino.cc/en/Guide/Libraries When the library is in place, you can look for the sketch in /software/arduino/firmwares/. Place the "ClydeFirmware" sketch folder inside your Arduino "sketches" folder. This sketch does not contain much, but it is a good place to start playing with Clyde's behaviors. -Before you open your Arduino IDE, you'll need to copy one more folder. This step will add Clyde as a device under the "Tools > Board menu". First, if you do not have a "hardware" folder at the same level as your Arduino "librairies" folder, time to create one. Browse to the "hardware" folder in /software/arduino/hardware/ and open the subfolder that matches the version of your Arduino IDE, 1.0.5 or 1.5. Inside you'll find a "fabule" folder. Copy the "fabule" folder, inside the Arduino "hardware" folder you just created. +Before you open your Arduino IDE, you'll need to copy one more folder. This step will add Clyde as a device under the "Tools > Board menu". First, if you do not have a "hardware" folder at the same level as your Arduino "libraries" folder, time to create one. Browse to the "hardware" folder in /software/arduino/hardware/ and open the subfolder that matches the version of your Arduino IDE, 1.0.5 or 1.5. Inside you'll find a "fabule" folder. Copy the "fabule" folder, inside the Arduino "hardware" folder you just created. At this point, you should be ready to compile and upload Clyde's firmware. Connect Clyde to your computer using the USB cable. If you are using Windows, you'll need to point to Clyde's driver found in /software/arduino/drivers/. Open the "ClydeFirmware" sketch in your Arduino IDE. Select Clyde under the "Tools > Board" menu, and the port assigned to Clyde under "Tools > Serial" Port. Press "Upload", and the firmware should compile and upload into Clyde. diff --git a/software/arduino/firmwares/ClydeFirmware/ClydeFirmware.ino b/software/arduino/firmwares/ClydeFirmware/ClydeFirmware.ino index 1eb9a76..f44d2dd 100644 --- a/software/arduino/firmwares/ClydeFirmware/ClydeFirmware.ino +++ b/software/arduino/firmwares/ClydeFirmware/ClydeFirmware.ino @@ -1,18 +1,70 @@ +/* + Copyright (c) 2013-2014, Fabule Fabrications Inc, All rights reserved. + + This is the main Clyde firmware that controls the user interface. + + + Function mapping and descriptions by N. Seidle July 12, 2014 + + Clyde.updateEye: Calibrate the eye and check for press + Clyde.updateMouth: update the mouth to play sounds + + Clyde.setAmbient(RGB(r, g, b)): Sets the ambient color of Clyde's eye without crossfade. 0 is off. + Clyde.setWhite(w): Sets the white level without crossfade (do it now). 0 is full blast, 250 is super soft. 255 is off. + + Clyde.fadeAmbient(RGB(r, g, b), speed): Set a color to go to over time. Speed is a float. 1.0 is very fast. 0.03 is nice and slow. + + Clyde.updateAmbientLight: update the lights + Clyde.updateWhiteLight: Slowly fade from the .targetBrightness to .brightness. Do so at the .fadeSpeed. + + Clyde.wasEyePressed: returns + + //make Clyde behave after the eye was calibrated once + if (Clyde.wasEyeCalibratedOnce()) + + New behavior: + RGB slowly moves through different colors. + + When I press the button, light comes on. Press again, light off. + Press and hold, the lamp gets more bright until it wraps to very dim. + + */ + #include #include #include -#include -#include -#include +#include //Library to tokenize and parse space separated commands receieved over serial +#include //Library to give any pin the ability to send or receive serial +#include //Library handles the capacitive touch input #define FIRMWARE_VERSION 1 SerialCommand sCmd; +int currentLevel = 255; //Used to keep track of the brightness of the desk light. 255 is off. 0 is 100% on. + +long lastChange = 0; //Used to track of the last time we had a color change + void setup() { - Wire.begin(); - + + Clyde.begin(); //Setup all the analog inputs and output pins. + + //Fade white reading light from bright to off + Clyde.setWhite(0); //analogWrite(m_white.pin, 0); + delay(500); + for(int x = 0 ; x < 256 ; x++) + { + Clyde.setWhite(x); + delay(4); + } + + randomSeed(analogRead(A5)); //Seed the random generator with noise + + Wire.begin(); //I2C being, used for cap sense + Serial.begin(9600); + + //?: Create incoming commands that will be recognized by user's sending serial to Clyde sCmd.addCommand("SERIAL", cmdSerial); sCmd.addCommand("VERSION", cmdVersion); sCmd.addCommand("RESET", cmdReset); @@ -20,36 +72,77 @@ void setup() { sCmd.addCommand("SET_WHITE", cmdSetWhite); sCmd.addCommand("WRITE_EEPROM", cmdWriteEEPROM); sCmd.addCommand("READ_EEPROM", cmdReadEEPROM); - + //Clyde.eeprom()->reset(); - Clyde.begin(); + + Clyde.updateEye(); //Calibrate the eye + } void loop() { - //read the serial communication if any - sCmd.readSerial(); - - //calibrate the eye and check for press - Clyde.updateEye(); - - //update the mouth to play sounds - Clyde.updateMouth(); - - //update the lights - Clyde.updateAmbientLight(); - Clyde.updateWhiteLight(); - - //make Clyde behave after the eye was calibrated once - if (Clyde.wasEyeCalibratedOnce()) - Clyde.updatePersonalities(); + //sCmd.readSerial(); //read the serial communication if any + + //Check to see if eye is pressed + if(Clyde.checkEye()) + { + //Toggle white light on each press + if(currentLevel == 255) + { + //Turn on + for(int x = 0 ; x < 256 ; x++) + { + Clyde.setWhite(255 - x); + delay(4); + } + + currentLevel = 0; //Remember that we are now on + } + else + { + //Turn off + for(int x = 0 ; x < 256 ; x++) + { + Clyde.setWhite(x); + delay(4); + } + + currentLevel = 255; //Remember that we are now off + } + } + + /*if(Clyde.pressedLength() > 1000) + { + Serial.println("Long press"); + }*/ + + //Change eye color every few seconds + if(millis() - lastChange > 5000) + { + int r = random(0, 256); //0 to 255 + int g = random(0, 256); //0 to 255 + int b = random(0, 256); //0 to 255 + + //Push more values to off, 25% chance + if(random(100) < 25) r = 0; //Turn red off + if(random(100) < 25) g = 0; //Turn green off + if(random(100) < 25) b = 0; //Turn blue off + + Clyde.fadeAmbient(RGB(r, g, b), 0.02); //Sets the ambient color of Clyde's eye without crossfade. 0 is off. + + lastChange = millis(); + } + + Clyde.updateAmbientLight(); //Fade to a new color if there is one to go to } // // Serial Commands // +//Respond with the serial number associated with Clyde? void cmdSerial() { - char serial[7] = {0}; + char serial[7] = { + 0 }; Clyde.eeprom()->readSerial(&serial[0]); Serial.print("OK "); Serial.println(serial); @@ -61,15 +154,17 @@ void cmdVersion() { Serial.println(vers); } +//?: Erases all of Clyde's settings void cmdReset() { Clyde.eeprom()->reset(); Serial.println("OK"); } +//Use the three incoming characters (RGB) and set Clydes ambient light to these values void cmdSetAmbient() { char *param1, *param2, *param3; int r, g, b; - + //Get arguments param1 = sCmd.next(); // Red param2 = sCmd.next(); // Green @@ -77,7 +172,7 @@ void cmdSetAmbient() { r = atoi(param1); g = atoi(param2); b = atoi(param3); - + Clyde.setAmbient(RGB(r, g, b)); Serial.println("OK"); } @@ -85,41 +180,47 @@ void cmdSetAmbient() { void cmdSetWhite() { char *param1; int w; - + //Get arguments param1 = sCmd.next(); // Brightness w = atoi(param1); - + if (w > 255) w = 255; w = 255 - w; - + Clyde.setWhite(w); Serial.println("OK"); } +//Receives an address and a value over serial +//Then records those values to EEPROM void cmdWriteEEPROM() { char *param1, *param2; int addr; byte value; - + //Get arguments param1 = sCmd.next(); // Address param2 = sCmd.next(); // Value addr = atoi(param1); value = atoi(param2); - + EEPROM.write(addr, value); Serial.println("OK"); } +//Receives an address over serial +//Reports the EEPROM value at that address void cmdReadEEPROM() { char *param1; int addr; - + //Get arguments param1 = sCmd.next(); // Address addr = atoi(param1); - + Serial.print("OK "); Serial.println(EEPROM.read(addr)); } + + diff --git a/software/arduino/libraries/clyde/Clyde.cpp b/software/arduino/libraries/clyde/Clyde.cpp index 563399e..ee4bf85 100644 --- a/software/arduino/libraries/clyde/Clyde.cpp +++ b/software/arduino/libraries/clyde/Clyde.cpp @@ -62,7 +62,8 @@ CClyde::CClyde() { //but this adds a flash on startup instead, which is kinda neat //TODO look for a better solution m_white.pin = 11; - setWhite(254); +// setWhite(254); + setWhite(255); //init eye m_eye.pin = 0; @@ -123,7 +124,7 @@ void CClyde::begin() { //setup white light pins pinMode(m_white.pin, OUTPUT); analogWrite(m_white.pin, m_white.brightness); - + //setup mouth / mp3 shield m_mouth.mp3.begin(9600); @@ -136,7 +137,7 @@ void CClyde::begin() { //load parameters from eeprom m_eeprom.readAmbientColor(&m_ambient.savedColor); - + //detect the personality modules detectPersonalities(); @@ -215,11 +216,37 @@ void CClyde::detectMouth() { m_mouth.waitingOpCode = Clyde.setPlayMode(PLAYMODE_SINGLE_CYCLE); } +//Checks to see if eye was pressed +//Returns true if it is +bool CClyde::checkEye() { + //read IR value + uint16_t irValue = analogRead(m_eye.pin); + + //calibrate the eye's IR sensor + calibrateEye(irValue); + + if (wasEyePressed(irValue)) + return(true); + else + return(false); +} + +//Reports the number of milliseconds the eye has been pressed +int CClyde::pressedLength() { + + //If the eye press has reached threshold then we know that this is a valid button press + if (m_eye.pressedCount == CEye::PRESS_COUNT_THRESHOLD) + return(millis() - m_eye.pressedStart); + else + return(0); +} + + void CClyde::updateEye() { //read IR value uint16_t irValue = analogRead(m_eye.pin); - //calibrated the eye's IR sensor + //calibrate the eye's IR sensor calibrateEye(irValue); //if the eye was pressed @@ -358,11 +385,12 @@ bool CClyde::wasEyePressed(uint16_t irValue) { //Serial.println(irValue); //#endif - //if the eye press is detected enough time, trigger press event + //if the eye press is detected enough times, trigger press event if (m_eye.pressedCount == CEye::PRESS_COUNT_THRESHOLD) { //and we detect that's it's still pressed, //then keep track of the last time is was detected - if (irValue >= m_eye.irThreshold) { + + if (irValue >= m_eye.irThreshold) { m_eye.pressedLast = millis(); //if the eye has been pressed for a some time, auto release if (millis() > m_eye.pressedStart+3000) { @@ -389,7 +417,9 @@ bool CClyde::wasEyePressed(uint16_t irValue) { else if (irValue >= m_eye.irThreshold) { m_eye.pressedCount++; m_eye.pressedLast = millis(); - if (m_eye.pressedCount == CEye::PRESS_COUNT_THRESHOLD) { + + //If the eye has been pressed for enough counts then report a positive press event + if (m_eye.pressedCount == CEye::PRESS_COUNT_THRESHOLD) { m_eye.pressedStart = m_eye.pressedLast; return true; } @@ -548,6 +578,8 @@ void CClyde::showAmbientLight() { analogWrite(m_ambient.b_pin, (uint8_t)(m_ambient.color.b * CAmbientLight::SCALE_CONSTRAINT)); } +//Slowly fade from the .targetBrightness to .brightness +//Do so at the .fadeSpeed void CClyde::updateWhiteLight() { //only fade if we haven't reached the desired level if (m_white.targetBrightness == m_white.brightness) @@ -564,6 +596,7 @@ void CClyde::updateWhiteLight() { showWhiteLight(); } +//Takes the value stored in .brightness and posts it to the analog pin void CClyde::showWhiteLight() { analogWrite(m_white.pin, m_white.brightness); } @@ -581,6 +614,8 @@ void CClyde::updatePersonalities() { } } +//Move from one color to the next at a speed. +//A speed of 1 is very fast. 0.01 is very gradual over a few seconds void CClyde::fadeAmbient(const RGB &c, float spd) { m_ambient.targetColor = c; @@ -597,6 +632,7 @@ void CClyde::fadeAmbient(const RGB &c, float spd) { if (m_ambient.fadeSpeed.b < 0) m_ambient.fadeSpeed.b *= -1; } + void CClyde::setWhite(uint8_t b) { m_white.brightness = m_white.targetBrightness = b; showWhiteLight(); @@ -609,6 +645,8 @@ void CClyde::fadeWhite(uint8_t b, float spd) { if (m_white.fadeSpeed < 0) m_white.fadeSpeed *= -1; } +//Called when eye is pressed +//Changes lights from ? to ? based on touch void CClyde::switchLights() { if (!m_white.isOn() && m_ambient.isOn()) { diff --git a/software/arduino/libraries/clyde/Clyde.h b/software/arduino/libraries/clyde/Clyde.h index 16c9d40..464e138 100644 --- a/software/arduino/libraries/clyde/Clyde.h +++ b/software/arduino/libraries/clyde/Clyde.h @@ -287,6 +287,16 @@ class CClyde { */ CAmbientLight* ambient() { return &m_ambient; } + /** + * Read the switch state. Returns true is eye is pressed. + */ + bool checkEye(); + + /** + * Returns the amount of time in ms the switch has been pushed. + */ + int pressedLength(); + /** * Set ambient color. */