提示:本章节出,最近三天研究的ESP8266 单片机物联网,智能家居台灯。
1、硬件: 购买ESP8266 淘宝十几块钱,有WiFi+蓝牙、WiFi两个版本
2、烧录程序: 准备一根安卓带钩的数据线,不能充电线,用来给ESP8266烧录程序
3、服务器:搭建EMQT服务器 用来发布订阅消息
4、客户端:搭建前端
提示:安装驱动 用来识别设备烧录程序
CH341SER
程序调试
http://nodemcu-dev.doit.am/3.html
CH340资料链接:
https://pan.baidu.com/s/1ILfWrsXIlnbidQav6qlw-w?pwd=4zk2
提取码: 4zk2
改进FT232芯片驱动下载链接:
https://pan.baidu.com/s/1x9Zwpr35d-R0rPVFrg7cAw
CP2102资料链接:
https://pan.baidu.com/s/1S4OzICkeNta739ZaDTr1jw?pwd=msjb
提取码: msjb
ESP01资料下载链接:https://pan.baidu.com/s/1pNsRH5OFq4NETx7Ty-mt1w
提取码:8888
ESP01S资料下载链接:https://pan.baidu.com/s/1Eic1kYrYyclKLQm490-EAA
提取码:8888
提示:安装 ArduinoIDE 烧录程序软件
安装&&使用教程
github
上面有三个版本,我这里选的是LED 版本,然后在此基础上做了小改动
加了控制板子上的led 和json 数据解析
esp8266库
PubSubClient库
ArduinoJson库
#include //WiFi 库
#include //做MQTT客户端
#include //JSON 解析
// GPIO 5 D1
#define LED 5
// WiFi
const char *ssid = "wifi名"; // Enter your WiFi name
const char *password = "WiFi密码"; // Enter WiFi password
// MQTT Broker
const char *mqtt_broker = "192.168.137.1"; //mqtt服务器IP
const char *topic = "esp8266"; //作用服务器订阅名,最后和其他设备不要重复
const char *mqtt_username = "admin"; //mqtt登录名、用户名
const char *mqtt_password = "00admin"; //密码
const int mqtt_port = 1883; //默认的端口
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
// Set software serial baud to 115200;
Serial.begin(115200); //Arduino调试用 串口监视器 选115200badu
pinMode(LED_BUILTIN, OUTPUT); //初始化led
digitalWrite(LED_BUILTIN, HIGH); //默认关闭 关HIGH/开LOW
// connecting to a WiFi network
WiFi.begin(ssid, password); //连接WiFi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
//connecting to a mqtt broker
client.setServer(mqtt_broker, mqtt_port); //连接mqtt
client.setCallback(callback);
while (!client.connected()) {
String client_id = "esp8266-client-"; //mqtt客户端ID client_id
client_id += String(WiFi.macAddress());
Serial.printf("The client %s connects to the public mqtt broker\n", client_id.c_str());
if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
Serial.println("Public emqx mqtt broker connected");
} else {
Serial.print("failed with state ");
Serial.print(client.state());
delay(2000);
}
}
// publish and subscribe
client.publish(topic, "hello emqx");
client.subscribe(topic);
}
//MQTT回调
void callback(char *topic, byte *payload, unsigned int length) {
Serial.print("Message arrived in topic: ");
Serial.println(topic);
Serial.print("Message:");
String message;
for (int i = 0; i < length; i++) {
message = message + (char)payload[i]; // convert *byte to string
//回调信息
}
Serial.print(message);
// 解析JSON数据
DynamicJsonDocument jsondoc(1024);
deserializeJson(jsondoc, message);
JsonObject obj = jsondoc.as<JsonObject>();
String value = obj["msg"];
if (value == "on") {
digitalWrite(LED, LOW);
digitalWrite(LED_BUILTIN, LOW);
} // LED on
if (value == "off") {
digitalWrite(LED, HIGH);
digitalWrite(LED_BUILTIN, HIGH);
} // LED off
Serial.println();
Serial.println("-----------------------");
}
void loop() {
client.loop();
}
提示:官方文档
emqx
建议通过Docker 安装
获取 docker 镜像
docker pull emqx/emqx:5.0.4
启动 docker 容器
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx:5.0.4
http://localhost:18083/
提示:如果在阿里云、腾讯云线上部署 一定要开放端口
提示:一定要注意端口,Arduino 烧录程序在板子里 的端口要统一
提示:目前没有做客户端,暂时用MQTTX 测试用;emqx里面可以配置数据库数据存入,后续更新
C# 后端—客户端代码
MqttClientService
安装NuGet 程序包 MQTTnet
public class MqttClientService
{
public static MqttClient _mqttClient;
public void MqttClientStart()
{
var optionsBuilder = new MqttClientOptionsBuilder()
.WithTcpServer("192.168.50.80", 1883) // 要访问的mqtt服务端的 ip 和 端口号
.WithCredentials("admin", "admints") // 要访问的mqtt服务端的用户名和密码
.WithClientId("testclient02") // 设置客户端id
.WithCleanSession()
.WithTls(new MqttClientOptionsBuilderTlsParameters
{
UseTls = false // 是否使用 tls加密
});
var clientOptions = optionsBuilder.Build();
_mqttClient = new MqttFactory().CreateMqttClient() as MqttClient;
_mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync; // 客户端连接成功事件
_mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync; // 客户端连接关闭事件
_mqttClient.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync; // 收到消息事件
_mqttClient.ConnectAsync(clientOptions);
}
///
/// 客户端连接关闭事件
///
///
///
private Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
{
Console.WriteLine($"客户端已断开与服务端的连接……");
return Task.CompletedTask;
}
///
/// 客户端连接成功事件
///
///
///
private Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
{
Console.WriteLine($"客户端已连接服务端……");
// 订阅消息主题
// MqttQualityOfServiceLevel: (QoS): 0 最多一次,接收者不确认收到消息,并且消息不被发送者存储和重新发送提供与底层 TCP 协议相同的保证。
// 1: 保证一条消息至少有一次会传递给接收方。发送方存储消息,直到它从接收方收到确认收到消息的数据包。一条消息可以多次发送或传递。
// 2: 保证每条消息仅由预期的收件人接收一次。级别2是最安全和最慢的服务质量级别,保证由发送方和接收方之间的至少两个请求/响应(四次握手)。
_mqttClient.SubscribeAsync("esp8266", MqttQualityOfServiceLevel.AtMostOnce);
//Publish("{\"type\":\"LED\",\"msg\":\"ON\",\"des\":\"conslog\"}");
return Task.CompletedTask;
}
///
/// 收到消息事件
///
///
///
private Task _mqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
{
Console.WriteLine($"ApplicationMessageReceivedAsync:客户端ID=【{arg.ClientId}】接收到消息。 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】");
return Task.CompletedTask;
}
//发送订阅信息
public void Publish(string data)
{
var message = new MqttApplicationMessage
{
Topic = "esp8266",
Payload = Encoding.Default.GetBytes(data),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
Retain = true // 服务端是否保留消息。true为保留,如果有新的订阅者连接,就会立马收到该消息。
};
//var appMsg = new MqttApplicationMessage();
//appMsg.Topic = "esp8266";
//appMsg.Payload = Encoding.Default.GetBytes(data);
//appMsg.QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
//appMsg.Retain = false;
//_mqttClient.PublishAsync(appMsg);
_mqttClient.PublishAsync(message);
}
///单独链接
readonly IMqttClient client = new MqttFactory().CreateMqttClient();
private async void MqttConnect()
{
var options = new MqttClientOptionsBuilder()
.WithClientId("xmr/" + Guid.NewGuid().ToString())
.WithTcpServer("mqtt IP", 1883) //默认端口
.WithCredentials("用户名", "密码")
.Build();
await client.ConnectAsync(options, CancellationToken.None);
var message = new MqttApplicationMessageBuilder()
.WithTopic("hello/world")
.WithPayload("hey")
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
.Build();
await client.PublishAsync(message, CancellationToken.None);
}
}
调用
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
MqttClientService mqttClient = new MqttClientService();
mqttClient.MqttClientStart();
Thread.Sleep(5000);
mqttClient.Publish("{\"type\":\"JDQ\",\"msg\":\"OFF\",\"des\":\"conslog\"}");
Console.ReadKey();
}
MQTT发送失败问题
winform 客户端测试工具
页面前端—客户端代码
mqtt.js 的安装引用
两只种安装方式根据前端框架选其一即可
提示:CDN 安装
<script src="https://cdn.bootcdn.net/ajax/libs/mqtt/4.1.0/mqtt.min.js"></script>
<script>
// 会在全局初始化一个 mqtt 变量,直接使用就可以
console.log(mqtt)
</script>
提示:如果是用vue 请用 npm 安装
npm install mqtt --save
// 页面使用,直接 import 就可以
import mqtt from 'mqtt';
这里我用javascript 引用演示
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://cdn.bootcdn.net/ajax/libs/mqtt/4.1.0/mqtt.min.js"></script>
</head>
<body>
<script>
// 连接地址,有很多连接失败都是因为地址没写对
const connectUrl = `ws://localhost:8083/mqtt`;
// 客户端ID 随机数以免重复
const clientId = `mqtt_${Math.random().toString(16).slice(3)}`;
console.log("客户端id:"+clientId);
// 连接设置
let options = {
clean: true, // 保留会话
connectTimeout: 4000, // 超时时间
reconnectPeriod: 1000, // 重连时间间隔
// 认证信息
clientId,
username: 'admin',
password: 'admints',
}
// 需要订阅的主题
const topic = 'esp8266';
const topic1 = 'tes';
// 创建客户端
var client = mqtt.connect(connectUrl, options);
// 成功连接后触发的回调
client.on('connect', () => {
console.log('已经连接成功');
// 订阅主题,这里可以订阅多个主题
client.subscribe([topic, topic1], () => {
console.log(`订阅了主题 ${[topic, topic1].join('和')}`)
})
});
// 当客户端收到一个发布过来的消息时触发回调
/**
* topic:收到的报文的topic
* message:收到的数据包的负载playload
* packet:MQTT 报文信息,其中包含 QoS、retain 等信息
*/
client.on('message', function (topic, message, packet) {
// 这里有可能拿到的数据格式是Uint8Array格式,可以直接用toString转成字符串
// let data = JSON.parse(message.toString());
console.log("获取到的数据:", message)
console.log("数据对应订阅主题:", topic)
console.log("获取到的数据包:", packet)
console.log(Uint8ArrayToString(packet.payload))
});
// 关闭客户端(断开连接)
//client.end();
// 发送信息给 topic(主题)
//client.publish(topic, '这是给topic发送的信息');
function Uint8ArrayToString(fileData) {
var dataString = "";
for (var i = 0; i < fileData.length; i++) {
dataString += String.fromCharCode(fileData[i]);
}
return dataString
}
</script>
</body>
</html>
提示:效果如图下,后续会更新客户端和EMQT与数据库存储