MQTT教學(九):使用ESP8266上傳資料到ThingSpeak MQTT伺服器

延續上一篇文章的說明,本文將使用ESP8266控制板(如:NodeMCU或Wemos D1 mini),經由Wi-Fi網路和MQTT協定傳送DHT11的溫濕度值到ThingSpeak雲端平台。底下是本文的硬體接線:

ESP8266連接DHT11

使用ESP8266控制板發布MQTT訊息的程式架構,和Arduino乙太網路程式相似,同樣採用PubSubClient程式庫(參閱《MQTT教學(六):使用PubSubClient程式庫開發Arduino MQTT應用》);兩者程式架構不同之處如下圖裡的紅字,登入ThingSpeak網站需要輸入使用者名稱和密碼。

使用ESP8266控制板發布MQTT訊息的程式架構

發布MQTT主題訊息的ESP8266 Arduino程式

程式開頭首先宣告一些變數,DHT11溫濕度感測器沿用書本裡的程式庫:

#include <ESP8266WiFi.h> 
#include <PubSubClient.h>
#include <dht11.h>
#define DHT11PIN 5 // DHT11資料接腳 

const char* ssid = "你的Wi-Fi網路名稱"; 
const char* password = "你的Wi-Fi網路密碼"; 
const char* mqttServer = "mqtt.thingspeak.com";	// MQTT伺服器位址 
const char* mqttUserName = "cubie";				// 使用者名稱,隨意設定。 
const char* mqttPwd = "你的MQTT API Key";			// MQTT密碼 
const char* clientID = "yard001";					// 用戶端ID,隨意設定。 
const char* topic = "channels/頻道ID/publish/你的寫入API KEY"; 

unsigned long prevMillis = 0;		// 暫存經過時間(毫秒) 
const long interval = 20000;		// 上傳資料的間隔時間,20秒。 
String msgStr = "";					// 暫存MQTT訊息字串 

int temp;	// 暫存溫度
int hum;	// 暫存濕度 

底下是建立設定Wi-Fi連線的自訂函式:

void setup_wifi() { 
	delay(10); 

	WiFi.begin(ssid, password); // 連接到Wi-Fi基地台 

	// 如果尚未連上Wi-Fi,則每隔0.5秒顯示一個‘.’。 
	while (WiFi.status() != WL_CONNECTED) { 
		delay(500); 
		Serial.print("."); 
	} 

	Serial.println(""); 
	Serial.println("WiFi connected"); // 顯示「Wi-Fi已連線」 
} 

連結MQTT伺服器的reconnect()自訂函式,要加入使用者名稱密碼

reconnect()自訂函式

完整的程式碼如下,請自行修改必要的參數:

#include <ESP8266WiFi.h> 
#include <PubSubClient.h>
#include <dht11.h>
#define DHT11PIN 5  // DHT11資料接腳

const char* ssid = "你的Wi-Fi網路名稱";
const char* password = "你的Wi-Fi網路密碼";
const char* mqttServer = "mqtt.thingspeak.com";  // MQTT伺服器位址
const char* mqttUserName = "cubie";  // 使用者名稱,隨意設定。
const char* mqttPwd = "你的MQTT API Key";  // MQTT密碼
const char* clientID = "yard001";      // 用戶端ID,隨意設定。
const char* topic = "channels/頻道ID/publish/你的寫入API KEY";

unsigned long prevMillis = 0;  // 暫存經過時間(毫秒)
const long interval = 20000;  // 上傳資料的間隔時間,20秒。
String msgStr = "";      // 暫存MQTT訊息字串

int temp;  // 暫存溫度
int hum;   // 暫存濕度

WiFiClient espClient;
PubSubClient client(espClient);
dht11 DHT11;

void setup_wifi() {
  delay(10);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect(clientID, mqttUserName, mqttPwd)) {
      Serial.println("MQTT connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);  // 等5秒之後再重試
    }
  }
}

void setup() {
  Serial.begin(9600);
  setup_wifi();
  client.setServer(mqttServer, 1883);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // 等待20秒
  if (millis() - prevMillis > interval) {
    prevMillis = millis();

    // 讀取DHT11的溫濕度資料
    int chk = DHT11.read(DHT11PIN);
    if (chk == 0) {
      temp = DHT11.temperature;
      hum = DHT11.humidity;
    }

    // 組合MQTT訊息;field1填入溫度、field2填入濕度
    msgStr=msgStr+"field1="+temp+"&field2="+hum;
    
    // 宣告字元陣列
    byte arrSize = msgStr.length() + 1;
    char msg[arrSize];

    Serial.print("Publish message: ");
    Serial.println(msgStr);
    msgStr.toCharArray(msg, arrSize); // 把String字串轉換成字元陣列格式
    client.publish(topic, msg);       // 發布MQTT主題與訊息
    msgStr = "";
  }
}

編譯並上傳程式到ESP8266控制板,它將每隔20秒上傳溫濕度資料到你的ThingSpeak通道。

延伸閱讀

Posts created 468

40 thoughts on “MQTT教學(九):使用ESP8266上傳資料到ThingSpeak MQTT伺服器

  1. 請問在Node-RED mqtt broker server 可以自定設定 username 、 password 、server port
    來源:https://flows.nodered.org/node/node-red-contrib-mqtt-broker

    在 pubsubclient.h 裡面有 boolean connect(const char* id, const char* user, const char* pass);
    可以由 Arduino 直接傳送 clientID、username、 password 字串嗎?
    資料來源:
    https://pubsubclient.knolleary.net/index.html

    可是只要加上 username 、 password Arduino Connect .. 就斷線

    1. 拍謝,我沒有使用Node-RED,但我相信它肯定有支援MQTT帳號密碼設置。

      thanks,
      jeffrey

    1. 谢谢,我是用Adobe Flash(现已改名Animate) 软件,搭配鼠标(偶尔用第一代微软Surface Pro平板的Wacom笔)徒手绘制的。

      thanks,
      jeffrey

  2. 我跟隨了你上面的所有做法 是用得了的
    可是我的DHT 出來的溫度濕度 不知道為什麼不怎麼動,我吹氣或者用電腦的發熱都改變不了數值,
    過了一段時間才會有一些的變化(就幾度) ,是因為那20S出來的數值DELAY?
    但我用點arduino uno 就完全沒問題,我這Wemos 用其他功能也是沒問題

    請問會不會是我的板子有問題? 還是其他問題?

  3. 赵老师,你好!
    请问Thingspeak平台现在登录是不是有问题?
    我注册了但总登录不上去。
    谢谢!

  4. 請問如果我想讓nodemcu讀到的數值傳到arduino的序列埠監控視窗要如何辦到?我希望用vb讀取序列埠監控視窗的數值

  5. sorry,老師我沒表達清楚,我是想讓nodemcu用wifi無線的方式傳到序列埠監控視窗,懇請指點 : )

    1. 「用wifi無線的方式傳到序列埠監控視窗」,我猜想你的意思是:

      從nodemcu透過wi-fi,傳遞資料給某個裝置(如:PC),該裝置再將接收到的資料輸出到序列埠。

      你可以在兩端寫個網路socket程式,互傳訊息,或者用HTTP前端和後端的概念,以nodemcu當前端,電腦上執行HTTP伺服器程式接收資料,再轉發到序列埠。

      thanks,
      jeffrey

  6. 老師 請問
    最近剛學習ESP8266 ,要傳到Thingspeak,已經可以順利傳上去了,
    但是不知道要怎麼做才能使用另外一塊ESP8266去Thingspeak上讀取數值
    上傳的程式是這樣
    if (client.connect(server,80)) // “184.106.153.149” or api.thingspeak.com
    {

    String postStr = apiKey;
    postStr +=”&field1=”;
    postStr += String(t);
    postStr +=”&field2=”;
    postStr += String(h);
    postStr +=”&field3=”;
    postStr += String(BPM);
    postStr += “\r\n\r\n”;

    client.print(“POST /update HTTP/1.1\n”);
    client.print(“Host: api.thingspeak.com\n”);
    client.print(“Connection: close\n”);
    client.print(“X-THINGSPEAKAPIKEY: “+apiKey+”\n”);
    client.print(“Content-Type: application/x-www-form-urlencoded\n”);
    client.print(“Content-Length: “);
    client.print(postStr.length());
    client.print(“\n\n”);
    client.print(postStr);

    但是使用另一塊板子讀取時,我改成這樣
    client.print(“GET /channels//1.json?&results=2 HTTP/1.1\n”);
    client.print(“Host: api.thingspeak.com\n”);
    client.print(“Connection: close\n”);
    client.println();

    while (client.available() == 0);

    unsigned long lastRead = millis();
    while (millis() – lastRead < 2000) {
    while (client.available()) {
    Serial.print(client.read());
    lastRead = millis();
    }
    }
    在序列埠監控視窗會出現一大串數字,不知道要怎麼修改才能得到我要的每個Field的值
    先謝謝老師

  7. 請問老師您有連過AWS IOT嗎?
    AWS IOT 連線據我所知只支援憑證認證,請問老師知不知道有甚麼支援憑證認證的Library

  8. 請問老師,可以將nodemcu的資料上傳至mcs嗎?
    程式的寫法,是不是跟Thingspeak類似?

  9. 請問老師要怎麼把keypad的值上傳至mcs上,序列埠有出現值,但網站上卻沒有?
    #include
    #include
    #include

    const char* ssid = “EE420”;
    const char* password = “xxxxxx”;
    const char* mqttServer = “mqtt.mcs.mediatek.com”; // MQTT伺服器位址
    const char* mqttUserName = “nodemcu”; // 使用者名稱,隨意設定。
    const char* mqttPwd = “J0Gsxxxxxx”; // MQTT密碼
    const char* clientID = “nodemcu”; // 用戶端ID,隨意設定。
    const char* topic = “channels/nodemcu/publish/DUCADaW9”;

    unsigned long prevMillis = 0;
    const long interval = 500;
    String msgStr = “”;

    #define n_rows 4
    #define n_cols 4

    char keys[n_rows][n_cols] = {
    {‘1′,’2′,’3’},
    {‘4′,’5′,’6’},
    {‘7′,’8′,’9’},
    {‘-‘,’0’}
    };

    byte colPins[n_rows] = {D3, D2, D1, D0};
    byte rowPins[n_cols] = {D7, D6, D5, D4};

    Keypad myKeypad = Keypad( makeKeymap(keys), rowPins, colPins, n_rows, n_cols);

    WiFiClient espClient;
    PubSubClient client(espClient);

    void setup_wifi() {
    delay(10);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(“.”);
    }

    Serial.println(“”);
    Serial.println(“WiFi connected”);
    }

    void reconnect() {
    while (!client.connected()) {
    if (client.connect(clientID, mqttUserName, mqttPwd)) {
    Serial.println(“MQTT connected”);
    } else {
    Serial.print(“failed, rc=”);
    Serial.print(client.state());
    Serial.println(” try again in 5 seconds”);
    delay(5000);
    }
    }
    }

    void setup() {
    Serial.begin(115200);
    setup_wifi();
    client.setServer(mqttServer, 1883);
    }

    void loop() {
    if (!client.connected()) {
    reconnect();
    }
    client.loop();

    if (millis() – prevMillis > interval) {
    prevMillis = millis();

    char myKey = myKeypad.getKey();
    if (myKey){
    Serial.println(myKey);
    }

    msgStr=msgStr+”number=”+myKey;

    byte arrSize = msgStr.length() + 1;
    char msg[arrSize];

    Serial.print(“Publish message: “);
    Serial.println(msgStr);
    client.publish(topic, msg);
    msgStr = “”;
    }
    }

    1. 建議先上傳一個簡單的數值或字串測試連線程式是否正確執行,我沒用過mcs,煩請自行查閱相關API文件。

      thanks,
      jeffrey

  10. 老師您好 平常我使用THINGSPEAK後
    會將數值至data import/export下方有一個下載CSV檔
    但是我今日在用時發現他竟然改成要寄信過去他們那邊
    這功能導致我無法對100筆之前的數值進行觀看

    這是他們最近改的新功能嗎?如果想要再直接載成CSV檔的話要怎麼做呢

    1. 咦?我按底下的Data Export按鈕,可以選擇匯出JSON, XML或CSV;按右上角的連結才需要發送Channel ID和Read API Key給他們(我相信這只需要發送一次,由程式自動認可)。

      匯出ThingSpeak資料

      thanks,
      jeffrey

  11. 老師 感謝您的回覆
    因實驗需要
    我要下載的就是右上方的 而也只有右上方的才能匯出我全部的數值
    您底下的那個可以下載 ,不過那個下載按鈕卻只有最後的100筆數值(沒記錯筆數的話)

    而剛剛我試著寄信過去確的到這樣的回覆
    {
    Your mail message to the following address(es) could not be delivered.
    This is a permanent error. Please verify the address(es) and try again.

    :
    The e-mail message could not be delivered because the user’s mailfolder is full.
    }
    他跟我說信箱已經爆炸了…
    看來大家也都在嘗試寄信給他,寄到他的信箱都已經爆滿了…

    在這特別感謝您願意回復 謝謝您

  12. 老師 您好
    我之前都採用HTTP協定傳輸資料上去THINGSPEAK
    近期看到您MQTT協定的寫法 最近也剛完成CODE

    HTTP我可以上傳最多8組都沒什麼問題
    MQTT模式目前測得最高上傳4組數值,超過後竟然莫名的不能上傳

    剛剛前去您的MQTT協定介紹那邊觀看他最高上傳65536個字元
    根據規定我也沒超過 想請問一下這個狀況還有甚麼可能性呢

    下方是我愈的字串
    msgStr=msgStr+”field1=”+temp1+”&field2=”+temp2+”&field3=”+temp3+”&field4=”+temp4+”&field5=”+temp5+”&field6=”+temp6+”&field7=”+temp7+”&field8=”+temp8

  13. 老師 您好
    剛剛使用MQTTLens教學操作發現到我竟然是使用頻道內之APIKEY 故而按照您的教學前往申請一MQTT APIKEY
    不過這時卻發現不管怎麼使用都會失敗,在無他法的情況改成頻道內APIKEY竟然意外成功了,且能傳8個值
    這時改用ESP8266再次上傳仍然最多只能6個值,這還真令我覺得困惑。

    另外上次跟您詢問的全部DATA下載之功能,目前ThingSpeak已經將之前的下載按鈕加回來了,特此通知一下

  14. 想請問如何在arduino 開發版上使用ESP8266,AT+CWMODE=3 狀況下,在thingspeak做上傳及下載
    目前皆可以做到兩隻上下傳程式,但將兩組程式,整合共同使用,發現下載的副程式不能動

  15. 老師您好,我使用的程式是motoduino mqtt publish, 我想要把超音波感測器的數值上傳資料,但似乎不知道哪裡接錯了,想請問老師我該如何將感測器資料上傳呢?

  16. 請問您用的dht11.h是哪個函式庫啊? 搜尋dht相關的都安裝一輪了還是找不到XD

  17. 老師您好,想請問3行程式碼:

    const char* mqttUserName = “cubie”; // 使用者名稱,隨意設定

    const char* clientID = “yard001”; // 用戶端ID,隨意設定

    const char* topic = “channels/頻道ID/publish/你的寫入API KEY”;

    使用者名稱以及用戶端ID是只要隨便打就好嗎? 需要按照thingspeak上面的甚麼去打嗎? 還是怎麼樣?

    topic的部分是 channels或頻道ID或寫入的API KEY 其中選一個就好嗎?

    謝謝老師

  18. 請問老師我的序列埠有出現數值,但網站上卻沒有是哪裡有問題嗎?

    #include
    #include
    #include “Timer.h”

    #define random(num) (rand() % (num))
    #define realtype float
    #define NearZero 1.0e-10
    #define MoreZero 1.0e-5
    #define NearInfi 1.0e10
    #define LessInfi 1.0e5
    #define pi 3.141592654

    uint8_t DACH[]={25, 26};
    uint8_t ADCH[]={32, 33, 34, 35, 36, 39, 12, 13, 14, 25, 26, 27};
    uint16_t AD_VAL[20];
    #define PWM_LED 19
    #define BLINK_LED 23
    #define LED_BUILTIN 2

    #define SERIAL_BAUDRATE 9600

    Timer T;
    uint32_t TS;

    // Replace the next variables with your SSID/Password combination
    //const char* ssid = “astrom_wifi”;
    //const char* ssid = “astrom1211”;
    const char* ssid = “00000000”;
    const char* password = “00000000”;

    // Add your MQTT Broker IP address, example:
    const char* mqttServer = “mqtt3.thingspeak.com”; //ThingSpeak MQTT Server
    const char* mqttUserName = “yu”;
    //const char* mqttServer = “3.139.80.99”; // NOT WORK
    const char* mqttPwd = “kSXyHw/tANI6Rg45SiW8/jpI”; //ThingSpeak MQTT API_Key
    const char* clientID = “ExANOjkVFyYCGgcROAgUKwk”;
    const char* mqttTopic = “channels/1712894/publish/8MU9ROL8UUQE1UR9”;

    WiFiClient espClient;
    PubSubClient client(espClient);
    uint32_t lastMsg = 0;
    char msg[50];
    int value = 0;

    TaskHandle_t Task[4];
    void DaqTask(uint16_t dt)
    {
    static uint32_t Millis, Micros;
    uint32_t nMillis, nMicros;

    nMillis = millis();
    uint16_t millis_ts = nMillis-Millis;

    nMicros = micros();
    uint16_t micros_ts = nMicros-Micros;

    if (millis_ts>=dt) {
    for (uint8_t i=0; i 15000) {
    lastMsg = now;
    for (uint8_t i=0; i<5; i++) {
    msgStr += field[i]+String(AD_VAL[i]); // By "mqtt.thingspeak.com", ThingSpeak MQTT Server to upload datas
    }
    byte arrSize = msgStr.length() + 1;
    char msg[arrSize];

    Serial.print("Publish message: ");
    Serial.println(msgStr);
    msgStr.toCharArray(msg, arrSize);
    client.publish(mqttTopic, msg);
    }
    vTaskDelay(1);
    }

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top