1. 程式人生 > >Arduino--ESP8266--ESP-01學習筆記--連線WiFi、連線MQTT伺服器、web顯示

Arduino--ESP8266--ESP-01學習筆記--連線WiFi、連線MQTT伺服器、web顯示

前言

做這個東西之前需要知道的事

 這個東西是通過使用arduino UNO R3板子獲取溫溼度模組DHT11中的溫溼度然後通過ESP8266WiFi模組傳送到MQTT伺服器上面,然後再在網頁上面顯示出來的。這裡面涉及一些技術:

  • arduino的基本使用
    • arduino軟體模擬串列埠的使用(網上資料很多)
    • DHT11的溫溼度讀取(有庫,網上資料也很多)
  • ESP8266WiFi模組AT指令的使用
    • 從某寶上面買一塊型號為ESP-01的WiFi模組(ESP8266是該類模組的晶片名字)
    • 買來的模組裡面已經有程式,上電就可以執行的,一般是官方的AT韌體
    • 該官方AT韌體,是有使用指導書的,可以問店家要,也可以從官網下載,然後就可以知道怎麼用了。
    • arduino模組的串列埠引腳(我程式碼裡面的串列埠引腳是軟體模擬的)和買來的ESP-01模組的串列埠引腳相連線(R-T,T-R),然後再在程式碼裡面,使用串列埠傳送例如“AT+RST\r\n”的指令。
  • MQTT伺服器的部署使用
    • 一般是要有自己的MQTT伺服器的,如果沒有,網上也有公共的MQTT Broker可以暫時用一下
  • 網頁HTML5的基礎知識
    • 這方面的知識已經是涉及到了網站開發了,但是這個是簡單的
    • 你應該要會一個最簡單的web後端框架比如python的Flask
    • 然後你就可以把你的網頁放在那個後端伺服器上,訪問那個網站就可以在瀏覽器上顯示出來了

系統概圖:
在這裡插入圖片描述

這裡寫圖片描述

需要的庫:

一、連上wifi

 一個wifi模組,要想連上網路首先得連上WiFi熱點,所以使用AT指令來讓WiFi模組連線上SSID。如果你的arduino IDE已經安裝了以上那些庫,那麼就可以把下面的程式碼拷貝再修改一下 ssid 和 password就可以連線WiFi了。

#include "WiFiEsp.h"

// Emulate Serial1 on pins 6/7 if not present
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
SoftwareSerial Serial1(6, 7); // RX, TX
#endif
char ssid[] = "liefyuan"; // your network SSID (name) char pass[] = "123456789"; // your network password int status = WL_IDLE_STATUS; // the Wifi radio's status void setup() { // initialize serial for debugging Serial.begin(9600); // initialize serial for ESP module Serial1.begin(115200); // initialize ESP module WiFi.init(&Serial1); // check for the presence of the shield if (WiFi.status() == WL_NO_SHIELD) { Serial.println("WiFi shield not present"); // don't continue while (true); } // attempt to connect to WiFi network while ( status != WL_CONNECTED) { Serial.print("Attempting to connect to WPA SSID: "); Serial.println(ssid); // Connect to WPA/WPA2 network status = WiFi.begin(ssid, pass); } Serial.println("You're connected to the network"); } void loop() { // print the network connection information every 10 seconds Serial.println(); printCurrentNet(); printWifiData(); delay(10000); } void printWifiData() { // print your WiFi shield's IP address IPAddress ip = WiFi.localIP(); Serial.print("IP Address: "); Serial.println(ip); // print your MAC address byte mac[6]; WiFi.macAddress(mac); char buf[20]; sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]); Serial.print("MAC address: "); Serial.println(buf); } void printCurrentNet() { // print the SSID of the network you're attached to Serial.print("SSID: "); Serial.println(WiFi.SSID()); // print the MAC address of the router you're attached to byte bssid[6]; WiFi.BSSID(bssid); char buf[20]; sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[5], bssid[4], bssid[3], bssid[2], bssid[1], bssid[0]); Serial.print("BSSID: "); Serial.println(buf); // print the received signal strength long rssi = WiFi.RSSI(); Serial.print("Signal strength (RSSI): "); Serial.println(rssi); }

連線成功(以下的資訊是開啟串列埠除錯助手,arduino會通過串列埠打印出來的資訊,從中我們可以看到已經連線成功!):

這裡寫圖片描述

二、真正的乾貨

問題一:由於波特率導致的ESP-01模組上傳資料不成功!這個問題讓我痛苦很久,Google、github…各種找-------最後居然是波特率導致!!!!arduino的軟串列埠連線ESP-01wifi模組不適合波特率為115200(出廠時預設設定的!)在AT模式下輸入AT+UART_DEF=9600,8,1,0,0修改為9600。修改過後問題全部解決!

結論:PubSubClient這個庫是個非常優秀的MQTT庫。WiFiESP這個底層是AT指令封裝的庫也是個非常優秀的庫!

程式碼:

#include "DHT.h"
#include <WiFiEspClient.h>
#include <WiFiEsp.h>
#include <WiFiEspUdp.h>
#include <PubSubClient.h>
#include "SoftwareSerial.h"

#define WIFI_AP "liefyuan"
#define WIFI_PASSWORD "123456789"

// DHT
#define DHTPIN 4
#define DHTTYPE DHT11

char MqttServer[] = "101.200.46.138";

// 初始化乙太網客戶端物件 -- WiFiEspClient.h
WiFiEspClient espClient;

// 初始化DHT11感測器
DHT dht(DHTPIN, DHTTYPE);

// 初始化MQTT庫PubSubClient.h的物件
PubSubClient client(espClient);

SoftwareSerial soft(2, 3); // RX, TX

int status = WL_IDLE_STATUS;
unsigned long lastSend;

void setup() {

  Serial.begin(9600);
  dht.begin();
  
  InitWiFi();                                // 連線WiFi
  client.setServer( MqttServer, 1883 );      // 連線WiFi之後,連線MQTT伺服器
  
  lastSend = 0;
}

void loop() {
  status = WiFi.status();
  if ( status != WL_CONNECTED) {
    while ( status != WL_CONNECTED) {
      Serial.print("[loop()]Attempting to connect to WPA SSID: ");
      Serial.println(WIFI_AP);
      // 連線WiFi熱點
      status = WiFi.begin(WIFI_AP, WIFI_PASSWORD);
      delay(500);
    }
    Serial.println("[loop()]Connected to AP");
  }

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

  if ( millis() - lastSend > 1000 ) { // 用於定時1秒鐘傳送一次資料
    getAndSendTemperatureAndHumidityData(); // 獲取溫溼度資料傳送到MQTT伺服器上去
    lastSend = millis();
  }

  client.loop();
}

/*
 * 
 * 讀取溫溼度資料,然後傳送到MQTT伺服器上去
 * 
 */
void getAndSendTemperatureAndHumidityData()
{
  Serial.println("Collecting temperature data.");

  // 大概250ms讀取一次
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  // 檢視是否讀取溫溼度失敗的
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");

  String temperature = String(t);
  String humidity = String(h);

  // Just debug messages
  Serial.print( "Sending temperature and humidity : [" );
  Serial.print( temperature ); Serial.print( "," );
  Serial.print( humidity );
  Serial.print( "]   -> " );

  // 構建一個 JSON 格式的payload的字串
  String payload = "{";
  payload += "\"temperature\":"; payload += temperature; payload += ",";
  payload += "\"humidity\":"; payload += humidity;
  payload += "}";

  // Send payload
  char attributes[100];
  payload.toCharArray( attributes, 100 );
  
  // boolean publish(const char* topic, const char* payload);
  
  client.publish( "v1/devices/me/telemetry", attributes );
  Serial.print("[publish]-->>");
  Serial.println( attributes );
}

void InitWiFi()
{
  // 初始化軟串列埠,軟串列埠連線ESP模組
  soft.begin(9600);
  // 初始化ESP模組
  WiFi.init(&soft);
  // 檢測WiFi模組在不在,巨集定義:WL_NO_SHIELD = 255,WL_IDLE_STATUS = 0,
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    while (true);
  }

  Serial.println("[InitWiFi]Connecting to AP ...");
  // 嘗試連線WiFi網路
  while ( status != WL_CONNECTED) {
    Serial.print("[InitWiFi]Attempting to connect to WPA SSID: ");
    Serial.println(WIFI_AP);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(WIFI_AP, WIFI_PASSWORD);
    delay(500);
  }
  Serial.println("[InitWiFi]Connected to AP");
}

/**
 * 
 * MQTT客戶端斷線重連函式
 * 
 */

void reconnect() {
  // 一直迴圈直到連線上MQTT伺服器
  while (!client.connected()) {
    Serial.print("[reconnect]Connecting to MQTT Server ...");
    // 嘗試連線connect是個過載函式 (clientId, username, password)
    if ( client.connect("liefyuan", NULL, NULL) ) {
      Serial.println( "[DONE]" );
    } else {
      Serial.print( "[FAILED] [ mqtt connect error code = " );
      Serial.print( client.state() );
      Serial.println( " : retrying in 5 seconds]" );// Wait 5 seconds before retrying
      delay( 5000 );
    }
  }
}

自己用Qt5編譯出來的MQTT客戶端除錯軟體:

這裡寫圖片描述

伺服器上:

這裡寫圖片描述

web顯示

效果圖:
這裡寫圖片描述

HTML5網頁程式碼:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>DHT11溫溼度WEB顯示</title>
    <script src="{{ url_for('static', filename=('js/jquery-3.1.1.min.js')) }}"></script>
    <script src="{{ url_for('static', filename=('js/echarts.common.min.js')) }}"></script>
    <style type="text/css">
        #main-container{
            margin-top: 20px;
            margin-left: 200px;
        }

        #main-temp{
            width: 400px;
            height:350px;
            float: left;
            border:1px solid red;
            margin-left: 100px;

        }

        #main-humi{
            width: 400px;
            height:350px;
            float: left;
            border:1px solid red;
            margin-left: 100px;
        }
    </style>
</head>
<body>
    <!-- 為 ECharts 準備一個具備大小(寬高)的 DOM -->
    <div id="main-container">

        <div id="main-temp"></div>
        <div id="main-humi"></div>
    </div>


    <script src="https://cdn.bootcss.com/paho-mqtt/1.0.2/mqttws31.min.js"></script>
    <script type="text/javascript">
        // 基於準備好的dom,初始化echarts例項
        var temprature = [];
        var humidity = [];
        var myChart = echarts.init(document.getElementById('main-temp'));
        var mychart2 = echarts.init(document.getElementById('main-humi'));
        option = {
            title: {
                text: '溫度動態資料圖'
            },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    type: 'cross',
                    label: {
                        backgroundColor: '#283b56'
                    }
                }
            },
            toolbox: {
                show: true,
                feature: {
                    dataView: {readOnly: false},
                    restore: {},
                    saveAsImage: {}
                }
            },
            dataZoom: {
                show: false,
                start: 0,
                end: 100
            },
            xAxis: [
                {
                    type: 'category',
                    boundaryGap: true,
                    data: (function (){
                        var now = new Date();
                        var res = [];
                        var len = 10;
                        while (len--) {
                            res.unshift(now.toLocaleTimeString().replace(/^\D*/,''));
                            now = new Date(now - 2000);
                        }
                        return res;
                    })()
                },
                {
                    type: 'category',
                    name: '時間',
                    boundaryGap: true,
                    data: (function (){
                        var res = [];
                        var len = 10;
                        while (len--) {
                            res.push(len + 1);
                        }
                        return res;
                    })()
                }
            ],
            yAxis: [
                {
                    type: 'value',
                    scale: true,
                    name: '溫度(℃)',
                    max: 100,
                    min: 0,
                    boundaryGap: [0.2, 0.2]
                },
                {
                    type: 'value',
                    scale: true,
                    max: 100,
                    min: 0,
                    boundaryGap: [0.2, 0.2]
                },
            ],
            series: [{
                    name:'溫度',
                    type:'line',
                    xAxisIndex: 1,
                    yAxisIndex: 1,
                    data:(function (){
                        var res = [];