Overview

Skill Level: Beginner

Easy

Many IoT platforms enable remote devices to send in readings, the Watson IoT Platform also supports doing the converse - sending commands to the remote device. This opens up all sorts of interesting uses, here I show using them to switch on a kettle.

Ingredients

  • A Watson IoT Platform Registered Device
  • A WiFi Smart Switch
  • An ESP8266 dev board or FTDI/Serial Adpater¬†
  • A few jumper cables

Step-by-step

  1. Overview

    This resipe is going to demonstrate using Watson IoT device commands to control a physical device.

    A traditional example demonstrating this would be something like using an Arduino to switch on and off an LED. Trying to make that a little more exciting, instead of the LED this will switch on and off a mains appliance!

    Controlling mains appliances from a DIY device can be fraught with fear and danger, and rightly so as mains electricity can kill you, but these days there are devices that make this safe and easy to do – by using a Wifi Smart Switch.

    There are a variety of WiFi Smart switches available and it so happens that some of them have an ESP8266 microprocessor inside so its quite easy to re-program these with your own Arduino code to connect to the Watson IoT Platform.

    Here, I’m using a Sonoff S20, with a bit of Internet browsing you should be able to find something similar and to check if its reprogramable.

    Sonoff S20

  2. Take it apart

    A single screw holds the S20 together, unscrew that and you can pull the case apart. Once inside you can see the circuit board and on that near the bottom are four connection points labeled with white writing – VCC, RX, TX and GND. These are directly connected to an ESP8266 on the otherside of the circuit board, and the button near there is connected to the ESP8266 GPIO-0.

    s20-open

    Connecting a serial adapter to the four connection points and pressing the button when you power it on will put the ESP8266 into flashing mode and enable you to replace its firmware with your own Arduino code.

    A seasoned hacker/maker would simply solder on some header pins and connect up their FTDI/serial adapter. You might not be such a hacker type though and soldering is a significant barrier for many people so I want to show how you can do this more easiliy without even needing any soldering. Instead of soldering you can hold the jumper cables in place with tape or Blu-Tack, and as you may well not have an FTDI/Serial adapter you can use another ESP8266 or Arduino development board instead to do the reprogramming. 

    Here I’m using a NodeMCU dev board with a jumper between EN and GND on the NodeMCU to disable the on-board ESP8266. Then connect the¬†VCC, RX, TX and GND on the NodeMCU to the connection points on the S20. Use male jumper cables, poke the ends into the holes on S20 circuit board and squash a bit of Blu-Tack against them to hold them in place. Its flimsy, but they only need a connection for the 30 seconds or so while you do the code upload.

    wiring-1

    Now, hold down the button on the S20 board and connect a USB cable from the NodeMCU to your PC, and you’re ready to go to upload you own code. For more of an introduction on programming ESP8266’s for Watson this see the developerWorks recipe – Connect an ESP8266 with the Arduino SDK to the IBM Watson IoT Platform.

    s20-programmed  

     

  3. The Arduino Sketch

    The Arduino sketch to run on the S20 uses WiFi and MQTT to connect to the Watson IoT Platform and subscribes to the device commands MQTT topic. The relay inside the S20 which switches on and off the appliance is connected to GPIO-12 so when on/off commands are sent from Watson the skecth simply sets GPIO-12 high or low to switch the appliance on or off.

    To make the S20 a bit easier to use the sketch also uses the green LED on GPIO-13 to indicate the connection status – fast flashing and its trying to connect to Wifi, slow flashing shows MQTT is trying to connect to Watson, and hard on means its successfully connected ok and ready to go. The sketch also adds support for using the S20 button to manually toggle the appliance on and off so you don’t always need to go to Watson to control your appliance.

    Here is the sketch code, and its also here in Github. The only update you need to make is in the place commented “Customise these values” to add your Wifi network and Watson device details:

    /**
    * Demonstrates how to send a command to a Watson IoT Platform device
    *
    * You can use curl to test sending the commands:
    * curl -u <yourApiKey>:<yourApiPassword> -H "Content-Type: text/plain" -v -X
    * POST http://<yourOrg>.messaging.internetofthings.ibmcloud.com:1883/api/v0002/
    * application/types/<yourDeviceType>/devices/<yourDeviceId>/commands/gpio -d "on"
    *
    * Author: Anthony Elder
    * License: Apache License v2
    */
    #include <ESP8266WiFi.h>
    #include <PubSubClient.h>
    #include <Ticker.h>

    //-------- Customise these values -----------
    const char* ssid = "<yourWifiSSID>";
    const char* password = "<WifiPassword>";

    #define ORG "<yourWatsonOrg>"
    #define DEVICE_TYPE "<yourDeviceType>"
    #define DEVICE_ID "<yourDeviceId>"
    #define TOKEN "<yourDeviceToken>"
    //-------- Customise the above values --------

    #define DEVICE_BUTTON 0
    #define DEVICE_RELAY 12
    #define DEVICE_GREEN_LED 13
    #define DEVICE_RED_LED 14

    Ticker ledBlinker;

    char server[] = ORG ".messaging.internetofthings.ibmcloud.com";
    char authMethod[] = "use-token-auth";
    char token[] = TOKEN;
    char clientId[] = "d:" ORG ":" DEVICE_TYPE ":" DEVICE_ID;

    #define CMD_STATE "/gpio/"

    // use the '+' wildcard so it subscribes to any command with any message format
    const char commandTopic[] = "iot-2/cmd/+/fmt/+";

    void gotMsg(char* topic, byte* payload, unsigned int payloadLength);

    WiFiClient wifiClient;
    PubSubClient client(server, 1883, gotMsg, wifiClient);

    int buttonPressDuration;

    void setup() {
    Serial.begin(115200); Serial.println();

    pinMode(DEVICE_RELAY, OUTPUT);
    pinMode(DEVICE_GREEN_LED, OUTPUT);
    pinMode(DEVICE_RED_LED, OUTPUT);

    ledBlinker.attach(0.1, ledBlink); // fast blink indicates Wifi connecting
    wifiConnect();
    ledBlinker.attach(0.4, ledBlink); // slower blink indicates MQTT connecting
    mqttConnect();
    ledBlinker.detach();
    digitalWrite(DEVICE_GREEN_LED, LOW); // low is led on to show connected

    pinMode(DEVICE_BUTTON, INPUT);
    attachInterrupt(DEVICE_BUTTON, buttonPress, CHANGE);
    }

    int lastHeartBeat;

    void loop() {
    if (buttonPressDuration > 0) {
    doCommand(digitalRead(DEVICE_RELAY) ? "off" : "on");
    buttonPressDuration = 0;
    }

    if (!client.loop()) {
    mqttConnect();
    }

    if (millis()-lastHeartBeat > 10000) {
    Serial.print("loop: gpio "); Serial.print(DEVICE_RELAY); Serial.print(" current state ");
    Serial.println(digitalRead(DEVICE_RELAY) ? "On" : "Off");
    digitalWrite(DEVICE_GREEN_LED, HIGH); // flicker LED to show its active
    delay(200);
    digitalWrite(DEVICE_GREEN_LED, LOW);
    lastHeartBeat = millis();
    }
    }

    void gotMsg(char* topic, byte* payload, unsigned int payloadLength) {
    Serial.print("gotMsg: invoked for topic: "); Serial.println(topic);

    if (String(topic).indexOf(CMD_STATE) > 0) {
    String cmd = "";
    for (int i=0; i<payloadLength; i++) {
    cmd += (char)payload[i];
    }
    doCommand(cmd);
    } else {
    Serial.print("gotMsg: unexpected topic: "); Serial.println(topic);
    }
    }

    void doCommand(String cmd) {
    int currentState = digitalRead(DEVICE_RELAY);
    int newState = (cmd == "on");
    digitalWrite(DEVICE_RELAY, newState);
    Serial.print("Relay switched from ");
    Serial.print(currentState ? "On" : "Off");Serial.print(" to "); Serial.println(newState ? "On" : "Off");
    }

    unsigned long startPress = 0;

    void buttonPress() {
    int currentState = digitalRead(DEVICE_BUTTON);
    if (currentState == 0) { // 0 is pressed, 1 is released
    startPress = millis();
    } else {
    int diff = millis() - startPress;
    if (diff > 100) { // debounce
    buttonPressDuration = diff;
    }
    }
    Serial.print("Button "); Serial.print(currentState ? "released" : "pressed");
    Serial.print(" duration="); Serial.println(buttonPressDuration);
    }

    void ledBlink() {
    digitalWrite(DEVICE_GREEN_LED, ! digitalRead(DEVICE_GREEN_LED));
    }

    void wifiConnect() {
    Serial.print("Connecting to "); Serial.print(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }
    Serial.print("\nWiFi connected, IP address: "); Serial.println(WiFi.localIP());
    }

    void mqttConnect() {
    if (!!!client.connected()) {
    Serial.print("Reconnecting MQTT client to "); Serial.println(server);
    while (!!!client.connect(clientId, authMethod, token)) {
    Serial.print(".");
    delay(500);
    }
    Serial.println();
    }

    subscribeTo(commandTopic);
    }

    void subscribeTo(const char* topic) {
    Serial.print("subscribe to "); Serial.print(topic);
    if (client.subscribe(topic)) {
    Serial.println(" OK");
    } else {
    Serial.println(" FAILED");
    }
    }

     

  4. Using the switch

    Thats all there is to it, you can put the S20 back together now and start using it as a Watson controled smart switch.

    To switch it on and off you need to use some application connecting to Watson. A NodeRed flow would be an easy way to make an application and then you could do all sorts of fun an useful things like automatically switch the kettle on at the time you get up in the morning. Without writing an application an even easier way to test it out is to just use command line curl to do an HTTP POST to the Watson IoT REST interface. Here’s an example of that:

    curl -u <yourApiKey>:<yourApiPassword> -H "Content-Type: text/plain" -v -X 
    POST http://<yourOrg>.messaging.internetofthings.ibmcloud.com:1883/api/v0002/
    application/types/<yourDeviceType>/devices/<yourDeviceId>/commands/gpio -d "on"

    Just change the the text a the end of that curl command to be “on” or “off” to switch your appliance on and off.

    Here’s what using it looks like:

    Running

    (I’d hoped to use an animated GIF or video to show that but sadly it doesn’t seem possible to use those with developerWorks recipes)

  5. Summay

    There you go, a Watson controlled kettle, or any other mains appliance. The main point of the recipe is to show how easy it is to use device commands with the Watson IoT Platform.

    Please click the star to like this recipe if you found it useful or interesting, and if you do give it a go then use the comment section below to say how you get on.

     

8 comments on"Build your own Watson controlled kettle!"

  1. electrodude@1995 June 14, 2017

    the program didnt show any errors! great job! i typed the “curl” command in the command prompt of my windows and it shows “curl is not recognized as an internal or external command”.
    I then tried executing it in the powershell and there I got this error “Parameter cannot be processed because the parameter name ‘u’ is ambiguous”. Please could you direct me in the right direction? Thank you

    • Hi electrodude. I think Windows might not have the curl command as standard and from a quick Internet search I think the Powershell version of the curl command might not be exactly the same as the official curl for Linux and that is why the -u parameter isn’t working. There are a few ways you can get curl for Windows, one is to install Git (https://www.atlassian.com/git/tutorials/install-git#windows), some other suggestions are here: http://thesociablegeek.com/azure/using-curl-in-powershell/

      I’d go with the Git approach then in Windows Explorer right click and choose “Git Bash Here” which opens a command prompt where you can use the curl command.

      • electrodude@1995 June 20, 2017

        thanks a lot man! it worked great!
        the next thing i am trying to do is to somehow execute this using postmaster. but i keep running into error 415(unsupported media). If you find time to look through it I would be in your debt! Thanks a lot for the initial help!

        • It sounds like a missing header – are you setting the content type header? In the curl command in the article thats this bit : -H “Content-Type: text/plain”

          • electrodude@1995 July 22, 2017

            yes thats set. still kept showing that error. I just was revisiting your code and I somehow have forgotten how I got the APIKey and the APIPassword. I opened my IoTplatform starter credentials and found APIkey and APIToken but I keep running into an error when executing it in the curl bash. apparently there is a “(” in my APIToken and its showing that as an error. Where do I find the correct APIKey and APIPassword?.
            Thank You

          • There are some hints about how to use special characters in a curl command here: https://stackoverflow.com/questions/10060093/special-characters-like-and-in-curl-post-data

            You can’t view the token/password for a device after it is first created, but you can reset it to a new one in the IoT Dashboard.

  2. Hi antelder. the connection is established but it is disconnecting again. So, Reconnecting MQTT client to … and subscribe to iot-2 / cmd / + / fmt / + OK. This cycle continues.
    I used ESP8266-12E Nodemcu, I’ve created an android app for the publisher.

  3. DIEGOPHOENIX July 17, 2018

    Just a contribution: In my case, I’m using curl in Windows and publishing with the following command:

    curl -v -H “Content-Type: text/plain” -u “use-token-auth:” -d “TestMessage” https://2bb629.messaging.internetofthings.ibmcloud.com:8883/api/v0002/device/types/Buttons/devices/Button1/events/MyTopic

    It’s working fine, although I can’t get back the data using the esp8266. I uploaded the code in this recipe, subscribing to all topics (“iot-2/cmd/+/fmt/+”) and nothing. Seems that I need to use an APP or something, to get the data and provide to the subscribers. Anyone can help me?

Join The Discussion