Project tutorial

Smart Kitchen Cube © Apache-2.0

Alexa controlled timers at a glimpse. A really fancy egg-timer with added temperature control.

  • 1,404 views
  • 0 comments
  • 9 respects

Components and supplies

Necessary tools and machines

Lasercutter
Laser cutter (generic)
Hy gluegun
Hot glue gun (generic)
09507 01
Soldering iron (generic)

Apps and online services

About this project

Idea

Both of us like to tinker with electronics and the latest hard- and software. While most projects never get out of "early-Alpha", we wanted to create something with a prototype look&feel for quite a while now. Participating in The Alexa and Arduino Smart Home Challenge finally made us consider creating something worthy of publishing here.

Having used Alexa on a daily basis for controlling lights and music throughout our home, something that really bothered me was the timers we would regularly set for things like cooking and making tea. The initial request for setting up a timer was usually followed by a couple of "Alexa, how much time left on the tea-timer?" so we rather quickly had the idea of creating an additional device, that let you glimpse at your timers without having to interact with Alexa again. We basically wanted to create an egg-timer with a really fancy hard- and software stack. For additional fanciness, we added a temperature controlled cooking mode.

Concept

While there are other options, having Alexa communicate with an Amazon Lambda function is the quickest and easiest way to get started with development. You can either set up your own skill and lambda or activate our Smart Kitchen Cube skill from your Alexa App.

You can view and activate the skill here, or search for "Smart Kitchen Cube" in the Alexa App. For a quick start, we recommend trying our skill first and then switch to your own skill and lambda in case you want to tinker with the software-side of this project. For detailed instructions on how to duplicate our setup, refer to the last two chapters of this project to get started, then modify things to your need. But first off, the necessary steps to build your own cube and link it to our skill!

Particle Cloud

You will need an account with particle.io to link the skill to. So head over there and either register as a new user or sign in to your existing account. Then go to build.particle.io, click on settings (the gearwheel in the lower left) and take note of your access token. You will need the token in your Arduino sketch so the Arduino can communicate with the particle cloud. While you are at it, flash the particle you will use for the cube with the software provided in the code section. If you haven't set up your particle yet, do so now via the particle app, it has great step-by-step instructions on how to do this.

Account Linking

With your Particle account you can already set up our skill, so head over to alexa.amazon.com, log in with the account you have your Echo connected to, click on the Skills tab and search for "smart kitchen cube".

Click on the skill and then on "Enable". A window will open where you have to login with your Particle account. Press "Ok" when it asks you for permission to link the account. As of now, the device discovery will not yield any results unless you have flashed our code to the particle, but once you did that and add a device, it will show up in the "Smart Home" section of the alexa.amazon.com website. Lets build a device and hook it up now!

Hardware - The Cube

As we wanted to build more than just a breadboard prototype, creating our cube was as much about constructing the case as it was about building the circuit.

After gathering and testing the basic parts, i.e. microcontrollers and LEDs, we went to a local Plexi shop and went through (almost) all of the available color and opacity variations. We originally had planned on a greyish color with very light transparency but the glooming effect on the white Plexi depicted above just looked way too cool, so we went with it instead. We bought a 30cm by 30cm sheet. As we hadn't yet decided on the final design of the cube, and had 2 different sized rings and a huge 8 by 8 pixel matrix, the size of the sheet let us design three different cubes. We created the plans for the cubes at www.makercase.com, a great resource for creating custom sized cases. Our university fablab has a lasercutter so we paid them a visit and were lucky enough they had enough time to go over our design and cut it right away.

The following fritzing shows how to assemble the cube's internals. It doesn't matter whether you hook up 12, 16 or even 64 LEDs, because the WS2812b are easily controlled via an awesome Arduino library. You have to set the pixel count in the sketch however, so make sure you do this, otherwise not all of your LEDs might work and the calculation for the timer are off. Once you have the cube internals assembled and loaded with our coded, your LEDs should light up and start a countdown on a push of the reset button.

Check the components and the wiring on a breadboard with our code before you solder everything together as neatly as possible so you can make it fit your Plexi cube. We used lots of hot glue to keep everything in place and have nothing touch to prevent short circuiting.

When assembling your cube make sure to check the maximum distance your inductive charger will still transmit enough voltage for the particle to boot and the battery to charge. The smaller one we got didn't provide quite enough power, so we had to get a slightly bigger one that works like a charm (see our components list). Once you have the cube assembled it is time to move on to the base.

Hardware - The Base

The base is where the cube charges and where you can hook up your thermometer. It houses an Arduino MKR1000 that sends the temperature data to the cloud and in our second revision also takes an input of the target temperature as well as have an OLED display to show the current and target temperatures.

Lets tackle something a little complicated first, we need to make sure our Arduino can talk to the Particle servers, which is a SSL secured connection, so we will have to let the Arduino know the servers certificate. Fire up the Arduino IDE and upload the Firmware Updater sketch located in the Sample Sketch, Wifi101 group. You can then go to Tools and start the Wifi101 Firmware Updater which is where you can download certificates to your Arduino. Click "Add domain" and enter api.particle.io:443 Then hit "Upload Certificates to Wifi Module". You are now set and can go ahead and flash the Arduino sourcecode attached to this project.

Again, wire everything up on a breadboard to make sure things are working as expected. Load up the Arduino with our sketch, after editing the code to your needs, i.e. entering the access token you took note of in the Particle Cloud step of this project. It will allow the Arduino to post data to your personal particle cloud. Additionally you will have to provide your wifi credentials, i.e. ssid and password to your personal wifi, so the Arduino can connect to the internet. Visit console.particle.io and check the event tab where you should see a temperature reading from your Arduino every ten seconds.

You can use the rotary encoder to set a target temperature and confirm via clicking the encoder. It will also show up as an event in your particle console.

After confirming everything works, solder the parts together or choose a base big enough to just fit your current build. For our first iteration we wanted to create a tiny base, so we ended up soldering everything together really neatly.

Piecing things together for the grand finale

Hook up your inductive charger and place the cube on the base. We have hooked up the power from the inductive transmitter to the Arduino as well, so there is only one line going to the base. Make sure the Arduino powers on and the Particle is powered through the battery/conductive receiver. You can now go ahead and ask Alexa to set a timer on your cube ("Alexa, two minutes on [your device name]") or set a target temperature that is visualised by the cube via the rotary encoder on the Arduino. The base will display current and target temperature, the cube will slowly light more and more LEDs until the target temperature is reached and all LEDs are lit. You should be able to carry your cube to wherever you have a Wifi connection, so you can keep an eye on your timer and/or temperature.

Alexa Smart Kitchen Cube Skill Demonstration

Done!

Thanks for making it through our little project, we hope you enjoyed reading and building and making things work. We surely enjoyed the build a lot and are awaiting your comments!

If you feel like extending the software, or even creating something completely different for that matter, go ahead and check out the project where we explain how to set up AWS Lambda and the Alexa Skill for this project, complete with source code for this build right here! It is right here on hackster.io.

Code

Arduino CodeC/C++
Code for the Arduino MKR1000
#include <Wire.h>
#include <SPI.h>
#include <WiFi101.h>
#include <Encoder.h>
#include <TaskScheduler.h>
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "math.h"


#include "secret.h"

#define ADC_SAMPLES 10 //more smples to avoid spikes
#define ADC_ZERO_THRESHOLD  10 // values smaler than this are set to Zero
#define ADC_RESOLUTION  1024  // resulution of your adc, at arduino 1024 
#define RESISTOR  100000  //your voltage dividing resistor

#define BCOEFFICIENT 3528 //constant from termistor datasheet
#define THERMISTORNOMINAL 100000 //constant from termistor datasheet
#define TEMPERATURENOMINAL 25 //constant from termistor datasheet

char ssid[] = SSID; //wifi ssid (wifiname)
char pass[] = WLANPASS; //wifi password

String accesstoken = ACCESS_TOKEN; //particle accesstoken from particle ide
String particleDeviceID = PARTICLE_DEVICE_ID; // id of your photon from particle ide

//wifi things
int status = WL_IDLE_STATUS;
WiFiClient client;

char server[] = "api.particle.io"; // particle rest api


void sendTempEvent(); // send actual the temperature to your cube every 5 seconds
Task particleEventTask(5000, TASK_FOREVER, &sendTempEvent);

void sendTempSetEvent();//  send chosen temperature to your cube, executed once per button press
Task particleSetEventTask(1, TASK_FOREVER, &sendTempSetEvent);

void updateDisplay(); //  update the oled display, executed every 500 ms
Task displayTask(500, TASK_FOREVER, &updateDisplay);

Scheduler runner; // task scheduler

Encoder myEnc(4, 5); // initialize the rotary encoder
long oldPosition  = -999;
bool changed = false; // true if the new chosen value is not send to particle
bool lastShown = false; // for blinking

volatile unsigned long lastTime = 0; // needed to debounce the button
volatile unsigned long debounceTime = 20; // needed to debounce the button

Adafruit_SSD1306 display(-1); // initialize the oled display, -1 because there is no resetpin at our display

void setup() {

  pinMode(A1, INPUT); //initialize input for the button

  //initialize the display
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  updateDisplay();
  // initialize wifi connection and wait for success
  while (status != WL_CONNECTED) {
    status = WiFi.begin(ssid, pass);
    delay(1000);
  }
  //initialize  the scheduler and at our tasks
  runner.init();
  runner.addTask(displayTask);
  displayTask.enable();
  runner.addTask(particleEventTask);
  particleEventTask.enable();
  runner.addTask(particleSetEventTask);

  attachInterrupt(1, interruptRoutine, LOW); // attach an interrupt for the button
}

void loop() {
  runner.execute(); // just run the scheduler
}

// executed on buttonpress, do some debounce stuff and on sucess schedule the task to send the chosen temperature
void interruptRoutine() {
  if ((millis() - lastTime) > debounceTime) {
    lastTime = millis();
    changed = false;
    particleSetEventTask.enable();
  }
}

// convert a given resistance to degree celsius
float convertResistanceToTemperature(float resistance) {
  float temp;
  temp = resistance / THERMISTORNOMINAL;
  temp = log(temp);
  temp /= BCOEFFICIENT;
  temp += 1.0 / (TEMPERATURENOMINAL + 273.15);
  temp = 1.0 / temp;
  temp -= 273.15;
  return temp;
}

// calc the resistance of the termistor at the pin
float calcResistance ( const uint8_t pin ) {
  float reading = 0;

  for ( uint8_t i = 0; i < ADC_SAMPLES; i++ ) {
    reading += analogRead(pin);
  }
  if ( reading <= (ADC_ZERO_THRESHOLD * ADC_SAMPLES) ) {
    // probe disconnected, should be zero
    return 0;
  } else {
    reading /= (float)ADC_SAMPLES;
    reading = (ADC_RESOLUTION / reading) - 1;
    return (RESISTOR / reading);
  }
}

// repaint the display, executed every 500 ms
void updateDisplay() {
  updateEncoderPosition();
  float resistance = calcResistance(A1);
  float temperature =  convertResistanceToTemperature(resistance);

  display.clearDisplay();

  display.fillRect(60, 0, 2, 64, WHITE);
  display.setTextColor(WHITE);


  display.setCursor(0, 0);
  display.setTextSize(1);
  display.print("act. temp");

  display.setCursor(70, 0);
  display.print("set temp");

  display.setCursor(0, 17);
  display.setTextSize(2);
  display.print(temperature, 0);
  display.setTextSize(1);
  display.print((temperature - ((int)temperature)) * 10.0f, 0);

  display.setCursor(35, 22);
  display.print((char)247);
  display.print("C");

  if ((changed & !lastShown) | !changed) { // let the choosen temperature blink if not sent to particle yet
    display.setCursor(70, 17);
    display.setTextSize(2);
    display.print( (float)oldPosition , 0);
    display.setTextSize(1);
    display.print( (oldPosition - ((int)oldPosition )) * 10.0f, 0);

    display.setCursor(105, 22);
    display.print((char)247);
    display.print("C");
    lastShown  = true;
  } else {
    lastShown = false;
  }

  display.display();
}
//update the chosen temperature from the rotary encoder
void updateEncoderPosition() {
  long newPosition = myEnc.read();
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    changed = true;
  }
}
//send chosen temperature to particle
void sendTempSetEvent() {
  String temperatureString  = String(oldPosition, DEC);
  sendEvent("tempset", temperatureString);
  particleSetEventTask.disable(); // disable execution since next buttonpress
}
// send the actual temperature to particle, executed every 5 seconds
void sendTempEvent() {
  float resistance = calcResistance(A1);
  float temperature =  convertResistanceToTemperature(resistance);
  String temperatureString  = String(temperature, DEC);
  sendEvent("tempin", temperatureString);
}
// call a given function at the particle cloud, via h ttp post
void sendEvent(String eventName, String data) {

  String payload =  "data=" + data + "&access_token=" + accesstoken;
  if (client.connectSSL(server, 443)) {
    client.println("POST /v1/devices/" + particleDeviceID + "/" + eventName + " HTTP/1.1");
    client.println("Host: api.particle.io");
    client.println("User-Agent: Arduino/unowifi");
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.println("Connection: close");
    client.print("Content-Length: ");
    client.println(payload.length());
    client.println();
    client.println(payload);
  }

  while (client.available()) {
    char c = client.read();
    Serial.print(c); // just for debug
  }
  if (!client.connected()) {
    client.stop();
  }
}
Photon CodeC/C++
#include <neopixel.h>
#include "Particle.h"
#define PIXEL_PIN D0 // pin you want to connect your ledstripe to
#define PIXEL_COUNT 64 // adjust here how many leds you want, but you have to bye them first
#define PIXEL_TYPE WS2812B
SYSTEM_MODE(SEMI_AUTOMATIC); // set to semiautomatic to save power

String alexa_device = "cube"; // <<==== set your devicename here

//init stuff
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
long counterTime = PIXEL_COUNT ;
long endTime = millis() + (counterTime * 1000);
float temperature = 0;
float targetTemperature = -1;

void setup()
{
  watchVoltage(); // first check battery voltage
  Particle.connect(); // connect to wifi and cloud
  // register functions for countdown an temperature purposes
  Particle.function("countdown", countdown);
  Particle.function("adjust", countdown_adjust);
  Particle.function("tempin", temperatureIn);
  Particle.function("tempset", temperatureSet);
  Particle.variable("alexa_device", alexa_device);  // needed to tell the skill, we are a smart kitchen cube
  // init the leds
  strip.begin();
  strip.setBrightness(180);
  strip.show();
}

void loop()
{
  watchVoltage();//  check battery voltage
  if (targetTemperature != -1) { // check if we want to work either with temperatures or run a countdown
    showTemperature();
  } else {
    showTimer();
  }

}

//check voltage on pin A1 power down if its to low
void watchVoltage() {
  int raw =  analogRead(A1);
  raw +=  analogRead(A1);
  raw +=  analogRead(A1);
  raw +=  analogRead(A1);
  raw /= 4;
  float voltage = raw * 0.0008f * 2.0f;

  if (voltage <= 3.0f && voltage  > 2) {
    //Power down
    for (uint16_t i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, 0);
    }
    System.sleep(SLEEP_MODE_DEEP, 300);
  } else   if (voltage < 3.2f) {
    //TODO send warning to alexa, not that easy
  }
}

// show the actual temperature relative to the target temperature
void showTemperature()
{
  int remaining = targetTemperature - temperature;

  if (remaining > 0) {

    int numPixels = map(remaining, 0, targetTemperature, 0, strip.numPixels()) ;
    for (uint16_t i = 0; i < strip.numPixels(); i++) {
      if (i < numPixels) {
        strip.setPixelColor(i, strip.Color(255, 0, 0));
      } else {
        strip.setPixelColor(i, 0);
      }
    }

    float lastPixel = (map(remaining, 0, targetTemperature, 0, strip.numPixels()) / 1000.0f) - numPixels;

    strip.setPixelColor(numPixels, strip.Color(lastPixel * 255, 0, 0));

  } else {
    for (uint16_t i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, 0);
    }

    targetTemperature  = -1; // reached target temperature, stop temperature mode
  }
  strip.show();
}

// show the contdown timer
void showTimer()
{
  long now = millis();
  long remaining = endTime - now;

  if (remaining > 0) {

    int numPixels = map(remaining, 0, counterTime, 0, strip.numPixels()) / 1000;
    for (uint16_t i = 0; i < strip.numPixels(); i++) {
      if (i < numPixels) {
        strip.setPixelColor(i, strip.Color(255, 0, 0));
      } else {
        strip.setPixelColor(i, 0);
      }
    }

    float lastPixel = (map(remaining, 0, counterTime, 0, strip.numPixels()) / 1000.0f) - numPixels;

    strip.setPixelColor(numPixels, strip.Color(lastPixel * 255, 0, 0));

  } else {
    for (uint16_t i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, 0);
    }
  }
  strip.show();
}

// callback for cloud function to start countdown
int countdown(String time) {
  counterTime = atoi(time);
  endTime = millis() + counterTime * 1000;
  return counterTime;
}
// callback for cloud function to adjust countdown
int countdown_adjust(String time) {
  counterTime += atoi(time);
  endTime = millis() + counterTime * 1000;
  return counterTime;
}
// callback for cloud function to set the actual temperature called every 5 seconds
int temperatureIn(String tempString)
{
  float temp = atof(tempString);
  temperature = temp;
}
// callback for cloud function to set the target temperature
int temperatureSet(String tempString)
{
  float temp = atof(tempString);
  targetTemperature = temp;
}
secret.hC/C++
add to arduino sketch
#define WLANPASS "wlanpass"
#define SSID "ssid"
#define PARTICLE_DEVICE_ID "paricle_device_id"
#define ACCESS_TOKEN "your_access_token"

Custom parts and enclosures

MakerCase Cube Case
This is the generated case file to be used with the lasercutter. We tried three different sizes for prototyping. While you are free to use this one, you might want to head to the website and generate one that better fits your needs and parts.
caseplans-10_PE8gKgddPL.svg

Schematics

Comments

Similar projects you might like

Smart Home - Smart Rules using ARTIK Cloud & Photon

Project tutorial by Raghavendra Ponuganti

  • 4,002 views
  • 2 comments
  • 13 respects

Rampiot - Cool Smart Lock

Project tutorial by Robinson Mesino

  • 4,070 views
  • 1 comment
  • 27 respects

Alexa Smart Power Strip with Temperature

Project tutorial by Muhammad Afzal

  • 1,203 views
  • 0 comments
  • 7 respects

Hygge Home - Alexa Smart Bath

Project tutorial by J Howard

  • 5,355 views
  • 2 comments
  • 18 respects

Smart Pool: Alexa Controlled Pool Manager

Project tutorial by Benjamin Winiarski

  • 1,355 views
  • 2 comments
  • 7 respects

Smart IOT Propane Monitoring Pedestal

Project in progress by A Bit Gassy

  • 1,389 views
  • 1 comment
  • 12 respects
Add projectSign up / Login