diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..bbc68c0 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,53 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +default_envs = serial + +[common] +ssid = wifi +password = foobar +mqtt_server = mqtt +mqtt_room = test +mqtt_device = ledstrip + +; must be a positive integer value +; must be incremented for automated updates to happen! +firmware_version = 1 + +[uploadconfig] +sign_prog = $PROJECT_PACKAGES_DIR/framework-arduinoespressif8266/tools/signing.py +sign_key = ../private.pem +sign_cert = ../public.pem +upl_url = http://mqtt/arduino-upload.php +upl_token = secretpassphrase + +[env] +platform = espressif8266 +board = d1_mini +framework = arduino +build_flags = + '-DINI_SSID="${common.ssid}"' + '-DINI_PSK="${common.password}"' + '-DINI_SERVER="${common.mqtt_server}"' + '-DINI_ROOM="${common.mqtt_room}"' + '-DINI_DEVICE="${common.mqtt_device}"' + '-DINI_VERSION="${common.firmware_version}"' +extra_scripts = pre:public_key.py + +[env:serial] +upload_port = /dev/ttyUSB* +upload_speed = 921600 +monitor_port = /dev/ttyUSB* +monitor_speed = 115200 + +[env:web] +upload_protocol = custom +extra_scripts = pre:sign_and_upload.py diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4b40426 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include "elapsedMillis.h" +#include "signedUpdatesHelper.h" + +// most of these configuration settings should be changed in the file platformio.ini +#define WIFI_SSID INI_SSID +#define WIFI_PSK INI_PSK + +#define MQTT_SERVER INI_SERVER +#define MQTT_ROOM INI_ROOM +#define MQTT_DEVICE INI_DEVICE +#define MQTT_RETAIN true + +#define UPDATE_URL "http://" MQTT_SERVER "/arduino-download.php?room=" MQTT_ROOM "&device=" MQTT_DEVICE +#define UPDATE_VERSION INI_VERSION + +const char* const topic_state = MQTT_ROOM "/" MQTT_DEVICE "/state"; +const char* const topic_bright = MQTT_ROOM "/" MQTT_DEVICE "/brightness"; +const char* const topic_color = MQTT_ROOM "/" MQTT_DEVICE "/color"; +const char* const topic_lwt = MQTT_ROOM "/" MQTT_DEVICE "/lwt"; +const char* const topic_rssi = MQTT_ROOM "/" MQTT_DEVICE "/stats/rssi"; +const char* const topic_vers = MQTT_ROOM "/" MQTT_DEVICE "/stats/update-version"; +const char* const topic_upderr = MQTT_ROOM "/" MQTT_DEVICE "/stats/update-error"; + +#define LED_CHIPSET APA102 +#define COLOR_ORDER BGR +#define NUM_LEDS 1 +#define DATA_PIN 4 +#define CLOCK_PIN 5 +#define SWITCH_PIN 15 + +bool is_auto_mode = false; +int brightness = 0; +String updateStatus; + +WiFiClient wifiClient, wifiClient2; +PubSubClient mqttClient; +CRGB leds[NUM_LEDS]; + +// by noby +inline uint8_t parse_hex_nibble(uint8_t nibble) { + return isdigit(nibble) ? nibble - '0' : toupper(nibble) - 'A'; +} +CRGB parse_colorcode(const char* payload) { + uint8_t r = ((parse_hex_nibble(payload[0]) << 4)) | parse_hex_nibble(payload[1]); + uint8_t g = ((parse_hex_nibble(payload[2]) << 4)) | parse_hex_nibble(payload[3]); + uint8_t b = ((parse_hex_nibble(payload[4]) << 4)) | parse_hex_nibble(payload[5]); + return CRGB(r, g, b); +} + +void mqtt_callback(char* topic, byte* payload, unsigned int length) { + if (strcmp(topic, topic_state) == 0 && length == 1) { + if ((char)payload[0] == '0') { + FastLED.setBrightness(0); + is_auto_mode = false; + } else if ((char)payload[0] == '1') { + FastLED.setBrightness(brightness); + is_auto_mode = false; + } else if ((char)payload[0] == '2') { + FastLED.setBrightness(0); + is_auto_mode = true; + } + } + else if (strcmp(topic, topic_bright) == 0 && length >= 1) { + brightness = atoi((const char*) payload); + FastLED.setBrightness(brightness); + } + else if (strcmp(topic, topic_color) == 0 && length == 6) { + fill_solid(leds, NUM_LEDS, parse_colorcode((const char*)payload)); + } +} + +void mqtt_connect() { + String mqtt_client_id = MQTT_DEVICE + ESP.getChipId(); + if (mqttClient.connect(mqtt_client_id.c_str(), topic_lwt, 0, true, "offline")) { + mqttClient.publish(topic_lwt, "online", MQTT_RETAIN); + mqttClient.subscribe(topic_state); + mqttClient.subscribe(topic_bright); + mqttClient.subscribe(topic_color); + } +} + +void publish_stats() +{ + if (mqttClient.connected()) { + mqttClient.publish(topic_rssi, String(WiFi.RSSI()).c_str(), MQTT_RETAIN); + mqttClient.publish(topic_vers, UPDATE_VERSION, MQTT_RETAIN); + mqttClient.publish(topic_upderr, updateStatus.c_str()); + } +} + +void setup() { + // FastLED setup +#ifndef CLOCK_PIN + FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); +#else + FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); +#endif + + // Wifi setup + WiFi.hostname(MQTT_ROOM "_" MQTT_DEVICE); + WiFi.begin(WIFI_SSID, WIFI_PSK); + while (WiFi.status() != WL_CONNECTED) + delay(500); + + // MQTT setup + mqttClient.setClient(wifiClient); + mqttClient.setServer(MQTT_SERVER, 1883); + mqttClient.setCallback(mqtt_callback); + + // misc setup + randomSeed(micros()); + signedUpdatesHelperInit(); +} + +elapsedSeconds lastReconnect = 0; +elapsedMillis lastLedShow = 0; +elapsedSeconds lastStats = 0; +elapsedSeconds lastUpdate = 0; + +void loop() { + if (WiFi.status() == WL_CONNECTED && !mqttClient.connected()) { + if (lastReconnect > 5) { + lastReconnect = 0; + mqtt_connect(); + } + } + + if (is_auto_mode) { + FastLED.setBrightness(digitalRead(SWITCH_PIN) == HIGH ? brightness : 0); + } + + if (lastLedShow > 200) { + lastLedShow = 0; + FastLED.show(); + } + + if (lastStats > 120) { + lastStats = 0; + publish_stats(); + } + + if (lastUpdate > 300) { + lastUpdate = 0; + updateStatus = signedUpdatesHelperRun(wifiClient2, UPDATE_URL, UPDATE_VERSION); + } + + mqttClient.loop(); +}