Project showcase
Breville Imperial Tea Maker

Breville Imperial Tea Maker © MIT

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.

  • 1,590 views
  • 1 comment
  • 6 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)

About this project

Short version: I hacked the most advanced tea making device to make it play music from Star Wars. To get a sense of what this project was about check out the video below. 

This is the final edited video of the device in action. It was a fun project and I'm glad it worked out. 

The goal of this project was to hack the Breville One-Touch Tea Maker to give it some extra personality while it was making a pot of tea. The whole concept was a challenge/request by my friend Vince at Econify.com He was the project sponsor and the end result of the hack became a great gift for one of his employee's who is really into his tea (or so I assume)! 

I should clarify that this is not a real "hack" or at least not a hack in the way most people would think. What I do here with that little black box next to the tea maker is read the power signature of the tea maker and determine what state it's in from how much power it's using now vs previous time periods. By monitoring the tea maker's current usage I'm able to tell when it's just sitting around waiting, boiling water, lowering the basket, brewing the tea, raising the basket, and when it's all done. 

This is not an entirely new idea. It's actually used quite a bit in the energy industry. It's technical term (usually when applied to a whole building or system) is called power disaggregation. This is a fancy term for "figuring out what thing in your house is turned on by looking at how much power (usually in watts) you are using." Here's some info on the idea https://lids.mit.edu/research/research-highlights/power-disaggregation

I wish I could say that the approach here was the first thing I tried. It actually was a last resort! When I first saw this tea maker function I thought the easy way into it's state was to just crack open the bottom base where all the smarts are and poke around until I found the logic lines that drive the state of the little motor that makes the basket go up and down.

Well the team at Breville did a great job designing this thing to keep people out of the base. I first tried to access the base by removing all the screws and trying to pry it apart. Keeping in mind that this is not a cheap device (retails at $250) AND it was supposed to be a gift I did not really want to go at it with a pair of pliers and a blow torch. I even looked for broken ones on eBay that I might sacrifice. The device is so new (or just never breaks) that there was not much there. 

After giving up on the base I thought the carafe would be a good next option. It has a small circuit board inside (I know this because I saw these for sale on Breville parts websites). On the base there were two kinds of screws, the regular old philips head, then they also had these strange triangular head screws. I found a set http://amzn.com/B004IUEHYS spoiler alert, they're called "Triangle Head" screws. I am assuming they're named after Mr. Triangle. https://www.youtube.com/watch?v=BQsO4WxAVb8

The carafe also was a dead-end. It's a really well designed carafe. Even If I was able to remove the bottom board I was a little concerned I would break some factory applied seal causing boiling hot water to leak out and cause some real problems for all parties involved. 

For a brief period of time I thought the lid would be a good place to try to install my hack. This would mean that the lid would have to sense the basket drop from above and wirelessly transmit that info to some other circuit to play the sound effects. The lid could contain a simple battery powered bluetooth transmitter and could use a simple contact sensor to know if the basket was located. (The basket is held in by magnets so I considered using a hall-effect sensor as well). 

The wireless lid sensor + basestation was the best idea I had at the time and it would have worked too if it were not for all that boiling hot water. Since the lid sits directly above the tea water it can get pretty hot. Steam is a really good way to transfer thermal energy. When you make tea (or boil water) a whole mess of steam rises off the water and hits the lid. When all that steam hits the lid it heats it up pretty good. Did I mention that the wireless sensor would run on batteries? It was really the only way to get power up where (except for some fancy peltier-effect based thermal energy harvesting system https://en.wikipedia.org/wiki/Thermoelectric_effect perhaps).

Turns out your standard AA battery has a operating temperature range of -18°C to 55°C. During my tests the lid easily reached well over 80°C. This would have probably meant exploding batteries. No one likes battery acid in their tea, for sure! http://data.energizer.com/PDFs/E91.pdf


Anyway, on to the actual hack! 


The concept of the hack was to have one Arduino measuring the power usage of the tea maker using a modified (and calibrated) circuit from http://openenergymonitor.org/emon/ The code on this arduino has to sense and determine the state of the tea maker based on it's past know states and some data samples it takes from the current transformer. It then will signal the second Arduino with the Wave shield using one of two signal lines. When one line goes high the tea maker basket is going down, when the other line goes high the tea basket is going up. The Wave shielded Arduino just has the simple job of reading these two lines and playing one of two audio clips for each event. 


For the curious here is the complete hardware list...

  • Appliance rated 15AMP power cable 
  • Arduino Pro Mini Sparkfun https://www.sparkfun.com/products/11113
  • Current Transformer Amazon http://www.crmagnetics.com/high-frequency-current-transformers/wire-lead/voltage-output/solid-core/cr8448
  • Project Box Amazon http://www.amazon.com/gp/product/B0002BENMI?psc=1&redirect=true&ref_=oh_aui_detailpage_o00_s00
  • Wave Shield Adafruit http://www.adafruit.com/products/94
  • Arduino uno Adafruit http://www.adafruit.com/product/50
  • Speaker Sparkfun https://www.sparkfun.com/products/9151
  • Proto Board Adafruit http://www.adafruit.com/products/1608
  • SD Card, Misc equp. cables, power plug misc
  • Breville Tea Maker Amazon http://www.amazon.com/gp/product/B003LNOPSG?psc=1&redirect=true&ref_=oh_aui_detailpage_o03_s00

And the sources for some ideas on this project...

  • Current sensing circuit design from open energy monitor http://openenergymonitor.org/emon/buildingblocks/ct-sensors-interface
  • WAV sheild example from adafruit https://learn.adafruit.com/adafruit-wave-shield-audio-shield-for-arduino?view=all#examples
  • Audio editing tool to make Wav files - Audacity http://audacityteam.org/
  • State sensing code on github https://gist.github.com/breakpointer/4c105f4276c1196fc421
  • Audio playing code on github https://gist.github.com/breakpointer/a700ef3620361a40546a  
Below is the datasheet for the HF CT that I used

Well, that's it. Hopefully you find this useful if you want to "power disaggregate" your own devices. Please be careful when working with 120VAC. I have been working with electronics like this for many years. If you have not worked with powerline electronics please consult one of the many references out there or, when in doubt ask a professional!


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

Schematics

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

Comments

Author

23939678248 65aaf15509 o 2baljdcvg0
Brian Chamberlain
  • 1 project
  • 18 followers

Additional contributors

Published on

October 17, 2015

Members who respect this project

0c48a85ProfessionalPhoto 2015 10 19 18 08 32No profileMerlin arch 4fnrmpnf6jDefault
See similar projects
you might like

Similar projects you might like

Servo Tea Maker

Project showcase by Gorkem Selcuk

  • 3,958 views
  • 2 comments
  • 12 respects

Countdown Timer

Project tutorial by Prasantha Jayakody

  • 21,664 views
  • 13 comments
  • 33 respects

Aquarium Control & Monitoring

Project showcase by mihaimascas

  • 4,777 views
  • 2 comments
  • 12 respects

Helium Solar Monitoring Board with Relays

Project tutorial by Jade Perreault

  • 3,874 views
  • 2 comments
  • 18 respects

Dooreo - US China Young Maker Comp Submission

Project tutorial by Keith Caskey

  • 3,178 views
  • 0 comments
  • 9 respects

Drinking Game for the Brave

Project showcase by jdmgolf123

  • 1,887 views
  • 0 comments
  • 14 respects
Add projectSign up / Login