/*************************************************************************/ /* Copyright (C) 2024 Wizardry and Steamworks - License: GNU GPLv3 */ /*************************************************************************/ // Removing comment for debugging over the first serial port. #define DEBUG 1 // The AP to connect to via Wifi. #define STA_SSID "" // The AP Wifi password. #define STA_PSK "" // 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 default MQTT client ID is "esp-CHIPID" where CHIPID is the ESP8266 // or ESP32 chip identifier. #define MQTT_CLIENT_ID() String("esp-" + String(GET_CHIP_ID(), HEX)) // The authentication password to use for OTA updates. #define OTA_PASSWORD "" // The OTA port on which updates take place. #define OTA_PORT 8266 // The default topic that the sketch subscribes to is "esp/CHIPID" where // CHIPID is the ESP8266 or ESP32 chip identifier. #define MQTT_TOPIC() String("esp/" + String(GET_CHIP_ID(), HEX)) // 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 // Miscellaneous defines. //#define CHIP_ID_HEX (String(GET_CHIP_ID()).c_str()) #define HOSTNAME() String("esp-" + String(GET_CHIP_ID(), HEX)) // Platform specific libraries. #if defined(ARDUINO_ARCH_ESP8266) #include #include #elif defined(ARDUINO_ARCH_ESP32) #include #include #endif // General libraries. #include #include #include #include #if defined(ARDUINO_ARCH_ESP32) #include #include #endif WiFiClient espClient; PubSubClient mqttClient(espClient); #define DECIBELMETER_PIN_ANALOG 32 #define MAX_ANALOG_VOLTAGE 3.3 // 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 mqttCallback(char *topic, byte *payload, unsigned int length) { String msgTopic = String(topic); // payload is not null terminated and casting will not work char msgPayload[length + 1]; snprintf(msgPayload, length + 1, "%s", payload); #ifdef DEBUG Serial.println("Message received on topic: " + String(topic) + " with payload: " + String(msgPayload)); #endif // Parse the payload sent to the MQTT topic as a JSON document. StaticJsonDocument<256> doc; #ifdef DEBUG Serial.println("Deserializing message...."); #endif DeserializationError error = deserializeJson(doc, msgPayload); if (error) { #ifdef DEBUG Serial.println("Failed to parse MQTT payload as JSON: " + String(error.c_str())); #endif return; } } bool mqttConnect() { #ifdef DEBUG Serial.println("Attempting to connect to MQTT broker: " + String(MQTT_HOST)); #endif mqttClient.setServer(MQTT_HOST, MQTT_PORT); StaticJsonDocument<256> msg; if (mqttClient.connect(MQTT_CLIENT_ID().c_str(), MQTT_USERNAME, MQTT_PASSWORD)) { #ifdef DEBUG Serial.println("Established connection with MQTT broker using client ID: " + MQTT_CLIENT_ID()); #endif // Uncomment this in order to receive messages from MQTT. //mqttClient.setCallback(mqttCallback); msg["action"] = "connected"; char output[256]; serializeJson(msg, output, 256); mqttClient.publish(MQTT_TOPIC().c_str(), output); #ifdef DEBUG Serial.println("Attempting to subscribe to MQTT topic: " + MQTT_TOPIC()); #endif if (!mqttClient.subscribe(MQTT_TOPIC().c_str())) { #ifdef DEBUG Serial.println("Failed to subscribe to MQTT topic: " + MQTT_TOPIC()); #endif return false; } #ifdef DEBUG Serial.println("Subscribed to MQTT topic: " + MQTT_TOPIC()); #endif msg.clear(); msg["action"] = "subscribed"; serializeJson(msg, output, 256); mqttClient.publish(MQTT_TOPIC().c_str(), output); return true; } #ifdef DEBUG Serial.println("Connection to MQTT broker failed with MQTT client state: " + String(mqttClient.state())); #endif return false; } bool satifsyMessagePumps() { // Process OTA loop first since emergency OTA updates might be needed. ArduinoOTA.handle(); // Process MQTT client loop. if (!mqttClient.connected()) { // If the connection to the MQTT broker has failed then sleep before carrying on. if (!mqttConnect()) { return false; } } mqttClient.loop(); return true; } // the setup function runs once when you press reset or power the board void setup() { // initialize the GPIO pins taht will be used pinMode (DECIBELMETER_PIN_ANALOG, INPUT); Serial.begin(19200); #ifdef DEBUG Serial.println("Booted, setting up Wifi in 10s..."); #endif delay(10000); WiFi.mode(WIFI_STA); #if defined(ARDUINO_ARCH_ESP8266) WiFi.hostname(HOSTNAME().c_str()); #elif defined(ARDUINO_ARCH_ESP32) WiFi.setHostname(HOSTNAME().c_str()); #endif WiFi.begin(STA_SSID, STA_PSK); while (WiFi.waitForConnectResult() != WL_CONNECTED) { #ifdef DEBUG Serial.println("Failed to connect to Wifi, rebooting in 5s..."); #endif delay(5000); ESP.restart(); } #ifdef DEBUG Serial.print("Connected to Wifi: "); Serial.println(WiFi.localIP()); Serial.println("Setting up OTA in 10s..."); #endif delay(10000); // Port defaults to 8266 ArduinoOTA.setPort(OTA_PORT); // Hostname defaults to esp-[ChipID] ArduinoOTA.setHostname(HOSTNAME().c_str()); // Set the OTA password ArduinoOTA.setPassword(OTA_PASSWORD); ArduinoOTA.onStart([]() { switch (ArduinoOTA.getCommand()) { case U_FLASH: // Sketch #ifdef DEBUG Serial.println("OTA start updating sketch."); #endif break; #if defined(ARDUINO_ARCH_ESP8266) case U_FS: #elif defined(ARDUINO_ARCH_ESP32) case U_SPIFFS: #endif #ifdef DEBUG Serial.println("OTA start updating filesystem."); #endif SPIFFS.end(); break; default: #ifdef DEBUG Serial.println("Unknown OTA update type."); #endif break; } }); ArduinoOTA.onEnd([]() { #ifdef DEBUG Serial.println("OTA update complete."); #endif SPIFFS.begin(); #if defined(ARDUINO_ARCH_ESP8266) // For what it's worth, check the filesystem on ESP8266. SPIFFS.check(); #endif ESP.restart(); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { #ifdef DEBUG Serial.printf("OTA update progress: %u%%\r", (progress / (total / 100))); #endif }); ArduinoOTA.onError([](ota_error_t error) { #ifdef DEBUG Serial.printf("OTA update error [%u]: ", error); #endif switch (error) { case OTA_AUTH_ERROR: #ifdef DEBUG Serial.println("OTA authentication failed"); #endif break; case OTA_BEGIN_ERROR: #ifdef DEBUG Serial.println("OTA begin failed"); #endif break; case OTA_CONNECT_ERROR: #ifdef DEBUG Serial.println("OTA connect failed"); #endif break; case OTA_RECEIVE_ERROR: #ifdef DEBUG Serial.println("OTA receive failed"); #endif break; case OTA_END_ERROR: #ifdef DEBUG Serial.println("OTA end failed"); #endif break; default: #ifdef DEBUG Serial.println("Unknown OTA failure"); #endif break; } ESP.restart(); }); ArduinoOTA.begin(); // Set up MQTT client. mqttClient.setServer(MQTT_HOST, MQTT_PORT); // Touchdown. #ifdef DEBUG Serial.println("Setup complete."); #endif } // the loop function runs over and over again forever void loop() { // Check the Wifi connection status. int wifiStatus = WiFi.status(); switch (wifiStatus) { case WL_CONNECTED: if (!satifsyMessagePumps()) { delay(1000); break; } delay(1); break; case WL_NO_SHIELD: #ifdef DEBUG Serial.println("No Wifi shield present."); #endif goto DEFAULT_CASE; break; case WL_NO_SSID_AVAIL: #ifdef DEBUG Serial.println("Configured SSID not found."); #endif goto DEFAULT_CASE; break; // Temporary statuses indicating transitional states. case WL_IDLE_STATUS: case WL_SCAN_COMPLETED: delay(1000); break; // Fatal Wifi statuses trigger a delayed ESP restart. case WL_CONNECT_FAILED: case WL_CONNECTION_LOST: case WL_DISCONNECTED: default: #ifdef DEBUG Serial.println("Wifi connection failed with status: " + String(wifiStatus)); #endif DEFAULT_CASE: delay(10000); ESP.restart(); break; } // 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 StaticJsonDocument<256> msg; msg["noise"] = noise; char output[256]; serializeJson(msg, output, 256); mqttClient.publish(MQTT_TOPIC().c_str(), output); delay(1000); }