From 1d6201eb2523af45eb9120ca49c32a4668a3a481 Mon Sep 17 00:00:00 2001 From: Florian Overkamp Date: Sat, 30 Mar 2019 08:39:24 +0100 Subject: [PATCH] Adding to repo --- 00_prologue.ino | 65 ++++++++++++ 10_wlan_conf.ino | 1 + 20_interface_webui.ino | 1 + 30_interface_mqtt.ino | 158 +++++++++++++++++++++++++++++ 60_module_sonoff.ino | 68 +++++++++++++ 70_module_moon.ino | 221 +++++++++++++++++++++++++++++++++++++++++ 99_setup_loop.ino | 52 ++++++++++ ESPGadget.ino | 70 +++++++++++++ 8 files changed, 636 insertions(+) create mode 100644 00_prologue.ino create mode 100644 10_wlan_conf.ino create mode 100644 20_interface_webui.ino create mode 100644 30_interface_mqtt.ino create mode 100644 60_module_sonoff.ino create mode 100644 70_module_moon.ino create mode 100644 99_setup_loop.ino create mode 100644 ESPGadget.ino diff --git a/00_prologue.ino b/00_prologue.ino new file mode 100644 index 0000000..8585523 --- /dev/null +++ b/00_prologue.ino @@ -0,0 +1,65 @@ +/* + * Libraries and global scope code + */ + +#include +#include +#include + +BearSSL::WiFiClientSecure net; +time_t now; +unsigned long lastMillis = 0; + +String devname; // Device identifier for MQTT + +struct Settings { + String name = ""; + String ip = ""; + uint8_t mode = 0, // Current animation effect + offset = 0, + brightness = 20; // Default brightness (range 0-255) + long color = 0x00A4B3; // Starting color +} settings; + +struct State { + String text = ""; + uint8_t mode = 0; + long color = 0x00A4B3; // Starting color +} state; + +/* + * Debugging might be nice sometimes + */ +#define USE_SERIAL //uncomment for Serial debugging statements +bool serialinitialised = false; + +char printbuffer[100]; + +void PRINT_SERIAL(const String& line) { +#ifdef USE_SERIAL + if(!serialinitialised){ + Serial.begin(115200); + serialinitialised = true; + } + Serial.print(settings.ip + " " + settings.name + " "); + Serial.print(line); +#endif +} +void APPEND_SERIAL(const String& line) { +#ifdef USE_SERIAL + Serial.print(line); +#endif +} +void PRINTLN_SERIAL(const String& line) { +#ifdef USE_SERIAL + PRINT_SERIAL(line); + Serial.println(); +#endif +} +void APPENDLN_SERIAL(const String& line) { +#ifdef USE_SERIAL + Serial.println(line); +#endif +} + +#define NOP __asm__ __volatile__ ("nop\n") diff --git a/10_wlan_conf.ino b/10_wlan_conf.ino new file mode 100644 index 0000000..8f26a79 --- /dev/null +++ b/10_wlan_conf.ino @@ -0,0 +1 @@ +// Future use diff --git a/20_interface_webui.ino b/20_interface_webui.ino new file mode 100644 index 0000000..8f26a79 --- /dev/null +++ b/20_interface_webui.ino @@ -0,0 +1 @@ +// Future use diff --git a/30_interface_mqtt.ino b/30_interface_mqtt.ino new file mode 100644 index 0000000..e4cc79d --- /dev/null +++ b/30_interface_mqtt.ino @@ -0,0 +1,158 @@ +#ifdef ENABLE_MQTT + +/* + * Define a function newcmd in your module to be able to accept mqtt commands + * Syntax and parameters are like this: + */ +void newcmd(String cmd); +void module_message(const String& shorttopic, const String& message); + +#include + +// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. + PubSubClient mqtt(net); +String deviceprefix; + +const char* fingerprint = "EA:2A:2B:80:C1:00:57:35:66:26:FF:FC:FA:27:EA:5F:3D:91:5A:F9"; + +long logtimerMillis = 0; + +void mqtt_subscribe(const String& topic) { + mqtt.subscribe(topic.c_str()); + PRINTLN_SERIAL("Subscribed to " + topic); +} + +void mqtt_publish(const String& topic, const String& message) { + mqtt.publish(topic.c_str(), message.c_str()); + PRINTLN_SERIAL("Published " + topic + ": " + message); +} + +/* + * Callback routine is called when a topic we subscribed to got a message + */ +void mqtt_callback(char* topic, byte* payload, unsigned int length) { + String fulltopic((char*)0); + String shorttopic((char*)0); + String message((char*)0); + + fulltopic.reserve(strlen(topic)); + fulltopic = String(topic); + + // If deviceprefix is in topic, subtract it + // Could do a similar thing for grouptopics if we implement them + shorttopic.reserve(strlen(topic)); + shorttopic = String(topic); + shorttopic.replace(deviceprefix, ""); + + // Decode message into string + message.reserve(length); + for (int i = 0; i < length; i++) { + message += (char)payload[i]; + } + PRINT_SERIAL(F("Received MQTT ")); + APPEND_SERIAL(fulltopic); + APPEND_SERIAL(F(": ")); + APPENDLN_SERIAL(message); + + // Deal with global topic messages + if(fulltopic.equals(MQTT_PREFIX "ping")) { + mqtt_publish(deviceprefix + "pong", "This is " + settings.name + " v" + FW_VERSION + " at " + settings.ip); + + // Next quietly ignore all the things we send out ourselves + } else if(shorttopic.equals(F("pong"))) { + // Ignore quietly + //NOP; // delay 62.5ns on a 16MHz AtMega + } else if(shorttopic.equals(F("log"))) { + // Ignore quietly + //NOP; // delay 62.5ns on a 16MHz AtMega + + // And now to add a catchall to hand off to other module + } else { + module_message(shorttopic, message); + } +} + +void WifiMQTTconnect() { + + if(WiFi.status() != WL_CONNECTED) { + PRINTLN_SERIAL("Wifi not connected, connecting..."); + WiFi.mode(WIFI_STA); // Weird MQTT connection bug, https://github.com/knolleary/pubsubclient/issues/138#issuecomment-326113915 + WiFi.begin(WLAN_SSID, WLAN_PASS); + while (WiFi.status() != WL_CONNECTED) { + PRINTLN_SERIAL("Waiting for Wifi..."); + delay(500); + } + settings.ip = WiFi.localIP().toString(); + PRINTLN_SERIAL("Wifi connected"); + } + + PRINT_SERIAL("Setting time using SNTP"); + configTime(-5 * 3600, 0, "pool.ntp.org", "time.nist.gov"); + now = time(nullptr); + while (now < 1510592825) { + delay(500); + APPEND_SERIAL("."); + now = time(nullptr); + } + APPENDLN_SERIAL(" done!"); + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + PRINT_SERIAL("Current time: "); + APPENDLN_SERIAL(asctime(&timeinfo)); + net.setInsecure(); // verification options set to none + mqtt.setServer(MQTT_SERVER, MQTT_SERVERPORT); + mqtt.setCallback(mqtt_callback); + + if(!mqtt.connected()) { + while (!mqtt.connected()) { + PRINTLN_SERIAL("MQTT not connected, connecting..."); + + // Attempt to connect + // Note - the default maximum packet size is 128 bytes. If the + // combined length of clientId, username and password exceed this, + // you will need to increase the value of MQTT_MAX_PACKET_SIZE in + // PubSubClient.h + if (mqtt.connect(settings.name.c_str(), MQTT_USERNAME, MQTT_PASSWORD)) { + PRINTLN_SERIAL("MQTT connected"); + } else { + PRINTLN_SERIAL("MQTT connection failed, rc=" + String(mqtt.state()) + "..."); + delay(10000); // @@@FIXME@@@ delays are not cool + } + } + + // (re)subscribe to the topics we need + deviceprefix = MQTT_PREFIX; + deviceprefix = deviceprefix + settings.name + "/"; + + mqtt_subscribe(MQTT_PREFIX "ping"); // global topic: send a ping, and we will respond with some device info + mqtt_subscribe(deviceprefix + "#"); + mqtt_publish(deviceprefix + "log", "Booting up " + settings.name + " running v" + FW_VERSION + " at " + settings.ip); + mqtt_publish(deviceprefix + "log", "Free Heap: " + String(ESP.getFreeHeap())); + + } +} + +void mqtt_setup() { + PRINTLN_SERIAL(devname + "initialising module MQTT"); +} + +void mqtt_loop() { + long currentMillis = millis(); + + // Check for MQTT instructions + if(!mqtt.connected()) { + WifiMQTTconnect(); + } else { + mqtt.loop(); + + // every 5 minutes (300000 milliseconds) + if(currentMillis - logtimerMillis > 300000) { + // reset timer + logtimerMillis = currentMillis; + // log something + mqtt_publish(deviceprefix + "log", "Free Heap: " + String(ESP.getFreeHeap())); + } + } +} + +#endif diff --git a/60_module_sonoff.ino b/60_module_sonoff.ino new file mode 100644 index 0000000..e06123f --- /dev/null +++ b/60_module_sonoff.ino @@ -0,0 +1,68 @@ +#ifdef ENABLE_SONOFF + +/* + * Interface with other modules + */ +void newcmd(String cmd) { + cmd.toLowerCase(); + if(cmd.equals("off")) { + settings.mode = 0; + PRINTLN_SERIAL("Lights off"); + state.text = "Sonoff switched off"; + } + if(cmd.equals("on")) { + settings.mode = 1; + PRINTLN_SERIAL("Lights on"); + state.text = "Sonoff switched off"; + } +} + +int sonoff_buttontoggle = 0; // counter for the number of button presses +int sonoff_buttonstate = LOW; // current state of the button +int sonoff_lastbuttonstate = LOW; // previous state of the button + +/* + * Setup and loop code. Be cooperative! No delays, timers. + */ + +void sonoff_setup() { + PRINTLN_SERIAL("initialising module Sonoff"); + + pinMode(SONOFF_LED, OUTPUT); // Note that this LED is powered when voltage is LOW + digitalWrite(SONOFF_LED, HIGH); // turn the LED off (HIGH is the voltage level) + pinMode(SONOFF_RELAY, OUTPUT); // Note that this relay is powered when voltage is HIGH + digitalWrite(SONOFF_RELAY, LOW); // turn the relay off (HIGH is the voltage level) +} + +void sonoff_loop() { + // This is a matter of last resort kind of thing: the built-in button will toggle power on or off, regardless of MQTT or network availability + // As good measure, we might try reporting some kind of state when connecting later on. @@@FIXME@@@ + + // read the pushbutton input pin: + sonoff_buttonstate = digitalRead(SONOFF_BUTTON); + + // compare the buttonState to its previous state + if (sonoff_buttonstate != sonoff_lastbuttonstate) { + // if the state has changed, increment the counter + if (sonoff_buttonstate == LOW) { + // if the current state is LOW then the button went from off to on: + sonoff_buttontoggle = 1-sonoff_buttontoggle; + PRINTLN_SERIAL("Button pressed"); + if (sonoff_buttontoggle == 1) { + newcmd("on"); + } else { + newcmd("off"); + } + } else { + // if the current state is LOW then the button went from on to off: + PRINTLN_SERIAL("Button released"); + } + // Delay a little bit to avoid bouncing + delay(50); // @@@FIXME@@@ delays are undesirable! + } + // save the current state as the last state, for next time through the loop + sonoff_lastbuttonstate = sonoff_buttonstate; + +} + +#endif diff --git a/70_module_moon.ino b/70_module_moon.ino new file mode 100644 index 0000000..7291367 --- /dev/null +++ b/70_module_moon.ino @@ -0,0 +1,221 @@ +#ifdef ENABLE_MOON + +// Needs refactoring +// Use https://adrianotiger.github.io/Neopixel-Effect-Generator/ for inspiration +// Read up on final comment at https://forum.arduino.cc/index.php?topic=534060.0 + +#include + +/* + * Based on https://adrianotiger.github.io/Neopixel-Effect-Generator/ + * TODO: Refactor for just a single string effect, less frills + * Rainbow pattern + */ +class Strip +{ +public: + uint8_t effect; + uint8_t effects; + uint16_t effStep; + unsigned long effStart; + Adafruit_NeoPixel strip; + Strip(uint16_t leds, uint8_t pin, uint8_t toteffects) : strip(leds, pin, NEO_GRB + NEO_KHZ800) { + effect = -1; + effects = toteffects; + Reset(); + } + void Reset(){ + effStep = 0; + effect = (effect + 1) % effects; + effStart = millis(); + } +}; + +struct Loop +{ + uint8_t currentChild; + uint8_t childs; + bool timeBased; + uint16_t cycles; + uint16_t currentTime; + Loop(uint8_t totchilds, bool timebased, uint16_t tottime) {currentTime=0;currentChild=0;childs=totchilds;timeBased=timebased;cycles=tottime;} +}; + +Strip strip_0(7, MOON_PIXEL, 7 ); +struct Loop strip0loop0(1, false, 1); + +//[GLOBAL_VARIABLES] +int speed = 200; // started at 101 + +uint8_t strip0_loop0_eff0() { + // Strip ID: 0 - Effect: Rainbow - LEDS: 7 + // Steps: 35 - Delay: 100 + // Colors: 6 (255.255.0, 0.255.0, 0.0.255, 255.0.255, 255.0.0, 255.255.255, ) + // Options: toLeft=true, + if(millis() - strip_0.effStart < 100 * (strip_0.effStep)) return 0x00; + float factor1, factor2; + uint16_t ind; + for(uint16_t j=0;j<7;j++) { + ind = strip_0.effStep + j * 1; + switch((int)((ind % 35) / 5.833333333333333)) { + case 0: factor1 = 1.0 - ((float)(ind % 35 - 0 * 5.833333333333333) / 5.833333333333333); + factor2 = (float)((int)(ind - 0) % 35) / 5.833333333333333; + strip_0.strip.setPixelColor(j, 255 * factor1 + 0 * factor2, 255 * factor1 + 255 * factor2, 0 * factor1 + 0 * factor2); + break; + case 1: factor1 = 1.0 - ((float)(ind % 35 - 1 * 5.833333333333333) / 5.833333333333333); + factor2 = (float)((int)(ind - 5.833333333333333) % 35) / 5.833333333333333; + strip_0.strip.setPixelColor(j, 0 * factor1 + 0 * factor2, 255 * factor1 + 0 * factor2, 0 * factor1 + 255 * factor2); + break; + case 2: factor1 = 1.0 - ((float)(ind % 35 - 2 * 5.833333333333333) / 5.833333333333333); + factor2 = (float)((int)(ind - 11.666666666666666) % 35) / 5.833333333333333; + strip_0.strip.setPixelColor(j, 0 * factor1 + 255 * factor2, 0 * factor1 + 0 * factor2, 255 * factor1 + 255 * factor2); + break; + case 3: factor1 = 1.0 - ((float)(ind % 35 - 3 * 5.833333333333333) / 5.833333333333333); + factor2 = (float)((int)(ind - 17.5) % 35) / 5.833333333333333; + strip_0.strip.setPixelColor(j, 255 * factor1 + 255 * factor2, 0 * factor1 + 0 * factor2, 255 * factor1 + 0 * factor2); + break; + case 4: factor1 = 1.0 - ((float)(ind % 35 - 4 * 5.833333333333333) / 5.833333333333333); + factor2 = (float)((int)(ind - 23.333333333333332) % 35) / 5.833333333333333; + strip_0.strip.setPixelColor(j, 255 * factor1 + 255 * factor2, 0 * factor1 + 255 * factor2, 0 * factor1 + 255 * factor2); + break; + case 5: factor1 = 1.0 - ((float)(ind % 35 - 5 * 5.833333333333333) / 5.833333333333333); + factor2 = (float)((int)(ind - 29.166666666666664) % 35) / 5.833333333333333; + strip_0.strip.setPixelColor(j, 255 * factor1 + 255 * factor2, 255 * factor1 + 255 * factor2, 255 * factor1 + 0 * factor2); + break; + } + } + if(strip_0.effStep >= 35) {strip_0.Reset(); return 0x03; } + else strip_0.effStep++; + return 0x01; +} + +uint8_t strip0_loop0() { + uint8_t ret = 0x00; + switch(strip0loop0.currentChild) { + case 0: + ret = strip0_loop0_eff0();break; + } + if(ret & 0x02) { + ret &= 0xfd; + if(strip0loop0.currentChild + 1 >= strip0loop0.childs) { + strip0loop0.currentChild = 0; + if(++strip0loop0.currentTime >= strip0loop0.cycles) {strip0loop0.currentTime = 0; ret |= 0x02;} + } + else { + strip0loop0.currentChild++; + } + }; + return ret; +} + +void strips_loop() { + if(strip0_loop0() & 0x01) + strip_0.strip.show(); +} + +/* + * Interface with other modules + */ + +void newcmd(String cmd) { + cmd.toLowerCase(); + if(cmd.equals("off")) { + settings.mode = 0; + PRINTLN_SERIAL("Moon off"); + strip_0.strip.setBrightness(0); // 0-255 + state.text = "Moon switched off"; + } + if(cmd.equals("on")) { + settings.mode = 1; + PRINTLN_SERIAL("Moon switched on"); + strip_0.strip.setBrightness(10); // 0-255 + state.text = "Moon switched off"; + } +} + +void module_message(const String& shorttopic, const String& message) { + char msgptr[10]; + String buffer; + + if(shorttopic.equalsIgnoreCase("cmd")) { + if(message.equalsIgnoreCase("off")) { + state.mode = 0; + PRINTLN_SERIAL("Moon off"); + strip_0.strip.setBrightness(0); // 0-255 + state.text = "Moon switched off"; + } else if(message.equalsIgnoreCase("on")) { + state.mode = 1; + PRINTLN_SERIAL("Moon switched on"); + strip_0.strip.setBrightness(10); // 0-255 + state.text = "Moon switched off"; + } + + } else if(shorttopic.equalsIgnoreCase("mode")) { + if(message.equals("off")) { + state.mode = 0; + PRINTLN_SERIAL("Shutting off"); + } else if(message.startsWith("0x")) { + state.mode = 1; + message.toCharArray(msgptr, 9); + state.color = strtoll( &msgptr[2], NULL, 16); + sprintf(printbuffer, "Switching to static color 0x%x", state.color); + PRINTLN_SERIAL(printbuffer); + } else if(message.startsWith("#")) { + state.mode = 1; + message.toCharArray(msgptr, 8); + state.color = strtoll( &msgptr[1], NULL, 16); + sprintf(printbuffer, "Switching to static color 0x%x", state.color); + PRINTLN_SERIAL(printbuffer); + } else if(message.equals("rainbow")) { + state.mode = 2; + PRINTLN_SERIAL("Switching to rainbow mode"); + } + mqtt_publish((deviceprefix + "log"), ("mode=" + String(state.mode))); // @@@FIXME@@@ this needs to go elsewhere + + } else if(shorttopic.equalsIgnoreCase("brightness")) { + PRINTLN_SERIAL("Setting brightness to " + message); + settings.brightness = message.toInt(); + strip_0.strip.setBrightness(settings.brightness); + buffer = "brightness="; + buffer.concat(settings.brightness); + mqtt_publish((deviceprefix + "log"), buffer.c_str()); // @@@FIXME@@@ this needs to go elsewhere + } +} + +/* + * Setup and loop code. Be cooperative! No delays, timers. + */ + +void moon_setup() { + PRINTLN_SERIAL("initialising module Moon"); + strip_0.strip.setBrightness(2); // 0-255 + strip_0.strip.begin(); +} + +void moon_loop() { + uint16_t r; + + switch(state.mode) { + case 0: // Off + for(r=0; r< strip_0.strip.numPixels(); r++) { + strip_0.strip.setPixelColor(r, 0,0,0); + } + strip_0.strip.show(); + break; + case 1: // Static color + for(r=0; r< strip_0.strip.numPixels(); r++) { + // Split the set color code up into r, g, b values + int rval = state.color >> 16; + int gval = state.color >> 8 & 0xFF; + int bval = state.color & 0xFF; + strip_0.strip.setPixelColor(r, rval, gval, bval); + } + strip_0.strip.show(); + break; + case 2: // Rainbow + strips_loop(); + break; + } +} + +#endif diff --git a/99_setup_loop.ino b/99_setup_loop.ino new file mode 100644 index 0000000..0754b90 --- /dev/null +++ b/99_setup_loop.ino @@ -0,0 +1,52 @@ +/* + * Setup and loop functions + */ + +/*************************** Sketch Code ************************************/ +void setup() { + // Write banner + PRINTLN_SERIAL(""); + PRINTLN_SERIAL(""); + PRINTLN_SERIAL("Booting " __FILE__ " built on " __DATE__ ", " __TIME__); + + // Generate device name from MAC + PRINTLN_SERIAL("Network interface: " + WiFi.macAddress()); + settings.name = DEVICEPREFIX; + int devlen = settings.name.length(); + settings.name = settings.name + "_" + WiFi.macAddress(); + settings.name.remove(devlen+3, 1); + settings.name.remove(devlen+5, 1); + settings.name.remove(devlen+7, 1); + settings.name.remove(devlen+9, 1); + settings.name.remove(devlen+11, 1); + // Only keep the last 6 characters from the MAC, hopefully that will be 'unique enough' in the LAN + // If we run in to trouble, this can painlessly be removed and the resulting device name will just be longer + settings.name.remove(devlen+1, 6); + + PRINTLN_SERIAL("device name generated"); + + +#ifdef ENABLE_MQTT + mqtt_setup(); +#endif +#ifdef ENABLE_SONOFF + sonoff_setup(); +#endif +#ifdef ENABLE_MOON + moon_setup(); +#endif + // Start the cooperative scheduler loops + PRINTLN_SERIAL(devname + "done setting up, ready for main loop"); +} + +void loop() { +#ifdef ENABLE_MQTT + mqtt_loop(); +#endif +#ifdef ENABLE_SONOFF + sonoff_loop(); +#endif +#ifdef ENABLE_MOON + moon_loop(); +#endif +} diff --git a/ESPGadget.ino b/ESPGadget.ino new file mode 100644 index 0000000..b977d9b --- /dev/null +++ b/ESPGadget.ino @@ -0,0 +1,70 @@ +/* + * ESP-SecureGadget by Florian Overkamp + * An ESP8266 based IoT/remote controlled gadget + * Design is modular so it can be used for things like Sonoff's but also for custom projects like Dex' Moon lamp + * + * Note that file ordering is important. + */ + +// Done: +// Enable MQTT over SSL as per https://github.com/debsahu/ESP_MQTT_Secure/tree/master/ESP8266_MQTT_SSL/Arduino/ESP8266_PubSubClient_SSL +// Cooperative multitasking: Use timers always. Avoid delays where they can be avoided. +// Restructure mqtt topics a bit (report back with unique names for multiple fx lights, for instance) +// Implement versioning so that new firmware can be done OTA + +// Todo: +// Split the wifi and mqtt code in order to support http interface +// Stay on the lookout for watchdog crashes, see also https://www.sigmdel.ca/michel/program/esp8266/arduino/watchdogs_en.html#ESP8266_WDT_RECOV +// Keeps crashing after a few days. Dunno why. https://www.pieterverhees.nl/sparklesagarbage/esp8266/130-difference-between-esp-reset-and-esp-restart +// More on this subject https://github.com/esp8266/Arduino/issues/1017 +// Implement more visual effects +// Implement speed controls for the visual effects +// Implement OTA https://www.bakke.online/index.php/2017/06/02/self-updating-ota-firmware-for-esp8266/ +// Refactoring sources: https://www.arduinolibraries.info/architectures/esp8266 +// Refactor String usage to minimize heap pollution. https://cpp4arduino.com/2018/11/21/eight-tips-to-use-the-string-class-efficiently.html + +#define DEVICEPREFIX "Gadget" // Should be no longer than 8(?) chars or the MQTT clientId will become too long +const int FW_VERSION = 12; + +// Version history: +// v12: Refactored inbound commands to hook in to the modules (module_message function), completed assimilation of (never-published) Moon_Connected sketch +// v11: Simplified scheduler, back to cooperative looping +// v10: First 'production' run. MQTT over TLS, but no OTA support. + +/* + * Compile time settings + */ + +/************************* WiFi Access Point *********************************/ +#define WLAN_SSID "MY_SSID" +#define WLAN_PASS "MY_WIFIPASSWD" + + +/*********************** Select active modules *******************************/ +#define ENABLE_MQTT +//#define ENABLE_SONOFF +#define ENABLE_MOON + + +#ifdef ENABLE_MQTT +/*************************** MQTT settings ***********************************/ +#define MQTT_SERVER "MY_MQTT_SERVER" +#define MQTT_SERVERPORT 8883 // 8883 for MQTTS +#define MQTT_USERNAME "MY_MQTT_USER" +#define MQTT_PASSWORD "MY_MQTT_PASSWD" +#define MQTT_PREFIX "iot/" // MQTT publish/subscribe will be prefixed by this (mind the trailing slash) +#endif + +#ifdef ENABLE_SONOFF +/*************************** MQTT settings ***********************************/ +#define SONOFF_BUTTON 0 +#define SONOFF_RELAY 12 +#define SONOFF_LED 13 +#endif + +#ifdef ENABLE_MOON +/*********************** NeoPixel Moon settings ******************************/ +#define MOON_PIXEL 4 // Which GPIO pin is the NeoPixel connected to +#define MOON_PIXEL_LEDS 7 // Amount of leds in the string +#endif +