--- disqus: ahb0222 GA : G-VF9ZT413CG --- # LilyGo T-Call SIM 連接 BME280 & MQTT & Node-Red 製作遠端氣壓溫溼度監測器 > [color=#40f1ef][name=LHB阿好伯, 2021/07/16][:earth_africa:](https://www.facebook.com/LHB0222/) ###### tags: `MQTT` `Arduino` [TOC] # 相關文章 [Node-RED安裝](/NMKfZcKwTaKWGaraPMQeKw) [MQTT 入門](/1UJl7NNvT6GdDq8KnnLQAQ) ![](https://i.imgur.com/o0Rb8Vw.png) 承接上一篇文章[LilyGo T-Call SIM 連接 MQTT & Node-Red & Telegram 製作遠端pH監測器(ESP32 SIM800L)_分享](/FlpNFLrxSpSbFl_CLTch-A) 我增加了一組BME280讀取大氣壓與溫溼度資料 後續一 樣使用MQTT將資料傳送到Node-Red 搭配[Node-Red dashboard](https://flows.nodered.org/node/node-red-dashboard)做為即時資料的展示 ![](https://i.imgur.com/92ChW65.png) ![](https://i.imgur.com/ZMLxbtj.png) :::spoiler 相關程式碼 ```cpp= /* Rui Santos Complete project details at https://RandomNerdTutorials.com/esp32-cloud-mqtt-broker-sim800l/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #define pHSensorPin 35 String msgStr = ""; String pHval2 = ""; String TempVal = ""; String HumiVal = ""; String PressVal = ""; char json[25]; char json2[25]; char json3[25]; char json4[25]; // Select your modem: #define TINY_GSM_MODEM_SIM800 // Modem is SIM800L // Set serial for debug console (to the Serial Monitor, default speed 115200) #define SerialMon Serial // Set serial for AT commands #define SerialAT Serial1 // Define the serial console for debug prints, if needed #define TINY_GSM_DEBUG SerialMon // set GSM PIN, if any #define GSM_PIN "" int pHval; // Your GPRS credentials, if any const char apn[] = "gtnet"; // APN (example: internet.vodafone.pt) use https://wiki.apnchanger.org const char gprsUser[] = ""; const char gprsPass[] = ""; // SIM card PIN (leave empty, if not defined) const char simPIN[] = ""; // MQTT details const char* broker = "mqtt.eclipseprojects.io"; // Public IP address or domain name const char* mqttUsername = "REPLACE_WITH_YOUR_MQTT_USER"; // MQTT username const char* mqttPassword = "REPLACE_WITH_YOUR_MQTT_PASS"; // MQTT password const char* topicOutput1 = "AHB0222/T_call_1"; const char* topicOutput2 = "AHB0222/T_call_2"; const char* topicTemperature = "AHB0222/T_call_Temp"; const char* topicHumidity = "AHB0222/T_call_Humi"; const char* topicpH = "AHB0222/T_call_pH"; const char* topicPress = "AHB0222/T_call_Press"; // Define the serial console for debug prints, if needed //#define DUMP_AT_COMMANDS #include <Wire.h> #include <TinyGsmClient.h> #include <PubSubClient.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #ifdef DUMP_AT_COMMANDS #include <StreamDebugger.h> StreamDebugger debugger(SerialAT, SerialMon); TinyGsm modem(debugger); #else TinyGsm modem(SerialAT); #endif #include <PubSubClient.h> //#include <Adafruit_Sensor.h> //#include <Adafruit_BME280.h> TinyGsmClient client(modem); PubSubClient mqtt(client); // TTGO T-Call pins #define MODEM_RST 5 #define MODEM_PWKEY 4 #define MODEM_POWER_ON 23 #define MODEM_TX 27 #define MODEM_RX 26 #define I2C_SDA 21 #define I2C_SCL 22 // BME280 pins #define I2C_SDA_2 18 #define I2C_SCL_2 19 #define OUTPUT_1 2 #define OUTPUT_2 15 uint32_t lastReconnectAttempt = 0; // I2C for SIM800 (to keep it running when powered from battery) TwoWire I2CPower = TwoWire(0); TwoWire I2CBME = TwoWire(1); Adafruit_BME280 bme; #define IP5306_ADDR 0x75 #define IP5306_REG_SYS_CTL0 0x00 float temperature = 0; float humidity = 0; long lastMsg = 0; bool setPowerBoostKeepOn(int en){ I2CPower.beginTransmission(IP5306_ADDR); I2CPower.write(IP5306_REG_SYS_CTL0); if (en) { I2CPower.write(0x37); // Set bit1: 1 enable 0 disable boost keep on } else { I2CPower.write(0x35); // 0x37 is default reg value } return I2CPower.endTransmission() == 0; } void mqttCallback(char* topic, byte* message, unsigned int len) { Serial.print("Message arrived on topic: "); Serial.print(topic); Serial.print(". Message: "); String messageTemp; for (int i = 0; i < len; i++) { Serial.print((char)message[i]); messageTemp += (char)message[i]; } Serial.println(); // Feel free to add more if statements to control more GPIOs with MQTT // If a message is received on the topic esp/output1, you check if the message is either "true" or "false". // Changes the output state according to the message if (String(topic) == topicOutput1) { Serial.print("Changing output to "); if(messageTemp == "true"){ Serial.println("true"); digitalWrite(OUTPUT_1, HIGH); } else if(messageTemp == "false"){ Serial.println("false"); digitalWrite(OUTPUT_1, LOW); } } else if (String(topic) == topicOutput2) { Serial.print("Changing output to "); if(messageTemp == "true"){ Serial.println("true"); digitalWrite(OUTPUT_2, HIGH); } else if(messageTemp == "false"){ Serial.println("false"); digitalWrite(OUTPUT_2, LOW); } } } boolean mqttConnect() { SerialMon.print("Connecting to "); SerialMon.print(broker); // Connect to MQTT Broker without username and password //boolean status = mqtt.connect("GsmClientN"); // Or, if you want to authenticate MQTT: boolean status = mqtt.connect("GsmClientN", mqttUsername, mqttPassword); if (status == false) { SerialMon.println(" fail"); ESP.restart(); return false; } SerialMon.println(" success"); mqtt.subscribe(topicOutput1); mqtt.subscribe(topicOutput2); return mqtt.connected(); } void setup() { // Set console baud rate SerialMon.begin(115200); delay(10); // Start I2C communication I2CPower.begin(I2C_SDA, I2C_SCL, 400000); I2CBME.begin(I2C_SDA_2, I2C_SCL_2, 400000); // Keep power when running from battery bool isOk = setPowerBoostKeepOn(1); SerialMon.println(String("IP5306 KeepOn ") + (isOk ? "OK" : "FAIL")); // Set modem reset, enable, power pins pinMode(MODEM_PWKEY, OUTPUT); pinMode(MODEM_RST, OUTPUT); pinMode(MODEM_POWER_ON, OUTPUT); digitalWrite(MODEM_PWKEY, LOW); digitalWrite(MODEM_RST, HIGH); digitalWrite(MODEM_POWER_ON, HIGH); pinMode(OUTPUT_1, OUTPUT); pinMode(OUTPUT_2, OUTPUT); SerialMon.println("Wait..."); // Set GSM module baud rate and UART pins SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX); delay(6000); // Restart takes quite some time // To skip it, call init() instead of restart() SerialMon.println("Initializing modem..."); modem.restart(); // modem.init(); String modemInfo = modem.getModemInfo(); SerialMon.print("Modem Info: "); SerialMon.println(modemInfo); // Unlock your SIM card with a PIN if needed if ( GSM_PIN && modem.getSimStatus() != 3 ) { modem.simUnlock(GSM_PIN); } // You might need to change the BME280 I2C address, in our case it's 0x76 if (!bme.begin(0x76, &I2CBME)) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } SerialMon.print("Connecting to APN: "); SerialMon.print(apn); if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { SerialMon.println(" fail"); ESP.restart(); } else { SerialMon.println(" OK"); } if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); } // MQTT Broker setup mqtt.setServer(broker, 1883); mqtt.setCallback(mqttCallback); } void loop() { if (!mqtt.connected()) { SerialMon.println("=== MQTT NOT CONNECTED ==="); // Reconnect every 10 seconds uint32_t t = millis(); if (t - lastReconnectAttempt > 10000L) { lastReconnectAttempt = t; if (mqttConnect()) { lastReconnectAttempt = 0; } } delay(100); return; } pHval2 = analogRead(pHSensorPin); SerialMon.println(pHval2); // 把String字串轉換成字元陣列格式 pHval2.toCharArray(json, 25); // 發布MQTT主題與訊息 mqtt.publish(topicpH, json); // 清空MQTT訊息內容 pHval2 = ""; delay(16000); temperature = bme.readTemperature(); // Uncomment the next line to set temperature in Fahrenheit // (and comment the previous temperature line) //temperature = 1.8 * bme.readTemperature() + 32; // Temperature in Fahrenheit // Convert the value to a char array char tempString[8]; TempVal = bme.readTemperature(); SerialMon.println(bme.readTemperature()); TempVal.toCharArray(json2, 25); dtostrf(temperature, 1, 2, tempString); Serial.print("Temperature: "); Serial.println(tempString); mqtt.publish(topicTemperature, json2); TempVal = ""; delay(16000); humidity = bme.readHumidity(); char humString[8]; dtostrf(humidity, 1, 2, humString); HumiVal = humString; SerialMon.println(HumiVal); HumiVal.toCharArray(json3, 25); // Convert the value to a char array Serial.print("Humidity: "); Serial.println(humString); mqtt.publish(topicHumidity, json3); HumiVal = ""; delay(16000); PressVal = bme.readPressure()/100.0F; PressVal.toCharArray(json4, 25); Serial.print("Pressure: "); Serial.println(bme.readPressure() / 100.0F); mqtt.publish(topicPress, json4); delay(16000); mqtt.loop(); } ``` ::: ```json= [{"id":"2a8e219c.b1f8ce","type":"tab","label":"流程1","disabled":false,"info":""},{"id":"5ab655c.5a0ceac","type":"mqtt in","z":"2a8e219c.b1f8ce","name":"","topic":"","qos":"2","datatype":"auto","broker":"7ca5ce14.19016","nl":false,"rap":true,"rh":0,"x":170,"y":200,"wires":[["f69b7a6a.0ea098","c44030ed.ed6e3","b1ba24c5.981ca8","5d9cc00d.6fc1e","3029c125.40261e"]]},{"id":"c632e0c0.d2dc4","type":"mqtt in","z":"2a8e219c.b1f8ce","name":"","topic":"","qos":"2","datatype":"auto","broker":"7ca5ce14.19016","nl":false,"rap":true,"rh":0,"x":160,"y":260,"wires":[["4403c4ea.10888c","8df5a61b.de2e68","dd757d5.725418","8fbde7c0.5cf2b8","fc12d609.1d3fa8"]]},{"id":"f69b7a6a.0ea098","type":"debug","z":"2a8e219c.b1f8ce","name":"AHB0222/T_call_1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":440,"y":220,"wires":[]},{"id":"4403c4ea.10888c","type":"debug","z":"2a8e219c.b1f8ce","name":"AHB0222/T_call_2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":460,"y":280,"wires":[]},{"id":"c44030ed.ed6e3","type":"ui_gauge","z":"2a8e219c.b1f8ce","name":"","group":"31c84fde.527f7","order":2,"width":0,"height":0,"gtype":"gage","title":"Temperature","label":"Celsius Degrees","format":"{{value}}","min":"-10","max":"40","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":430,"y":240,"wires":[]},{"id":"8df5a61b.de2e68","type":"ui_gauge","z":"2a8e219c.b1f8ce","name":"","group":"ac61e344.1c471","order":2,"width":0,"height":0,"gtype":"gage","title":"Humidity","label":"%","format":"{{value}}","min":"0","max":"100","colors":["#93dae6","#0074cc","#002561"],"seg1":"","seg2":"","x":440,"y":300,"wires":[]},{"id":"5eeb002f.5e794","type":"telegrambot-notify","z":"2a8e219c.b1f8ce","name":"Node-Red_2","bot":"","chatId":"","message":"","parseMode":"","x":630,"y":160,"wires":[]},{"id":"b1ba24c5.981ca8","type":"function","z":"2a8e219c.b1f8ce","name":"Temp :","func":"var message = \"Temperature : \" + msg.payload + \" degrees Celsius\";\nmsg.payload = message;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":160,"wires":[["5eeb002f.5e794"]]},{"id":"562d1573.a25f1c","type":"telegrambot-notify","z":"2a8e219c.b1f8ce","name":"Node-Red_2","bot":"","chatId":"","message":"","parseMode":"","x":630,"y":340,"wires":[]},{"id":"dd757d5.725418","type":"function","z":"2a8e219c.b1f8ce","name":"Humi :","func":"var message = \"Humidity : \" + msg.payload + \" %\";\nmsg.payload = message;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":340,"wires":[["562d1573.a25f1c"]]},{"id":"5d9cc00d.6fc1e","type":"ui_chart","z":"2a8e219c.b1f8ce","name":"","group":"31c84fde.527f7","order":0,"width":0,"height":0,"label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"40","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"x":640,"y":220,"wires":[[]]},{"id":"8fbde7c0.5cf2b8","type":"ui_chart","z":"2a8e219c.b1f8ce","name":"","group":"ac61e344.1c471","order":0,"width":0,"height":0,"label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"100","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"x":710,"y":280,"wires":[[]]},{"id":"6426e56c.ab30ec","type":"mqtt in","z":"2a8e219c.b1f8ce","name":"","topic":"","qos":"2","datatype":"auto","broker":"7ca5ce14.19016","nl":false,"rap":true,"rh":0,"x":150,"y":360,"wires":[["87719c8.d06666","32d8cdf.f55f232","50ea302a.720ac","f2fb87ca.7bb588","b80847f.3f938b8"]]},{"id":"87719c8.d06666","type":"debug","z":"2a8e219c.b1f8ce","name":"AHB0222/T_call_2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":450,"y":380,"wires":[]},{"id":"32d8cdf.f55f232","type":"ui_gauge","z":"2a8e219c.b1f8ce","name":"","group":"f6f1e0b0.8472b","order":2,"width":0,"height":0,"gtype":"gage","title":"Pressure","label":"hPa","format":"{{value}}","min":"0","max":"1200","colors":["#93dae6","#0074cc","#002561"],"seg1":"","seg2":"","x":430,"y":400,"wires":[]},{"id":"4726e499.b1c16c","type":"telegrambot-notify","z":"2a8e219c.b1f8ce","name":"Node-Red_2","bot":"","chatId":"","message":"","parseMode":"","x":620,"y":440,"wires":[]},{"id":"50ea302a.720ac","type":"function","z":"2a8e219c.b1f8ce","name":"Press :","func":"var message = \"Pressure : \" + msg.payload + \" hPa\";\nmsg.payload = message;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":440,"wires":[["4726e499.b1c16c"]]},{"id":"f2fb87ca.7bb588","type":"ui_chart","z":"2a8e219c.b1f8ce","name":"","group":"f6f1e0b0.8472b","order":0,"width":0,"height":0,"label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"300","ymax":"1100","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"x":640,"y":380,"wires":[[]]},{"id":"3029c125.40261e","type":"function","z":"2a8e219c.b1f8ce","name":"Temp","func":"var message = \"Temp,\" + msg.payload \nvar today=new Date();\n\nvar currentDateTime =\ntoday.getFullYear()+'/'+\n(today.getMonth()+1)+'/'+\ntoday.getDate()+ \" \" +\ntoday.getHours()+':'+today.getMinutes()+\n':' + today.getSeconds();\nmessage = currentDateTime + \",\" + message;\nmsg.payload = message;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":770,"y":200,"wires":[["5fa7b551.0b921c"]]},{"id":"fc12d609.1d3fa8","type":"function","z":"2a8e219c.b1f8ce","name":"Humi","func":"var message = \"Humi,\" + msg.payload ;\nvar today=new Date();\n\nvar currentDateTime =\ntoday.getFullYear()+'/'+\n(today.getMonth()+1)+'/'+\ntoday.getDate()+ \" \" +\ntoday.getHours()+':'+today.getMinutes()+\n':' + today.getSeconds();\nmessage = currentDateTime + \",\" + message;\nmsg.payload = message;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":300,"wires":[["5fa7b551.0b921c"]]},{"id":"b80847f.3f938b8","type":"function","z":"2a8e219c.b1f8ce","name":"Press","func":"var message = \"Press,\" + msg.payload ;\nvar today=new Date();\n\nvar currentDateTime =\ntoday.getFullYear()+'/'+\n(today.getMonth()+1)+'/'+\ntoday.getDate()+ \" \" +\ntoday.getHours()+':'+today.getMinutes()+\n':' + today.getSeconds();\nmessage = currentDateTime + \",\" + message;\nmsg.payload = message;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":770,"y":400,"wires":[["5fa7b551.0b921c"]]},{"id":"5fa7b551.0b921c","type":"file","z":"2a8e219c.b1f8ce","name":"","filename":"/home/pi/Node-Red/Node-Red.csv","appendNewline":true,"createDir":true,"overwriteFile":"false","encoding":"none","x":1100,"y":180,"wires":[[]]},{"id":"7ca5ce14.19016","type":"mqtt-broker","name":"Esp32-eclipseprojects.io","broker":"mqtt.eclipseprojects.io","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""},{"id":"31c84fde.527f7","type":"ui_group","name":"Temperature","tab":"95308fb2.d533b","order":3,"disp":true,"width":"6","collapse":false},{"id":"ac61e344.1c471","type":"ui_group","name":"Humidity","tab":"95308fb2.d533b","order":4,"disp":true,"width":"6","collapse":false},{"id":"f6f1e0b0.8472b","type":"ui_group","name":"Pressure","tab":"95308fb2.d533b","order":5,"disp":true,"width":"6","collapse":false},{"id":"95308fb2.d533b","type":"ui_tab","name":"BME280","icon":"dashboard","order":3,"disabled":false,"hidden":false}] ``` 然而只使用Node-Red dashboard並無法將數據妥善使用 搭配之前的文章我們可以選擇將資料保存在google sheet上 [Node-RED 連結MQTT 資料上傳至 Google Sheet服務](/IZ2k09W8TSumVdrA-NBLsw) 而google sheet的資料數量是有500萬筆的上限 換算每十秒一筆也只能存放一年的資料 ![](https://i.imgur.com/vDKiSX5.gif) 也可以選擇保存在樹莓派上成csv檔 ![](https://i.imgur.com/GyuBfXf.png) ![](https://i.imgur.com/IAHz8QV.png) 檔案不大也可以使用teleram傳送 設計一個按鍵用於回傳檔案 ![](https://i.imgur.com/s78ReTv.png) ![](https://i.imgur.com/a4MDXhp.png) ![](https://i.imgur.com/UBlF247.png) ```json= [{"id":"16c0d472.d7871c","type":"telegrambot-payload","z":"2a8e219c.b1f8ce","name":"","bot":"","chatId":"","sendMethod":"sendDocument","payload":"{\n \"document\" :\"/home/pi/Node-Red/Node-Red.csv\",\n \"caption\" : \"Node-Red\" } ","x":490,"y":180,"wires":[[]]},{"id":"8740fd16.db599","type":"ui_button","z":"2a8e219c.b1f8ce","name":"","group":"31c84fde.527f7","order":7,"width":"0","height":"0","passthru":false,"label":"傳送記錄檔案","tooltip":"","color":"","bgcolor":"","icon":"description","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":280,"y":180,"wires":[["16c0d472.d7871c"]]},{"id":"16d8b10f.9ac85f","type":"inject","z":"2a8e219c.b1f8ce","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":300,"y":240,"wires":[["16c0d472.d7871c"]]},{"id":"31c84fde.527f7","type":"ui_group","name":"Temperature","tab":"95308fb2.d533b","order":3,"disp":true,"width":"6","collapse":false},{"id":"95308fb2.d533b","type":"ui_tab","name":"BME280","icon":"dashboard","order":3,"disabled":false,"hidden":false}] ``` ## [按鍵icon](https://fonts.google.com/icons) 後續使用R或excel繪圖就十分簡單了 ![](https://i.imgur.com/kWc7MdB.png) 或是用shiny做儀表板感覺也是不錯的做法 🌟 🌟全文可以至下方連結觀看或是補充 全文分享至 https://www.facebook.com/LHB0222/ https://www.instagram.com/ahb0222/ 有疑問想討論的都歡迎於下方留言 喜歡的幫我分享給所有的朋友 \o/ 有所錯誤歡迎指教 # [:page_with_curl: 全部文章列表](https://hackmd.io/@LHB-0222/AllWritings) ![](https://i.imgur.com/nHEcVmm.jpg)