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

Arduino Like Function on Kinetis K82 Freedom Stupid Board
Intermediate
  • 89
  • 4

Protip

1000 of K82 Freedom Boards are collecting dust because of the hobbyist unfriendly IDE, pump up the duds with easy Arduino-like functions.

DIY Patient Thermometer for Healthcare Training
Intermediate
  • 91
  • 3

Work in progress

DIY thermometer, IR remote controlled. 9 different temperatures. For use in healthcare simulation training. Cost-effective solution.

Reducing Arduino Power Consumption
Intermediate
  • 181
  • 5

Full instructions

One the most important feature of portable electronics should be long battery life. We can reduce the current drawn by several ways.

Raspberry Pi Motion Tracking Gun Turret
Intermediate
  • 7,969
  • 19

Full instructions

A motion tracking airsoft or Nerf gun turret with autonomous and manual operation modes, controlled by a Raspberry Pi.

Arduinomated Car Parking with Voice Assistance in Smartphone
Intermediate
  • 721
  • 12

Full instructions

Make an assistant which tells you about how much to drive backwards without any collision through your smartphone by Arduino and 1Sheeld.

Simple Weather Station with amazing web monitoring
Intermediate
  • 773
  • 6

Full instructions

Do you want see from anywhere what is the temperature, pressure and humidity in your house? If yes this project will show you how to do it.

Sign up / LoginProjectsPlatformsTopicsContestsLiveAppsBetaFree StoreBlog