As mentioned in my last post at the end of January, I added some more functionality to my ESP8266 project. In addition to the first attempts to include the information from the various thermostats on one screen (which, as you can see, doesn’t really work too well the way I implemented it at the moment), I also wanted to show the the current status about pollen, taking this information from the achoo website.
The challenge was the used protocol – the data is only available via https, not via basic http. The ESP8266 is quite versatile, but also quite slow when it comes to some more complex things (like handling https). Initially I tried to somehow implement accessing the url with https and the fingerprint from the supplied certificate. This didn’t work as described and it also seemed to be a bit over the top for just reading that information from the json that is returned (not to mention that the fingerprint changes when the certificate is renewed).
Instead, I opted for the unsafe but quicker option, to use the WiFiClientSecure class and calling the setInsecure method which just works fine for my purposes:
const char* host = "https://api.github.com";
WiFiClientSecure client;
client.setInsecure();
client.connect(host, httpsPort);
The resulting Json from querying the pollen information is then quite simply put into a DynamicJsonDocument so it can be parsed using names:
// Check https://arduinojson.org/v6/assistant/ for coming up with an appropriate size. DynamicJsonDocument doc(4096); DeserializationError error = deserializeJson(doc, httpsResult); if (error) { Serial.println(error.f_str()); return; } const char* sub_region = doc["sub_region"];
for (JsonObject elem : doc["pollen"].as<JsonArray>()) { const char* name = elem["name"]; const char* today_severity = elem["today"]["severity"]; const char* tomorrow_severity = elem["tomorrow"]["severity"]; // Only keep info for those plants/trees that have a severity above 0. if (*today_severity != '0' || *tomorrow_severity != '0') { pollenStructure[arrayElementsCounter] = { name, today_severity, tomorrow_severity }; }
So as you can see, it is a very simple approach (still getting used to the Arduino IDE) where I only keep those entries that have a severity for any of the days not equal to ‘0’.
The second task was the implementation of the thermostat information, based on the type of device returned from the Fritz!Box. For simplicity, I check every device in the returned XML structure for a <temperature> element and then just use this – you could, if you wanted to be more precise, check the productName and/or the functionbitmask attributes, but right now I don’t see the need for it.
The code basically works, but as you can see on the photo below, it doesn’t fit on the small display that I’m using right now, so this will still have to be changed (either the code or the display, that is…).
while (httpResult.indexOf("<device", startDeviceIndex) != -1) { startDeviceIndex = httpResult.indexOf("<device ", startDeviceIndex); endDeviceIndex = httpResult.indexOf("</device>", endDeviceIndex); String currentDevice = httpResult.substring(startDeviceIndex, endDeviceIndex); startDeviceIndex++; endDeviceIndex++; // Get the device name. int deviceNameStartIndex = currentDevice.indexOf("<name>"); int deviceNameEndIndex = currentDevice.indexOf("</name>"); String deviceName = currentDevice.substring(deviceNameStartIndex + 6, deviceNameEndIndex); // Get the device temperature int deviceTemperatureStartIndex = currentDevice.indexOf("<temperature>"); int deviceTemperatureEndIndex = currentDevice.indexOf("</temperature>"); float degreesCelsius = -271; if (deviceTemperatureStartIndex != -1 && deviceNameEndIndex != -1) { String temperatureInfo = currentDevice.substring(deviceTemperatureStartIndex, deviceTemperatureEndIndex); float degreesCelsius = temperatureInfo.substring(temperatureInfo.indexOf("<celsius>") + 9, temperatureInfo.indexOf("</celsius>")).toFloat(); float offset = temperatureInfo.substring(temperatureInfo.indexOf("<offset>") + 8, temperatureInfo.indexOf("</offset>")).toFloat(); degreesCelsius += offset; degreesCelsius /= 10.0; fritzDeviceStructure[deviceCounter].Name = deviceName; fritzDeviceStructure[deviceCounter].CurrentTemperature = degreesCelsius; deviceCounter++; } else { break; } }