diff --git a/coffee.wav b/coffee.wav new file mode 100644 index 0000000..fc8b8dc Binary files /dev/null and b/coffee.wav differ diff --git a/die Geräusche der Kaffeemaschine.aac b/die Geräusche der Kaffeemaschine.aac new file mode 100644 index 0000000..7fce50c Binary files /dev/null and b/die Geräusche der Kaffeemaschine.aac differ diff --git a/firmware/button.h b/firmware/button.h new file mode 100644 index 0000000..462f82c --- /dev/null +++ b/firmware/button.h @@ -0,0 +1,64 @@ +#include "debounce.h" + + +enum class ButtonEvent : uint8_t { + NoEvent, + Press, + LongPress, + DoublePress, +}; + +// template wird komplett wegoptimiert (wie #define) +template +class Button { + public: + enum class State : uint8_t { + Idle, + Pressed, + LongPressed, + Released, + }; + + + ButtonEvent event(RawButtonEvent result) { + switch(state) { + case State::Idle : + if(result == RawButtonEvent::Pressed) { + state = State::Pressed; + counter = millis(); + } + break; + case State::Pressed : + if(result == RawButtonEvent::Released) { + state = State::Released; + counter = millis(); + } else if((uint16_t) millis() - counter >= LONGPRESS_TIMEOUT) { + state = State::LongPressed; + return ButtonEvent::LongPress; + } + break; + case State::Released : + if(result == RawButtonEvent::Pressed) { + state= State::Idle; + return ButtonEvent::DoublePress; + } else if((uint16_t) millis() - counter >= DOUBLEPRESS_TIMEOUT) { + state = State::Idle; + return ButtonEvent::Press; + } + break; + case State::LongPressed : + if(result == RawButtonEvent::Released) { + state = State::Idle; + } else { + return ButtonEvent::LongPress; + } + break; + } + + return ButtonEvent::NoEvent; + } + + private: + State state = State::Idle; + uint16_t counter = 0; +}; diff --git a/firmware/debounce.h b/firmware/debounce.h new file mode 100644 index 0000000..080100c --- /dev/null +++ b/firmware/debounce.h @@ -0,0 +1,58 @@ +#ifndef _DEBOUNCE_H_ +#define _DEBOUNCE_H_ + + +enum class RawButtonEvent : uint8_t { + NoEvent, + Pressed, + Released, +}; + +template +class Debounce { + public: + enum class State : uint8_t { + Unpressed, + UnpressedDebounced, + Pressed, + PressedDebounced, + }; + + + RawButtonEvent event(bool pressed) { + switch(state) { + case State::Unpressed : + if(pressed) { + state = State::PressedDebounced; + counter = millis(); + return RawButtonEvent::Pressed; + } + break; + case State::PressedDebounced : + if((uint16_t) millis() - counter > DEBOUNCE_PRESSED) { + state = State::Pressed; + } + break; + case State::Pressed : + if(!pressed) { + state = State::UnpressedDebounced; + counter = millis(); + return RawButtonEvent::Released; + } + break; + case State::UnpressedDebounced : + if((uint16_t) millis() - counter > DEBOUNCE_UNPRESSED) { + state = State::Unpressed; + } + break; + } + + return RawButtonEvent::NoEvent; + } + + private: + State state = State::Unpressed; + uint16_t counter = 0; +}; + +#endif diff --git a/firmware/firmware.ino b/firmware/firmware.ino index 32e887f..2fc6847 100644 --- a/firmware/firmware.ino +++ b/firmware/firmware.ino @@ -1,65 +1,101 @@ -#include // need to include the SD library -//#define SD_ChipSelectPin 53 //example uses hardware SS pin 53 on Mega2560 -#define SD_ChipSelectPin 5 //using digital pin 4 on arduino nano 328, can use other pins -#include // also need to include this library... +#include +#include #include +#include "button.h" +#include "debounce.h" + +using MainButton = Button<150, 150>; + +#define SD_ChipSelectPin 5 +#define MainButtonPin 6 +#define CoffeeLedPin 7 +#define EegLedPin 8 + +Debounce<20> debounce; +MainButton button; + TMRpcm tmrpcm; // create an object for use in this sketch unsigned long time = 0; void setup(){ + tmrpcm.speakerPin = 9; - tmrpcm.speakerPin = 9; //5,6,11 or 46 on Mega, 9 on Uno, Nano, etc - //Complimentary Output or Dual Speakers: - //pinMode(10,OUTPUT); Pin pairs: 9,10 Mega: 5-2,6-7,11-12,46-45 - - Serial.begin(9600); - //pinMode(13,OUTPUT); //LED Connected to analog pin 0 - if (!SD.begin(SD_ChipSelectPin)) { // see if the card is present and can be initialized: - Serial.println("SD fail"); - return; // don't do anything more if not + Serial.begin(9600); + Serial.println("Startshduhsd"); - } - else{ - Serial.println("SD ok"); - } - tmrpcm.volume(2); - tmrpcm.play("coffee.wav"); //the sound file "music" will play each time the arduino powers up, or is reset -} - - - -void loop(){ - //blink the LED manually to demonstrate music playback is independant of main loop - if(tmrpcm.isPlaying() && millis() - time > 50 ) { - //digitalWrite(13,!digitalRead(13)); - // time = millis(); - }else - if(millis() - time > 500){ - //digitalWrite(13,!digitalRead(13)); - //time = millis(); - } - - - if(Serial.available()){ - switch(Serial.read()){ - case 'c': tmrpcm.play("coffee.wav"); break; - case 'p': tmrpcm.pause(); break; - case '?': if(tmrpcm.isPlaying()){ Serial.println("A wav file is being played");} else{Serial.println("No wav file is being played");} break; - case 'S': tmrpcm.stopPlayback(); break; - case '0': tmrpcm.volume(0); break; - case '1': tmrpcm.volume(1); break; - case '2': tmrpcm.volume(2); break; - case '3': tmrpcm.volume(3); break; - case '4': tmrpcm.volume(4); break; - case '5': tmrpcm.volume(5); break; - case '6': tmrpcm.volume(6); break; - case '7': tmrpcm.volume(7); break; - case '=': tmrpcm.quality(0); break; - case '-': tmrpcm.quality(1); break; - default: break; + if (!SD.begin(SD_ChipSelectPin)) { // see if the card is present and can be initialized: + Serial.println("SD fail"); + return; // don't do anything more if not + } else{ + Serial.println("SD ok"); } - } + pinMode(MainButtonPin, INPUT); + pinMode(CoffeeLedPin, OUTPUT); + pinMode(EegLedPin, OUTPUT); + + digitalWrite(CoffeeLedPin, HIGH); + + tmrpcm.volume(2); +} + +enum class State : uint8_t { + Idle, + MakingCoffee, + Slowing +}; + +auto state = State::Idle; +uint16_t coffeeLedTimeout = millis(); +uint16_t eegLedTimeout = millis(); + +const uint16_t minEegLedDelay = 50; +const uint16_t maxEegLedDelay = 500; +uint16_t eegLedDelay = maxEegLedDelay; +uint16_t eegLedDelayTimeout = millis(); + +void loop() { + bool buttonPressed = digitalRead(MainButtonPin); + auto event = button.event(debounce.event(buttonPressed)); + + if((uint16_t) millis() - eegLedTimeout >= eegLedDelay) { + eegLedTimeout = millis(); + digitalWrite(EegLedPin, !digitalRead(EegLedPin)); + } + + switch(state) { + case State::Idle: + if(event == ButtonEvent::Press || event == ButtonEvent::LongPress) { + tmrpcm.play("coffee.wav"); + state = State::MakingCoffee; + coffeeLedTimeout = millis(); + } + break; + case State::MakingCoffee: + if(!tmrpcm.isPlaying()) { + state = State::Slowing; + eegLedDelay = minEegLedDelay; + digitalWrite(CoffeeLedPin, HIGH); + } else { + if((uint16_t) millis() - coffeeLedTimeout >= 500) { + coffeeLedTimeout = millis(); + digitalWrite(CoffeeLedPin, !digitalRead(CoffeeLedPin)); + } + } + break; + case State::Slowing: + Serial.println(eegLedDelay); + if((uint16_t) millis() - eegLedDelayTimeout >= 300) { + eegLedDelayTimeout = millis(); + if(eegLedDelay > maxEegLedDelay) { + eegLedDelay = maxEegLedDelay; + state = State::Idle; + } else { + eegLedDelay = eegLedDelay * 1.1; + } + } + break; + } }