当前位置: 首页 > news >正文

(介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)

前言

本文详细介绍了如何利用物联网技术,通过NodeMCU ESP8266(ESP-12F)模块连接到新版的OneNet平台,使用MQTT协议实现数据的上传与指令的下发。文中首先对NodeMCU ESP8266模块及其特性进行了简介,随后详细阐述了如何配置和使用MQTT协议连接到OneNet平台,实现温湿度数据的实时上传。同时,文章也演示了如何从OneNet平台下发指令控制远端的LED灯,实现了物联网设备的双向通信。通过本文的步骤指导,读者能够掌握利用ESP8266模块与OneNet平台结合,通过MQTT协议进行数据通信的基本方法,并能够在此基础上开展更复杂的物联网项目开发。本文适合对物联网技术感兴趣,希望了解ESP8266模块与OneNet平台结合应用的读者。

前不久手上有了一个NodeMCUESP8266型号(ESP-12F)的单片机,然后也想做一个基于WIFI功能的远程控制项目,我是用VScode的platformio组件,基本等同于Arduion IDE开发
下面我先说一下这两者的区别,然后对每个部分的代码讲解一下,因为之前在网上搜的教程有点杂乱,就自己写个教程方便自己参考

后续也会出一个微信小程序控制LED,舵机等等的操作

之前也是做过ESP8266-01S WIFI模块
下面放上实物图
NodeMCU ESP8266

NodeMCUESP8266
ESP8266-01S
在这里插入图片描述

NodeMCU ESP8266和ESP8266-01S都是基于ESP8266芯片的开发板,它们可以用来开发物联网(IoT)项目。尽管它们都基于相同的ESP8266芯片,但这两个开发板在设计、功能、使用场景上有所不同。

区别

NodeMCU ESP8266

  1. 微控制器:NodeMCU内置的ESP8266芯片带有一个32位的Tensilica L106微控制器,核心频率可达80MHz到160MHz,配备了大约80KB的用户数据RAM和最高上16MB的SPI闪存,用于程序存储。

  2. IO功能丰富:NodeMCU板上一般提供可编程的GPIO引脚达到17个,支持UART、I2C、SPI等通信协议,以及1路ADC(模拟-数字转换器),最大分辨率为10位。

  3. 网络能力:支持IEEE 802.11 b/g/n Wi-Fi标准,集成式天线,可作为STA(客户端)或AP(热点)模式运行,也支持这两种模式的混合运行,非常适合开发需要Wi-Fi功能的物联网项目。

  4. 开发和编程:可以使用Lua语言进行脚本编程,同时支持Arduino IDE和其他ESP8266 SDK开发,方便开发者根据自己的喜好和项目需求进行选择。USB端口直接连接电脑即可编程,也可用于电源供电。

使用场景展开

  • 环境监测:利用其GPIO接口连接多个传感器,例如温湿度传感器、PM2.5空气质量监测传感器,实现环境监测站。
  • 智能家居控制中心:作为家中智能设备的中枢,控制灯光、空调、窗帘等。
  • 远程监控系统:配合摄像头使用,通过WiFi传输视音频数据实现远程监控。

ESP8266-01S

  1. 核心功能集中:ESP8266-01S是ESP8266系列的一种简化模块,尽管引脚较少,但提供了基本的GPIO、TX、RX等引脚,适合不需要大量外设连接的应用。

  2. 体积小巧:尺寸小,适合空间受限的设计,如穿戴设备、小型传感器模块等。

  3. 电源要求:需要稳定的3.3V电源供电,电流需求较高时(比如在WiFi通信时)至少为500mA,这对电源设计提出了一定的要求。

使用场景展开

  • 无线数据通讯模块:为其他不具备WiFi功能的微控制器或设备添加无线数据通讯能力。
  • 物联网节点:在节点数量众多但每个节点功能相对简单的物联网应用场景,如简单数据采集和传输,温湿度监测等。
  • 家居自动化简单项目:可以控制一两个设备的打开关闭,例如智能插座。

创建OneNET新版 MQTT协议

这里我就直接放之前的文章,那些文章是使用ESP8266-01S配合其他型号单片机(不具备WIFI功能如51/stm32单片机)连接WIFI实现数据上传和下发,外加微信小程序段教程
创建OneNET新版MQTT设备:实现远程控制单片机 为微信小程序与单片机通信打基础(微信小程序通信单片机前置任务)
在这里插入图片描述
这里查看上面的文章创立图中的物模型,如果没有经验的话强烈建议和我创建的内容一模一样,等后续代码跑通了再自己按需修改

编写代码芯片选择

在这里插入图片描述
串口设置
在这里插入图片描述

注意事项

你需要将代码改为你信息,信息获取在上面的文章中有详细的讲解

有个重要的事项
在数据上传那里,你必须确保上报的标识符和数据大小是符合你创建的物模型属性的,否则串口显示上报成功,onenet也会过滤你的信息,你可以每次一个一个的测试
例如你的温度范围是0-100,步长是0.1,你上传了一个100.01或者101.1都是错的,你整个上报的所有内容都失效

完整讲解

#include <Arduino.h>
#include "ESP8266WiFi.h"
#include <PubSubClient.h>
String ssid= "ESP";
String password = "123456789";const char* mqtt_server = "mqtts.heclouds.com";
const int mqtt_port = 1883;
const char* deviceID = "test";  // 您的设备ID
const char* productID = "eb4Lr0apkE";  // 您的产品ID
const char* apiKey = "version=2018-10-31&res=products%2Feb4Lr0apkE%2Fdevices%2Ftest&et=2017757596000&method=md5&sign=y6ej0XbtuWmRH7gQKGUeSg%3D%3D";  // 您的APIKeyString commandTopic = "$sys/" + String(productID) + "/" + String(deviceID) + "/thing/property/set";WiFiClient espClient;
PubSubClient OneNET(espClient);void setup_wifi(String ssid,String password){WiFi.begin(ssid, password);static uint8_t count = 0;Serial.print("WiFi connecting");while (WiFi.status() != WL_CONNECTED) {if(++count >= 25) break;delay(500);Serial.print(".");}if(WiFi.status() == WL_CONNECTED){Serial.println("");Serial.println("WiFi connected!");Serial.println("IP: ");Serial.println(WiFi.localIP());}else if(WiFi.status() != WL_CONNECTED){Serial.println("");Serial.println("WiFi connected fail!");}
}void reconnect() {while (!OneNET.connected()) {Serial.print("Attempting MQTT connection...");// 尝试连接if (OneNET.connect(deviceID, productID, apiKey)) {Serial.println("connected");// 一旦连接上,订阅相应的主题OneNET.subscribe(commandTopic.c_str());} else {Serial.print("failed, rc=");Serial.print(OneNET.state());Serial.println(" try again in 5 seconds");delay(5000);}}
}void callback(char* topic, byte* payload, unsigned int length) {Serial.print("Message arrived [");Serial.print(topic);Serial.println("] ");for (size_t i = 0; i < length; i++) {Serial.print((char)payload[i]);}Serial.println();
}void publishSensorData(int count, String identifiers[], float values[], int stringCount, String stringIdentifiers[], String stringValues[]) {String jsonData = "{\"id\":\"123\",\"params\":{";// 首先处理数字类型的数据for(int i = 0; i < count; ++i) {jsonData += "\"" + identifiers[i] + "\":{\"value\":" + String(values[i], 1) + "}";if (i < count - 1 || stringCount > 0) { // 如果后面还有数据,就添加一个逗号jsonData += ",";}}// 处理字符串类型的数据for (int j = 0; j < stringCount; ++j) {jsonData += "\"" + stringIdentifiers[j] + "\":{\"value\":\"" + stringValues[j] + "\"}";if (j < stringCount - 1) { // 如果不是最后一个字符串数据,就添加一个逗号jsonData += ",";}}jsonData += "}}";// MQTT主题,假设已经定义String topic = "$sys/eb4Lr0apkE/test/thing/property/post";Serial.println(jsonData); // 输出验证// 将String类型的JSON数据转换为字符数组char jsonBuffer[jsonData.length() + 1];jsonData.toCharArray(jsonBuffer, sizeof(jsonBuffer));// 假设OneNET.publish函数和Serial对象已经定义if (!OneNET.publish(topic.c_str(), jsonBuffer)) {Serial.println("数据发布失败");} else {Serial.println("数据发布成功");}
}void setup() {Serial.begin(115200);setup_wifi(ssid,password);OneNET.setServer(mqtt_server, mqtt_port);OneNET.setCallback(callback);// 连接MQTTif (OneNET.connect(deviceID, productID, apiKey)) {Serial.println("MQTT Connected");// 订阅命令下发Topicif(OneNET.subscribe(commandTopic.c_str())) {Serial.println("Subscribed to command topic");} else {Serial.println("Subscription failed");}}// 模拟数据String identifiers[] = {"temp", "humi","adcx"};float values[] = {12.1, 23.2,34.3};int numCount = sizeof(values) / sizeof(values[0]);String stringIdentifiers[] = {"Time", "command"};String stringValues[] = {"OFF", "close"};int stringCount = sizeof(stringValues) / sizeof(stringValues[0]);// 调用函数publishSensorData(1, identifiers, values, 1, stringIdentifiers, stringValues);//publishSensorData(numCount, identifiers, values, 0, stringIdentifiers, stringValues);//publishSensorData(numCount, identifiers, values, stringCount, stringIdentifiers, stringValues);
}
void loop() {if (!OneNET.connected()) {reconnect();}OneNET.loop(); // 处理接收到的消息和保持MQTT连接
}

代码讲解

连接WIFI

连接WIFI部分大家应该都理解,这里就不展开说了
主要的代码就是
#include "ESP8266WiFi.h"

WiFi.begin(ssid, password);

连接OneNET MQTT协议设备

这里需要安装一个库:#include <PubSubClient.h>
然后创建两个对象
WiFiClient espClient
PubSubClient OneNET(espClient)
在成功连接WIFI后,我们还要下面代码
分别是连接OneNET的mqtt服务器和设置订阅的回调函数
OneNET.setServer(mqtt_server, mqtt_port);
OneNET.setCallback(callback);

void callback(char* topic, byte* payload, unsigned int length) {Serial.print("Message arrived [");Serial.print(topic);Serial.println("] ");for (size_t i = 0; i < length; i++) {Serial.print((char)payload[i]);}Serial.println();
}

连接好OneNET的mqtt服务器后就是需要登录(设备id,产品id,API)
OneNET.connect(deviceID, productID, apiKey)
登录成功后就能在OneNET官网上看到自己的设备在线了

订阅主题

然后就可以订阅主题
我这里订阅的是属性设置,因为我们创建的是OneJson流设备而不是数据流设备,所有不能用MQTT下发指令,但是这个属性设置的意思是一样的作用,订阅主题后我们就可以实现单片机接收平台上发送的内容了,(当时一直在测试MQTT下发指令,弄了很久都不行,最后发了个工单客服告诉我物模型不能用,我哭死)

下面的这个手册上写了我们对应订阅的功能和作用
OneNET MQTT通信主题 官方手册
OneNET.subscribe(commandTopic.c_str())

发送数据到OneNET

这里的代码是用Json格式,这里我使用的手动拼装Json,当然也可以直接用库写
下面是官方给的示例,这里如果对时间准确度不要求的话 ,可以不用时间戳
在这里插入图片描述
这是最初的上传代码
有很明显的缺点,就是只能固定的上传两个数字类型,如果要改动非常不方便

String defined_humi = "humi";
String defined_temp = "temp";
void publishSensorData(float temperature, float humidity) {// 动态构造符合OneNET要求的JSON数据,去除时间戳String jsonData = "{\"id\":\"123\",\"params\":{\""+ defined_humi + "\":{\"value\":" + String(humidity, 1) + "},\"" + defined_temp + "\":{\"value\":" + String(temperature, 1) + "}}}";// MQTT主题,已填写示例中的产品ID和设备名称Serial.println(jsonData);String topic = "$sys/eb4Lr0apkE/test/thing/property/post";// 将String类型的JSON数据转换为字符数组,因为publish函数需要字符数组作为参数char jsonBuffer[jsonData.length() + 1];jsonData.toCharArray(jsonBuffer, sizeof(jsonBuffer));// 发布到MQTTif (!OneNET.publish(topic.c_str(), jsonBuffer)) {Serial.println("数据发布失败");} else {Serial.println("数据发布成功");}
}

对应上面说的情况我就改进了一下代码

void publishSensorData(int count, String identifiers[], float values[]) {String jsonData = "{\"id\":\"123\",\"params\":{";for(int i = 0; i < count; ++i) {jsonData += "\"" + identifiers[i] + "\":{\"value\":" + String(values[i], 1) + "}";if (i < count - 1) {jsonData += ","; // 如果不是最后一个参数,就添加一个逗号}}jsonData += "}}";// MQTT主题,假设已经定义String topic = "$sys/eb4Lr0apkE/test/thing/property/post";Serial.println(jsonData); // 输出验证// 将String类型的JSON数据转换为字符数组char jsonBuffer[jsonData.length() + 1];jsonData.toCharArray(jsonBuffer, sizeof(jsonBuffer));// 假设OneNET.publish函数和Serial对象已经定义if (!OneNET.publish(topic.c_str(), jsonBuffer)) {Serial.println("数据发布失败");} else {Serial.println("数据发布成功");}
}
下面为初始函数的代码String identifiers[] = {"temp", "humi","adcx"};float values[] = {12.1, 23.2,34.3};int numCount = sizeof(values) / sizeof(values[0]);// 调用函数publishSensorData(numCount , identifiers, values);

这样子的话我就可以实现如果我需要多少个我就创建多少个标识符和数据,然后只需要修改数组就能更改上传的数据
但是又有一个问题,我还有字符串的数据,我一开始想着是两个函数实现分别的发送,但是OneNET规定了每次上传数据的间隔要大于2s,所以为了避免一些其他问题,我决定再修改一下函数实现字符串和数据同时发送

这就有了目前的版本

void publishSensorData(int count, String identifiers[], float values[], int stringCount, String stringIdentifiers[], String stringValues[]) {String jsonData = "{\"id\":\"123\",\"params\":{";// 首先处理数字类型的数据for(int i = 0; i < count; ++i) {jsonData += "\"" + identifiers[i] + "\":{\"value\":" + String(values[i], 1) + "}";if (i < count - 1 || stringCount > 0) { // 如果后面还有数据,就添加一个逗号jsonData += ",";}}// 处理字符串类型的数据for (int j = 0; j < stringCount; ++j) {jsonData += "\"" + stringIdentifiers[j] + "\":{\"value\":\"" + stringValues[j] + "\"}";if (j < stringCount - 1) { // 如果不是最后一个字符串数据,就添加一个逗号jsonData += ",";}}jsonData += "}}";// MQTT主题,假设已经定义String topic = "$sys/eb4Lr0apkE/test/thing/property/post";Serial.println(jsonData); // 输出验证// 将String类型的JSON数据转换为字符数组char jsonBuffer[jsonData.length() + 1];jsonData.toCharArray(jsonBuffer, sizeof(jsonBuffer));// 假设OneNET.publish函数和Serial对象已经定义if (!OneNET.publish(topic.c_str(), jsonBuffer)) {Serial.println("数据发布失败");} else {Serial.println("数据发布成功");}
下面为初始函数的代码// 模拟数据String identifiers[] = {"temp", "humi","adcx"};float values[] = {12.1, 23.2,34.3};int numCount = sizeof(values) / sizeof(values[0]);String stringIdentifiers[] = {"Time", "command"};String stringValues[] = {"OFF", "close"};int stringCount = sizeof(stringValues) / sizeof(stringValues[0]);// 调用函数publishSensorData(numCount , identifiers, values, stringCount , stringIdentifiers, stringValues);
}

这样子就可以实现有几个标识符和数据就发送几个了,不限数据个数,最大灵活性

操作演示

单片机上传数据

在这里插入图片描述

平台下发数据

在这里插入图片描述
在这里插入图片描述

这里你接收到的数据,例如:“Open XXX”,你可以用JSON解析出来,然后再自己创建指令规则,用来控制其他的内容
这里你可以不回应平台的操作,平台会收到超时,但是不影响正常使用,如果你要回应平台,可以参考数据手册中的“最佳实例–物模型”
官方手册

总结

自从去年开始接触ESP8266-01S和OneNET平台以来,我便投入了大量的时间和精力,撰写了许多有关联网的教程和代码。初期,我遇到了不少挑战:手头的教程内容过时,不适应平台的新版本;对于错误信息缺乏足够的理解,经常出现连接MQTT服务时的失败情况。这一系列问题让我一度想要放弃。

然而,通过不懈的努力,我开始自己查阅资料,尝试解决这些问题。在这个过程中,我学到了如何独立分析问题,并逐步找到了导致问题的根源。最终,我不仅成功解决了连接MQTT服务的难题,对联网技术的理解和应用能力也有了显著的提高。这段经历让我深刻认识到,面对挑战和困难时,不要轻易放弃。通过仔细分析问题的原因,往往能够找到解决问题的办法。

因此,我想告诉大家:在学习技术或解决技术问题的过程中,难免会遇到各种挑战和困难。当遇到问题时,不要急于放弃。耐心地分析问题,寻找问题的根源所在,你就会发现,很多时候,问题并没有想象中那么难以解决。

通过这段文字,我希望能鼓励更多的朋友在遇到技术上的困惑和难题时,保持乐观和坚持的态度,不断学习和探索,相信终有一日,你将会像我一样,找到属于自己的解决之道,收获成长与成功。

相关文章:

  • 特征值和特征向量及其在机器学习中的应用
  • 题目 1501: 蓝桥杯-分苹果
  • LeetCode hot100-5
  • VS2022如何添加行号?(VS2022不显示行号解决方法)
  • centos7安装kafka、zookeeper
  • hnust 湖南科技大学 2022 数据挖掘课设 完整代码+报告+图源文件+指导书
  • IPD(集成产品开发)—核心思想
  • Java自学day5
  • 基于springboot+vue的善筹网众筹网站
  • 综合素质保分卷一
  • HCIA-Datacom实验指导手册:7 构建简单 IPv6 网络
  • 吴恩达机器学习全课程笔记第七篇
  • 【比较mybatis、lazy、sqltoy、mybatis-flex、easy-query操作数据】操作批量新增、分页查询(三)
  • 物联网常见协议篇
  • QT tcp通信
  • Angular数据绑定机制
  • javascript面向对象之创建对象
  • Java应用性能调优
  • js数组之filter
  • Lucene解析 - 基本概念
  • Mysql5.6主从复制
  • python3 使用 asyncio 代替线程
  • rc-form之最单纯情况
  • Sass 快速入门教程
  • Spark RDD学习: aggregate函数
  • spark本地环境的搭建到运行第一个spark程序
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 检测对象或数组
  • 浅谈Golang中select的用法
  • 温故知新之javascript面向对象
  • 【云吞铺子】性能抖动剖析(二)
  • 国内开源镜像站点
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #在 README.md 中生成项目目录结构
  • $(selector).each()和$.each()的区别
  • (11)MSP430F5529 定时器B
  • (libusb) usb口自动刷新
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (十六)Flask之蓝图
  • (一)基于IDEA的JAVA基础10
  • .bat文件调用java类的main方法
  • .NET MVC第三章、三种传值方式
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • .NET命令行(CLI)常用命令
  • .NET设计模式(2):单件模式(Singleton Pattern)
  • .NET下ASPX编程的几个小问题
  • @GlobalLock注解作用与原理解析
  • @property @synthesize @dynamic 及相关属性作用探究