---
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)

承接上一篇文章[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)做為即時資料的展示


:::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萬筆的上限
換算每十秒一筆也只能存放一年的資料

也可以選擇保存在樹莓派上成csv檔


檔案不大也可以使用teleram傳送
設計一個按鍵用於回傳檔案



```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繪圖就十分簡單了

或是用shiny做儀表板感覺也是不錯的做法
🌟
🌟全文可以至下方連結觀看或是補充
全文分享至
https://www.facebook.com/LHB0222/
https://www.instagram.com/ahb0222/
有疑問想討論的都歡迎於下方留言
喜歡的幫我分享給所有的朋友 \o/
有所錯誤歡迎指教
# [:page_with_curl: 全部文章列表](https://hackmd.io/@LHB-0222/AllWritings)
