Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ At this point, you should be ready to compile and upload Clyde's firmware. Conne

If you want to make modifications to the bootloader, please refer to the information found here: https://github.com/fabule/Clyde/tree/master/software/arduino/bootloaders/caterina

Configurations
--------------

### Using `TimeAlarms` library to enable alarms

The `Time` and `TimeAlarms` libraries provide functionality to set/get the time on an arduino and enable repeated or single alarms. An example configuration to use Clyde as an *alarm clock* is given in `/software/arduino/libraries/Clyde/Examples/ClydeTimeAlarm.ino`. This firmware can be uploaded instead of the `ClydeOriginal` firmware above by loading it under "File > Examples > Clyde > ClideTimeAlarms" and following the steps described above to compile and upload the firmware. The essential configuration steps for this alternative firmware are to set the time and to define an alarm. Without any additional hardware, the time has to be set manually in and the firmware has to be uploaded to Clyde each time Clyde is detached from the power source. In principle, the `Time` and `TimeAlarms` would allow to synchronize the arduino time e.g. with internet time over a Wifi shield.

### Using Clyde without the eye

The eye of some Clydes can become quite buggy after some time. Thus, the preprocessor variable `ENABLE_EYE` has been included, that, if not defined allows to switch the lights by touching one of the legs of Clyde (requires the TouchyFeely module). The `ENABLE_EYE` can be set in the `Clyde.h` file, the leg that is to be used as a switch can be defined with the `LEG_SWITCH` variable in `ClydeTouchyFeely.cpp`.

Credits
-------
Clyde is an open source project manage by Fabule Fabrications in Montréal, Québec, Canada.
Expand Down
123 changes: 92 additions & 31 deletions software/arduino/libraries/Clyde/Clyde.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ CClyde Clyde;

const float CClyde::CAmbientLight::SCALE_CONSTRAINT = 225.0f / 255.0f;

#ifdef ENABLE_MOUTH
SoftwareSerial CClyde::CMouth::mp3(CClyde::CMouth::RX_PIN, CClyde::CMouth::TX_PIN);
#endif

CClyde::CClyde() {
//init modules
Expand Down Expand Up @@ -63,7 +65,8 @@ CClyde::CClyde() {
//TODO look for a better solution
m_white.pin = 11;
setWhite(254);


#ifdef ENABLE_EYE
//init eye
m_eye.pin = 0;
m_eye.onceCalibrated = false;
Expand All @@ -84,7 +87,7 @@ CClyde::CClyde() {
#ifdef CLYDE_DEBUG
m_eye.restartCount = 0;
#endif
#endif
//init ambient cycle
m_cycle.type = OFF;
m_cycle.numSteps = 0;
Expand All @@ -96,9 +99,11 @@ CClyde::CClyde() {
memset((void*)&m_cycle.intervals[0], 0, sizeof(uint32_t)*CAmbientCycle::MAX_CYCLE_LENGTH);
m_cycle.loop = NO_LOOP;

#ifdef ENABLE_MOUTH
m_mouth.detected = false;
m_mouth.waitingOpCode = OP_NONE;
m_mouth.lastCmdTime = 0;
#endif
}

void CClyde::begin() {
Expand All @@ -124,6 +129,7 @@ void CClyde::begin() {
pinMode(m_white.pin, OUTPUT);
analogWrite(m_white.pin, m_white.brightness);

#ifdef ENABLE_MOUTH
//setup mouth / mp3 shield
m_mouth.mp3.begin(9600);

Expand All @@ -133,15 +139,18 @@ void CClyde::begin() {
pinMode(CMouth::TX_PIN, OUTPUT);

digitalWrite(CMouth::SELECT_PIN, HIGH);
#endif

//load parameters from eeprom
m_eeprom.readAmbientColor(&m_ambient.savedColor);

//detect the personality modules
detectPersonalities();

#ifdef ENABLE_MOUTH
//detect the loudmouth shield
detectMouth();
#endif

//set default lights off
setAmbient(RGB(0,0,0));
Expand Down Expand Up @@ -212,9 +221,12 @@ void CClyde::detectMouth() {
#ifdef CLYDE_DEBUG
Serial.println("Clyde: Trying to detect Loudmouth. Request set play mode: Single Loop Mode");
#endif
m_mouth.waitingOpCode = Clyde.setPlayMode(PLAYMODE_SINGLE_CYCLE);
#ifdef ENABLE_MOUTH
m_mouth.waitingOpCode = Clyde.setPlayMode(PLAYMODE_SINGLE_CYCLE);
#endif
}

#ifdef ENABLE_EYE
void CClyde::updateEye() {
//read IR value
uint16_t irValue = analogRead(m_eye.pin);
Expand Down Expand Up @@ -293,7 +305,8 @@ void CClyde::calibrateEye(uint16_t irValue) {
if (irAvg < (uint16_t)((CEye::CALIB_FORMULA_B - CEye::CALIB_MIN_THRESHOLD_DIFF) / CEye::CALIB_FORMULA_A)) {
//if the eye was not calibrated, turn on ambient light to show feedback
if (!m_eye.calibrated)
fadeAmbient(m_ambient.savedColor, 0.1f);
//fadeAmbient(m_ambient.savedColor, 0.1f);
fadeAmbient(m_ambient.savedColor, 10);

if (!m_eye.onceCalibrated)
setWhite(255);
Expand Down Expand Up @@ -371,10 +384,10 @@ bool CClyde::wasEyePressed(uint16_t irValue) {
//blink(RGB(255,0,0), 200, 200, 3);
setAmbient(RGB(0, 0, 0));
setWhite(255);

#ifdef ENABLE_MOUTH
setPlayMode(PLAYMODE_SINGLE);
play(SND_ERROR);
#endif
#ifdef CLYDE_DEBUG
Serial.println("Clyde: eye long press detected. auto release.");
#endif
Expand All @@ -401,7 +414,10 @@ bool CClyde::wasEyePressed(uint16_t irValue) {

return false;
}
#endif // the endif for #ifdef ENABLE_EYE


#ifdef ENABLE_MOUTH
void CClyde::updateMouth() {
//detect the loudmouth shield at startup by waiting for mp3 player response
if (!m_mouth.detected) {
Expand Down Expand Up @@ -514,6 +530,7 @@ EOpCode CClyde::stop(void)

return OP_PAUSE;
}
#endif

void CClyde::updateAmbientLight() {
//update ambient cycle
Expand Down Expand Up @@ -581,14 +598,31 @@ void CClyde::updatePersonalities() {
}
}

void CClyde::fadeAmbient(const RGB &c, float spd) {
// void CClyde::fadeAmbient(const RGB &c, float spd) {
// m_ambient.targetColor = c;

// //calculate fade speed for each color
// m_ambient.fadeSpeed = RGBf(
// (m_ambient.targetColor.r - m_ambient.color.r) / 255.0f * spd,
// (m_ambient.targetColor.g - m_ambient.color.g) / 255.0f * spd,
// (m_ambient.targetColor.b - m_ambient.color.b) / 255.0f * spd
// );

// //make sure that fade speeds are positive
// if (m_ambient.fadeSpeed.r < 0) m_ambient.fadeSpeed.r *= -1;
// if (m_ambient.fadeSpeed.g < 0) m_ambient.fadeSpeed.g *= -1;
// if (m_ambient.fadeSpeed.b < 0) m_ambient.fadeSpeed.b *= -1;
// }

// the fadeAmbient version without floats.
void CClyde::fadeAmbient(const RGB &c, uint8_t tm) {
m_ambient.targetColor = c;

//calculate fade speed for each color
m_ambient.fadeSpeed = RGBf(
(m_ambient.targetColor.r - m_ambient.color.r) / 255.0f * spd,
(m_ambient.targetColor.g - m_ambient.color.g) / 255.0f * spd,
(m_ambient.targetColor.b - m_ambient.color.b) / 255.0f * spd
(m_ambient.targetColor.r - m_ambient.color.r) / 255 / tm,
(m_ambient.targetColor.g - m_ambient.color.g) / 255 / tm,
(m_ambient.targetColor.b - m_ambient.color.b) / 255 / tm
);

//make sure that fade speeds are positive
Expand All @@ -602,32 +636,56 @@ void CClyde::setWhite(uint8_t b) {
showWhiteLight();
}

void CClyde::fadeWhite(uint8_t b, float spd) {
// void CClyde::fadeWhite(uint8_t b, float spd) {
// m_white.targetBrightness = b;

// m_white.fadeSpeed = (m_white.targetBrightness - m_white.brightness) / 255.0 * spd;
// if (m_white.fadeSpeed < 0) m_white.fadeSpeed *= -1;
// }

// fadeWhite without a float...
void CClyde::fadeWhite(uint8_t b, uint16_t tm) {
m_white.targetBrightness = b;

m_white.fadeSpeed = (m_white.targetBrightness - m_white.brightness) / 255.0 * spd;
m_white.fadeSpeed = (m_white.targetBrightness - m_white.brightness) / 255 / tm;
if (m_white.fadeSpeed < 0) m_white.fadeSpeed *= -1;
}

void CClyde::switchLights()
{
if (!m_white.isOn() && m_ambient.isOn()) {
fadeWhite(0, 0.1f);
}
else if (m_white.isOn() && m_ambient.isOn()) {
fadeAmbient(RGB(0,0,0), 0.5f);
}
else if (m_white.isOn() && !m_ambient.isOn()) {
fadeWhite(255, 0.3f);
setPlayMode(PLAYMODE_SINGLE);
play(SND_OFF);
}
else if (!m_white.isOn() && !m_ambient.isOn()) {
fadeAmbient(m_ambient.savedColor, 0.1f);
setPlayMode(PLAYMODE_SINGLE);
play(SND_ON);
}

{
#ifdef CLYDE_DEBUG
Serial.println( "Clyde: switchLights" );
#endif
// just making sure to switch off any cycle...
m_cycle.off();

if( m_white.isOn() ){
if( m_ambient.isOn() ){
// save the current ambient light before switching off.
m_ambient.save();
//fadeAmbient(RGB(0,0,0), 0.5f);
fadeAmbient(RGB(0,0,0), 2 );
}else{
// fadeWhite(255, 0.3f);
fadeWhite(255, 3);
#ifdef ENABLE_MOUTH
setPlayMode(PLAYMODE_SINGLE);
play(SND_OFF);
#endif
}
}else{
if( m_ambient.isOn() ){
// fadeWhite(0, 0.1f);
fadeWhite(0, 10);
}else{
//fadeAmbient(m_ambient.savedColor, 0.1f);
fadeAmbient(m_ambient.savedColor, 10 );
#ifdef ENABLE_MOUTH
setPlayMode(PLAYMODE_SINGLE);
play(SND_ON);
#endif
}
}
#ifdef CLYDE_DEBUG
Serial.print("Switched lights: white is ");
Serial.print(m_white.isOn() ? "ON" : "OFF");
Expand Down Expand Up @@ -685,6 +743,7 @@ void CClyde::stopCycle() { //TODO should this be a function pointer set when st
}
}

// just commented this out to save space...
void CClyde::blink(const RGB& rgb, uint32_t onDuration, uint32_t offDuration, uint8_t numBlinks) {
//calculate number of steps needed in the cycle
uint8_t steps = numBlinks*2 + 1;
Expand Down Expand Up @@ -754,7 +813,9 @@ void CClyde::updateCycleNextStep(uint32_t now) {
else {
m_cycle.type = OFF;
setAmbient(m_cycle.colors[m_cycle.numSteps-1]);
#ifdef ENABLE_MOUTH
stop();
#endif
return;
}
}
Expand Down Expand Up @@ -786,4 +847,4 @@ void CClyde::speedUpCycle(uint32_t factor) {

//jump cycle to next color
cycleNextStep(millis());
}
}
Loading