mqttRSSI_FS.ino 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. ESP8266/ESP32 publish the RSSI as the WiFi signal strength to ThingSpeak channel.
  3. This example is for explaining how to use the AutoConnect library.
  4. In order to execute this example, the ThingSpeak account is needed. Sing up
  5. for New User Account and create a New Channel via My Channels.
  6. For details, please refer to the project page.
  7. https://hieromon.github.io/AutoConnect/howtoembed.html#used-with-mqtt-as-a-client-application
  8. Also, this example uses AutoConnectAux menu customization which stored in SPIFFS.
  9. To evaluate this example, you upload the contents as mqtt_setting.json of
  10. the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu
  11. in Arduino IDE).
  12. This example is based on the thinkspeak.com environment as of Dec. 20, 2018.
  13. Copyright (c) 2020 Hieromon Ikasamo.
  14. This software is released under the MIT License.
  15. https://opensource.org/licenses/MIT
  16. */
  17. #if defined(ARDUINO_ARCH_ESP8266)
  18. #include <ESP8266WiFi.h>
  19. #include <ESP8266HTTPClient.h>
  20. #define GET_CHIPID() (ESP.getChipId())
  21. #elif defined(ARDUINO_ARCH_ESP32)
  22. #include <WiFi.h>
  23. #include <WebServer.h>
  24. #include <SPIFFS.h>
  25. #include <HTTPClient.h>
  26. #define GET_CHIPID() ((uint16_t)(ESP.getEfuseMac()>>32))
  27. #endif
  28. #include <PubSubClient.h>
  29. #include <AutoConnect.h>
  30. /*
  31. AC_USE_SPIFFS indicates SPIFFS or LittleFS as available file systems that
  32. will become the AUTOCONNECT_USE_SPIFFS identifier and is exported as showng
  33. the valid file system. After including AutoConnect.h, the Sketch can determine
  34. whether to use FS.h or LittleFS.h by AUTOCONNECT_USE_SPIFFS definition.
  35. */
  36. #include <FS.h>
  37. #if defined(ARDUINO_ARCH_ESP8266)
  38. #ifdef AUTOCONNECT_USE_SPIFFS
  39. FS& FlashFS = SPIFFS;
  40. #else
  41. #include <LittleFS.h>
  42. FS& FlashFS = LittleFS;
  43. #endif
  44. #elif defined(ARDUINO_ARCH_ESP32)
  45. #include <SPIFFS.h>
  46. fs::SPIFFSFS& FlashFS = SPIFFS;
  47. #endif
  48. #define PARAM_FILE "/param.json"
  49. #define AUX_MQTTSETTING "/mqtt_setting"
  50. #define AUX_MQTTSAVE "/mqtt_save"
  51. #define AUX_MQTTCLEAR "/mqtt_clear"
  52. // Adjusting WebServer class with between ESP8266 and ESP32.
  53. #if defined(ARDUINO_ARCH_ESP8266)
  54. typedef ESP8266WebServer WiFiWebServer;
  55. #elif defined(ARDUINO_ARCH_ESP32)
  56. typedef WebServer WiFiWebServer;
  57. #endif
  58. AutoConnect portal;
  59. AutoConnectConfig config;
  60. WiFiClient wifiClient;
  61. PubSubClient mqttClient(wifiClient);
  62. String serverName;
  63. String channelId;
  64. String userKey;
  65. String apiKey;
  66. String apid;
  67. String hostName;
  68. unsigned int updateInterval = 0;
  69. unsigned long lastPub = 0;
  70. #define MQTT_USER_ID "anyone"
  71. bool mqttConnect() {
  72. static const char alphanum[] = "0123456789"
  73. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  74. "abcdefghijklmnopqrstuvwxyz"; // For random generation of client ID.
  75. char clientId[9];
  76. uint8_t retry = 3;
  77. while (!mqttClient.connected()) {
  78. if (serverName.length() <= 0)
  79. break;
  80. mqttClient.setServer(serverName.c_str(), 1883);
  81. Serial.println(String("Attempting MQTT broker:") + serverName);
  82. for (uint8_t i = 0; i < 8; i++) {
  83. clientId[i] = alphanum[random(62)];
  84. }
  85. clientId[8] = '\0';
  86. if (mqttClient.connect(clientId, MQTT_USER_ID, userKey.c_str())) {
  87. Serial.println("Established:" + String(clientId));
  88. return true;
  89. } else {
  90. Serial.println("Connection failed:" + String(mqttClient.state()));
  91. if (!--retry)
  92. break;
  93. delay(3000);
  94. }
  95. }
  96. return false;
  97. }
  98. void mqttPublish(String msg) {
  99. String path = String("channels/") + channelId + String("/publish/") + apiKey;
  100. mqttClient.publish(path.c_str(), msg.c_str());
  101. }
  102. int getStrength(uint8_t points) {
  103. uint8_t sc = points;
  104. long rssi = 0;
  105. while (sc--) {
  106. rssi += WiFi.RSSI();
  107. delay(20);
  108. }
  109. return points ? static_cast<int>(rssi / points) : 0;
  110. }
  111. String loadParams(AutoConnectAux& aux, PageArgument& args) {
  112. (void)(args);
  113. File param = FlashFS.open(PARAM_FILE, "r");
  114. if (param) {
  115. aux.loadElement(param);
  116. param.close();
  117. }
  118. else
  119. Serial.println(PARAM_FILE " open failed");
  120. return String("");
  121. }
  122. String saveParams(AutoConnectAux& aux, PageArgument& args) {
  123. serverName = args.arg("mqttserver");
  124. serverName.trim();
  125. channelId = args.arg("channelid");
  126. channelId.trim();
  127. userKey = args.arg("userkey");
  128. userKey.trim();
  129. apiKey = args.arg("apikey");
  130. apiKey.trim();
  131. String upd = args.arg("period");
  132. updateInterval = upd.substring(0, 2).toInt() * 1000;
  133. String uniqueid = args.arg("uniqueid");
  134. hostName = args.arg("hostname");
  135. hostName.trim();
  136. // The entered value is owned by AutoConnectAux of /mqtt_setting.
  137. // To retrieve the elements of /mqtt_setting, it is necessary to get
  138. // the AutoConnectAux object of /mqtt_setting.
  139. File param = FlashFS.open(PARAM_FILE, "w");
  140. portal.aux("/mqtt_setting")->saveElement(param, { "mqttserver", "channelid", "userkey", "apikey", "period", "uniqueid", "hostname" });
  141. param.close();
  142. // Echo back saved parameters to AutoConnectAux page.
  143. AutoConnectText& echo = aux["parameters"].as<AutoConnectText>();
  144. echo.value = "Server: " + serverName + "<br>";
  145. echo.value += "Channel ID: " + channelId + "<br>";
  146. echo.value += "User Key: " + userKey + "<br>";
  147. echo.value += "API Key: " + apiKey + "<br>";
  148. echo.value += "Update period: " + String(updateInterval / 1000) + " sec.<br>";
  149. echo.value += "Use APID unique: " + uniqueid + "<br>";
  150. echo.value += "ESP host name: " + hostName + "<br>";
  151. return String("");
  152. }
  153. void handleRoot() {
  154. String content =
  155. "<html>"
  156. "<head>"
  157. "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
  158. "</head>"
  159. "<body>"
  160. "<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>"
  161. "<p style=\"padding-top:5px;text-align:center\">" AUTOCONNECT_LINK(COG_24) "</p>"
  162. "</body>"
  163. "</html>";
  164. content.replace("{{CHANNEL}}", channelId);
  165. WiFiWebServer& webServer = portal.host();
  166. webServer.send(200, "text/html", content);
  167. }
  168. // Clear channel using Thingspeak's API.
  169. void handleClearChannel() {
  170. HTTPClient httpClient;
  171. String endpoint = serverName;
  172. endpoint.replace("mqtt", "api");
  173. String delUrl = "http://" + endpoint + "/channels/" + channelId + "/feeds.json?api_key=" + userKey;
  174. Serial.print("DELETE " + delUrl);
  175. if (httpClient.begin(wifiClient, delUrl)) {
  176. Serial.print(":");
  177. int resCode = httpClient.sendRequest("DELETE");
  178. const String& res = httpClient.getString();
  179. Serial.println(String(resCode) + String(",") + res);
  180. httpClient.end();
  181. }
  182. else
  183. Serial.println(" failed");
  184. // Returns the redirect response. The page is reloaded and its contents
  185. // are updated to the state after deletion.
  186. WiFiWebServer& webServer = portal.host();
  187. webServer.sendHeader("Location", String("http://") + webServer.client().localIP().toString() + String("/"));
  188. webServer.send(302, "text/plain", "");
  189. webServer.client().flush();
  190. webServer.client().stop();
  191. }
  192. // Load AutoConnectAux JSON from the flash on the board.
  193. bool loadAux(const String auxName) {
  194. bool rc = false;
  195. String fn = auxName + ".json";
  196. File fs = FlashFS.open(fn.c_str(), "r");
  197. if (fs) {
  198. rc = portal.load(fs);
  199. fs.close();
  200. }
  201. else
  202. Serial.println("Filesystem open failed: " + fn);
  203. return rc;
  204. }
  205. void setup() {
  206. delay(1000);
  207. Serial.begin(115200);
  208. Serial.println();
  209. #if defined(ARDUINO_ARCH_ESP8266)
  210. FlashFS.begin();
  211. #elif defined(ARDUINO_ARCH_ESP32)
  212. FlashFS.begin(true);
  213. #endif
  214. loadAux(AUX_MQTTSETTING);
  215. loadAux(AUX_MQTTSAVE);
  216. AutoConnectAux* setting = portal.aux(AUX_MQTTSETTING);
  217. if (setting) {
  218. PageArgument args;
  219. AutoConnectAux& mqtt_setting = *setting;
  220. loadParams(mqtt_setting, args);
  221. AutoConnectCheckbox& uniqueidElm = mqtt_setting["uniqueid"].as<AutoConnectCheckbox>();
  222. AutoConnectInput& hostnameElm = mqtt_setting["hostname"].as<AutoConnectInput>();
  223. if (uniqueidElm.checked) {
  224. config.apid = String("ESP") + "-" + String(GET_CHIPID(), HEX);
  225. Serial.println("apid set to " + config.apid);
  226. }
  227. if (hostnameElm.value.length()) {
  228. config.hostName = hostnameElm.value;
  229. Serial.println("hostname set to " + config.hostName);
  230. }
  231. config.homeUri = "/";
  232. portal.on(AUX_MQTTSETTING, loadParams);
  233. portal.on(AUX_MQTTSAVE, saveParams);
  234. }
  235. else
  236. Serial.println("aux. load error");
  237. // Reconnect and continue publishing even if WiFi is disconnected.
  238. config.autoReconnect = true;
  239. config.reconnectInterval = 1;
  240. portal.config(config);
  241. Serial.print("WiFi ");
  242. if (portal.begin()) {
  243. config.bootUri = AC_ONBOOTURI_HOME;
  244. Serial.println("connected:" + WiFi.SSID());
  245. Serial.println("IP:" + WiFi.localIP().toString());
  246. } else {
  247. Serial.println("connection failed:" + String(WiFi.status()));
  248. while (1) {
  249. delay(100);
  250. yield();
  251. }
  252. }
  253. WiFiWebServer& webServer = portal.host();
  254. webServer.on("/", handleRoot);
  255. webServer.on(AUX_MQTTCLEAR, handleClearChannel);
  256. }
  257. void loop() {
  258. portal.handleClient();
  259. if (updateInterval > 0) {
  260. if (millis() - lastPub > updateInterval) {
  261. if (!mqttClient.connected()) {
  262. mqttConnect();
  263. }
  264. String item = String("field1=") + String(getStrength(7));
  265. mqttPublish(item);
  266. mqttClient.loop();
  267. lastPub = millis();
  268. }
  269. }
  270. }