Things used in this project

Schematics

Emon curent monitoring circuit diagram
From http://openenergymonitor.org/emon/buildingblocks/ct-sensors-interface
Arduino%20ac%20current%20input%20a

Code

Arduino power monitor codeC/C++
Runs on the Arduino pro mini to monitor the power usage of the tea maker
#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(" !!!!!!!");

}
Arduino audio/wav player codeC/C++
Run on the arduino uno with the Adafruit wav shield
#include <FatReader.h>
#include <SdReader.h>
#include <avr/pgmspace.h>
#include "WaveUtil.h"
#include "WaveHC.h"

SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the filesystem on the card
FatReader f;      // This holds the information for the file we're play

WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

byte PrevValUpin = HIGH; // Storing the previous reading to "debounce" startup of the current sense board
byte PrevValDpin = HIGH;

int DownPinNum = 8; // The pins to sense the up/down signals
int UpPinNum = 9;

// this handy function will return the number of bytes currently free in RAM, great for debugging!   
int freeRam(void)
{
  extern int  __bss_end; 
  extern int  *__brkval; 
  int free_memory; 
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end); 
  }
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval); 
  }
  return free_memory; 
} 

void sdErrorCheck(void)
{
  if (!card.errorCode()) return;
  Serial.println("\n\rSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  Serial.print(", ");
  Serial.println(card.errorData(), HEX);
  while(1);
}

void setup() {
  // set up serial port
  Serial.begin(9600);
  
  // Set the output pins for the DAC control. This pins are defined in the library
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
 
  // Listening on these pins for triggers
  pinMode(DownPinNum, INPUT);  // Basket down
  pinMode(UpPinNum, INPUT);  // Basket up

  if (!card.init()) {         //play with 8 MHz spi (default faster!)  
    Serial.println("Card init. failed!");  // Something went wrong, lets print out why
    sdErrorCheck();
    while(1);                            // then 'halt' - do nothing!
  }
  
  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);
 
// Now we will look for a FAT partition!
  uint8_t part;
  for (part = 0; part < 5; part++) {     // we have up to 5 slots to look in
    if (vol.init(card, part)) 
      break;                             // we found one, lets bail
  }
  if (part == 5) {                       // if we ended up not finding one  :(
    Serial.println("No valid FAT partition!");
    sdErrorCheck();      // Something went wrong, lets print out why
    while(1);                            // then 'halt' - do nothing!
  }
  
  // Lets tell the user about what we found
  Serial.print("Using partition ");
  Serial.print(part, DEC);
  Serial.print(", type is FAT");
  Serial.println(vol.fatType(),DEC);     // FAT16 or FAT32?
  
  // Try to open the root directory
  if (!root.openRoot(vol)) {
    Serial.println("Can't open root dir!"); // Something went wrong,
    while(1);                             // then 'halt' - do nothing!
  }
  
  delay(5000); // wait a few seconds for the secondary board to boot
}

void loop() {
  byte downPin;
  byte upPin;
  downPin = digitalRead(DownPinNum);
  upPin = digitalRead(UpPinNum);
   
  if (PrevValDpin == LOW && downPin == HIGH){
    Serial.print("DOWN");
    playcomplete("DOWN.wav");
    delay(1000);
  }
  if (PrevValUpin == LOW && upPin == HIGH){
    Serial.print("UP");
    playcomplete("UP.wav");
    delay(1000);
  }
  PrevValDpin = downPin;
  PrevValUpin = upPin;
  delay(2);
}


// Plays a full file from beginning to end with no pause.
void playcomplete(char *name) {
  // call our helper to find and play this name
  playfile(name);
  while (wave.isplaying) {
  // do nothing while its playing
  }
  // now its done playing
}

void playfile(char *name) {
  // see if the wave object is currently doing something
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
  }
  // look in the root directory and open the file
  if (!f.open(root, name)) {
    putstring("Couldn't open file "); Serial.print(name); return;
  }
  // OK read the file and turn it into a wave object
  if (!wave.create(f)) {
    putstring_nl("Not a valid WAV"); return;
  }
  
  // ok time to play! start playback
  wave.play();
}
Emon Lib
Power monitoring library

Credits

43358b1dc76c91f999cd7258873dbcb8
Brian Chamberlain

Software Engineer and Hardware Hacker

Contact

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

Direction Indicators for Bikes
Intermediate
  • 103
  • 4

Full instructions

Riding bike is a little dangerous. With the Direction Indicators for a bike, car's know if you want to turn right or left. Make biking safe!

Starry Night Prom
Intermediate
  • 655
  • 7

How did I stand out at prom? In a light up dress of course!

The Trump Button
Intermediate
  • 722
  • 6

Full instructions

The famous connected button to order your favorite drink using Sigfox network.

Third Eye for The Blind
Intermediate
  • 605
  • 8

Full instructions

An innovative wearable technology for visually impaired peoples.

DIY Fidget LED Display
Intermediate
  • 997
  • 6

Work in progress

Fidget spinners...the simple spinning toy that came out of nowhere. I thought it'd be cool give it some flare and make it a POV display.

Controlling 2 motors with Bean+ and TB6612FNG
Intermediate
  • 19
  • 1

Full instructions

This project aims to demonstrate the operation of the TB6612FNG drive together with the Bean+ to control two DC motors trough an iPhone app.

ProjectsCommunitiesTopicsContestsLiveAppsBetaFree StoreBlogAdd projectSign up / Login