/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2025 - License: GNU MIT // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// // This is an Arduino sketch template that is meant to power a sensor // // cocktail / nexus for the following project: // // * https://grimore.org/iot/creating_a_sensor_cocktail // // Note that this sketch as it is, does not carry too much value because // // it is just a variant of the WiFi preboot environment to be found at: // // * https://grimore.org/arduino/wifipreboot // with the changes being local to the sensors application that contains // // stuff like GPIO to sensor pin mapps that are wortheless on their own. // // If you are looking for the template that this sketch is based on then // // check out the wifipreboot template mentioned previously. // /////////////////////////////////////////////////////////////////////////// // configurable parameters // /////////////////////////////////////////////////////////////////////////// // comment out to enable debugging #define DEBUG // set the master password for OTA updates and access to the soft AP #define PREBOOT_MASTER_PASSWORD "" // the name and length of the cookie to use for authentication #define PREBOOT_COOKIE_NAME "ArduinoPrebootCookie" #define PREBOOT_COOKIE_MAX_LENGTH 256 // timeout to establish STA connection in milliseconds #define WIFI_RETRY_TIMEOUT 10000 // retries as multiples of WIFI_RETRY_TIMEOUT milliseconds #define WIFI_CONNECT_TRIES 60 // the time between blinking a single digit #define BLINK_DIT_LENGTH 250 // the time between blinking the whole number #define BLINK_DAH_LENGTH 2500 // GPIO application // The MQTT broker to connect to. #define MQTT_HOST "" // The MQTT broker username. #define MQTT_USERNAME "" // The MQTT broker password. #define MQTT_PASSWORD "" // The MQTT broker port. #define MQTT_PORT 1883 // The MQTT topic #define MQTT_TOPIC "" // Maximal size of an MQTT payload #define MQTT_PAYLOAD_MAX_LENGTH 256 /////////////////////////////////////////////////////////////////////////// // includes // /////////////////////////////////////////////////////////////////////////// #include #if defined(ARDUINO_ARCH_ESP32) #include #include #include #elif defined(ESP8266) #include #include #include #endif #include #include #include // Arduino OTA #include #include #include // Sensors application. #include // DHT11 #include #include #include // HX710B air pressure #include "HX710.h" // Uncomment the type of sensor in use: #define DHTTYPE DHT11 // DHT 11 //#define DHTTYPE DHT22 // DHT 22 (AM2302) //#define DHTTYPE DHT21 // DHT 21 (AM2301) // Sensors application. #define UV_PIN_ANALOG 35 #define LIGHT_PIN_ANALOG 34 #define BAROMETER_OUT_PIN 26 #define BAROMETER_SCK_PIN 27 #define GAS_PIN_ANALOG 33 #define DHT_PIN_ANALOG 25 #define DECIBELMETER_PIN_ANALOG 32 #define MAX_ANALOG_VOLTAGE 3.3 // Platform specific defines. #if defined(ARDUINO_ARCH_ESP8266) #define GET_CHIP_ID() (ESP.getChipId()) #elif defined(ARDUINO_ARCH_ESP32) #define GET_CHIP_ID() ((uint16_t)(ESP.getEfuseMac() >> 32)) #endif #define HOSTNAME() String("esp-" + String(GET_CHIP_ID(), HEX)) #define CONFIGURATION_FILE_NAME "/config.json" #define CONFIGURATION_MAX_LENGTH 1024 /////////////////////////////////////////////////////////////////////////// // function definitions // /////////////////////////////////////////////////////////////////////////// byte* getHardwareAddress(void); char* getHardwareAddress(char colon); String computeTemporarySsid(void); void arduinoOtaTickCallback(void); void blinkDigitsDahTickCallback(void); void blinkDigitsDitTickCallback(void); void blinkDigitsBlinkTickCallback(void); void clientWifiTickCallback(void); void serverWifiTickCallback(void); void handleServerWifi(void); void handleClientWifi(void); void setConfiguration(const char* configurationFile, DynamicJsonDocument configuration, int bufferSize); DynamicJsonDocument getConfiguration(const char* configurationFile, int bufferSize); void handleRootHttpRequest(void); void handleSetupHttpRequest(void); void handleRootHttpGet(void); void handleSetupHttpGet(void); void handleRootHttpPost(void); void handleSetupHttpPost(void); void handleHttpNotFound(void); bool fsWriteFile(fs::FS &fs, const char *path, const char *payload); bool fsReadFile(fs::FS &fs, const char *path, char *payload, size_t maxLength); void rebootTickCallback(void); // Sensors application void mqttTickCallback(void); String getMqttTopic(void); String getMqttId(void); char *mqttSerialize(const JsonDocument doc, size_t maxLength); float mapValueToRange(float value, float xMin, float xMax, float yMin, float yMax); void sensorsTickCallback(void); /////////////////////////////////////////////////////////////////////////// // variable declarations // /////////////////////////////////////////////////////////////////////////// #if defined(ARDUINO_ARCH_ESP8266) ESP8266WebServer server(80); #elif defined(ARDUINO_ARCH_ESP32) WebServer server(80); #endif TickTwo arduinoOtaTick(arduinoOtaTickCallback, 1000); TickTwo rebootTick(rebootTickCallback, 1000); TickTwo clientWifiTick(clientWifiTickCallback, 25); TickTwo serverWifiTick(serverWifiTickCallback, 250); TickTwo blinkDigitsDahTick(blinkDigitsDahTickCallback, BLINK_DAH_LENGTH); TickTwo blinkDigitsDitTick(blinkDigitsDitTickCallback, BLINK_DIT_LENGTH); TickTwo blinkDigitsBlinkTick(blinkDigitsBlinkTickCallback, 25); TickTwo sensorsTick(sensorsTickCallback, 1000); char* authenticationCookie = NULL; bool otaStarted; bool networkConnected; int connectionTries; bool rebootPending; int temporarySsidLength; int temporarySsidIndex; int* temporarySsidNumbers; int blinkLedState; // GPIO application WiFiClient espClient; PubSubClient mqttClient(espClient); // Sensors application TickTwo mqttTick(mqttTickCallback, 250); String subscribedMqttTopic; HX710 pressure_sensor; /////////////////////////////////////////////////////////////////////////// // HTML templates // /////////////////////////////////////////////////////////////////////////// const char* HTML_BOOT_TEMPLATE = R"html( ESP Setup

ESP Setup


AP: %AP%
MAC: %MAC%




)html"; const char* HTML_AUTH_TEMPLATE = R"html( Preboot Access

Preboot Access


)html"; /////////////////////////////////////////////////////////////////////////// // begin Arduino // /////////////////////////////////////////////////////////////////////////// void setup() { #ifdef DEBUG Serial.begin(115200); // wait for serial while (!Serial) { delay(100); } Serial.println(); #else Serial.end(); #endif #if defined(ARDUINO_ARCH_ESP8266) if (!LittleFS.begin()) { #ifdef DEBUG Serial.println("LittleFS mount failed, formatting and rebooting..."); #endif LittleFS.format(); delay(1000); ESP.restart(); #elif defined(ARDUINO_ARCH_ESP32) if (!LittleFS.begin(true)) { #endif #ifdef DEBUG Serial.println("LittleFS mount failed..."); #endif return; } #ifdef DEBUG Serial.printf("Checking if WiFi server must be started...\n"); #endif // check if Ssid is set and start soft AP or STA mode DynamicJsonDocument configuration = getConfiguration(CONFIGURATION_FILE_NAME, CONFIGURATION_MAX_LENGTH); if(configuration.isNull() || !configuration.containsKey("Ssid")) { #ifdef DEBUG Serial.printf("No stored STA SSID found, proceeding to soft AP...\n"); #endif // start soft AP rebootTick.start(); serverWifiTick.start(); return; } #ifdef DEBUG Serial.printf("No stored STA Ssid found, proceeding to soft AP...\n"); #endif clientWifiTick.start(); // setup OTA ArduinoOTA.setHostname(configuration["name"].as()); // allow flashing with the master password ArduinoOTA.setPassword(PREBOOT_MASTER_PASSWORD); ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) { type = "sketch"; } else { // U_FS type = "filesystem"; } // NOTE: if updating FS this would be the place to unmount FS using FS.end() #ifdef DEBUG Serial.println("Start updating " + type); #endif }); ArduinoOTA.onEnd([]() { #ifdef DEBUG Serial.println("\nEnd"); #endif }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { #ifdef DEBUG Serial.printf("Progress: %u%%\r", (progress / (total / 100))); #endif }); ArduinoOTA.onError([](ota_error_t error) { #ifdef DEBUG Serial.printf("Error[%u]: ", error); #endif if (error == OTA_AUTH_ERROR) { #ifdef DEBUG Serial.println("Auth Failed"); #endif } else if (error == OTA_BEGIN_ERROR) { #ifdef DEBUG Serial.println("Begin Failed"); #endif } else if (error == OTA_CONNECT_ERROR) { #ifdef DEBUG Serial.println("Connect Failed"); #endif } else if (error == OTA_RECEIVE_ERROR) { #ifdef DEBUG Serial.println("Receive Failed"); #endif } else if (error == OTA_END_ERROR) { #ifdef DEBUG Serial.println("End Failed"); #endif } }); // start timers / threads arduinoOtaTick.start(); rebootTick.start(); // Sensors application pinMode (DECIBELMETER_PIN_ANALOG, INPUT); sensorsTick.start(); mqttTick.start(); } void loop() { arduinoOtaTick.update(); rebootTick.update(); clientWifiTick.update(); serverWifiTick.update(); blinkDigitsDitTick.update(); blinkDigitsDahTick.update(); blinkDigitsBlinkTick.update(); // Sensors application mqttTick.update(); sensorsTick.update(); } /////////////////////////////////////////////////////////////////////////// // end Arduino // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // Sensors-MQTT // /////////////////////////////////////////////////////////////////////////// // https://grimore.org/fuss/mathematics/algebra#linearly_map_a_value_in_a_range_into_another_range float mapValueToRange(float value, float xMin, float xMax, float yMin, float yMax) { return yMin + ( ( yMax - yMin ) * ( value - xMin ) / ( xMax - xMin ) ); } void sensorsTickCallback(void) { DynamicJsonDocument msg(MQTT_PAYLOAD_MAX_LENGTH); // measure noise level (dBA) float ai, vc, noise; ai = analogRead(DECIBELMETER_PIN_ANALOG); vc = mapValueToRange(ai, 0, 4095, 0, MAX_ANALOG_VOLTAGE); noise = vc * 50.0; #ifdef DEBUG Serial.println("Noise (dBA): " + String(noise)); #endif msg["noise"] = noise; // grab temperature DHT_Unified dht(DHT_PIN_ANALOG, DHTTYPE); dht.begin(); sensors_event_t event; // grab temperature dht.temperature().getEvent(&event); while (isnan(event.temperature)) { #ifdef DEBUG Serial.println("Failed to get temperature, retrying."); #endif dht.temperature().getEvent(&event); } msg["temperature"] = event.temperature; // grab humidity dht.humidity().getEvent(&event); while (isnan(event.relative_humidity)) { #ifdef DEBUG Serial.println("Failed to get humidity, retrying."); #endif dht.humidity().getEvent(&event); } msg["humidity"] = event.relative_humidity; // grab gas float gv, gas; gv = analogRead(GAS_PIN_ANALOG); gas = mapValueToRange(gv, 0, 255, 0, MAX_ANALOG_VOLTAGE); msg["gas"] = gas; // get pressure sensor pressure_sensor.initialize(BAROMETER_SCK_PIN, BAROMETER_OUT_PIN); while (!pressure_sensor.isReady()) { #ifdef DEBUG Serial.println("Failed to initialize pressure sensor, retrying."); #endif delay(1); } float pressure; pressure_sensor.readAndSelectNextData(HX710_DIFFERENTIAL_INPUT_10HZ); pressure = pressure_sensor.getLastDifferentialInput() / 1000.0; msg["pressure"] = pressure; /* * TEMT6000 - light sensor */ /* float lv, la, mal, lux; lv = analogRead(LIGHT_PIN_ANALOG) * 5.0 / 1024.0; #ifdef DEBUG Serial.println("light: " + String(lv)); #endif la = lv / 10000.0; // across 10,000 Ohms mal = la * 1000000; lux = mal * 2.0; msg["light"] = lux; */ // LM393 light sensor msg["light"] = analogRead(LIGHT_PIN_ANALOG); // get UV value float uv, vu, ux; //pinMode(UV_PIN_ANALOG, INPUT); uv = analogRead(UV_PIN_ANALOG); #ifdef DEBUG Serial.println("UV: " + String(uv)); #endif vu = uv * (5.0 / 1023.0); ux = vu / 0.1; msg["ultraviolet"] = ux; // payload is not null terminated and casting will not work char* msgPayload = mqttSerialize(msg, MQTT_PAYLOAD_MAX_LENGTH); #ifdef DEBUG Serial.println(String(msgPayload)); #endif mqttClient.publish(getMqttTopic().c_str(), msgPayload); free(msgPayload); } String getMqttTopic(void) { String mqttTopic = String(MQTT_TOPIC); if(mqttTopic == NULL || mqttTopic.length() == 0) { DynamicJsonDocument configuration = getConfiguration(CONFIGURATION_FILE_NAME, CONFIGURATION_MAX_LENGTH); if(configuration.containsKey("name")) { mqttTopic = configuration["name"].as(); } } return mqttTopic; } String getMqttId(void) { String mqttId = String(HOSTNAME()); DynamicJsonDocument configuration = getConfiguration(CONFIGURATION_FILE_NAME, CONFIGURATION_MAX_LENGTH); if(configuration.containsKey("name")) { mqttId = configuration["name"].as(); } return mqttId; } char *mqttSerialize(const JsonDocument doc, size_t maxLength) { char* buff = (char*) malloc(maxLength * sizeof(char)); serializeJson(doc, buff, maxLength); return buff; } bool mqttConnect() { #ifdef DEBUG Serial.println("Attempting to connect to MQTT broker: " + String(MQTT_HOST)); #endif mqttClient.setServer(MQTT_HOST, MQTT_PORT); DynamicJsonDocument msg(MQTT_PAYLOAD_MAX_LENGTH); if (mqttClient.connect(getMqttId().c_str(), MQTT_USERNAME, MQTT_PASSWORD)) { #ifdef DEBUG Serial.println("Established connection with MQTT broker using client ID: " + getMqttId()); #endif msg["action"] = "connected"; char *payload = mqttSerialize(msg, MQTT_PAYLOAD_MAX_LENGTH); mqttClient.publish(getMqttTopic().c_str(), payload); free(payload); #ifdef DEBUG Serial.println("Attempting to subscribe to MQTT topic: " + getMqttTopic()); #endif if (!mqttClient.subscribe(getMqttTopic().c_str())) { #ifdef DEBUG Serial.println("Failed to subscribe to MQTT topic: " + getMqttTopic()); #endif return false; } #ifdef DEBUG Serial.println("Subscribed to MQTT topic: " + getMqttTopic()); #endif msg["action"] = "subscribed"; payload = mqttSerialize(msg, MQTT_PAYLOAD_MAX_LENGTH); mqttClient.publish(getMqttTopic().c_str(), payload); free(payload); return true; } #ifdef DEBUG Serial.println("Connection to MQTT broker failed with MQTT client state: " + String(mqttClient.state())); #endif return false; } /////////////////////////////////////////////////////////////////////////// // Arduino loop // /////////////////////////////////////////////////////////////////////////// void mqttTickCallback(void) { if(!networkConnected) { return; } // Process MQTT client loop. if (mqttClient.connected()) { mqttClient.loop(); return; } if (!mqttConnect()) { #ifdef DEBUG Serial.printf("Unable to connect to MQTT\n"); #endif } } /////////////////////////////////////////////////////////////////////////// // OTA updates // /////////////////////////////////////////////////////////////////////////// void arduinoOtaTickCallback(void) { ArduinoOTA.handle(); if(!networkConnected) { return; } if(!otaStarted) { ArduinoOTA.begin(); otaStarted = true; } } /////////////////////////////////////////////////////////////////////////// // system-wide reboot // /////////////////////////////////////////////////////////////////////////// void rebootTickCallback(void) { // check if a reboot has been scheduled. if(!rebootPending) { return; } #ifdef DEBUG Serial.printf("Reboot pending, restarting in 1s...\n"); #endif ESP.restart(); } /////////////////////////////////////////////////////////////////////////// // HTTP route handling // /////////////////////////////////////////////////////////////////////////// void handleRootHttpPost(void) { String password; for(int i = 0; i < server.args(); ++i) { if(server.argName(i) == "password") { password = server.arg(i); continue; } } if(!password.equals(PREBOOT_MASTER_PASSWORD)) { server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); server.send(302); return; } #ifdef DEBUG Serial.println("Authentication succeeded, setting cookie and redirecting."); #endif // clear old authentication cookie if(authenticationCookie != NULL) { free(authenticationCookie); authenticationCookie = NULL; } authenticationCookie = randomStringHex(8); char* buff = (char*) malloc(PREBOOT_COOKIE_MAX_LENGTH * sizeof(char)); snprintf(buff, PREBOOT_COOKIE_MAX_LENGTH, "%s=%s; Max-Age=600; SameSite=Strict", PREBOOT_COOKIE_NAME, authenticationCookie); #ifdef DEBUG Serial.printf("Preboot cookie set to: %s\n", buff); #endif server.sendHeader("Set-Cookie", buff); server.sendHeader("Location", "/setup"); server.sendHeader("Cache-Control", "no-cache"); server.send(302); free(buff); } void handleSetupHttpPost(void) { String espName, staSsid, password; for(int i = 0; i < server.args(); ++i) { if(server.argName(i) == "name") { espName = server.arg(i); continue; } if(server.argName(i) == "Ssid") { staSsid = server.arg(i); continue; } if(server.argName(i) == "password") { password = server.arg(i); continue; } } if(espName == NULL || staSsid == NULL || password == NULL) { server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); server.send(302); return; } #ifdef DEBUG Serial.printf("Ssid %s and password %s received from web application.\n", staSsid, password); #endif DynamicJsonDocument configuration(CONFIGURATION_MAX_LENGTH); configuration["name"] = espName; configuration["Ssid"] = staSsid; configuration["password"] = password; setConfiguration(CONFIGURATION_FILE_NAME, configuration, CONFIGURATION_MAX_LENGTH); server.send(200, "text/plain", "Parameters applied. Scheduling reboot..."); #ifdef DEBUG Serial.printf("Configuration applied...\n"); #endif rebootPending = true; } void handleRootHttpGet(void) { // send login form #ifdef DEBUG Serial.printf("Sending authentication webpage.\n"); #endif String processTemplate = String(HTML_AUTH_TEMPLATE); server.send(200, "text/html", processTemplate); } void handleSetupHttpGet(void) { DynamicJsonDocument configuration = getConfiguration(CONFIGURATION_FILE_NAME, CONFIGURATION_MAX_LENGTH); String espName = HOSTNAME(); if(configuration.containsKey("name")) { espName = configuration["name"].as(); } // send default boot webpage #ifdef DEBUG Serial.printf("Sending configuration form webpage.\n"); #endif String processTemplate = String(HTML_BOOT_TEMPLATE); processTemplate.replace("%AP%", computeTemporarySsid()); processTemplate.replace("%MAC%", getHardwareAddress(':')); processTemplate.replace("%NAME%", espName); server.send(200, "text/html", processTemplate); } void handleRootHttpRequest(void) { switch(server.method()) { case HTTP_GET: handleRootHttpGet(); break; case HTTP_POST: handleRootHttpPost(); break; } } void handleSetupHttpRequest(void) { #ifdef DEBUG Serial.println("HTTP setup request received."); #endif if(!server.hasHeader("Cookie")) { #ifdef DEBUG Serial.println("No cookie header found."); #endif server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); server.send(302); return; } String cookie = server.header("Cookie"); if(authenticationCookie == NULL || cookie.indexOf(authenticationCookie) == -1) { #ifdef DEBUG Serial.println("Authentication failed."); #endif server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); server.send(302); return; } switch(server.method()) { case HTTP_GET: #ifdef DEBUG Serial.printf("HTTP GET request received for setup.\n"); #endif handleSetupHttpGet(); break; case HTTP_POST: #ifdef DEBUG Serial.printf("HTTP POST request received for setup.\n"); #endif handleSetupHttpPost(); break; } } void handleHttpNotFound(void) { server.sendHeader("Location", "/"); server.send(302); } /////////////////////////////////////////////////////////////////////////// // LittleFS file operations // /////////////////////////////////////////////////////////////////////////// bool fsWriteFile(fs::FS &fs, const char *path, const char *payload) { #if defined(ARDUINO_ARCH_ESP8266) File file = fs.open(path, "w"); #elif defined(ARDUINO_ARCH_ESP32) File file = fs.open(path, FILE_WRITE); #endif if (!file) { #ifdef DEBUG Serial.println("Failed to open file for writing."); #endif return false; } bool success = file.println(payload); file.close(); return success; } bool fsReadFile(fs::FS &fs, const char *path, char *payload, size_t maxLength) { #if defined(ARDUINO_ARCH_ESP8266) File file = fs.open(path, "r"); #elif defined(ARDUINO_ARCH_ESP32) File file = fs.open(path); #endif if (!file || file.isDirectory()) { #ifdef DEBUG Serial.println("Failed to open file for reading."); #endif return false; } int i = 0; while(file.available() && i < maxLength) { payload[i] = file.read(); ++i; } file.close(); payload[i] = '\0'; return true; } /////////////////////////////////////////////////////////////////////////// // set the current configuration // /////////////////////////////////////////////////////////////////////////// void setConfiguration(const char* configurationFile, DynamicJsonDocument configuration, int bufferSize) { char payload[bufferSize]; serializeJson(configuration, payload, bufferSize); if(!fsWriteFile(LittleFS, configurationFile, payload)) { #ifdef DEBUG Serial.printf("Unable to store configuration.\n"); #endif } } /////////////////////////////////////////////////////////////////////////// // get the current configuration // /////////////////////////////////////////////////////////////////////////// DynamicJsonDocument getConfiguration(const char* configurationFile, int bufferSize) { DynamicJsonDocument configuration(bufferSize); #ifdef DEBUG Serial.printf("Attempting to read configuration...\n"); #endif char* payload = (char *) malloc(bufferSize * sizeof(char)); if (fsReadFile(LittleFS, configurationFile, payload, bufferSize)) { #ifdef DEBUG Serial.printf("Found a valid configuration payload...\n"); #endif DeserializationError error = deserializeJson(configuration, payload); if(error) { #ifdef DEBUG Serial.printf("Deserialization of configuration failed.\n"); #endif } } #ifdef DEBUG Serial.printf("Configuration read complete.\n"); #endif free(payload); return configuration; } /////////////////////////////////////////////////////////////////////////// // generate random string // /////////////////////////////////////////////////////////////////////////// char* randomStringHex(int length) { const char alphabet[] = "0123456789abcdef"; char* payload = (char*) malloc(length * sizeof(char)); int i; for (i=0; i WIFI_CONNECT_TRIES) { // zap the Ssid in order to start softAP if(configuration.containsKey("Ssid")) { configuration.remove("Ssid"); } if(configuration.containsKey("password")) { configuration.remove("password"); } setConfiguration(CONFIGURATION_FILE_NAME, configuration, CONFIGURATION_MAX_LENGTH); #ifdef DEBUG Serial.printf("Restarting in 1 second...\n"); #endif rebootPending = true; return; } #ifdef DEBUG Serial.printf("Attempting to establish WiFi STA connecton [%d/%d]\n", (WIFI_CONNECT_TRIES - connectionTries) + 1, WIFI_CONNECT_TRIES); #endif #if defined(ARDUINO_ARCH_ESP8266) WiFi.hostname(configuration["name"].as()); #elif defined(ARDUINO_ARCH_ESP32) WiFi.setHostname(configuration["name"].as()); #endif String Ssid = configuration["Ssid"].as(); String password = configuration["password"].as(); #ifdef DEBUG Serial.printf("Trying connection to %s with %s...\n", Ssid, password); #endif //WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); WiFi.begin(Ssid, password); } /////////////////////////////////////////////////////////////////////////// // blink the temporary Ssid // /////////////////////////////////////////////////////////////////////////// void blinkDigitsDahTickCallback(void) { // wait for the dits to complete if(blinkDigitsDitTick.state() != STOPPED) { return; } if(temporarySsidIndex >= temporarySsidLength) { blinkDigitsDahTick.stop(); blinkDigitsDitTick.stop(); blinkDigitsBlinkTick.stop(); free(temporarySsidNumbers); #ifdef DEBUG Serial.println(); Serial.println("Dah-dit blink sequence completed."); #endif return; } #ifdef DEBUG Serial.printf("Starting to blink %d times: ", temporarySsidNumbers[temporarySsidIndex]); #endif pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); blinkDigitsDitTick.start(); } void blinkDigitsDitTickCallback(void) { #ifdef DEBUG Serial.printf("Dit: %d/%d\n", blinkDigitsDitTick.counter(), temporarySsidNumbers[temporarySsidIndex]); #endif if(blinkDigitsDitTick.counter() > temporarySsidNumbers[temporarySsidIndex]) { blinkDigitsDitTick.stop(); ++temporarySsidIndex; #ifdef DEBUG Serial.println("Dits completed..."); #endif return; } blinkDigitsDitTick.pause(); blinkDigitsBlinkTick.start(); } void blinkDigitsBlinkTickCallback(void) { if(blinkDigitsBlinkTick.counter() > 2) { blinkDigitsBlinkTick.stop(); blinkDigitsDitTick.resume(); return; } blinkLedState = !blinkLedState; digitalWrite(LED_BUILTIN, blinkLedState); }