diff --git a/Arduino/SelfomatController/AgreementState.cpp b/Arduino/SelfomatController/AgreementState.cpp index d86a339..3ce8412 100644 --- a/Arduino/SelfomatController/AgreementState.cpp +++ b/Arduino/SelfomatController/AgreementState.cpp @@ -9,7 +9,7 @@ bool AgreementState::processCommand(const uint8_t* buffer, size_t size) { sendCommand('A'); return true; } - + // Process base commands. if(BaseState::processCommand(buffer, size)) return true; @@ -31,11 +31,11 @@ void AgreementState::animationStep(unsigned long dt) { } } - + float angleOffset = timeInState()/300.0f; float anglePerLed = (2.0 * PI) / ring.numPixels(); for(int j = 0; j < ring.numPixels(); j++) { - float angle = (float)j * anglePerLed + angleOffset; + float angle = (float)j * anglePerLed + angleOffset; float sin_x = sin(angle); uint8_t brightness = sin_x*sin_x * 255.0f * b; ring.setPixelColor(j, 0, brightness, 0); @@ -48,9 +48,10 @@ BaseState* AgreementState::logicStep() { shouldExit = true; sendCommand('c'); } - + if(shouldExit && exitAnimationDone) { sendCommand('a'); + logger.println( F("exit to -> IdleState") ); return &IdleState::INSTANCE; } return this; @@ -58,6 +59,7 @@ BaseState* AgreementState::logicStep() { void AgreementState::enter() { BaseState::enter(); + logger.println( F("Entering AgreementState") ); b = 0; frame = 0; exitAnimationDone = shouldExit = false; @@ -65,7 +67,7 @@ void AgreementState::enter() { } void AgreementState::exit() { - + logger.println( F("Leaving AgreementState") ); } bool AgreementState::needsHeartbeat() { diff --git a/Arduino/SelfomatController/BootingState.cpp b/Arduino/SelfomatController/BootingState.cpp index f1ccedb..499e7a2 100644 --- a/Arduino/SelfomatController/BootingState.cpp +++ b/Arduino/SelfomatController/BootingState.cpp @@ -45,6 +45,7 @@ BaseState* BootingState::logicStep() { return parentStep; if(shouldExit && exitAnimationDone) { + logger.println( F("exit to -> IdleState") ); return &IdleState::INSTANCE; } @@ -54,12 +55,14 @@ BaseState* BootingState::logicStep() { void BootingState::enter() { frame = 0; exitAnimationDone = shouldExit = false; + logger.println( F("Entering BootingState") ); } void BootingState::exit() { // Booting done, we need a heartbeat. Assume we got it now heartbeatDeactivated = false; - lastHeartbeat = millis(); + lastHeartbeat = millis(); + logger.println( F("Leaving BootingState") ); } bool BootingState::needsHeartbeat() { diff --git a/Arduino/SelfomatController/BusyState.cpp b/Arduino/SelfomatController/BusyState.cpp index af13901..0e0e97f 100644 --- a/Arduino/SelfomatController/BusyState.cpp +++ b/Arduino/SelfomatController/BusyState.cpp @@ -33,18 +33,20 @@ BaseState* BusyState::logicStep() { if(flashTriggered) { if(settings.flashDurationMicros < 0) { delay(10); - } else { + } else { while(micros() - flashStartMicros < settings.flashDurationMicros); } digitalWrite(PIN_FLASH_ON, LOW); flashTriggered = false; } - + // timeout in busy state if(timeInState() > 15000 || exitIdle) { + logger.println( F("exit to -> IdleState") ); return &IdleState::INSTANCE; } if(exitPrint) { + logger.println( F("exit to -> PrintingState") ); return &PrintingState::INSTANCE; } return this; @@ -52,6 +54,7 @@ BaseState* BusyState::logicStep() { void BusyState::enter() { BaseState::enter(); + logger.println( F("Entering BusyState") ); flashTriggered = exitPrint = exitIdle = false; attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(PIN_FLASH_CAM_TRIGGER), triggerFlash, FALLING); } @@ -61,6 +64,7 @@ void BusyState::exit() { detachPinChangeInterrupt(digitalPinToPinChangeInterrupt(PIN_FLASH_CAM_TRIGGER)); // then turn off the light for safety digitalWrite(PIN_FLASH_ON, LOW); + logger.println( F("Leaving BusyState") ); } bool BusyState::needsHeartbeat() { diff --git a/Arduino/SelfomatController/CountDownState.cpp b/Arduino/SelfomatController/CountDownState.cpp index e511dc5..092e977 100644 --- a/Arduino/SelfomatController/CountDownState.cpp +++ b/Arduino/SelfomatController/CountDownState.cpp @@ -11,9 +11,9 @@ bool CountDownState::processCommand(const uint8_t* buffer, size_t size) { void CountDownState::animationStep(unsigned long dt) { uint32_t color = colors[animationCycle]; float percentage = ((float)timeInState() - animationCycle * oneCycleMillis) / oneCycleMillis; - + float pixelBorder = ring.numPixels() - ring.numPixels() * percentage; - + for(int8_t i=0; i pixelBorder) { @@ -37,6 +37,7 @@ BaseState* CountDownState::logicStep() { if(timeInState() > settings.countDownMillis) { // Trigger the capture and go to idle sendCommand('t'); + //logger.println("Cntdn expired, triggered -> BusyState"); return &BusyState::INSTANCE; } return this; @@ -44,16 +45,18 @@ BaseState* CountDownState::logicStep() { void CountDownState::enter() { BaseState::enter(); + logger.println( F("Entering CountDownState") ); oneCycleMillis = (settings.countDownMillis)/3.0f; animationCycle = 0; lastColor = 0; } void CountDownState::exit() { - for(int8_t i=0; i> 2); + ring.setPixelColor(i, 0, g, g >> 2); } ring.show(); } @@ -36,12 +36,13 @@ BaseState* IdleState::logicStep() { void IdleState::enter() { BaseState::enter(); + logger.println( F("Entering IdleState") ); externalTrigger = false; green = 0; } void IdleState::exit() { - + logger.println( F("Leaving IdleState") ); } bool IdleState::needsHeartbeat() { diff --git a/Arduino/SelfomatController/Logging.cpp b/Arduino/SelfomatController/Logging.cpp new file mode 100644 index 0000000..2004411 --- /dev/null +++ b/Arduino/SelfomatController/Logging.cpp @@ -0,0 +1,30 @@ +#include "Logging.h" + +#ifdef LOGGING_ACTIVE + #include + #ifndef NEOSWSERIAL_EXTERNAL_PCINT + #error NEOSWSERIAL_EXTERNAL_PCINT needs to be defined inside of NeoSWSerial.h or as compile flag. + #endif +#endif + +Logging::Logging(uint8_t transmitPin) { +#ifdef LOGGING_ACTIVE + /* using the same pin for RX and TX seems to work; we won't RX anyways */ + pSwSerial = new NeoSWSerial(transmitPin, transmitPin); +#endif +} + +void Logging::begin(uint16_t baudRate) { +#ifdef LOGGING_ACTIVE + ((NeoSWSerial*)pSwSerial)->begin(baudRate); + ((NeoSWSerial*)pSwSerial)->ignore(); // do not use RX +#endif +} + +size_t Logging::write(uint8_t txChar) { +#ifdef LOGGING_ACTIVE + return ((NeoSWSerial*)pSwSerial)->write(txChar); +#else + return 0; +#endif +} diff --git a/Arduino/SelfomatController/Logging.h b/Arduino/SelfomatController/Logging.h new file mode 100644 index 0000000..c0b1af2 --- /dev/null +++ b/Arduino/SelfomatController/Logging.h @@ -0,0 +1,20 @@ +#ifndef LOGGING_H +#define LOGGING_H + +#include "Print.h" + +//#define LOGGING_ACTIVE /* Comment out when logging is unwanted; could also overwrite class! */ + +class Logging : public Print { +public: + Logging(uint8_t transmitPin); + + void begin(uint16_t baudRate=38400); + + size_t write(uint8_t txChar); + +private: + void* pSwSerial; +}; + +#endif diff --git a/Arduino/SelfomatController/OffState.cpp b/Arduino/SelfomatController/OffState.cpp index f1338e7..65984c6 100644 --- a/Arduino/SelfomatController/OffState.cpp +++ b/Arduino/SelfomatController/OffState.cpp @@ -18,6 +18,7 @@ BaseState* OffState::logicStep() { if (digitalRead(PIN_SWITCH) == LOW) { // The switch is on and we're allowed to exit the off state->do it + logger.println( F("HW switch LOW -> booting") ); return &BootingState::INSTANCE; } return this; @@ -25,10 +26,12 @@ BaseState* OffState::logicStep() { void OffState::enter() { BaseState::enter(); - + logger.println( F("Entering OffState") ); + // read settings + logger.println( F("Read settings on entry") ); readSettings(); - + allowExitState = false; @@ -58,6 +61,7 @@ void OffState::exit() { ring.show(); blink(2); + logger.println( F("Leaving OffState") ); } bool OffState::needsHeartbeat() { diff --git a/Arduino/SelfomatController/PrintingState.cpp b/Arduino/SelfomatController/PrintingState.cpp index d57ce70..6d9e84e 100644 --- a/Arduino/SelfomatController/PrintingState.cpp +++ b/Arduino/SelfomatController/PrintingState.cpp @@ -27,11 +27,11 @@ void PrintingState::animationStep(unsigned long dt) { } } - + float angleOffset = timeInState()/300.0f; float anglePerLed = (2.0 * PI) / ring.numPixels(); for(int j = 0; j < ring.numPixels(); j++) { - float angle = (float)j * anglePerLed + angleOffset; + float angle = (float)j * anglePerLed + angleOffset; float sin_x = sin(angle); uint8_t brightness = sin_x*sin_x * 255.0f * b; ring.setPixelColor(j, 0, brightness, brightness >> 2); @@ -48,7 +48,7 @@ BaseState* PrintingState::logicStep() { cancelSent = true; sendCommand('c'); } - + if(timeInState() > 30000 || (shouldExit && exitAnimationDone)) { return &IdleState::INSTANCE; } @@ -57,6 +57,7 @@ BaseState* PrintingState::logicStep() { void PrintingState::enter() { BaseState::enter(); + logger.println( F("Entering PrintingState") ); cancelSent = false; b = 0; frame = 0; @@ -64,7 +65,7 @@ void PrintingState::enter() { } void PrintingState::exit() { - + logger.println( F("Leaving PrintingState") ); } bool PrintingState::needsHeartbeat() { diff --git a/Arduino/SelfomatController/SelfomatController.ino b/Arduino/SelfomatController/SelfomatController.ino index a51e350..59a70ce 100644 --- a/Arduino/SelfomatController/SelfomatController.ino +++ b/Arduino/SelfomatController/SelfomatController.ino @@ -12,6 +12,7 @@ #include "OffState.h" #include "IdleState.h" #include "BusyState.h" +#include "Logging.h" /* @@ -23,6 +24,7 @@ unsigned long lastAnimationStep = 0; void goToState(BaseState *nextState) { if(nextState == currentState) return; + logger.println( F("Switch state") ); if(currentState) { currentState->exit(); } @@ -38,19 +40,22 @@ void onPacketReceived(const uint8_t* buffer, size_t size) { if(size == 1) { switch(buffer[0]) { case '0': { + logger.print( F("Rx'ed reboot request") ); typedef void (*do_reboot_t)(void); const do_reboot_t do_reboot = (do_reboot_t)((FLASHEND - 511) >> 1); cli(); MCUSR = TCCR0A = TCCR1A = TCCR2A = 0; // make sure interrupts are off and timers are reset. - do_reboot(); + do_reboot(); } return; case 'i': { + logger.print( F("Rx'ed id request") ); // Identify yourself Serial.write('b'); - Serial.flush(); + Serial.flush(); } return; case '.': { + logger.print( F("Rx'ed heartbeat") ); // Reset our software watchdog lastHeartbeat = millis(); // no return here, we want it to propagate to the other states @@ -72,13 +77,13 @@ void setup() { MCUSR = 0; wdt_disable(); + logger.begin(38400); + // Setup Pins pinMode(PIN_STATUS, OUTPUT); digitalWrite(PIN_STATUS, LOW); - - - pinMode(PIN_BUTTON, INPUT); + pinMode(PIN_BUTTON, INPUT); digitalWrite(PIN_BUTTON, HIGH); pinMode(PIN_SWITCH, INPUT); digitalWrite(PIN_SWITCH, HIGH); @@ -90,32 +95,38 @@ void setup() { // flash off by default digitalWrite(PIN_FLASH_ON, LOW); - pinMode(PIN_ON, OUTPUT); + pinMode(PIN_ON, OUTPUT); pinMode(PIN_LEVEL_SHIFTER_OE, OUTPUT); + logger.println( F("Pin setup complete") ); packetSerial.begin(38400); packetSerial.setPacketHandler(&onPacketReceived); + logger.println( F("Paket handler setup complete") ); + // If the switch is on, turn on instantly BUT ONLY THIS FIRST TIME if(digitalRead(PIN_SWITCH) == LOW) { + logger.println( F("HW SW LOW -> booting") ); // We want to call exit function but not enter function currentState = &OffState::INSTANCE; goToState(&BootingState::INSTANCE); } else { + logger.println( F("HW SW HIGH -> off") ); goToState(&OffState::INSTANCE); } } -void loop() { +void loop() { bool checkHeartbeat = currentState == 0 || currentState->needsHeartbeat(); - + if(checkHeartbeat && millis() - lastHeartbeat > 15000) { + logger.println( F("No heartbeat received -> off") ); // Turn off if heartbeat was not sent goToState(&OffState::INSTANCE); return; } - + if(currentState) { BaseState *nextState = currentState->logicStep(); unsigned long dt = millis() - lastAnimationStep; diff --git a/Arduino/SelfomatController/ShutDownState.cpp b/Arduino/SelfomatController/ShutDownState.cpp index 8fa66a2..e554093 100644 --- a/Arduino/SelfomatController/ShutDownState.cpp +++ b/Arduino/SelfomatController/ShutDownState.cpp @@ -8,7 +8,7 @@ bool ShutDownState::processCommand(const uint8_t* buffer, size_t size) { } void ShutDownState::animationStep(unsigned long dt) { - + // Fade if (shouldExit) { if (b > 0) { @@ -41,6 +41,7 @@ BaseState* ShutDownState::logicStep() { void ShutDownState::enter() { BaseState::enter(); + logger.println( F("Entering ShutDownState") ); writeSettings(); b = 0; frame = 0; @@ -52,7 +53,7 @@ void ShutDownState::enter() { } void ShutDownState::exit() { - + logger.println( F("Leaving ShutDownState") ); } bool ShutDownState::needsHeartbeat() { diff --git a/Arduino/SelfomatController/State.cpp b/Arduino/SelfomatController/State.cpp index 4bbfc76..9439083 100644 --- a/Arduino/SelfomatController/State.cpp +++ b/Arduino/SelfomatController/State.cpp @@ -16,12 +16,13 @@ BaseState* BaseState::logicStep() { forcedNextState = 0; return tmp; } - + // else, we do not care return 0; }; void BaseState::sendCurrentSettings() { + logger.println( F("Sending current settings") ); size_t data_size = sizeof(settings)+1; uint8_t data[data_size]; data[0] = '$'; @@ -33,54 +34,62 @@ void BaseState::sendCurrentSettings() { bool BaseState::processCommand(const uint8_t* buffer, size_t size) { if (size == 0) return false; + logger.println( F("Processing cmd") ); unsigned char c = buffer[0]; switch (c) { case '#': // TODO: reimplement here return true; case 'a': + logger.println( F("Forced switch to AgreementState") ); forcedNextState = &AgreementState::INSTANCE; return true; break; case '$': if(size != sizeof(settings)+1) { + logger.println( F("Invalid settings message") ); // we have received the wrong amount of bytes. Ignore this message and throw an error sendCommand("E1",2); return true; } else { // We know the buffer has the correct size, cast it struct settings *received_settings = (struct settings*)(buffer+1); - + // Check the CRC uint16_t crc = CRC16.ccitt((const uint8_t *) received_settings, sizeof(struct settings)-2); if(crc != received_settings->crcChecksum) { + logger.println( F("Rx'ed settings CRC error") ); // CRC error, notify receiver and ignore sendCommand("E2", 2); return true; } - + // check if settings are valid if(!checkSettings(received_settings)) { + logger.println( F("Rx'ed settings invalid") ); sendCommand("E3", 2); return true; } - + // CRC is OK. Set dirty by comparing the new settings to the old ones and overwrite the settings if changed. if(0 == memcmp(&settings, received_settings, sizeof(struct settings))) { // Settings are the same, don't do anything. + logger.println( F("Rx'ed settings are not new") ); } else { // We have new settings. Set the dirty flag and overwrite settings + logger.println( F("Rx'ed settings are new") ); settingsDirty = true; settings = *received_settings; updateSettingDependencies(); } - + // Send a k to notify setting reception success sendCommand('k'); } return true; break; case 'f': + logger.println( F("Forced switch to FlashingState") ); forcedNextState = &FlashingState::INSTANCE; sendCommand('F'); return true; @@ -89,12 +98,15 @@ bool BaseState::processCommand(const uint8_t* buffer, size_t size) { if(size == 2) { heartbeatDeactivated = buffer[1] == 0; lastHeartbeat = millis(); + logger.println( F("Set heartbeat state") ); } break; case '?': + logger.println( F("Rx'ed request for current settings") ); sendCurrentSettings(); break; case '<': + logger.println( F("Shift LED offset -") ); if(settings.ledOffset == 0) { settings.ledOffset = settings.ledCount; } else { @@ -106,6 +118,7 @@ bool BaseState::processCommand(const uint8_t* buffer, size_t size) { showCenterLed(400); break; case '>': + logger.println( F("Shift LED offset +") ); settings.ledOffset = (settings.ledOffset+1)%settings.ledCount; settings.crcChecksum = CRC16.ccitt((const uint8_t *) &settings, sizeof(settings)-2); settingsDirty = true; @@ -128,6 +141,7 @@ void BaseState::blink(uint8_t count) { } void BaseState::readSettings() { + logger.println( F("Reading settings") ); EEPROM.get(0, settings); settingsDirty = false; @@ -135,11 +149,13 @@ void BaseState::readSettings() { uint16_t crc = CRC16.ccitt((const uint8_t *) &settings, sizeof(settings) - 2); if (crc == settings.crcChecksum) { + logger.println( F("Settings CRC valid -> apply") ); updateSettingDependencies(); return; } // load defaults + logger.println( F("Settings CRC invalid -> apply defaults") ); settings = default_settings; // calculate crc for default settings settings.crcChecksum = CRC16.ccitt((const uint8_t *) &settings, sizeof(settings) - 2); @@ -148,8 +164,12 @@ void BaseState::readSettings() { void BaseState::writeSettings() { if(!settingsDirty) + { + logger.println( F("Settings not dirty, skip write") ); return; - + } + + logger.println( F("Settings dirty, calc CRC & write") ); settings.crcChecksum = CRC16.ccitt((const uint8_t *) &settings, sizeof(settings)-2); EEPROM.put(0, settings); settingsDirty = false; @@ -175,6 +195,8 @@ void BaseState::sendCommand(const char command) { } void BaseState::sendCommand(const char *command, uint8_t length) { + logger.print( F("Sending cmd with len ") ); + logger.println(length); packetSerial.send((const uint8_t*)command, length); Serial.flush(); } @@ -204,7 +226,7 @@ void BaseState::showCenterLed(uint16_t duration) { } } ring.show(); - delay(duration); + delay(duration); for(int8_t i=0; i #include "FastCRC.h" #include "PacketSerial.h" @@ -8,21 +9,22 @@ /* * PIN Defines */ -#define PIN_LED 4 -#define PIN_LED_OFF 9 - -#define PIN_STATUS 20 +#define PIN_LED (6) //(4) TODO/FIXME: change back to 4 at least during review as this has been adapter for some special hardware +#define PIN_LED_OFF (9) +#define PIN_STATUS (20) // DO NOT CHANGE!!! WE HAVE DIRECT HARDWARE ACCESS ON THIS PIN!!!! -#define PIN_FLASH_ON 11 -#define PIN_FLASH_CAM_TRIGGER 7 +#define PIN_FLASH_ON (11) +#define PIN_FLASH_CAM_TRIGGER (7) + +#define PIN_BUTTON (2) +#define PIN_SWITCH (3) +#define PIN_ON (8) +#define PIN_LEVEL_SHIFTER_OE (19) -#define PIN_BUTTON 2 -#define PIN_SWITCH 3 -#define PIN_ON 8 -#define PIN_LEVEL_SHIFTER_OE 19 +#define PIN_SWSERIAL_LOGGING_TX (13) -#define PIXEL_TYPES 2 +#define PIXEL_TYPES (2) extern neoPixelType supportedPixels[PIXEL_TYPES]; @@ -54,6 +56,7 @@ extern bool settingsDirty; extern Adafruit_NeoPixel ring; extern FastCRC16 CRC16; extern COBSSpacePacketSerial packetSerial; +extern Logging logger; extern unsigned long lastHeartbeat; extern bool heartbeatDeactivated;