diff --git a/multi-esp.ino b/multi-esp.ino index 3dd2649..5edaef5 100644 --- a/multi-esp.ino +++ b/multi-esp.ino @@ -4,6 +4,7 @@ #include #include #include +#include #include "secrets.h" #define MQTT_VERSION MQTT_VERSION_3_1_1 @@ -17,6 +18,7 @@ const PROGMEM uint16_t MQTT_SERVER_PORT = 1883; //const PROGMEM char* MQTT_PASSWORD = "[Redacted]"; + // RFSOCKET subsystem mqtt topic components const char* MQTT_RFSOCKET_SUBSYSTEM = "rfsockets/"; const char* MQTT_RFSOCKET_STATE_TOPIC = "/status"; @@ -37,6 +39,21 @@ const char* MQTT_BH1750_STATE_TOPIC = "/status"; unsigned long last_bh1750_publish = millis(); +// SimpleRGB subsystem +const PROGMEM uint8_t RGB_LIGHT_RED_PIN = 14; //D5 +const PROGMEM uint8_t RGB_LIGHT_GREEN_PIN = 12; //D6 +const PROGMEM uint8_t RGB_LIGHT_BLUE_PIN = 15; //D8 + +const char* MQTT_SIMPLERGB_SUBSYSTEM = "simplergb/"; +const char* MQTT_SIMPLERGB_LOCATION = "livingroom_window"; + +const char* MQTT_SIMPLERGB_STATE_TOPIC = "/light/status"; +const char* MQTT_SIMPLERGB_COMMAND_TOPIC = "/light/switch"; +const char* MQTT_SIMPLERGB_BRIGHTNESS_STATE_TOPIC = "/brightness/status"; +const char* MQTT_SIMPLERGB_BRIGHTNESS_COMMAND_TOPIC = "/brightness/set"; +const char* MQTT_SIMPLERGB_RGB_STATE_TOPIC = "/rgb/status"; +const char* MQTT_SIMPLERGB_RGB_COMMAND_TOPIC = "/rgb/set"; + WiFiClient wifiClient; PubSubClient client(wifiClient); @@ -44,6 +61,17 @@ PubSubClient client(wifiClient); RCSwitch mySwitch = RCSwitch(); BH1750 lightMeter; +RGBConverter colorConverter; + +typedef struct { + uint8_t red; + uint8_t green; + uint8_t blue; + bool powered; +} SimpleRGBStrip; + +SimpleRGBStrip simpleRGBStrip = (SimpleRGBStrip) {0, 0, 0, false}; + typedef struct { char* channel; // First DIP set @@ -70,6 +98,94 @@ PowerSocket sockets[] = { int numofsockets = sizeof(sockets)/sizeof(sockets[0]); +// function called to adapt the brightness and the color of the led +void setColorHSV(double hue, double saturation, double value) { + hue += 1.0/(360.0*4); + if(hue >= 1.0) { + hue = 0.0; + } + + byte rgb[] = {0.0, 0.0, 0.0}; + colorConverter.hsvToRgb(hue, saturation, value, rgb); + setColorRGB(rgb[0], rgb[1], rgb[2]); +} + +void setColorRGB(uint8_t red, uint8_t green, uint8_t blue) { + analogWrite(RGB_LIGHT_RED_PIN, (1.0/255.0)*red*1023); + analogWrite(RGB_LIGHT_GREEN_PIN, (1.0/255.0)*green*1023); + analogWrite(RGB_LIGHT_BLUE_PIN, (1.0/255.0)*blue*1023); + + simpleRGBStrip.red = red; + simpleRGBStrip.green = green; + simpleRGBStrip.blue = blue; +} + +//// function called to publish the state of the led (on/off) +void publishRGBState() { + char topic[ + strlen(MQTT_SIMPLERGB_SUBSYSTEM) + + strlen(MQTT_SIMPLERGB_LOCATION) + + strlen(MQTT_SIMPLERGB_STATE_TOPIC) + ]; + strcpy(topic, MQTT_SIMPLERGB_SUBSYSTEM); + strcat(topic, MQTT_SIMPLERGB_LOCATION); + strcat(topic, MQTT_SIMPLERGB_STATE_TOPIC); + + if (simpleRGBStrip.powered) { + client.publish(topic, LIGHT_ON, true); + } else { + client.publish(topic, LIGHT_OFF, true); + } +} + +// function called to publish the brightness of the led (0-100) +void publishRGBBrightness() { + char topic[ + strlen(MQTT_SIMPLERGB_SUBSYSTEM) + + strlen(MQTT_SIMPLERGB_LOCATION) + + strlen(MQTT_SIMPLERGB_STATE_TOPIC) + ]; + strcpy(topic, MQTT_SIMPLERGB_SUBSYSTEM); + strcat(topic, MQTT_SIMPLERGB_LOCATION); + strcat(topic, MQTT_SIMPLERGB_BRIGHTNESS_STATE_TOPIC); + + double hsv[] = {0.0, 0.0, 0.0}; + colorConverter.rgbToHsv(simpleRGBStrip.red, simpleRGBStrip.green, simpleRGBStrip.blue, hsv); + char brightness[3]; + itoa(hsv[2] * 100, brightness, 10); + + client.publish(topic, brightness, true); +} + +// function called to publish the colors of the led (xx(x),xx(x),xx(x)) +void publishRGBColor() { + char *separator = ","; + char red[8]; + char green[8]; + char blue[8]; + itoa(simpleRGBStrip.red, red, 10); + itoa(simpleRGBStrip.green, green, 10); + itoa(simpleRGBStrip.blue, blue, 10); + + char payload[11]; + strcpy(payload, red); + strcat(payload, separator); + strcat(payload, green); + strcat(payload, separator); + strcat(payload, blue); + + char topic[ + strlen(MQTT_SIMPLERGB_SUBSYSTEM) + + strlen(MQTT_SIMPLERGB_LOCATION) + + strlen(MQTT_SIMPLERGB_STATE_TOPIC) + ]; + strcpy(topic, MQTT_SIMPLERGB_SUBSYSTEM); + strcat(topic, MQTT_SIMPLERGB_LOCATION); + strcat(topic, MQTT_SIMPLERGB_RGB_STATE_TOPIC); + client.publish(topic, payload, true); +} + + // function called to measure and publish the state of the BH1750 void publishBH1750State() { uint16_t lux = lightMeter.readLightLevel(); @@ -119,6 +235,102 @@ void setSocketState(byte socketIdx) { } } +// handler for mqtt messages arriving on that subsystems topic +void subsystem_simplergb(char *topic_ptr, String payload) { + const char* delimiter = "/"; + topic_ptr = strtok(NULL, delimiter); + if (topic_ptr != NULL) { + char *location = topic_ptr; + if(strcmp(location, "livingroom_window") == 0) { + topic_ptr = strtok(NULL, ""); + char *command = topic_ptr; + if(strcmp(command, "light/switch") == 0) { + Serial.println("Command: light"); + if (payload.equals(String(LIGHT_ON))) { + if (simpleRGBStrip.powered != true) { + simpleRGBStrip.powered = true; + setColorRGB(simpleRGBStrip.red, simpleRGBStrip.green, simpleRGBStrip.blue); + publishRGBState(); + } + } else if (payload.equals(String(LIGHT_OFF))) { + if (simpleRGBStrip.powered != false) { + simpleRGBStrip.powered = false; + setColorRGB(0, 0, 0); + publishRGBState(); + } + } else { + Serial.print("Unknown payload received: "); + Serial.println(payload); + Serial.println("Ignoring it…\n"); + } + } else if(strcmp(command, "brightness/set") == 0) { + Serial.println("Command: brightness"); + uint8_t brightness = payload.toInt(); + if (brightness >= 0 && brightness <= 100) { + Serial.print("Parsed brightness value of: "); + Serial.println(brightness); + double hsv[] = {0.0, 0.0, 0.0}; + colorConverter.rgbToHsv(simpleRGBStrip.red, simpleRGBStrip.green, simpleRGBStrip.blue, hsv); + setColorHSV(hsv[0], hsv[1], (1.0/100.0) * brightness); + + publishRGBBrightness(); + } else { + Serial.print("Parsed invalid brightness value of: "); + Serial.println(brightness); + } + } else if(strcmp(command, "rgb/set") == 0) { + Serial.println("Command: rgb"); + uint8_t rgb[] = {0, 0, 0}; + parseRGBPayload(payload, rgb); + setColorRGB(rgb[0], rgb[1], rgb[2]); + publishRGBColor(); + } else { + Serial.print("Unknown command \""); + Serial.print(command); + Serial.println("\" in topic found. Ignoring…"); + }; + } else { + Serial.print("Unknown location: "); + Serial.println(location); + } + } +} + +void parseRGBPayload(String payload, uint8_t rgb[]) { + Serial.print("String payload: "); + Serial.println(payload); + char payload_c[10]; + payload.toCharArray(payload_c, 10); + char *payload_ptr; + + const char* delimiter = ","; + int red; + int green; + int blue; + payload_ptr = strtok(payload_c, delimiter); + + uint8_t color_idx = 0; + while(payload_ptr != NULL) { + int color = atoi(payload_ptr); + payload_ptr = strtok(NULL, delimiter); + if(color_idx < sizeof(rgb)/sizeof(uint8_t)) { + rgb[color_idx] = color; + Serial.print("Setting color idx: "); + Serial.print(color_idx); + Serial.print(" with value "); + Serial.println(color); + } + color_idx++; + } + Serial.print("Parsed colors: "); + Serial.print(" red: "); + Serial.print(rgb[0]); + Serial.print(" green: "); + Serial.print(rgb[1]); + Serial.print(" blue: "); + Serial.println(rgb[2]); +} + // handler for mqtt messages arriving on that subsystems topic void subsystem_rfsockets(char *topic_ptr, String payload) { const char* delimiter = "/"; @@ -173,7 +385,11 @@ void callback(char* p_topic, byte* p_payload, unsigned int p_length) { Serial.print("Found subsystem: "); Serial.println(subsystem); if(strcmp(subsystem, "rfsockets") == 0) { + Serial.println("Selecting subsystem rfsockets"); subsystem_rfsockets(topic_ptr, payload); + } else if(strcmp(subsystem, "simplergb") == 0) { + Serial.println("Selecting subsystem simplergb"); + subsystem_simplergb(topic_ptr, payload); } else { Serial.print("Unknown subsystem \""); Serial.print(subsystem); @@ -195,12 +411,19 @@ void reconnect() { // Attempt to connect if (client.connect(MQTT_CLIENT_ID)) { Serial.println("INFO: connected"); + publishRGBState(); + publishRGBColor(); + publishRGBBrightness(); for (int i = 0; i < numofsockets; i++) { publishSocketState(i); } for (int i = 0; i < numofsockets; i++) { char *socket_name = sockets[i].name; - char topic[strlen(MQTT_RFSOCKET_SUBSYSTEM) + strlen(MQTT_RFSOCKET_COMMAND_TOPIC) + strlen(socket_name)]; + char topic[ + strlen(MQTT_RFSOCKET_SUBSYSTEM) + + strlen(MQTT_RFSOCKET_COMMAND_TOPIC) + + strlen(socket_name) + ]; strcpy(topic, MQTT_RFSOCKET_SUBSYSTEM); strcat(topic, socket_name); strcat(topic, MQTT_RFSOCKET_COMMAND_TOPIC); @@ -210,6 +433,48 @@ void reconnect() { Serial.println("\""); client.subscribe(topic); } + + char simplergb_topic[ + strlen(MQTT_SIMPLERGB_SUBSYSTEM) + + strlen(MQTT_SIMPLERGB_LOCATION) + + strlen(MQTT_SIMPLERGB_COMMAND_TOPIC) + ]; + strcpy(simplergb_topic, MQTT_SIMPLERGB_SUBSYSTEM); + strcat(simplergb_topic, MQTT_SIMPLERGB_LOCATION); + strcat(simplergb_topic, MQTT_SIMPLERGB_COMMAND_TOPIC); + + Serial.print("INFO: subscribing to topic \""); + Serial.print(simplergb_topic); + Serial.println("\""); + client.subscribe(simplergb_topic); + + char simplergb_b_topic[ + strlen(MQTT_SIMPLERGB_SUBSYSTEM) + + strlen(MQTT_SIMPLERGB_LOCATION) + + strlen(MQTT_SIMPLERGB_BRIGHTNESS_COMMAND_TOPIC) + ]; + strcpy(simplergb_b_topic, MQTT_SIMPLERGB_SUBSYSTEM); + strcat(simplergb_b_topic, MQTT_SIMPLERGB_LOCATION); + strcat(simplergb_b_topic, MQTT_SIMPLERGB_BRIGHTNESS_COMMAND_TOPIC); + + Serial.print("INFO: subscribing to topic \""); + Serial.print(simplergb_b_topic); + Serial.println("\""); + client.subscribe(simplergb_b_topic); + + char simplergb_rgb_topic[ + strlen(MQTT_SIMPLERGB_SUBSYSTEM) + + strlen(MQTT_SIMPLERGB_LOCATION) + + strlen(MQTT_SIMPLERGB_RGB_COMMAND_TOPIC) + ]; + strcpy(simplergb_rgb_topic, MQTT_SIMPLERGB_SUBSYSTEM); + strcat(simplergb_rgb_topic, MQTT_SIMPLERGB_LOCATION); + strcat(simplergb_rgb_topic, MQTT_SIMPLERGB_RGB_COMMAND_TOPIC); + + Serial.print("INFO: subscribing to topic \""); + Serial.print(simplergb_rgb_topic); + Serial.println("\""); + client.subscribe(simplergb_rgb_topic); } else { Serial.print("ERROR: failed, rc="); Serial.print(client.state()); @@ -217,6 +482,7 @@ void reconnect() { // Wait 5 seconds before retrying delay(5000); } + } } @@ -308,6 +574,11 @@ void setup() { Wire.begin(); lightMeter.begin(); + // setup rgb light outputs + pinMode(RGB_LIGHT_BLUE_PIN, OUTPUT); + pinMode(RGB_LIGHT_RED_PIN, OUTPUT); + pinMode(RGB_LIGHT_GREEN_PIN, OUTPUT); + // init the MQTT connection client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT); client.setCallback(callback);