Brian Chamberlain
Published © MIT

Breville Imperial Tea Maker

I hacked the Breville One-Touch Tea Maker to play music from Star Wars. It plays when the tea basket moves down and when it moves back up.

IntermediateShowcase (no instructions)2,887
Breville Imperial Tea Maker

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×1
Adafruit Wave Shield
×1
high frequency current transformer
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Emon curent monitoring circuit diagram

From http://openenergymonitor.org/emon/buildingblocks/ct-sensors-interface

Code

Arduino power monitor code

C/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 code

C/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

Brian Chamberlain

Brian Chamberlain

11 projects • 19 followers
Hi! I am a software engineer who enjoys hacking on hardware on the side. My interests are in DIY projects for the home, IOT, and 3D printing

Comments