关于ESP8266-NodeMCU和onenet通信传输学习总结(二)
1.更新了一些功能和代码优化和注释
添加了电机的网络控制(后续将用于门锁控制和窗帘控制),过程有点坎坷,本想用一块ESP8266-NodeMCU实现整体功能,结果发现这块板控制电机的同时不能干别的事,多线程也是伪多线程。不好用,便计划用两块开发板,一块负责通信连接和数据传输,另一块用来做下面的硬件控制,结果是可行的,且很容易实现,但不甘于资源的浪费,又想方设法放在一块开发板上实现,最终应该算是成功了,但似乎电机很热发烫,还待优化。ps:arduino官方的电机库放我这就直接跑飞了很奇怪,若有同样问题的博友希望留下经验分享。
这里电机用的是28BYJ-48,这里可能有人吐槽为什么不用42电机,原因在于我只有这个,还是从同学那收破烂来的。。。
说了这么多,不如直接看代码(代码规范有点辣鸡,希望不影响阅读):
/**
* author: topthemaster
* time: 2021.1.10
*
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "AccelStepper.h"
#include <Ticker.h>
#include <ArduinoJson.h>
#define PRODUCT_ID "396066" //产品名
#define API_KEY "tx6WM==zmW21Z2pt4susBRlHMuY="//产品密钥
#define DEVICE_ID "666259032"//设备名
#define TOPIC "ceshitopic1"//订阅主题
WiFiClient wifiClient;
Ticker ticker;
Ticker ticker2;
int count=0;//ticker1控制 数据上传下发的间隔时间(s)
int count2=0;//ticker2控制 电机转动的时间(s)
AccelStepper stepper1(4, D0, D2, D1, D3);//电机引脚
String door ="close";//默认检测门关闭
PubSubClient mqttClient(wifiClient);
const char* ssid = "Fishing WiFi ";//wifi名
const char* password = "zheshiyigewifi";//wifi密码
const char* mqttServer = "183.230.40.39";//onenet地址
const uint16_t mqttPort = 6002;//mqtt接口端口
String doorLock="close";//默认门锁指令关闭
char msgJson[75];//存json下发信息数据
char msg_buf[200];//存json上传数据及标识位
//初始化
void setup() {
stepper1.setMaxSpeed(500); // 设置电机最大速度为500
stepper1.setSpeed(0); // 初始化电机速度为300
pinMode(LED_BUILTIN, OUTPUT); // 设置板上LED引脚为输出模式
digitalWrite(LED_BUILTIN, HIGH); // 启动后关闭板上LED
pinMode(D5, OUTPUT);
pinMode(D6, INPUT_PULLUP);
digitalWrite(D5, LOW);
digitalWrite(D6, LOW);
Serial.begin(9600);
WiFi.mode(WIFI_STA);
connectWifi();
mqttClient.setServer(mqttServer,mqttPort);
// 设置MQTT订阅回调函数
mqttClient.setCallback(receiveCallback);
connectMQTTServer();
ticker.attach(1, addCount);
}
void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
// 每隔2秒钟发布一次信息
// 保持心跳 若电机正在运转,暂时不发信息(由于mcu没有多线程,不能同时运转电机和上传下发数据)
if(count>=2)
{
if(count2==0)
{ Serial.print("门锁和门状态");
Serial.print(doorLock);
Serial.print(door);
pubMQTTmsg();
mqttClient.loop();
count=0;
}
}
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
//电机控制
if(doorLock=="close"&&door=="open")
{
stepper1.setSpeed(-500);
Serial.print("1");
ticker2.attach(1, controlDoor);
door="close";
}
else if(doorLock=="open"&&door=="close")
{stepper1.setSpeed(500);
ticker2.attach(1, controlDoor);
door="open";
}
stepper1.runSpeed();
}
//ticker2控制 电机运转时间
void controlDoor(){
count2++;
Serial.print("ticker调用");
if (count2 >= 6) {
stepper1.setSpeed(0);
count2=0;
ticker2.detach(); // 使用detach来停止ticker对象定时调用函数
Serial.print("ticker.detach()");
}
}
//连接mqtt服务器
void connectMQTTServer(){
String clientId = DEVICE_ID;
String productId=PRODUCT_ID;
String apiKey=API_KEY;
// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str(),productId.c_str(),apiKey.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
subscribeTopic(); // 订阅指定主题
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}
// 订阅指定主题
void subscribeTopic(){
// 建立订阅主题。主题名称以Taichi-Maker-Sub为前缀,后面添加设备的MAC地址。
// 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时,所订阅的主题名称不同
String topicString = "temperature";
char subTopic[topicString.length() + 1];
strcpy(subTopic, topicString.c_str());
// 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
if(mqttClient.subscribe(subTopic)){
Serial.println("Subscrib Topic:");
Serial.println(subTopic);
} else {
Serial.print("Subscribe Fail...");
}
}
//获取下发指令topic 指定主题 payload 下发信息,以字节存储 length 下发信息长度
void receiveCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message Received [");
Serial.print(topic);
Serial.print("] ");
String receivePassage;
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
receivePassage+=(char)payload[i];
}
Serial.println("----"+receivePassage+"----");
Serial.print("Message Length(Bytes) ");
Serial.println(length);
///
receivePassage="";
//测试下发数据
if ((char)payload[0] == 'L') { // 如果收到的信息以“1”为开始
if((char)payload[1] == '0')
{
digitalWrite(BUILTIN_LED, LOW); // 则点亮LED。
Serial.println("LED ON");
}
else if((char)payload[1] == '1')
{
digitalWrite(BUILTIN_LED, HIGH); // 否则熄灭LED。
Serial.println("LED OFF");
}
}else if((char)payload[0] == 'D') { // 如果收到的信息以“1”为开始
if((char)payload[1] == '1')
doorLock="open";
else if((char)payload[1] == '2')
doorLock="close";
}
//
}
//对指定主题上传信息
void pubMQTTmsg(){
//onenet数据点上传系统主题
String topicString = "$dp";
char publishTopic[topicString.length() + 1];
strcpy(publishTopic, topicString.c_str());
//json数据转换为数组
DynamicJsonDocument doc(64);
bool pinState = !digitalRead(BUILTIN_LED);
bool doorState = !digitalRead(D6);
doc["led"] = pinState;
doc["door"] = doorState;
serializeJson(doc, Serial);
// 建立发布信息。温度
String jsonCode;
serializeJson(doc, jsonCode);
Serial.print("json Code: ");Serial.println(jsonCode);
String messageString = jsonCode;
char publishMsg[messageString.length() + 1];
strcpy(publishMsg, messageString.c_str());
int json_len=strlen(publishMsg);
memset(msg_buf,0,200);
msg_buf[0]=char(0x03);
msg_buf[1]=char(json_len>>8);
msg_buf[2]=char(json_len &0xff);
memcpy(msg_buf+3,publishMsg,json_len);
// 实现ESP8266向主题发布信息
if(mqttClient.publish(publishTopic, (uint8_t*)msg_buf,3+json_len)){
Serial.println("Publish Topic:");Serial.println(publishTopic);
String msg_bufTotal;
for(int i=0;i<sizeof(msg_buf)/sizeof(msg_buf[0]);i++)
{
msg_bufTotal+=msg_buf[i];
}
Serial.println("Publish message:");Serial.println(msg_bufTotal);
} else {
Serial.println("Message Publish Failed.");
}
}
void addCount(){
count++;
}
void connectWifi(){
WiFi.begin(ssid, password);
//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
后续等买的硬件需要的东西到了再更新。