About a year ago we picked up an Echo and began kitting our house out with Wemo switches and plugs. It's soooo nice to be able to tell Alexa to turn the lights off from the warmth of bed. This is where our 3 year old daughter comes into the picture. Too short to reach the light switches and lacking the vernacular to direct Alexa, but determined enough to keep trying. That's when I realized, the same tech that's made our lives convenient as adults can empower our children to use the same devices. At the same time, we can build a companion that grows with them. A toy that can be programmed with different games every week, or evolve into a study partner. Eventually, a friend she will one day learn to program herself, bridging not only the gap between children and smart homes, but driving interest in technology through a fluffy pal that matures in parallel.
This is where the Seeed Grove Starter Kit is ideal. It comes with a plethora of fun I/O devices and the potential of adding many more. Swapping in something new (like a distance sensor) is as simple as plugging in a cable.
To drive the shield I'm using the new Arduino 101 (Genuino in Canada & Europe). Much like the Uno, but with the notable additions of a gyroscope, accelerometer and bluetooth.
Finally, to control the Wemo's from the Arduino I'm using an old Android phone (S4 Active) using Evothings Studio, a cordova based mobile ide that lets you code apps using HTML5 and JS. This will pair with the 101 over bluetooth and control the Wemo's via the Maker IFTTT channel.
Bill of MaterialsHardware
- A BLE capable mobile device with the Evothings Viewer app installed
- Sewing Machine/Fabric (or use an existing toy)
Software
Arduino/Grove
Place the Grove Base Shield onto the Arduino, then attach the included sensors and outputs to match the diagram below.
Install the Arduino IDE according to the instructions online. With the software running plug in the Arduino 101 board with the usb cable. In the IDE go to Tools>Board and choose "Arduino/Genuino 101". Then under Tools>Port select the port your board is listed by, ex. COM3(Arduino/Genuino 101).
Clone, download or copy the code from GitHub and open /arduino/Tedduino/Tedduino.ino
/*
* Tedduino.ino
* @author : Justin Revelstoke
* @date : 3/25/2017
* @description : Arduino code for BLE connection and Grove Shield I/O.
* Displays a menu over RGB LCD offering sensor data and
* an example game.
*/
#include <Wire.h>
#include <CurieBLE.h>
#include "CurieIMU.h"
#include "config.h"
BLEPeripheral blePeripheral;
BLEService bleService(serviceUUID);
BLECharacteristic bleCharacteristic(characteristicUUID, BLERead | BLENotify, 2);
#include "rgb_lcd.h"
rgb_lcd lcd;
const int B = 3975;
const int pinTouch = 2;
const int pinBuzzer = 3;
const int pinButton = 4;
const int pinVibe = 5;
const int pinLED = 13;
const int pinSound = A0;
const int pinLight = A1;
const int pinTemp = A2;
const int pinPot = A3;
int menu = 0;
int previousButtonState = 0;
int length = 15; // the number of notes
char notes[] = "ccggaagffeeddc "; // a space represents a rest
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 };
int tempo = 300;
float aix, aiy, aiz; // accelerometer values
float gix, giy, giz; // gyroscope values
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
pinMode(pinLED, OUTPUT);
pinMode(pinPot, INPUT);
pinMode(pinTouch, INPUT);
pinMode(pinButton, INPUT);
blePeripheral.setLocalName(bleServiceName);
blePeripheral.setAdvertisedServiceUuid(bleService.uuid());
blePeripheral.addAttribute(bleService);
blePeripheral.addAttribute(bleCharacteristic);
const unsigned char bleCharArray[2] = { 0, (char)1 };
bleCharacteristic.setValue(bleCharArray, 2);
blePeripheral.begin();
CurieIMU.begin();
CurieIMU.setGyroRate(25);
CurieIMU.setAccelerometerRate(25);
CurieIMU.setAccelerometerRange(2);
CurieIMU.setGyroRange(250);
}
void loop() {
printGyroAccel();
digitalWrite(pinLED, LOW);
breath(REG_RED);
breath(REG_GREEN);
breath(REG_BLUE);
BLECentral central = blePeripheral.central();
if (central) {
Serial.print("Connected to central: ");
Serial.println(central.address());
while (central.connected()) {
displayMenu();
delay(100);
}
}
delay(100);
}
void nextMenu() {
if (menu == 5) {
menu = 0;
}
else {
menu++;
}
}
void displayMenu() {
resetLCD();
int buttonState = digitalRead(pinButton);
int touchState = digitalRead(pinTouch);
buttonState = buttonState | touchState;
if (previousButtonState != buttonState) {
if (buttonState == 1) {
const unsigned char bleCharArray[2] = { 0, (char)1 };
bleCharacteristic.setValue(bleCharArray, 2);
nextMenu();
delay(1000);
}
else {
const unsigned char bleCharArray[2] = { 0, (char)0 };
bleCharacteristic.setValue(bleCharArray, 2);
}
}
delay(100);
previousButtonState = buttonState;
if (menu == 0) {
printSound();
}
else if (menu == 1) {
printLight();
}
else if (menu == 2) {
printTemp();
}
else if (menu == 3) {
printVibe();
}
else if (menu == 4) {
printPot();
}
else if (menu == 5) {
nightMode();
}
else if (menu == 6) {
printGyroAccel();
}
else {
lcd.print("Error");
}
}
void resetLCD() {
lcd.clear();
lcd.setCursor(0, 0);
}
void printGyroAccel() {
CurieIMU.readAccelerometerScaled(aix, aiy, aiz);
CurieIMU.readGyroScaled(gix, giy, giz);
//CurieIMU.readMotionSensor(aix, aiy, aiz, gix, giy, giz);
lcd.print(aix);
lcd.print(' ');
lcd.print(aiy);
lcd.print(' ');
lcd.print(aiz);
lcd.setCursor(0, 1);
lcd.print(gix);
lcd.print(' ');
lcd.print(giy);
lcd.print(' ');
lcd.print(giz);
delay(100);
resetLCD();
}
int getSound() {
int sensorValue = analogRead(pinSound);
return sensorValue;
}
void printSound() {
int sensorValue = getSound();
lcd.print("Sound:");
if (sensorValue > 0) {
for (int i = 0; i < round(sensorValue/100); i++) {
lcd.print(">");
}
}
}
int getLight() {
int sensorValue = analogRead(pinLight);
return sensorValue;
}
void printLight() {
// range 0 - 1023
int sensorValue = getLight();
lcd.print("Light:");
if (sensorValue > 0) {
for (int i = 0; i < round(sensorValue/100); i++) {
lcd.print(">");
}
}
}
int getTemp() {
int sensorValue = analogRead(pinTemp);
float resistance = (float)(1023-sensorValue)*10000/sensorValue;
int temperature = 1/(log(resistance/10000)/B+1/298.15)-273.15;
return temperature;
}
void printTemp() {
lcd.print("Temp:");
lcd.print(getTemp());
lcd.print("F");
}
int getPot() {
return analogRead(pinPot);
}
void printPot() {
lcd.print("Pot:");
lcd.print(getPot());
}
int getVibe() {
return digitalRead(pinVibe);
}
void printVibe() {
lcd.print("Vibe:");
lcd.print(getVibe());
}
void nightMode() {
bool warm = false;
bool dark = false;
bool quiet = false;
if (getTemp() >= 50) {
lcd.print("Warm ");
warm = true;
}
else {
lcd.print("Cold ");
}
if (getLight() == 0) {
lcd.print("Dark ");
dark = true;
}
else {
lcd.print("Light ");
}
if (getSound() < 100) {
lcd.print("Quiet ");
quiet = true;
}
else {
lcd.print("Loud ");
}
if (warm && dark && quiet) {
playSong();
}
}
void playSong() {
for (int i = 0; i < length; i++) {
if (notes[i] == ' ') {
delay(beats[i] * tempo); // rest
}
else {
playNote(notes[i], beats[i] * tempo);
}
// pause between notes
delay(tempo / 2);
}
}
void playTone(int tone, int duration) {
for (long i = 0; i < duration * 1000L; i += tone * 2) {
digitalWrite(pinBuzzer, HIGH);
delayMicroseconds(tone);
digitalWrite(pinBuzzer, LOW);
delayMicroseconds(tone);
}
}
void playNote(char note, int duration) {
char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
// play the tone corresponding to the note name
for (int i = 0; i < 8; i++) {
if (names[i] == note) {
playTone(tones[i], duration);
}
}
}
void breath(unsigned char color)
{
for(int i=0; i<255; i++)
{
lcd.setPWM(color, i);
delay(5);
}
delay(500);
for(int i=254; i>=0; i--)
{
lcd.setPWM(color, i);
delay(5);
}
delay(500);
}
Be sure config.h is in the same directory
/*
* config.h
* @author : Justin Revelstoke
* @date : 3/25/2017
* @description : Configuration
*/
// You're device will appear in bluetooth connection lists under this name
#define bleServiceName "Tedduino"
#define serviceUUID "180D"
#define characteristicUUID "2A37"
Click the Upload button in the Arduino IDE, the console should read :
Starting download script...
SUCCESS: Sketch will execute in about 5 seconds.
If it worked correctly you should now see the LCD pulsing red, green and blue.
At this point the Arduino is advertising a BLE connection, but has a number of "offline" functions. Pressing the button or touch sensor will rotate through various displays on the LCD showing reading from the attached sensors. There is also a sample "game" which asks you to place the Arduino somewhere light|dark, warm|cold, or quiet|loud. Upon success a song will play :)
Evothings
Now to set up the BLE companion app. If you are interested in learning more about Bluetooth Low Energy I highly recommend this primer from Adafruit. Also, if you want to use the BLE connection without needing to make a web request Blynk is a great way to get connected to your Arduino fast. It just doesn't support the functions I needed over BLE.
So the first thing we need to do is download and install Evothings Studio (version 2.2.0 as of this writing). Be sure to get a cloud token from the download page.
Next, download the Evothings Viewer from the app store.
Type in the connect key from Studio into the Viewer to link them.
Now, from the same repository you cloned earlier open the cordova folder and find the evothings.json file. Click and drag this file into Evothings Studio to create the project. You should now see ...
And if you click "Run", the app should start on your phone
IFTTT Integration
To finalize the Evothings code we'll need to set up a Maker channel in IFTTT. To do this go to IFTTT. Log in or create an account if you don't already have one. Then under "My Applets" click "New Applet". Follow the series of images below to set up your Maker channels. Be sure to copy the key Maker generates for you after it sets up your url. We'll need that for cordova. Create two channels, one with "tedduino_on" as the trigger, the other with "tedduino_off". Choose "Turn On" and "Turn Off" for the Wemo actions accordingly.
If all goes well we should now have two channels and a Maker key. You can test the URLs in a browser (they should look something like http://maker.ifttt.com/trigger/tedduino_off/with/key/{your maker key}
).
Putting it all together
Back in the cordova code from GitHub open index.html inside the app folder. Find the line:
var
makerKey
= '/with/key/INSERT YOUR KEY HERE';
And replace "INSERT YOUR KEY HERE" with your maker channel key. Save and reconnect your Evothings studio to the viewer.
Congrats! At this point you should be able to run the Evothings app and click "Connect". If it successfully connects to the Arduino you can press "Test" and the LCD should stop changing colors. You should also be able to push the Grove button to turn the lights on and off.
If you're handy with a sewing machine I'd recommend designing your own doll. Otherwise you can easily retrofit these parts into an existing one. Just replace the usb cable with the Arduino battery pack. Hot glue is a great way to keep the pieces in place. Have fun with it!
As time goes on and more aspects of our homes are becoming automated I hope more devices like this are made to give our children more independence.
Comments