/* ESP8266/ESP32 publish the RSSI as the WiFi signal strength to ThingSpeak channel. This example is for explaining how to use the AutoConnect library. In order to execute this example, the ThingSpeak account is needed. Sing up for New User Account and create a New Channel via My Channels. For details, please refer to the project page. https://hieromon.github.io/AutoConnect/examples/index.html#used-with-mqtt-as-a-client-application This example is based on the environment as of March 20, 2018. Copyright (c) 2018 Hieromon Ikasamo. This software is released under the MIT License. https://opensource.org/licenses/MIT */ #if defined(ARDUINO_ARCH_ESP8266) #include #include #define GET_CHIPID() (ESP.getChipId()) #elif defined(ARDUINO_ARCH_ESP32) #include #include #include #define GET_CHIPID() ((uint16_t)(ESP.getEfuseMac()>>32)) #endif #include #include #include #define PARAM_FILE "/param.json" #define AUX_SETTING_URI "/mqtt_setting" #define AUX_SAVE_URI "/mqtt_save" #define AUX_CLEAR_URI "/mqtt_clear" // JSON definition of AutoConnectAux. // Multiple AutoConnectAux can be defined in the JSON array. // In this example, JSON is hard-coded to make it easier to // understand the AutoConnectAux API. In practice, it will be // an external content which separated from the sketch, // as the mqtt_RSSI_FS example shows. static const char AUX_mqtt_setting[] PROGMEM = R"raw( [ { "title": "MQTT Setting", "uri": "/mqtt_setting", "menu": true, "element": [ { "name": "header", "type": "ACText", "value": "

MQTT broker settings

", "style": "text-align:center;color:#2f4f4f;" }, { "name": "caption", "type": "ACText", "value": "Publishing the WiFi signal strength to MQTT channel. RSSI value of ESP8266 to the channel created on ThingSpeak", "style": "font-family:serif;color:#4682b4;" }, { "name": "mqttserver", "type": "ACInput", "value": "", "placeholder": "MQTT broker server", "label": "Server" }, { "name": "channelid", "type": "ACInput", "label": "Channel ID" }, { "name": "userkey", "type": "ACInput", "label": "User Key" }, { "name": "apikey", "type": "ACInput", "label": "API Key" }, { "name": "newline", "type": "ACElement", "value": "
" }, { "name": "uniqueid", "type": "ACCheckbox", "value": "unique", "label": "Use APID unique", "checked": false }, { "name": "period", "type": "ACRadio", "label": "Update period", "value": [ "30 sec.", "60 sec.", "180 sec." ], "arrange": "vertical", "checked": 1 }, { "name": "newline", "type": "ACElement", "value": "
" }, { "name": "hostname", "type": "ACInput", "label": "ESP host name", "value": "" }, { "name": "save", "type": "ACSubmit", "value": "Save&Start", "uri": "/mqtt_save" }, { "name": "discard", "type": "ACSubmit", "value": "Discard", "uri": "/" } ] }, { "title": "MQTT Setting", "uri": "/mqtt_save", "menu": false, "element": [ { "name": "caption", "type": "ACText", "value": "

Parameters saved as:

", "style": "text-align:center;color:#2f4f4f;padding:10px;" }, { "name": "parameters", "type": "ACText" }, { "name": "clear", "type": "ACSubmit", "value": "Clear channel", "uri": "/mqtt_clear" } ] } ] )raw"; // Adjusting WebServer class with between ESP8266 and ESP32. #if defined(ARDUINO_ARCH_ESP8266) typedef ESP8266WebServer WiFiWebServer; #elif defined(ARDUINO_ARCH_ESP32) typedef WebServer WiFiWebServer; #endif AutoConnect portal; AutoConnectConfig config; WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); String serverName; String channelId; String userKey; String apiKey; String apid; String hostName; unsigned int updateInterval = 0; unsigned long lastPub = 0; #define MQTT_USER_ID "anyone" bool mqttConnect() { static const char alphanum[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; // For random generation of client ID. char clientId[9]; uint8_t retry = 10; while (!mqttClient.connected()) { if (serverName.length() <= 0) break; mqttClient.setServer(serverName.c_str(), 1883); Serial.println(String("Attempting MQTT broker:") + serverName); for (uint8_t i = 0; i < 8; i++) { clientId[i] = alphanum[random(62)]; } clientId[8] = '\0'; if (mqttClient.connect(clientId, MQTT_USER_ID, userKey.c_str())) { Serial.println("Established:" + String(clientId)); return true; } else { Serial.println("Connection failed:" + String(mqttClient.state())); if (!--retry) break; } delay(3000); } return false; } void mqttPublish(String msg) { String path = String("channels/") + channelId + String("/publish/") + apiKey; int tLen = path.length(); char topic[tLen]; path.toCharArray(topic, tLen + 1); int mLen = msg.length(); char payload[mLen]; msg.toCharArray(payload, mLen + 1); mqttClient.publish(topic, payload); } int getStrength(uint8_t points) { uint8_t sc = points; long rssi = 0; while (sc--) { rssi += WiFi.RSSI(); delay(20); } return points ? static_cast(rssi / points) : 0; } // Load parameters saved with saveParams from SPIFFS into // the elements defined in /mqtt_setting JSON. String loadParams(AutoConnectAux& aux, PageArgument& args) { (void)(args); SPIFFS.begin(); File param = SPIFFS.open(PARAM_FILE, "r"); if (param) { if (aux.loadElement(param)) Serial.println(PARAM_FILE " loaded"); else Serial.println(PARAM_FILE " failed to load"); param.close(); } else { Serial.println(PARAM_FILE " open failed"); Serial.println("If you get error as 'SPIFFS: mount failed, -10025', Please modify with 'SPIFFS.begin(true)'."); } SPIFFS.end(); return ""; } // Save the value of each element entered by /mqtt_setting to // the parameter file. The saveParams as below is a callback // function of /mqtt_save. // When this callback is invoked, the input value of each element // of /mqtt_setting is already stored in the AutoConnectAux object. // In Sketch, you can output to stream its elements specified by name. String saveParams(AutoConnectAux& aux, PageArgument& args) { // PageArgument is a copy set of the elements that AutoConnectAux has. serverName = args.arg("mqttserver"); serverName.trim(); channelId = args.arg("channelid"); channelId.trim(); userKey = args.arg("userkey"); userKey.trim(); apiKey = args.arg("apikey"); apiKey.trim(); String upd = args.arg("period"); updateInterval = upd.substring(0, 2).toInt() * 1000; String uniqueid = args.arg("uniqueid"); hostName = args.arg("hostname"); hostName.trim(); // The entered value is owned by AutoConnectAux of /mqtt_setting. // In order to retrieve the elements of /mqtt_setting, // it is necessary to get the AutoConnectAux object of /mqtt_setting. SPIFFS.begin(); File param = SPIFFS.open(PARAM_FILE, "w"); portal.aux("/mqtt_setting")->saveElement(param, { "mqttserver", "channelid", "userkey", "apikey", "uniqueid", "hostname" }); param.close(); SPIFFS.end(); // Echo back saved parameters to AutoConnectAux page. AutoConnectText& echo = aux.getElement("parameters"); echo.value = "Server: " + serverName + "
"; echo.value += "Channel ID: " + channelId + "
"; echo.value += "User Key: " + userKey + "
"; echo.value += "API Key: " + apiKey + "
"; echo.value += "Update period: " + String(updateInterval / 1000) + " sec.
"; echo.value += "Use APID unique: " + uniqueid + "
"; echo.value += "ESP host name: " + hostName + "
"; return ""; } void handleRoot() { String content = "" "" "" "" "" "" "

" AUTOCONNECT_LINK(COG_24) "

" "" ""; WiFiWebServer& server = portal.host(); server.send(200, "text/html", content); } // Clear channel using ThingSpeak's API. void handleClearChannel() { HTTPClient httpClient; String endpoint = serverName; endpoint.replace("mqtt", "api"); String delUrl = "http://" + endpoint + "/channels/" + channelId + "/feeds.json?api_key=" + userKey; Serial.print("DELETE " + delUrl); if (httpClient.begin(delUrl)) { Serial.print(":"); int resCode = httpClient.sendRequest("DELETE"); String res = httpClient.getString(); httpClient.end(); Serial.println(String(resCode) + "," + res); } else Serial.println(" failed"); // Returns the redirect response. The page is reloaded and its contents // are updated to the state after deletion. WiFiWebServer& server = portal.host(); server.sendHeader("Location", String("http://") + server.client().localIP().toString() + String("/")); server.send(302, "text/plain", ""); server.client().flush(); server.client().stop(); } void setup() { delay(1000); Serial.begin(115200); Serial.println(); if (portal.load(FPSTR(AUX_mqtt_setting))) { AutoConnectAux* setting = portal.aux(AUX_SETTING_URI); PageArgument args; loadParams(*setting, args); AutoConnectCheckbox& uniqueidElm = setting->getElement("uniqueid"); AutoConnectInput& hostnameElm = setting->getElement("hostname"); if (uniqueidElm.checked) { config.apid = String("ESP") + "-" + String(GET_CHIPID(), HEX); Serial.println("apid set to " + config.apid); } if (hostnameElm.value.length()) { config.hostName = hostnameElm.value; Serial.println("hostname set to " + config.hostName); } config.bootUri = AC_ONBOOTURI_HOME; config.homeUri = "/"; portal.config(config); portal.on(AUX_SETTING_URI, loadParams); portal.on(AUX_SAVE_URI, saveParams); } else Serial.println("load error"); Serial.print("WiFi "); if (portal.begin()) { Serial.println("connected:" + WiFi.SSID()); Serial.println("IP:" + WiFi.localIP().toString()); } else { Serial.println("connection failed:" + String(WiFi.status())); while (1) { delay(100); yield(); } } WiFiWebServer& server = portal.host(); server.on("/", handleRoot); server.on(AUX_CLEAR_URI, handleClearChannel); } void loop() { if (updateInterval > 0) { if (millis() - lastPub > updateInterval) { if (!mqttClient.connected()) { mqttConnect(); } String item = String("field1=") + String(getStrength(7)); mqttPublish(item); mqttClient.loop(); lastPub = millis(); } } portal.handleClient(); }