TheParticleGuy
Published © GPL3+

Alexa Powered Arduino Kitchen Assistant

Build an Alexa powered kitchen assistant that makes cooking more interactive by setting timers, showing recipes, leaving notes and more.

AdvancedFull instructions provided6,512
Alexa Powered Arduino Kitchen Assistant

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
×1
ESP8266 ESP-12E
Espressif ESP8266 ESP-12E
×1
SparkFun FTDI Basic Breakout - 3.3V
SparkFun FTDI Basic Breakout - 3.3V
×1
Multi-Turn Precision Potentiometer- 10k ohms (25 Turn)
Multi-Turn Precision Potentiometer- 10k ohms (25 Turn)
×1
Perfboard
×1
4 inch x 4 inch PVC Box
×1
Wires
×1

Software apps and online services

Arduino IDE
Arduino IDE
Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
MicroPython
MicroPython

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

Circuit

Temperature sensor Circuit

Code

Lambda Function Code

JavaScript
'use strict';
var https = require('https');
var http = require('http');
var server_url = "Your Ip Goes here" //Replace the value with the ip of your nodejs server
const Alexa = require('alexa-sdk');
const APP_ID = undefined;  
function check_timer(){  //Function to check if timer is already running
   var endpoint = server_url+"/readtime";
       http.get(endpoint, function (res) { 
           res.on('data', function (body) {
               body = JSON.parse(body);
               return body.time;
             });
         }).on('error', function (err) {
           console.log('Error - ' + err.message);
         });
}
const handlers = {
   'LaunchRequest': function () {
       this.response.speak("Hi im your kitchen assistant, I can set reminders, take notes, show recipes and lot more.").listen("How may I help you today");
       this.emit(':responseReady');
   },
   'addnote': function () { //handler function for adding notes to the display
       var self= this;
       var note = this.event.request.intent.slots.task.value;
       var endpoint = server_url+"/addnote?q="+ encodeURI(note);
       http.get(endpoint, function (res) { 
           res.on('data', function (body) {
               //body = JSON.parse(body);
               self.response.speak("Note "+note+" has been added.");
               self.emit(':responseReady');
             });
         }).on('error', function (err) {
           console.log('Error - ' + err.message);
           self.response.speak("An error occured please try again in some time");
               self.emit(':responseReady');
         });
   },
   'deletenotes': function () { //handler function for deleting all the notes on the display
       var self= this;
       var endpoint = server_url+"/deletenotes";
       http.get(endpoint, function (res) { 
           res.on('data', function (body) {
               //body = JSON.parse(body);
               self.response.speak("All notes have been deleted");
               self.emit(':responseReady');
             });
         }).on('error', function (err) {
           console.log('Error - ' + err.message);
           self.response.speak("An error occured please try again in some time");
               self.emit(':responseReady');
         });
   },
   'timer': function () { //function to set timer in seconds
       var time = this.event.request.intent.slots.time.value;
       var self= this;
       var endpoint = server_url+"/settime?q="+ encodeURI(time);
       if(check_timer()){
           http.get(endpoint, function (res) { 
               res.on('data', function (body) {
                   //body = JSON.parse(body);
                   self.response.speak("A timer for "+time+" seconds has been set");
                   self.emit(':responseReady');
                 });
             }).on('error', function (err) {
               console.log('Error - ' + err.message);
               self.response.speak("An error occured please try again in some time");
                   self.emit(':responseReady');
             });
         }
         else{
           self.response.speak("A timer is already running");
           self.emit(':responseReady');
         }
   },
   'temp': function () { //function to read the temperature of the dishes
       var tem = 0;
       var self= this;
       var endpoint = server_url+"/readtemp";
       http.get(endpoint, function (res) { 
           res.on('data', function (body) {
               body = JSON.parse(body);
               tem = body.temp;
               if(tem==0){
                   self.response.speak("I'm sorry could you please check the connection to the temperature sensor");
                   self.emit(':responseReady');
               }
               else{
                   self.response.speak("The temperature is "+tem+" degrees celsius");
                   self.emit(':responseReady');
               }
             });
         }).on('error', function (err) {
           console.log('Error - ' + err.message);
           self.response.speak("An error occured please try again in some time");
               self.emit(':responseReady');
         });
   },
   'recipes': function () { //handler function to display a recipe 
       var name = this.event.request.intent.slots.recipe_name.value;
       var self= this;
       var endpoint = server_url+"/newrecp?q="+encodeURI(name);
       http.get(endpoint, function (res) { 
           res.on('data', function (body) {
               body = JSON.parse(body);
               stat = body.status;
               if(stat == 1){
                   self.response.speak("Starting Recipie for "+name); 
                   self.emit(':responseReady');
               }
               else{
                   self.response.speak("Sorry I could not find that recipe"); 
                   self.emit(':responseReady');
               }
             });
         }).on('error', function (err) {
           console.log('Error - ' + err.message);
           self.response.speak("An error occured please try again in some time");
               self.emit(':responseReady');
         });
   },
   'metric': function () { //function to convert one metric unit to another
       var val = this.event.request.intent.slots.value.value;
       var from_met = this.event.request.intent.slots.from_metric.value;
       var to_met = this.event.request.intent.slots.to_metric.value;
       var self= this;
       var endpoint = server_url+"/newrecp?val="+encodeURI(val)+"&to="+encodeURI(to_met)+"&from="+encodeURI(from_metric);
       http.get(endpoint, function (res) { 
           res.on('data', function (body) {
               body = JSON.parse(body);
               stat = body.status;
               if(stat == 1){
                   self.response.speak(val+" "+from_met+" is "+body.data+" "+to_met); 
                   self.emit(':responseReady');
               }
               else{
                   self.response.speak("Sorry an error just occured"); 
                   self.emit(':responseReady');
               }
             });
         }).on('error', function (err) {
           console.log('Error - ' + err.message);
           self.response.speak("An error occured please try again in some time");
               self.emit(':responseReady');
         });
   },
   
  //Amazon Built in handlers for help, cancel, Stop
  
   'AMAZON.HelpIntent': function () {
       this.emit('You can say tell me to set reminders, take notes, show recipes and lot more.');
   },
   'AMAZON.CancelIntent': function () {
       this.emit('Goodbye');
   },
   'AMAZON.StopIntent': function () {
       this.emit('Goodbye');
   },
};
exports.handler = function (event, context) {
   const alexa = Alexa.handler(event, context);
   alexa.APP_ID = APP_ID;
   alexa.registerHandlers(handlers);
   alexa.execute();
};

Boot file Mircopython

Python
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import gc
import webrepl
webrepl.start()
gc.collect()


from machine import UART, Pin #built in module for serial and gpio control 
import time
import urequests as requests
import ujson as json

server_url = "Your ip comes here" #Replace this field with the ip of your nodejs server

uart = UART(0, 9600)                         
uart.init(9600, bits=8, parity=None, stop=1)

pin = Pin(2, Pin.OUT)

def serial_write(text): # function to transmit data to the arduino and display it on the screen
	uart.write(str(text))

def get_data(url):
	r = requests.get(url) 
	return r.text

def wait_timer(sec): #timer function
	time.sleep(int(sec)) #sleeps for x secs 

def get_note(): #gets notes from nodejs server
	r = requests.get(server_url+"/allnotes")
	data = json.loads(r.text)
	return data["note"]

def run_recp(): #Start reading out the recipie
	r = requests.get(server_url+"/startrecp")
	data = json.loads(r.text)
	for i in range(1,len(data)):
		serial_write(data[str(i)]["read"])
		wait_timer(3) #if you want the recipe to stay longer or shorter on the display change this value
	r = requests.get(server_url+"/newrecp?q=0")


def check_recp():
	r = requests.get(server_url+"/recp")
	data = json.loads(r.text)
	if data["recipies"] != "0":
		run_recp()

def beep(): #turns on the buzzer for 2 seconds, used when timer is compete
	pin.value(1)
	time.sleep(2)
	pin.value(0)

def check_timer(): # checks and runs the timer
	r = requests.get(server_url+"/readtime")
	data = json.loads(r.text)
	if int(data['time']) > 0:
		serial_write("Timer for "+str(data['time'])+"sec")
		wait_timer(int(data['time']))
		serial_write("Timer Complete")
		beep()
		wait_timer(3)
		r = requests.get(server_url+"/settime?q=0")
	return 1

#This loop runs forever and each time checks for note updates, timer changes and new recipe calls

while(1):
	serial_write(get_note())
	check_timer()
	check_recp()
	wait_timer(1)

Nodejs Code (app.js)

JavaScript
var fs = require('fs');
var app = express();
var newnote =[];
var temp = 0.0;
var time = 0;
var recipies = "0";
var conversion = {"liters":"l","mililiters":"ml","pounds":"lb","ounces":"oz","kilograms":"kg"}
//API to add and delete notes
var convert = require('convert-units')
app.get('/addnote', function (req, res) {
	if(req.query.q != null){
	  var send_data = {};
	  send_data["status"] = 1;
	  newnote.unshift(req.query.q);
	  res.send(send_data);
	}
});
app.get('/allnotes', function (req, res) {
	var send_data = {};
	send_data["note"] = newnote[0];
	send_data["status"] = 1;
 res.send(send_data);
});
app.get('/deletenotes', function (req, res) {
	var send_data = {};
	newnote=[];
	send_data["status"] = 1;
 res.send(send_data);
});
//API for recipies
app.get('/recp', function (req, res) {
	var send_data = {};
	send_data["recipies"] = recipies;
	send_data["status"] = 1;
	res.send(send_data);
});
app.get('/newrecp', function (req, res) {
	recipies = req.query.q;
	if(fs.existsSync(recipies+".json")){
		var send_data = {};
		send_data["status"] = 1;
		res.send(send_data);
	}
	else{
		var send_data = {};
		send_data["status"] = 0;
		res.send(send_data);
	}
});
app.get('/startrecp', function (req, res) {
  var contents = fs.readFileSync(recipies+".json");
	 var jsonContent = JSON.parse(contents);
	 jsonContent['status'] = 1;
 res.send(jsonContent);
});
//API to set timer
app.get('/settime', function (req, res) {
	if(req.query.q !=null){
		time = req.query.q;
 		var send_data = {};
		send_data["status"] = 1;
		res.send(send_data);
	 }
});
app.get('/readtime', function (req, res) {
	var send_data = {};
	send_data["status"] = 1;
	send_data["time"] = time;
	res.send(send_data);
});
//Api to read temperature
app.get('/settemp', function (req, res) {
	if(req.query.q !=null){
		var send_data = {};
		send_data["status"] = 1;
		res.send(send_data);
		temp = req.query.q;
	}
});
app.get('/readtemp', function (req, res) {
		var send_data = {};
		send_data["status"] = 1;
		send_data["temp"] = temp;
		res.send(send_data);
});
app.get('/metric', function (req, res) {
		var val = req.query.val;
		var to = req.query.to;
		var from = req.query.from;
		var send_data = {};
		send_data["status"] = 1;
		send_data["data"] = convert(parseInt(val)).from(conversion[from]).to(conversion[to]);
		res.send(send_data);
});
app.listen(3000, function () {
 console.log('Example app listening on port 3000!');
});

Arduino Code

Arduino
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
void setup() {
 // set up the LCD's number of columns and rows:
 lcd.begin(16, 2);
 // initialize the serial communications:
 Serial.begin(9600);
}
void loop() {
 // when characters arrive over the serial port...
 if (Serial.available()) {
   // wait a bit for the entire message to arrive
   delay(100);
   // clear the screen
   lcd.clear();
   // read all the available characters
   while (Serial.available() > 0) {
     // display each character to the LCD
     lcd.write(Serial.read());
   }
 }
}

Code for Alexa Skill

JSON
{
  "languageModel": {
    "types": [
      {
        "name": "from_metric_type",
        "values": [
          {
            "id": null,
            "name": {
              "value": "ml",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "liters",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "ounces",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "killogram",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "kg",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "pounds",
              "synonyms": []
            }
          }
        ]
      },
      {
        "name": "metric_value_type",
        "values": [
          {
            "id": null,
            "name": {
              "value": "10",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "20",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "3.2",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "4.5",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "5",
              "synonyms": []
            }
          }
        ]
      },
      {
        "name": "name",
        "values": [
          {
            "id": null,
            "name": {
              "value": "Poched Egg",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "tea",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "Omlet",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "Pan Cake",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "Cake",
              "synonyms": []
            }
          }
        ]
      },
      {
        "name": "task_type",
        "values": [
          {
            "id": null,
            "name": {
              "value": "Get some eggs",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "Get some Milk",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "Take out the trash",
              "synonyms": []
            }
          }
        ]
      },
      {
        "name": "timer_time",
        "values": [
          {
            "id": null,
            "name": {
              "value": "12",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "30",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "50",
              "synonyms": []
            }
          },
          {
            "id": null,
            "name": {
              "value": "2",
              "synonyms": []
            }
          }
        ]
      }
    ],
    "intents": [
      {
        "name": "addnote",
        "samples": [
          "Take a note to {task}",
          "Remind me to {task}",
          "Add a note to {task}"
        ],
        "slots": [
          {
            "name": "task",
            "type": "task_type"
          }
        ]
      },
      {
        "name": "AMAZON.CancelIntent",
        "samples": []
      },
      {
        "name": "AMAZON.HelpIntent",
        "samples": []
      },
      {
        "name": "AMAZON.StopIntent",
        "samples": []
      },
      {
        "name": "deletenotes",
        "samples": [
          "Delete Note",
          "Clear all notes",
          "Delete all notes",
          "Remove all notes"
        ],
        "slots": []
      },
      {
        "name": "metric",
        "samples": [
          "Convert {value} {from_metric} to {to_metric}",
          "{value} {from_metric} to {to_metric}",
          "how much is {value} {from_metric} in {to_metric}"
        ],
        "slots": [
          {
            "name": "value",
            "type": "metric_value_type"
          },
          {
            "name": "from_metric",
            "type": "from_metric_type"
          },
          {
            "name": "to_metric",
            "type": "from_metric_type"
          }
        ]
      },
      {
        "name": "recipes",
        "samples": [
          "Show me the recipe for {recipe_name}",
          "Hot to make {recipe_name}",
          "Recipe for {recipe_name}"
        ],
        "slots": [
          {
            "name": "recipe_name",
            "type": "name"
          }
        ]
      },
      {
        "name": "temp",
        "samples": [
          "Whats the cooking temperature",
          "whats the temperature of the dish",
          "whats the temperature "
        ],
        "slots": []
      },
      {
        "name": "timer",
        "samples": [
          "Set timer for {time} seconds",
          "Create timer {time} seconds",
          "Timer for {time} seconds"
        ],
        "slots": [
          {
            "name": "time",
            "type": "timer_time"
          }
        ]
      }
    ],
    "invocationName": "kitchen assitant"
  }
}

Tea Recipe

JSON
{
	"1":{
		"read":"Bring freshly drawn, cold water to a boil in a kettle"
	},
	"2":{
		"read":"When water is at a gentle boil, remove heat."
	},
	"3":{
		"read":"Pour hot water into teapot and teacups and pour off. By warming the cups in this way, the water temperature will be more consistent."
	},
	"4":{
		"read":"Add the proper amount of tea leaves per person to the pot."
	},
	"5":{
		"read":"Allow water to cool to the proper temperature, if necessary, and pour over the tea leaves."
	},
	"6":{
		"read":"Steep for the proper length of time."
	},
	"7":{
		"read":"Strain completely into another teapot or directly into the serving cups."
	}
}

Temperature sensor Code

Python
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import gc
import webrepl
webrepl.start()
gc.collect()
import machine
import math
import time
import urequests as requests
import ujson as json
server_url = "Your ip goes here" #enter the ip of your nodejs server here
adc = machine.ADC(0)
THERMISTORNOMINAL = 10000
TEMPERATURENOMINAL = 25
NUMSAMPLES =5
BCOEFFICIENT =3950
SERIESRESISTOR =10000 
def calculate_temp(): #this function calculates the temperature from the analog values
	reading = adc.read()
	reading = (1023 / reading)  - 1
	reading = SERIESRESISTOR / reading
	steinhart = reading / THERMISTORNOMINAL     
	steinhart = math.log(steinhart)                  
	steinhart /= BCOEFFICIENT                 
	steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15) 
	steinhart = 1.0 / steinhart                 
	steinhart -= 273.15
	return  steinhart      
#this loop updates the temperature to the server every 5 seconds
while(1):
	r = requests.get(server_url+"/settemp?q="+str(calculate_temp()))
	time.sleep(5)

Credits

TheParticleGuy

TheParticleGuy

3 projects • 12 followers

Comments