Arduino--ESP8266--ESP-01學習筆記--連線WiFi、連線MQTT伺服器、web顯示
阿新 • • 發佈:2019-02-01
前言
做這個東西之前需要知道的事
這個東西是通過使用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 = [];