Last active
October 5, 2015 00:00
-
-
Save brian-lc/4c105f4276c1196fc421 to your computer and use it in GitHub Desktop.
Power monitoring and state inspection of Breville tea maker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "EmonLib.h" // Include Emon Library | |
| EnergyMonitor Emon1; // Create an instance | |
| int LedPin = 5; | |
| int BasketDownPin = 2; // Will pulse this pin 10ms when the basket drops | |
| int BasketUpPin = 3; // Will pulse this pin 10ms when the basket raises | |
| double IrmsBase = 0.0; // Baseline current sampled at startup | |
| int BaseLen = 5; | |
| double Baseline[5]; // History for baseline so we update baseline overtime | |
| String States[3] = {"Waiting", "Heating", "Basket"}; | |
| int HistLen = 4; | |
| int StateHist[4] = {0,0,0,0}; // Stores the state history | |
| int TrigerState[3] = {2,2,2}; // When the history array shows Basket Motion (3 events) | |
| boolean Heating = false; // Flag to indicate we have started heating | |
| boolean Basket = false; // Flag to indicate basket in motion | |
| boolean Brewing = false; // Flag to indicate we have started brewing | |
| boolean Done = false; // Flag to indicate we have finished brewing | |
| void setup() | |
| { | |
| //Serial.begin(9600); // Serial for debugging | |
| Emon1.current(1, 13.43); // Current: input pin, manual calibration constant | |
| pinMode(LedPin, OUTPUT); // Calibration process: http://openenergymonitor.org/emon/buildingblocks/calibration | |
| pinMode(BasketDownPin, OUTPUT); | |
| pinMode(BasketUpPin, OUTPUT); | |
| digitalWrite(BasketDownPin, LOW); | |
| digitalWrite(BasketUpPin, LOW); | |
| digitalWrite(LedPin, HIGH); // Baseline capture, power up LED on | |
| double sum = 0.0; | |
| int samples = 10; | |
| for (int i=0; i <= samples; i++){ // Sample Irms and average to create baseline | |
| sum = sum + Emon1.calcIrms(1480); | |
| digitalWrite(LedPin, LOW); | |
| delay(100); | |
| digitalWrite(LedPin, HIGH); | |
| } | |
| double bootBase = sum/samples; // set the baseline Irms for sensing tea maker states | |
| initialBaseline(bootBase); // Baseline capture done | |
| } | |
| void loop() | |
| { | |
| digitalWrite(BasketDownPin, LOW); | |
| digitalWrite(BasketUpPin, LOW); | |
| double irms = Emon1.calcIrms(1480); // Calculate Irms only | |
| int teaStat = senseStatus(irms); | |
| saveStat(teaStat); | |
| if (histCheck(1)){ // if we have applied heat for a little bit then | |
| Heating = true; // We're heating water! | |
| Brewing = false; // Reset all state | |
| Basket = false; | |
| Done = false; | |
| } | |
| if (Heating && !Basket && !Brewing && histCheck(2)){ // if we were heating, and not brewing and the basket is not moving, but now the basket IS moving... | |
| printTrans("DOWN"); // basket going down! | |
| digitalWrite(BasketDownPin, HIGH); | |
| delay(10); | |
| digitalWrite(BasketDownPin, LOW); | |
| Heating = false; //done heating | |
| Brewing = false; //not yet brewing | |
| Basket = true; //basket is moving | |
| } | |
| if (!Heating && !Basket && Brewing && histCheck(2)){ // if we are not heating and not moing the basket and were brewing but now the basket is moving... | |
| printTrans("UP"); // basket coming up! | |
| digitalWrite(BasketUpPin, HIGH); | |
| delay(10); | |
| digitalWrite(BasketUpPin, LOW); | |
| Brewing = false; // Must be done brewing | |
| Basket = true; // basket is moving | |
| Done = true; | |
| } | |
| if (!Done && Basket && histCheck(0)){ // if not done and the basket was in motion and we are waiting now... | |
| Basket = false; // basket is done moving | |
| Brewing = true; // We are brewing!! | |
| } | |
| if (Done && Basket && !Brewing && histCheck(0)){ // if done, the basket was moving, brew is done and we are waiting now... | |
| Basket = false; // All done with the brew cycle | |
| } | |
| // Fairly certain this is causing more problems than it solves | |
| // if (Done && !Heating && !Basket && !Brewing && histCheck(0)){ // To adjust for drift | |
| // updateBaseline(irms); // update our baseline when not in a cycle | |
| // } | |
| updateBrewLight(); | |
| //printStatus(irms, teaStat); | |
| delay(10); //if not printing serial, delay. | |
| } | |
| // Pushes the current tea Status value to the top of the | |
| // StatHist array, shifts all existing values right. | |
| void saveStat(int teaStat){ | |
| int temp[HistLen]; | |
| temp[0] = teaStat; | |
| for(int x=0; x < (HistLen-1); x++){ | |
| temp[x+1] = StateHist[x]; | |
| } | |
| for(int x=0; x < HistLen; x++){ | |
| StateHist[x] = temp[x]; | |
| } | |
| } | |
| // Senses our teamaker state by looking at RMS current. | |
| // Based on what component is on, heater vs motor vs base | |
| // there are different levels of current used. The heater is | |
| // 100x the base current, and the motor is 1.5x the base current. | |
| int senseStatus(double irms){ | |
| String tStat; | |
| if (irms >= (IrmsBase * 100)){ | |
| return 1; // Heating | |
| } | |
| else if (irms >= (IrmsBase * 1.4) && irms < (IrmsBase * 10)){ | |
| return 2; // Basket Motion | |
| } | |
| else { | |
| return 0; // Waiting | |
| } | |
| } | |
| // Look back at the previous status values | |
| // and see if they all match the tStat value | |
| boolean histCheck(int tStat){ | |
| int x = 0; | |
| while (x < HistLen){ | |
| if (tStat == StateHist[x]){ | |
| x++; //move on to check the next | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // Initialize our history to the boot baseline | |
| void initialBaseline(double allBase){ | |
| for (int x=0; x < BaseLen; x++){ | |
| Baseline[x] = allBase; | |
| } | |
| IrmsBase = allBase; | |
| } | |
| // Compute updated average based on new Irms sample | |
| // Store it in our Baseline and update the global | |
| void updateBaseline(double newBase){ | |
| // add to top of array | |
| double temp[BaseLen]; | |
| temp[0] = newBase; | |
| for(int x=0; x < (BaseLen-1); x++){ | |
| temp[x+1] = Baseline[x]; | |
| } | |
| for(int x=0; x < BaseLen; x++){ | |
| Baseline[x] = temp[x]; | |
| } | |
| double sum = 0.0; | |
| for (int x=0; x < BaseLen; x++){ | |
| sum = sum + Baseline[x]; | |
| } | |
| IrmsBase = sum/BaseLen; | |
| } | |
| // Simple time-based LED flasher | |
| boolean LEDon = false; | |
| void updateBrewLight(){ | |
| if (Brewing && !Done){ | |
| if (!LEDon){ | |
| digitalWrite(LedPin, HIGH); | |
| LEDon = true; | |
| } | |
| else { | |
| digitalWrite(LedPin, LOW); | |
| LEDon = false; | |
| } | |
| } | |
| if (!Brewing){ | |
| digitalWrite(LedPin, HIGH); | |
| } | |
| } | |
| // Simple debugging print function | |
| void printStatus(double irms, int teaStat){ | |
| Serial.print(irms); // Irms current | |
| Serial.print(" "); | |
| Serial.print(States[teaStat]); // Waiting, Heating, Basket Motion | |
| // Print the state array | |
| Serial.print(" ["); | |
| for(int x=0; x < HistLen; x++){ | |
| Serial.print(StateHist[x]); | |
| Serial.print(","); | |
| } | |
| Serial.print("]"); | |
| Serial.print(" Bsl:"); | |
| Serial.print(IrmsBase); | |
| // Print the baseline history | |
| Serial.print(" ["); | |
| for(int x=0; x < BaseLen; x++){ | |
| Serial.print(Baseline[x]); | |
| Serial.print(","); | |
| } | |
| Serial.print("]"); | |
| // print our boolean states | |
| Serial.print(" Htg:"); | |
| Serial.print(Heating); | |
| Serial.print(" Bsk:"); | |
| Serial.print(Basket); | |
| Serial.print(" Brw:"); | |
| Serial.print(Brewing); | |
| Serial.print(" Dn:"); | |
| Serial.print(Done); | |
| Serial.println(" "); | |
| } | |
| void printTrans(String updown){ | |
| Serial.println("!!!!!!! BASKET TIME !!!!!!!"); | |
| Serial.print("!!!!!!! GOING "); | |
| Serial.print(updown); | |
| Serial.println(" !!!!!!!"); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment