Created
April 18, 2019 01:08
-
-
Save ddrager/fecb65d2999cc197801292699a993696 to your computer and use it in GitHub Desktop.
Wired up a neopixel "ring" LED light to a esp8266 with oled readout. This code grabs data from my local HomeAssistant server, and displays it on the oled display, as well as changing the ring's color depending on home energy usage. It was a cool little project to hack together!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ring_power_display.ino | |
// | |
// Libraries needed: | |
// Time.h & TimeLib.h: https://github.com/PaulStoffregen/Time | |
// Timezone.h: https://github.com/JChristensen/Timezone | |
// SSD1306.h & SSD1306Wire.h: https://github.com/squix78/esp8266-oled-ssd1306 | |
// NTPClient.h: https://github.com/arduino-libraries/NTPClient | |
// ESP8266WiFi.h & WifiUDP.h: https://github.com/ekstrand/ESP8266wifi | |
// | |
// 128x64 OLED pinout: | |
// GND goes to ground | |
// Vin goes to 3.3V | |
// Data to I2C SDA (GPIO 0) | |
// Clk to I2C SCL (GPIO 2) | |
#include <ESP8266WiFi.h> | |
#include <WiFiUdp.h> | |
#include <Wire.h> | |
#include <SSD1306.h> | |
#include <SSD1306Wire.h> | |
#include <NTPClient.h> | |
#include <Time.h> | |
#include <TimeLib.h> | |
#include <Timezone.h> | |
#include <ArduinoJson.h> | |
#include <Adafruit_NeoPixel.h> | |
#ifdef __AVR__ | |
#include <avr/power.h> | |
#endif | |
#define PIN 15 | |
Adafruit_NeoPixel strip = Adafruit_NeoPixel(12, PIN, NEO_GRB + NEO_KHZ800); | |
// Define NTP properties | |
#define NTP_OFFSET 60 * 60 // In seconds | |
#define NTP_INTERVAL 300 * 1000 // In miliseconds | |
#define NTP_ADDRESS "pool.ntp.org" // change this to whatever pool is closest (see ntp.org) | |
// Set up the NTP UDP client | |
WiFiUDP ntpUDP; | |
NTPClient timeClient(ntpUDP, NTP_ADDRESS, NTP_OFFSET, NTP_INTERVAL); | |
// Create a display objectpool.ntp.org | |
// SSD1306 display(0x3d, 0, 2); //0x3d for the Adafruit 1.3" OLED, 0x3C being the usual address of the OLED | |
#define DISPLAY_HEIGHT 32 | |
#define DISPLAY_WIDTH 128 | |
SSD1306 display(0x3c, SDA, SCL, OLED_RST, GEOMETRY_128_32); | |
const char* ssid = ""; // insert your own ssid | |
const char* password = ""; // and password | |
String date; | |
String t; | |
const char * days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} ; | |
const char * months[] = {"Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec"} ; | |
const char * ampm[] = {"AM", "PM"} ; | |
const char* host = ""; | |
const int httpsPort = 8124; | |
String url = "/influx/query?db=home_assistant&q=SELECT%20last(%22value%22)%20FROM%20%22W%22%20WHERE%20(%22entity_id%22%20%3D~%20%2Faeotec_dsb09104_home_energy_meter_power_*%2F)%20AND%20time%20%3E%3D%20now()%20-%201h&epoch=ms"; | |
// Use web browser to view and copy | |
// SHA1 fingerprint of the certificate | |
const char* fingerprint = "9E 57 66 08 CB 8D 61 1C D4 90 C5 2E 74 1B 61 BB 87 BD EA 91"; | |
void setup () | |
{ | |
Serial.begin(115200); // most ESP-01's use 115200 but this could vary | |
timeClient.begin(); // Start the NTP UDP client | |
// reset leds | |
strip.begin(); | |
strip.show(); // Initialize all pixels to 'off' | |
// reset oled | |
display.init(); | |
display.flipScreenVertically(); | |
// Connect to wifi | |
Serial.println(""); | |
Serial.print("Connecting to "); | |
Serial.print(ssid); | |
display.drawString(0, 10, "Connecting to Wifi..."); | |
display.display(); | |
WiFi.begin(ssid, password); | |
while (WiFi.status() != WL_CONNECTED) | |
{ | |
delay(500); | |
Serial.print("."); | |
} | |
Serial.println(""); | |
Serial.print("Connected to WiFi at "); | |
Serial.print(WiFi.localIP()); | |
Serial.println(""); | |
display.drawString(0, 24, "Connected."); | |
display.display(); | |
delay(1000); | |
} | |
void loop() | |
{ | |
if (WiFi.status() == WL_CONNECTED) //Check WiFi connection status | |
{ | |
date = ""; // clear the variables | |
t = ""; | |
// update the NTP client and get the UNIX UTC timestamp | |
timeClient.update(); | |
unsigned long epochTime = timeClient.getEpochTime(); | |
// convert received time stamp to time_t object | |
time_t local, utc; | |
utc = epochTime; | |
// Then convert the UTC UNIX timestamp to local time | |
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -300}; //UTC - 5 hours - change this as needed | |
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -360}; //UTC - 6 hours - change this as needed | |
Timezone usEastern(usEDT, usEST); | |
local = usEastern.toLocal(utc); | |
// now format the Time variables into strings with proper names for month, day etc | |
date += days[weekday(local)-1]; | |
date += ", "; | |
date += months[month(local)-1]; | |
date += " "; | |
date += day(local); | |
date += ", "; | |
date += year(local); | |
// format the time to 12-hour format with AM/PM and no seconds | |
t += hourFormat12(local); | |
t += ":"; | |
if(minute(local) < 10) // add a zero if minute is under 10 | |
t += "0"; | |
t += minute(local); | |
t += " "; | |
t += ampm[isPM(local)]; | |
// Display the date and time | |
Serial.println(""); | |
Serial.print("Local date: "); | |
Serial.print(date); | |
Serial.println(""); | |
Serial.print("Local time: "); | |
Serial.print(t); | |
// print the date and time on the OLED | |
display.clear(); | |
display.setTextAlignment(TEXT_ALIGN_CENTER); | |
display.setFont(ArialMT_Plain_16); | |
display.drawStringMaxWidth(64, 0, 128, t); | |
display.setFont(ArialMT_Plain_10); | |
display.drawStringMaxWidth(64, 20, 128, date); | |
display.display(); | |
delay(5000); | |
WiFiClientSecure client; | |
if (!client.connect(host, httpsPort)) { | |
Serial.println("connection failed"); | |
} | |
if (client.verify(fingerprint, host)) { | |
Serial.println("certificate matches"); | |
} else { | |
Serial.println("certificate doesn't match"); | |
} | |
Serial.print(F("Requesting URL: ")); | |
Serial.println(url); | |
client.print(String("GET ") + url + " HTTP/1.1\r\n" + | |
"Host: " + host + "\r\n" + | |
"Connection: close\r\n\r\n"); | |
delay(10); | |
while (client.connected()) { | |
String line = client.readStringUntil('\n'); | |
if (line == "\r") { | |
Serial.println("headers received"); | |
break; | |
} | |
} | |
String something = client.readStringUntil('\n'); | |
String json_raw = client.readStringUntil('\n'); | |
int json_len = json_raw.length()+1; | |
char json[json_len]; | |
json_raw.toCharArray(json, json_len); | |
const size_t bufferSize = 3*JSON_ARRAY_SIZE(1) + 2*JSON_ARRAY_SIZE(2) + JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 100; | |
DynamicJsonBuffer jsonBuffer(bufferSize); | |
JsonObject& root = jsonBuffer.parseObject(json); | |
int results0_statement_id = root["results"][0]["statement_id"]; // 0 | |
JsonObject& results0_series0 = root["results"][0]["series"][0]; | |
const char* results0_series0_name = results0_series0["name"]; // "W" | |
const char* results0_series0_columns0 = results0_series0["columns"][0]; // "time" | |
const char* results0_series0_columns1 = results0_series0["columns"][1]; // "last" | |
int results0_series0_values00 = int(results0_series0["values"][0][0]); // 1543458088845 | |
float results0_series0_values01 = results0_series0["values"][0][1]; // 1625.91 | |
int watts; | |
watts = (int)results0_series0_values01; | |
// convert char for display with unit | |
char eusage[8]; | |
sprintf(eusage,"%i W",watts); | |
display.clear(); | |
display.setTextAlignment(TEXT_ALIGN_CENTER); | |
display.setFont(ArialMT_Plain_24); // 24, 18, 10 | |
display.drawStringMaxWidth(64, 6, 128, eusage); | |
//display.setFont(ArialMT_Plain_10); | |
//display.drawStringMaxWidth(64, 20, 128, "Watts"); | |
display.display(); | |
//Serial.println(results0_series0_values00); | |
// set LED based on current energy usage | |
if (watts < 600) { | |
colorFade(0, 10, 0, 50); // Green 10 total | |
} else if (watts < 800) { | |
colorFade(5, 10, 0, 50); // Yellow 15 total | |
} else if (watts < 1000) { | |
colorFade(15, 15, 0, 50); // Yellow 30 total | |
} else if (watts < 1200) { | |
colorFade(20, 25, 0, 50); // Yellow 45 total | |
} else if (watts < 1500) { | |
colorFade(25, 20, 0, 50); // Yellow 45 total | |
} else if (watts < 2000) { | |
colorFade(30, 15, 0, 50); // Yellow 50 total | |
} else { | |
colorFade(50, 0, 0, 50); // Red 50 total | |
} | |
delay(5000); | |
} | |
else // attempt to connect to wifi again if disconnected | |
{ | |
display.clear(); | |
display.drawString(0, 2, "Connecting to Wifi..."); | |
display.display(); | |
WiFi.begin(ssid, password); | |
display.drawString(0, 12, "Connected."); | |
display.display(); | |
delay(1000); | |
} | |
//delay(5000); //Send a request to update every 10 sec (= 10,000 ms) | |
} | |
void colorFade(uint8_t r, uint8_t g, uint8_t b, uint8_t wait) { | |
for(uint16_t i = 0; i < strip.numPixels(); i++) { | |
uint8_t curr_r, curr_g, curr_b; | |
uint32_t curr_col = strip.getPixelColor(i); // get the current colour | |
curr_b = curr_col & 0xFF; curr_g = (curr_col >> 8) & 0xFF; curr_r = (curr_col >> 16) & 0xFF; // separate into RGB components | |
while ((curr_r != r) || (curr_g != g) || (curr_b != b)){ // while the curr color is not yet the target color | |
if (curr_r < r) curr_r++; else if (curr_r > r) curr_r--; // increment or decrement the old color values | |
if (curr_g < g) curr_g++; else if (curr_g > g) curr_g--; | |
if (curr_b < b) curr_b++; else if (curr_b > b) curr_b--; | |
strip.setPixelColor(i, curr_r, curr_g, curr_b); // set the color | |
strip.show(); | |
//delay(wait); // add a delay if its too fast | |
} | |
delay(50); | |
} | |
} | |
// Fill the dots one after the other with a color | |
void colorWipe(uint32_t c, uint8_t wait) { | |
for(uint16_t i=0; i<strip.numPixels(); i++) { | |
strip.setPixelColor(i, c); | |
strip.show(); | |
delay(wait); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment