123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /*
- 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/howtoembed.html#used-with-mqtt-as-a-client-application
- This example is based on the environment as of March 20, 2018.
- Copyright (c) 2020 Hieromon Ikasamo.
- This software is released under the MIT License.
- https://opensource.org/licenses/MIT
- */
- #if defined(ARDUINO_ARCH_ESP8266)
- #include <ESP8266WiFi.h>
- #include <ESP8266HTTPClient.h>
- #define GET_CHIPID() (ESP.getChipId())
- #elif defined(ARDUINO_ARCH_ESP32)
- #include <WiFi.h>
- #include <SPIFFS.h>
- #include <HTTPClient.h>
- #define GET_CHIPID() ((uint16_t)(ESP.getEfuseMac()>>32))
- #endif
- #include <PubSubClient.h>
- #include <AutoConnect.h>
- /*
- AC_USE_SPIFFS indicates SPIFFS or LittleFS as available file systems that
- will become the AUTOCONNECT_USE_SPIFFS identifier and is exported as showng
- the valid file system. After including AutoConnect.h, the Sketch can determine
- whether to use FS.h or LittleFS.h by AUTOCONNECT_USE_SPIFFS definition.
- */
- #include <FS.h>
- #if defined(ARDUINO_ARCH_ESP8266)
- #ifdef AUTOCONNECT_USE_SPIFFS
- FS& FlashFS = SPIFFS;
- #else
- #include <LittleFS.h>
- FS& FlashFS = LittleFS;
- #endif
- #elif defined(ARDUINO_ARCH_ESP32)
- #include <SPIFFS.h>
- fs::SPIFFSFS& FlashFS = SPIFFS;
- #endif
- #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": "style",
- "type": "ACStyle",
- "value": "label+input,label+select{position:sticky;left:120px;width:230px!important;box-sizing:border-box;}"
- },
- {
- "name": "header",
- "type": "ACText",
- "value": "<h2>MQTT broker settings</h2>",
- "style": "text-align:center;color:#2f4f4f;padding:10px;"
- },
- {
- "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": "",
- "label": "Server",
- "pattern": "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$",
- "placeholder": "MQTT broker server"
- },
- {
- "name": "channelid",
- "type": "ACInput",
- "label": "Channel ID",
- "pattern": "^[0-9]{6}$"
- },
- {
- "name": "userkey",
- "type": "ACInput",
- "label": "User Key"
- },
- {
- "name": "apikey",
- "type": "ACInput",
- "label": "API Key"
- },
- {
- "name": "newline",
- "type": "ACElement",
- "value": "<hr>"
- },
- {
- "name": "uniqueid",
- "type": "ACCheckbox",
- "value": "unique",
- "label": "Use APID unique",
- "checked": false
- },
- {
- "name": "period",
- "type": "ACRadio",
- "value": [
- "30 sec.",
- "60 sec.",
- "180 sec."
- ],
- "label": "Update period",
- "arrange": "vertical",
- "checked": 1
- },
- {
- "name": "hostname",
- "type": "ACInput",
- "value": "",
- "label": "ESP host name",
- "pattern": "^([a-zA-Z0-9]([a-zA-Z0-9-])*[a-zA-Z0-9]){1,24}$"
- },
- {
- "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": "<h4>Parameters saved as:</h4>",
- "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;
- bool uniqueid;
- 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 = 3;
- 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;
- mqttClient.publish(path.c_str(), msg.c_str());
- }
- int getStrength(uint8_t points) {
- uint8_t sc = points;
- long rssi = 0;
- while (sc--) {
- rssi += WiFi.RSSI();
- delay(20);
- }
- return points ? static_cast<int>(rssi / points) : 0;
- }
- void getParams(AutoConnectAux& aux) {
- serverName = aux["mqttserver"].value;
- serverName.trim();
- channelId = aux["channelid"].value;
- channelId.trim();
- userKey = aux["userkey"].value;
- userKey.trim();
- apiKey = aux["apikey"].value;
- apiKey.trim();
- AutoConnectRadio& period = aux["period"].as<AutoConnectRadio>();
- updateInterval = period.value().substring(0, 2).toInt() * 1000;
- uniqueid = aux["uniqueid"].as<AutoConnectCheckbox>().checked;
- hostName = aux["hostname"].value;
- hostName.trim();
- }
- // Load parameters saved with saveParams from SPIFFS into the
- // elements defined in /mqtt_setting JSON.
- String loadParams(AutoConnectAux& aux, PageArgument& args) {
- (void)(args);
- File param = FlashFS.open(PARAM_FILE, "r");
- if (param) {
- if (aux.loadElement(param)) {
- getParams(aux);
- Serial.println(PARAM_FILE " loaded");
- }
- else
- Serial.println(PARAM_FILE " failed to load");
- param.close();
- }
- else
- Serial.println(PARAM_FILE " open failed");
- return String("");
- }
- // 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 invoking this handler, the input value of each
- // element is already stored in '/mqtt_setting'.
- // In Sketch, you can output to stream its elements specified by name.
- String saveParams(AutoConnectAux& aux, PageArgument& args) {
- // The 'where()' function returns the AutoConnectAux that caused
- // the transition to this page.
- AutoConnectAux& mqtt_setting = *portal.aux(portal.where());
- getParams(mqtt_setting);
- AutoConnectInput& mqttserver = mqtt_setting["mqttserver"].as<AutoConnectInput>();
- // The entered value is owned by AutoConnectAux of /mqtt_setting.
- // To retrieve the elements of /mqtt_setting, it is necessary to get
- // the AutoConnectAux object of /mqtt_setting.
- File param = FlashFS.open(PARAM_FILE, "w");
- mqtt_setting.saveElement(param, { "mqttserver", "channelid", "userkey", "apikey", "uniqueid", "period", "hostname" });
- param.close();
- // Echo back saved parameters to AutoConnectAux page.
- AutoConnectText& echo = aux["parameters"].as<AutoConnectText>();
- echo.value = "Server: " + serverName;
- echo.value += mqttserver.isValid() ? String(" (OK)") : String(" (ERR)");
- echo.value += "<br>Channel ID: " + channelId + "<br>";
- echo.value += "User Key: " + userKey + "<br>";
- echo.value += "API Key: " + apiKey + "<br>";
- echo.value += "Update period: " + String(updateInterval / 1000) + " sec.<br>";
- echo.value += "Use APID unique: " + String(uniqueid == true ? "true" : "false") + "<br>";
- echo.value += "ESP host name: " + hostName + "<br>";
- return String("");
- }
- void handleRoot() {
- String content =
- "<html>"
- "<head>"
- "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
- "</head>"
- "<body>"
- "<iframe width=\"450\" height=\"260\" style=\"transform:scale(0.79);-o-transform:scale(0.79);-webkit-transform:scale(0.79);-moz-transform:scale(0.79);-ms-transform:scale(0.79);transform-origin:0 0;-o-transform-origin:0 0;-webkit-transform-origin:0 0;-moz-transform-origin:0 0;-ms-transform-origin:0 0;border: 1px solid #cccccc;\" src=\"https://thingspeak.com/channels/{{CHANNEL}}/charts/1?bgcolor=%23ffffff&color=%23d62020&dynamic=true&type=line\"></iframe>"
- "<p style=\"padding-top:5px;text-align:center\">" AUTOCONNECT_LINK(COG_24) "</p>"
- "</body>"
- "</html>";
- content.replace("{{CHANNEL}}", channelId);
- WiFiWebServer& webServer = portal.host();
- webServer.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(wifiClient, delUrl)) {
- Serial.print(":");
- int resCode = httpClient.sendRequest("DELETE");
- const String& res = httpClient.getString();
- Serial.println(String(resCode) + String(",") + res);
- httpClient.end();
- }
- else
- Serial.println(" failed");
- // Returns the redirect response. The page is reloaded and its contents
- // are updated to the state after deletion.
- WiFiWebServer& webServer = portal.host();
- webServer.sendHeader("Location", String("http://") + webServer.client().localIP().toString() + String("/"));
- webServer.send(302, "text/plain", "");
- webServer.client().flush();
- webServer.client().stop();
- }
- void setup() {
- delay(1000);
- Serial.begin(115200);
- Serial.println();
- #if defined(ARDUINO_ARCH_ESP8266)
- FlashFS.begin();
- #elif defined(ARDUINO_ARCH_ESP32)
- FlashFS.begin(true);
- #endif
- if (portal.load(FPSTR(AUX_mqtt_setting))) {
- AutoConnectAux& mqtt_setting = *portal.aux(AUX_SETTING_URI);
- PageArgument args;
- loadParams(mqtt_setting, args);
- if (uniqueid) {
- config.apid = String("ESP") + "-" + String(GET_CHIPID(), HEX);
- Serial.println("apid set to " + config.apid);
- }
- if (hostName.length()) {
- config.hostName = hostName;
- Serial.println("hostname set to " + config.hostName);
- }
- config.homeUri = "/";
- portal.on(AUX_SETTING_URI, loadParams);
- portal.on(AUX_SAVE_URI, saveParams);
- }
- else
- Serial.println("load error");
- // Reconnect and continue publishing even if WiFi is disconnected.
- config.autoReconnect = true;
- config.reconnectInterval = 1;
- portal.config(config);
- Serial.print("WiFi ");
- if (portal.begin()) {
- config.bootUri = AC_ONBOOTURI_HOME;
- Serial.println("connected:" + WiFi.SSID());
- Serial.println("IP:" + WiFi.localIP().toString());
- }
- else {
- Serial.println("connection failed:" + String(WiFi.status()));
- Serial.println("Needs WiFi connection to start publishing messages");
- }
- WiFiWebServer& webServer = portal.host();
- webServer.on("/", handleRoot);
- webServer.on(AUX_CLEAR_URI, handleClearChannel);
- }
- void loop() {
- if (WiFi.status() == WL_CONNECTED) {
- // MQTT publish control
- 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();
- }
|