Category: ESP8266

Domoticz, ESP & MQTT, startup

Domoticz, ESP & MQTT, startup

Of course domotica with Domoticz is fun but it’s more fun if you can combine your own ESPs with Domoticz. That is what I have been fiddling with and what I will describe here.

There are basically 2 ways to connect to Domoticz, through HTTP or MQTT. With HTTP you can only tell Domoticz what to do. With MQTT however, Domoticz shares it’s changes so you can also respond to changes. I choose to go the MQTT route because that way I can create sensors and switches with my ESPs.

To get things started, you need to have an MQTT broker. There have been many people describing this on their blogs, so I will not cover that here. Suffice it to say that I choose to put the MQTT broker on my NAS since that is always up anyway. Once you have the MQTT broker, you need to configure MQTT for Domoticz. This is described nicely on their Wiki. You will also find instructions on how to install Node-RED. I did install that but you don’t need it for these projects. Once you have installed MQTT for Domoticz, you need to tell it to publish everything on the flat version (called out in the drop down list).

We are getting there. Once you have installed and configured MQTT for Domoticz, use an MQTT viewer to view the messages it generates. I personally like mqtt-spy. Remember you have to subscribe to the topic domoticz/out. Look at some of the messages and how they are structured. As you can see it’s JSON formatted. The messages you post to Domoticz on the domoticz/in topic are also JSON formatted.

Ok, let’s configure a dummy switch in Domoticz. I called mine TESTMQTT but you can call it whatever you want. Make sure it is a switch. Once you did that, write down the id number.

Good, now let’s play. Turn it on and back off again. Now go to your viewer and look for the id of your switch. If you are using mqtt-spy, it is the search button on the right of the screen next to where all the messages are shown. Once you find your message, you will see that it looks something like this:

{
  "Battery" : 255,
  "RSSI" : 12,
  "description" : "",
  "dtype" : "Light/Switch",
  "id" : "00014648",
  "idx" : 1528,
  "name" : "TESTMQTT",
  "nvalue" : 0,
  "stype" : "Switch",
  "svalue1" : "0",
  "switchType" : "On/Off",
  "unit" : 1
}

I won’t go in detail now, just now that “idx” is your id and in the case of a switch, “nvalue” tells whether it was turned on (1) or off (0).

Next, let’s turn on the switch using MQTT. Make sure the switch is off and than go to your viewer and send this message (remember to use your id number, not mine)

topic: domoticz/in
message: { "command" : "switchlight" , "idx" : 1528 , "switchcmd" : "On" }

And click on publish. Your dummy switch should turn on.

Next up is coding an ESP to be a light. As we need JSON to listen to the switches, we need to install the ArduinoJson library which you install like any library. We also need to install the pubsubclient library which again installs like any library. However in this case we need to make a change after the installation. Go to your libraries directory and go to the pubsubclient-master/src directory. There is a file called PubSubClient.h. Open it to make a change. We need to increase the maximum packet size. Around line 26 there is a line that we need to change. It should read after you’re done:

#define MQTT_MAX_PACKET_SIZE 512

Next save the file and we are ready to try some code. We will start with something simple, the internal led being turned on and off through Domoticz. I will start by sharing the code (also available on github-mark-32px Github) and discuss it below the code:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

const char* ssid = "MySSID";
const char* password = "MyPassword";
const char* mqtt_server = "mqtt.broker.com";
#define LED D4
#define MYIDX 1528

WiFiClient espClient;
PubSubClient client(espClient);

long lastMsg = 0;
char msg[50];
int value = 0;
char macAddr[18];
char mqttClient[25];

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void actionOn () {
  digitalWrite(LED,LOW);
}

void actionOff () {
  digitalWrite(LED,HIGH);
}

void errorOccurred (String err) {
  Serial.println(err);
}

void callback(char* topic, byte* payload, unsigned int length) {
  DynamicJsonBuffer jsonBuffer(512);
  JsonObject& root = jsonBuffer.parseObject(payload);
  if (!root.success()) {
    errorOccurred("Parse json failed");
  } else {
    int idx = root["idx"];
    if (idx==MYIDX) {
      if (root["nvalue"]==0) {
        actionOff();
      } else {
        actionOn();
      }
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    sprintf(mqttClient,"ESP%s",macAddr);
    Serial.println(mqttClient);
    if (client.connect(mqttClient)) {
      Serial.println("connected");
      client.publish("outTopic","hello world");
      client.subscribe("domoticz/out");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  pinMode(LED,OUTPUT);
  digitalWrite(LED,LOW);
  Serial.begin(9600);
  setup_wifi();
  client.setServer(mqtt_server,1883);
  client.setCallback(callback);
  byte ar[6];
  WiFi.macAddress(ar);
  sprintf(macAddr,"%02X%02X%02X%02X%02X%02X",ar[0],ar[1],ar[2],ar[3],ar[4],ar[5]);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

Let’s start at the top. We include the needed libraries and define our constants for wifi and the MQTT broker. I use a Wemos D1 so my internal led is linked to D4 and my test Domoticz dummy switch has id 1528. Initialize the wifi client and the MQTT client. I keep the mac address which I use for creating the client id and the name of the MQTT client.

The function setup_wifi() does what it says. The functions actionOn() and actionOff() are called to turn on and turn off our switch. In this case it is just write to a port but you can put more here. The function errorOccurred gets called when an error happens. It now just outputs to serial but there might be a need to change that.

The function callback() is the interesting one. This one gets called every time we receive a message on a topic that we are subscribed to. We assume the data is in JSON format and decode it using the ArduinoJson library. Next we check if this was a success and if so, if the idx passed is the one we are looking for. If it is, check nvalue and determine what to do based on it’s value.

The function reconnect() is straight from the pubsubclient example and basically reconnects if the connection is dropped and subscribes to our domoticz/out topic.

Finally the function setup() sets everything up, including getting a unique MQTT client name and the function loop() continously checks for messages.

If you upload this to your ESP it should work. Remember that we do not set any initial values so, the light starts on but maybe in Domoticz it is still off which means you will have to click twice for any change.

This is a very simple example, hardly any error checking but for some of you this may be enough to get going. In the next example I will expand a bit and also publish to Domoticz.

 

Hacking the SONOFF RF

Hacking the SONOFF RF

Some time ago I bought two SONOFF RF’s, the power switch with an ESP8266 inside. I wasn’t planning on using them out of the box, mostly because I don’t want another app on my phone. I have domotica and I want to control all with the same app. My domotica can control devices based on an http call, so this should work.

While digging around on the internet, I found several sites which explain how to hack the normal SONOFF, without the RF. I liked this page but there are many others. Unfortunately, I couldn’t find any page on how to hack the SONOFF RF so I decided to figure it out myself.

I couldn’t get the ESP in flash mode, no matter how hard I tried. When you’re hacking the SONOFF apparently you can use the button which is connected to GPIO0 to put it in flash mode but that did not work on the SONOFF RF. Finally I decided to connect the GPIO0 to ground straight from the chip and see what happens. That worked!

Let’s look at the chip layout:

esp8266_extra_gpio

The LNA (pin 2) should be easy to find since that is the antenna. I looked at the bottom of the print and found it. Forgive the lousy quality but you can see what I found:

antenna and gpio0

You can see C27 connected to the 2nd pin from the top on the left side. That aligns with the schematic above and this means that the 2nd pin from the right on the bottom side should be GPIO0. It runs to a resistor at which point I soldered a wire to it.

The rest of the connections are the same as with the normal SONOFF:

sonoffrf1.jpg

I soldered a strip to the board, from the button to the top the pins are:

  • 3.3V
  • TX
  • RX
  • GND
  • GPIO14

You connect the 3.3V to your FTDI module on the 3.3V, TX on SONOFF RF to RX on FTDI, RX on SONOFF RF to TX on FTDI and GND to GND. What I did is put a break switch on the line to the power because that way I can quickly reboot the SONOFF RF without unplugging the FTDI (reboot to put it in flash or take it out of flash):
(forgive the sloppy soldering 🙂

SONOFF 2 FTDI

Almost there… Now I want to be able to put the SONOFF RF into flash mode whenever I want and apparently the button does not work. So I soldered a small push button (break if not pressed) between my wire on the bottom and GND but I made the button at the top so I can access it easily. I passed the wire to the front:

sonoffrf2

and connected it to the GND of the button of the SONOFF RF (which is the left side in the picture below) via a small push button:

sonoffrf3

Now if I want to put the ESP in flash mode, I push the button on the power cable (cutting the power to the ESP), push the small button (connecting GPIO0 to GND), release the power button (which boots the ESP) and after a short while release the small button because the ESP will now be in flash mode.

Now that I can program the ESP I can figure out why the regular push button does not work to connect GPIO0 to GND like in the normal SONOFFs.

In short, these are the connections:

  • The relay is connected to GPIO12
  • The led is connected to GPIO13
  • The button is connected to GPIO0

Even though the button is connected to GPIO0, pushing it however only brings GPIO0 to GND for a short while. Even if you keep the button pushed, it only briefly connects GPIO0 to ground which is why you cannot use the button to put the ESP in flash mode. The reason for that is that the RF module also does the same. Pushing the button on the remote actually causes GPIO0 to connect to GND which is probably why they did something different with the button than in the normal SONOFF.

Now that we know that we can build the program. We need to control the relay and the led and we need to watch the button/RF button through GPIO0.

I have created this simple program which should do the trick:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

// Replace with your network credentials
const char* ssid = "YourSSID";
const char* password = "YourPassword";

ESP8266WebServer server(80);

String pagePart1, pagePart2;

int sonoffLed = 13;
int sonoffRelay = 12;
int sonoffButton = 0;
bool buttonState = false;
bool powerState = false;

void powerOn (void) {
  digitalWrite(sonoffLed,LOW);
  digitalWrite(sonoffRelay,HIGH);
  powerState = true;
}

void powerOff (void) {
  digitalWrite(sonoffLed,HIGH);
  digitalWrite(sonoffRelay,LOW);
  powerState = false;
}

void switchPower (void) {
  if (powerState) {
    powerOff();
  } else {
    powerOn();
  }
}

void setup(void){
  pagePart1 = "<h1>SONOFF Switch</h1><br><br>Your switch is ";
  pagePart2 = "</font>.<br><br><a href='on'><button>ON</button></a> ";
  pagePart2 = pagePart2+<a href=\"off\"><button>OFF</button></a></p>";  
  // preparing GPIOs
  pinMode(sonoffLed,OUTPUT);
  pinMode(sonoffRelay,OUTPUT);
  powerOff();

  Serial.begin(9600); 
  Serial.println("");
  WiFi.begin(ssid,password);

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/",[](){
    String tmp = pagePart1+"<font color=";
    if (powerState) {
      tmp = tmp+"green>on";
    } else {
      tmp = tmp+"red>off";
    }
    tmp = tmp+pagePart2;
    server.send(200,"text/html",tmp);
  });
  server.on("/on",[](){
    String tmp = pagePart1+"now switched <font color=green>on"+pagePart2;
    server.send(200,"text/html",tmp);
    powerOn();
    delay(1000);
  });
  server.on("/off",[](){
    String tmp = pagePart1+"now switched <font color=red>off"+pagePart2;
    server.send(200,"text/html",tmp);
    powerOff();
    delay(1000); 
  });
  server.begin();
  Serial.println("HTTP server started");
}
 
void loop(void){
  server.handleClient();
  bool currentState = digitalRead(sonoffButton);
  if (currentState!=buttonState) {
    buttonState = currentState;
    if (buttonState==LOW) {
      switchPower();
    }
  }
  delay(50);
}

The nice thing about the way the SONOFF RF is designed is that the RF part is completely separate from the ESP part so after reprogramming the ESP, the RF still works as originally designed. This means that you can still program the RF receiver as you would normally:

sonoff_rf_pair_with_433-1

Press the button on the SONOFF RF twice quickly and than press a button on the remote. This button will than be linked to the RF and can be used, with the above program, to turn the switch on and off.

Update for library PAM_WiFiConnect

Update for library PAM_WiFiConnect

I recently had a short power outage. Nothing major but a feature request for the connect library did come out of that. When the power came back on, my ESP with my temperature logger was back on faster than my wifi which meant that after the wifiConnect nothing else happened. Ok, I could have coded that in the program but than I realized that if you have some sort of IoT device, it does not make sense to return to the setup function from the wifiConnect if you have no wifi. A need was born.

The result? A function called waitWiFiConnect which does what it’s name implies; it waits for a wifi connection. It does this by turning on the internal led, GPIO2, try to connect via wifiConnect. When it returns from that function it checks whether there is a connection. If there is a connection the led is turned off and the function ends. If not, the led blinks shortly after which we wait for 1 minute before trying again. It will only return to the caller after it has a wifi connection.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Arduino IDE libraries

Arduino IDE libraries

After creating my initial library, I saw it grow rapidly with more generic functions I wanted so instead of staying with one library which could grow out of proportion (from a code size perspective), I decided to create a library for each of my needs (which of course makes sense from a lot of angles). So far this I have created the following libraries (so far mostly aimed at the ESP8266):

  • PAM_Tools
    This library contains functions to access the file system to store and retrieve information;
  • PAM_WiFiConnect
    This library contains functions to easily connect to the wifi, including selecting one of multiple access points;
  • PAM_WiFiClient
    This library contains functions to easily retrieve a webpage;
  • PAM_WebAPI
    This library contains functions to use web based APIs such as IFTTT and ThingSpeak.
  • PAM_WiFiServer
    This library contains functions for creating a webserver.

 

Library PAM_WiFiServer

Library PAM_WiFiServer

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is there to easily create a webserver.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

void wifiServerStart()

This function needs to be called once before you use any of the other functions.

bool serverRequestReceived()

This function you should call frequently in your loop function. If true, you should create a webpage to be send back using the other functions.

String pageRequested ()

You can call this function if a serverRequestReceived returned true and it will return the name of the page that is requested.

String giveURLParam (String search)

If a serverRequestReceived than you can check with this function whether a specific parameter has been send. It will return the value of that parameter or PARAMNOTFOUND if the parameter is not in the list. This function is used to get the values submitted for example throught a form.

void serverSendPage (String paramName[], String paramValue[], int paramCount, String pageName)

This is the function that sends back a page. The way it works is that a pageName is retrieved from the filesystem /page/<pageName> and line by line each parameter given in paramName is replaced by the corresponding paramValue. So for every line retrieved, the function loops through all paramNames and replaces each instance of %paramName% with the corresponding value. The value paramCount should contain the number of elements in both paramName and paramValue. The pageName variable should contain the page that needs to be parsed and send back to the client.

There are multiple instances of this function defined. If no pageName is given, it is assumed to be index.htm. If no parameters are given, no parameter replacement will be done. If neither parameters nor a pageName is given, the file index.htm will be send without parsing.

If the requested pageName is not found, the headers will be send put the page will be blank.

 

Library PAM_WebAPI

Library PAM_WebAPI

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is there to use different web based APIs such as that of IFTTT and ThingSpeak.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

String ifttt (String event, String value1, String value2, String value3)

If This Than That, IFTTT, is a very extensive online tool that allows you to act on an event. The concept is based on channels that can send and receive data. A specific combination of channels and actions is called an applet.

There is also a channel called Maker which you can use for your own IoT devices. This is what this library is for. You get your own Maker key which you should put in the library PAM_Defines.h which is nothing more than a way for you to select which APIs are compiled in. If you don’t use IFTTT for example, you should also not have to overhead when compiling so if you do not define a key, the functions will be excluded from the library which should save memroy. I will not go into detail on IFTTT usage just now. Suffice it to say you need a key which you define in PAM_Defines.h:

#ifndef PAM_Defines_H
#define PAM_Defines_H

//
// This is where you define your own IFTTT.com key
//
#define IFTTTKEY "YOUR-KEY-HERE"

#endif

and you need an event type which defines what kind of trigger you want to send. I have also defined these in my PAM_Defines.h:

#define IFTTT_BOOT "PAM_BOOT"
#define IFTTT_HTTP "PAM_HTTP"
#define IFTTT_MSG "PAM_MSG"

There are multiple instances of the same function created in the library so you can call it with no values, one, two or all three.

As an example, I use the event IFTTT_BOOT to send me a message if an IoT device boots where I send it’s IP as value 1. If it also contains a webserver I use the event IFTTT_HTTP along with it’s IP as value 1 so the message can actually contain a link to the IP.

I use IFTTT_MSG to send a message, for example if my plant needs watering.

void thingspeak (String api, String field1, String field2, String field3, String field4)

void thingspeak (String api, float field1, float field2, float field3, float field4)

The next API included is that for ThingSpeak. ThingSpeak call themselves an open data platform for the Internet of Things. In order to work with ThingSpeak, you need to set up an account and than set up a channel. Unlike IFTTT, you need a different API key per channel so in the library I choose to have one define to say you want to use the library. I use seperate defines for each of my channels:

#define THINGSPEAK
#define TS_TEST "MY-API-KEY"

The #define THINGSPEAK is needed to compile this part of the library. Like IFTTT there are multiple instances of the library. You can call the function with just one parameter or up to four parameters (ThingSpeak actually allows eight but I have not set up for that yet. If it is needed, let me know). The API key is mandator as well as the first field. The API of ThingSpeak allows you to set a date time for the event and if you don’t current date time is used. Since I wanted to keep the library small, I did not implement the seperate date time parameter.

You can use String for the values or int/float.

Library PAM_WiFiClient

Library PAM_WiFiClient

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library can be used for easy webpage retrieval. You can do a fire and forget or parse through every line, with or without the header.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

String getURLParse (String host, String url, bool fullContent, void (*g)(String l), int port)

This is the main function to retrieve a webpage. All other page retrieval functions call this function. You pass a host and a url, starting with a /, tell it whether to send just the body (false) or include the header (true) followed by a function which receives one String at a time which will be the retrieved String and finally a port. The port can be omitted in which case it defaults to port 80.

This function will send a request to the host asking for the specificed url. It will than retrieve every line of the page, it will determine where the header stops and the page begins and send it to the function g (only the body if that was requested in the boolean fullContent).

The last line received will be returned in the String.

String getURLFullParse (String host, String url, void (*g)(String l), int port)

This function will call the main function requesting to send all lines, including the header to the function g. The port is optional. If omitted it will default to port 80.

String getURLBodyParse (String host, String url, void (*g)(String l), int port)

The function will call the main function requesting to send all lines of the body of the page to the fuction g. The port is optional. If omitted it will default to port 80.

String getURL (String host, String url, int port)

The fire and forget version, get the url but do not use the resulting webpage. Again, the port is optional and will default to port 80.

void doSerial (String webLine)

If you are debugging it could be convenient to see what the host is sending back. If you use the function doSerial as the function to process your lines, you will see the page received on your serial monitor.

 

Library PAM_Tools

Library PAM_Tools

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is aimed at using the file system to store keys.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

The following four functions are used to store and retrieve String and int values persistently (meaning between reboots).

String getStringKey (String location, String key)

This function will retrieve the content of a file in a string where the filename is /<location>/<key>.txt The .txt extension is used so on Windows you can create
the needed files in a data directory which you can than upload using the ESP8266 Sketch Data Upload. If the key is not found a NOSTRINGKEYFOUND will be returned.

int getIntKey (String location, String key)

This function will retrieve the content of a file and put it in an int where the filename is /<location>/<key>.txt The .txt extension is used so on Windows you can create
the needed files in a data directory which you can than upload using the ESP8266 Sketch Data Upload. If the key is not found a NOINTKEYFOUND will be returned.

void putStringKey (String location, String key, String value)

This function will put a String value into the file with filename
/<location>/<key>.txt If the file exists it will be overwritten.

void putIntKey (String location, String key, int value)

This function will put an int value into the file with filename
/<location>/<key>.txt If the file exists it will be overwritten.

 

Library PAM_WiFiConnect

Library PAM_WiFiConnect

After working with the Arduino IDE for some time, I decided it is time to write some convenient libraries that will help me quickly do some of the tasks that occur often and could do with a convenient wrapper.

This library is aimed at easily connecting to wifi. I know that there are other libraries that support this but mine supports me better and might support you better too.

The library needs one other library, PAM_Tools.

github-mark-32px Available on GitHub with a lot of comment in the code. Direct download.

Let’s look at the functions:

void wifiConnect (String ssid, String password)

This function will connect you to the specified SSID using the specified  password. It is different from the standard connect to wifi and just waiting on a connected in two ways:

  • It will break trying to connect after 5 runs of 10 seconds waiting on a connection
  • It will disconnect from wifi and try again in every run.

In my experience some of the connections might not work in the first try (especially hotspots on a mobile phone) and by stopping the connection and starting again I noticed that most connections will succeed within 15 seconds.

I also wanted the wifi connection not to run forever but break off at some point so you could do some error handling.

void wifiConnect (String ssid)

This instance of the function will get the password for the specified SSID from the file system, through one of the functions in PAM_Tools, and than uses wifiConnect to connect to it.

Passwords are stored in a file on the file system in the directory wifi where the full name of the file will be /wifi/ssid.txt and this file will contain one line and that is the password for that ssid. There are two advantages to do this:

  • If you share your code and you forget to change your ssid to Your-SSID at least you will not have given away your password
  • If you have multiple ssid’s that you can connect to, which is what wifiConnect does if you do not supply any SSID, you do not have to have all SSIDs and passwords in your file.

void wifiConnect (bool debug)

This instance of the function in the library is probably the most powerful/convenient. What it does is scan the available networks and check for each one found whether there is a associated password stored in the file system. If so, it will try to connect.

You can list preferred networks by storing SSIDs in a numbered file in the same directory. So if there is a file called 1.txt in the wifi directory, it will open that file and check whether that SSID is available. If not it will try 2.txt etc. If none of them are available or there is no 1.txt then it will try to use any of it’s other credentials. If there is still no connection, it will try the SSID in 0.txt which could be a hidden SSID. I found this function works great if you have a mobile IoT device. Even with fixed devices I now use it and put my mobile hotspot as the first to check (1.txt). It usually isn’t on, but if I want to isolate the IoT, for example for testing, I can turn on my hotspot, reset it and it will connect to my hotspot.

If you want to see which networks are found and how it decides to connect to it, you can call it with a parameter true after which it prints out what it does on Serial.

void wifiConnect ()

This instance of the function in the library will call wifiConnect(false) and as a result try to automatically connect to a wifi network as described above.

Fun with ESP, WS2812 and Homewizard

Fun with ESP, WS2812 and Homewizard

Well, I managed to get the ESP8266 working with the WS2812 in the Arduino IDE and it is time for some fun in combination with my Homewizard.

What I wanted to accomplish is this: I have solar panels at home and I have connected these to my Homewizard (a domotica appliance) along with the power meters which means my Homewizard knows about my power usage and my solar power production. What I wanted to do is have a small WS2812 ring show my usage and my production simultaneously. I defined usage as being the ‘red’ color and solar production as ‘yellow’. I want to show the production at the same time as the usage so I thought I would define each led to be the equivalent of 250 W power. I would ‘plot’ both the production and the usage on the circle starting at the same point. The overlap would be orange and excess solar power would be yellow and excess usage would be red, so if the circle showed 3 orange leds and 3 yellow, it meant I was producing 1500W (3+3 times 250W) while at the same time using 750W (3 times 250W). If the circle would show 3 orange and 3 red, it would mean 750W production and 1500W usage. After attempt 1 I found out that it was possible to use more than the LEDs would allow (ok, has not happened yet, but theoratically it is possible so I decided to add overflow. If production or usage would be over 3000W (12 times 250W), any excess would result in blue leds at the beginning, symbolizing double ‘value’ LEDs.

Still with me so far? After implementing, I found out that during Autumn, which is now when I created this project, production is not that high and my usage is usually also not that high, so often only 2 or 3 LEDs would light up. What I needed was increased resolution in such cases. I know, way too complex but he, it was meant to be a fun project. So I introduced green LEDs at the beginning. One green LED meant the resolution of the remaining 11 LEDs was 125W and two green LEDs mean the resolution of the remaining 10 LEDs was 50W.

The code will probably not be something you want to use without modifying but it might help you get some ideas on how to play with the WS2812. The code for getting the solar production and usage might be useful to others with a Homewizard.

github-mark-32px Available on GitHub with more comment in the code. Direct download.

Let’s look at the code:

#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include <PAM_WiFiConnect.h>
#include <PAM_WiFiClient.h>

#define CIRCLEPIN 2
#define PIXELCOUNT 12
#define POWERPERLED1 250
#define POWERPERLED2 125
#define POWERPERLED3 50

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXELCOUNT,CIRCLEPIN,
  NEO_GRB+NEO_KHZ800);

int solarPower = 0;
int powerConsumption = 0;
float solarPowerDay = 0;
float powerConsumptionDay = 0;

void setup() {
  Serial.begin(115200);
  wifiConnect("YourSSID","YourPassword");
  pixels.begin();
  for(byte i=0;i<PIXELCOUNT;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void readSolar () {
  String t = getURL("YourHomewizardUrl","/YourHWPassword/get-status",80);
  t = t.substring(t.indexOf("energylinks"));
  t = t.substring(t.indexOf("s1"));
  t = t.substring(t.indexOf("po")+4);
  int xsolarPower = t.substring(0,t.indexOf(",")).toInt();
  t = t.substring(t.indexOf("dayTotal")+10);
  int xsolarPowerDay = t.substring(0,t.indexOf(",")).toFloat()*1000;
  t = t.substring(t.indexOf("used"));
  t = t.substring(t.indexOf("po")+4);
  int xpowerConsumption = t.substring(0,t.indexOf(",")).toInt();
  t = t.substring(t.indexOf("dayTotal")+10);
  int xpowerConsumptionDay = t.substring(0,t.indexOf(",")).toFloat()*1000;
  t = t.substring(0,t.indexOf("heatlinks"));
  Serial.print("Solar       now :");
  Serial.println(solarPower);
  Serial.print("Consumption now :");
  Serial.println(powerConsumption);
  Serial.print("Solar       day :");
  Serial.println(solarPowerDay);
  Serial.print("Consumption day :");
  Serial.println(powerConsumptionDay);
  Serial.println();
  if (xsolarPower!=0 || xpowerConsumption!=0) {
    solarPower = xsolarPower;
    solarPowerDay = xsolarPowerDay;
    powerConsumption = xpowerConsumption;
    powerConsumptionDay = xpowerConsumptionDay;
  }
}

void loop() {
  readSolar();

  byte solarLed;
  byte powerLed;
  byte base;
  if (_max(solarPower,powerConsumption)<=POWERPERLED3*(PIXELCOUNT-2)) {
    solarLed = round((solarPower+POWERPERLED3/2)/POWERPERLED3)+4;
    powerLed = round((powerConsumption+POWERPERLED3/2)/POWERPERLED3)+4;
    base = 2;
    for (byte i=0;i<base;i++) {
      pixels.setPixelColor(i,pixels.Color(0,42,0));
    }
  } else if (_max(solarPower,powerConsumption)<=POWERPERLED2*(PIXELCOUNT-1)) {
    solarLed = round((solarPower+POWERPERLED2/2)/POWERPERLED2)+2;
    powerLed = round((powerConsumption+POWERPERLED2/2)/POWERPERLED2)+2;
    base = 1;
    pixels.setPixelColor(0,pixels.Color(0,42,0));
  } else {
    solarLed = round((solarPower+POWERPERLED1/2)/POWERPERLED1);
    powerLed = round((powerConsumption+POWERPERLED1/2)/POWERPERLED1);
    base = 0;
    if (solarLed>PIXELCOUNT || powerLed>PIXELCOUNT) {
      base = _max(solarLed,powerLed)-PIXELCOUNT;
      for (byte i=0;i<base;i++) {
        pixels.setPixelColor(i,pixels.Color(0,0,42));
      }
    }
  }
  for (byte i=base;i<_min(solarLed,powerLed)-base;i++) {
    pixels.setPixelColor(i,pixels.Color(110,27,0));
  }
  if (powerLed>solarLed) {
    for (byte i=_max(base,_min(solarLed,powerLed)-base);i<powerLed-base;i++) {
      pixels.setPixelColor(i,pixels.Color(55,0,0));
    }
    for (byte i=powerLed-base;i<PIXELCOUNT;i++) {
      pixels.setPixelColor(i,pixels.Color(0,0,0));
    }
  } else {
    for (byte i=_max(base,_min(solarLed,powerLed)-base);i<solarLed-base;i++) {
      pixels.setPixelColor(i,pixels.Color(55,42,0));
    }
    for (byte i=solarLed-base;i<PIXELCOUNT;i++) {
      pixels.setPixelColor(i,pixels.Color(0,0,0));
    }
  }
  pixels.show();
  delay(500);
}

Ok, maybe way to much code to just dump here but it’s not that difficult to read.

Let’s start with the includes; NeoPixel library from AdaFruit, the standard ESP8266WiFi library and my own two libraries, PAM_WiFiConnect and PAM_WiFiClient. Next we define our constants and initalize the NeoPixel library.

The setup function is straight forward, set up Serial, connect to wifi and set all pixels to black.

The readSolar function connects to the Homewizard and gets the status information. This json file is than parsed a couple of times to get the current solar power production in solarPower, the days solar power production solarPowerDay, the current power consumption, powerConsumption and finally the power consumption of the day, powerConsumptionDay. Both day values are not used in this sketch. I intend to make some kind of switches where you can choose to see the current production/consumption or the totals of the day.

The loop function is where the number of leds and color is determined. The first section, the extensive if statement, determines which value to assign to each led, 250W, 125W or 5oW. This is done by checking the maximum of the values of solarPower and powerConsumption. If the maximum is small enough to fit within the 50W scale that is used, if not it is checked whether it will fit in the 125W scale and else it will be put in the 250W scale. If it is 50W, the first 2 leds are made green (signifying a change from the default scale) and 125W shows 1 green led.

Next the color of the remaining leds is determined by checking the maximum (to see if blue leds are needed) and than counting how many should be red (power usage), yellow (power production) or orange (production and usage).

As mentioned before, you probably cannot use the supplied code straight ‘out of the box’ but you can use it for ideas.

The end result of my solar meter in a small plastic box:

ws2812funa

At the bottom left, a USB to serial adapter. In the bottom middle a ESP-12E on a small breakout board. At the middle left a reed contact which allows for an update to the code by holding a magnet near the back of the box and finally the 12 bit neopixel circle.

When the box is closed, there is a normal piece of paper at the top abive the lights so that they are not too bright and slightly diffused which gives this end result:

ws2812funb