本帖最后由 yaojin919000 于 2021-1-4 15:15 编辑

本文简单介绍利用STM32和esp8266等元器件,利用ONENET平台,实现手机端、电脑端和大屏三种办法对现场温湿度光照度和开关信息的采集显示,并在手机端和电脑端对现场的LED灯以及蜂鸣器进行控制。

实现思路:根据ONENET平台的协议要求,先选择多协议接入建立一个产品,记住产品ID,在这个产品下再创建两个设备,记住设备的ID和鉴权信息,这些信息在编程的过程中需要使用。为了体现使用平台的灵活性,我创建了2个不同硬件的设备,一个是使用STM32+8266ESP-01S的硬件组成,一个是采用NODEMCU模块的硬件组成,两个设备都可以独立运行使用,各自单独组成一个独立的控制系统。两个设备通过ONENET平台每5秒钟发布一次数据,并且随时等待平台对自己的控制命令并完成执行。

硬件部分介绍:设备一采用nodemcu设备硬件简单,直接某宝购买(十几元)用杜邦线外配几个LED灯。自己配上5V电源和手机充电线(见图一)完活。设备二稍微复杂一点,选用STM32F103C8T6最小系统板,参照萝卜兄弟设计一个PCB线路板,把蜂鸣器、BHT11、BHT1750、0.96OLED、通讯模块8266ESP01S还有TCP1211,再配上两个按钮和LED指示灯(参图二),线路板选择嘉立创线上编辑,直接5片一元下单,一周内顺丰邮到家,全部硬件成本约50-60元。

软件部分介绍:针对两个不同设备,分别开发两套独立的软件。主要的功能都是完成数据采集,打包,上传,对平台下发的数据进行解析并执行相关的操作。下面将详细讲解一下:使用STM32+8266ESP-01S的软件讲:这部分软件采用c语言编写,参照ONENET平台上张继瑞老师和正点原子的例程,分成几个部分:

初始化部分:/硬件初始化void Hardware_Init(void)

{

delay_init();                        //延时函数初始化

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级

Usart1_Init(115200);         //串口1初始化为115200

Usart2_Init(115200);         //串口2,驱动ESP8266用

EXTIX_Init();                //外部中断初始化

LED_Init();                                //LED初始化

ADCx_Init();                        //ADC   DMA初始化

DHT11_GPIO_Config();        //DHT11温湿度传感器初始化

BH1750_Init();                        //光照

KEY_Init();

BEEP = 0;//鸣叫提示接入成功

delay_ms(250);

BEEP = 1;

数据采集部分:

/*光照度传感器获取数据*/

value_light = LIght_Intensity();

/* 温湿度传感器获取数据*/

/*调用Read_DHT11读取温湿度,若成功则输出该信息*/

if( Read_DHT11(&DHT11_Data)==SUCCESS)

{

sprintf(str, "温度:%d.%d ℃ ",DHT11_Data.temp_int,DHT11_Data.temp_deci);

sprintf(str, "湿度:%d.%d %c ",DHT11_Data.humi_int,DHT11_Data.humi_deci,'%');

}

else

{

printf("Read DHT11 ERROR!\r\n");//读取数据失败,串口打印:Read DHT11 ERROR.

}

Led_Status = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);//读取LED0的状态

Led_Status1 = GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);//读取LED1的状态

上传部分:

memset(buf, 0, sizeof(buf));

body_len = OneNet_FillBuf(buf);                                                                                          //获取当前需要发送的数据流的总长度

printf("send data:%s\n",buf);

if(body_len)

{

if(MQTT_PacketSaveData(DEVID, body_len, NULL, 5, &mqttPacket) == 0)                             //封包

{

for(; i < body_len; i++)

mqttPacket._data[mqttPacket._len++] = buf;     ESP8266_SendData(mqttPacket._data, mqttPacket._len);                                                      //上传数据到平台

MQTT_DeleteBuffer(&mqttPacket);                                                                                   //删包

}

else

printf("WARN:        EDP_NewBuffer Failed\r\n");

}

下发指令解析:

void OneNet_RevPro(unsigned char *cmd)

{

MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                                                                //协议包

char *req_payload = NULL;

char *cmdid_topic = NULL;

unsigned short req_len = 0;

unsigned char type = 0;

short result = 0;

char *dataPtr = NULL;

char numBuf[10];

int num = 0;

type = MQTT_UnPacketRecv(cmd);

switch(type)

{

case MQTT_PKT_CMD:                                                                                                                        //命令下发

result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len);        //解出topic和消息体

if(result == 0)

{

printf("cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len);

if(MQTT_PacketCmdResp(cmdid_topic, req_payload, &mqttPacket) == 0)        //命令回复组包

{

printf("Tips:        Send CmdResp\r\n");

ESP8266_SendData(mqttPacket._data, mqttPacket._len);                        //回复命令

MQTT_DeleteBuffer(&mqttPacket);                                                                        //删包

}

}

break;

case MQTT_PKT_PUBACK:                                                                                                                //发送Publish消息,平台回复的Ack

if(MQTT_UnPacketPublishAck(cmd) == 0)

break;

default:

result = -1;

break;

}

ESP8266_Clear();                                                                        //清空缓存

if(result == -1)

return;

dataPtr = strchr(req_payload, ':');                                        //搜索':'

if(dataPtr != NULL && result != -1)                                        //如果找到了

{

dataPtr++;

while(*dataPtr >= '0' && *dataPtr <= '9')                //判断是否是下发的命令控制数据

{

numBuf[num++] = *dataPtr++;

}

numBuf[num] = 0;

num = atoi((const char *)numBuf);                                //转为数值形式

if(strstr((char *)req_payload, "LED0:0"))                //搜索"LED0:0"字符串

{

GPIO_SetBits(GPIOA, GPIO_Pin_4);           //共阳极,拉高PA8电平,熄灭LED0

printf("LED0=0");

}

else if(strstr((char *)req_payload, "LED0:1"))

{

GPIO_ResetBits(GPIOA, GPIO_Pin_4);                        //点亮LED0

printf("LED0=1");

}

else if(strstr((char *)req_payload, "LED1:0"))                //搜索"LED1:0"字符串

{

GPIO_SetBits(GPIOC, GPIO_Pin_13);           //共阳极,拉高PC13电平,熄灭LED1

printf("LED1=0");

}

else if(strstr((char *)req_payload, "LED1:1"))

{

GPIO_ResetBits(GPIOC, GPIO_Pin_13);                        //点亮LED1

printf("LED1=1");

}

else if(strstr((char *)req_payload, "BEEP:0"))

{

GPIO_SetBits(GPIOA, GPIO_Pin_0);        //关闭蜂鸣器

alarmFlag=0;

alarm_is_free=0;

printf("BEEP=0");

}

else if(strstr((char *)req_payload, "BEEP:1"))

{

GPIO_ResetBits(GPIOA, GPIO_Pin_0);        //打开蜂鸣器

alarmFlag=1;

alarm_is_free=1;

printf("BEEP=1");

网络连接以及检测:

MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                                        //协议包

unsigned char *dataPtr;

_Bool status = 1;

printf("OneNet_DevLink\r\nPROID: %s,        AUIF: %s,        DEVID:%s\r\n", PROID, AUTH_INFO, DEVID);

if(MQTT_PacketConnect(PROID, AUTH_INFO, DEVID, 512, 0, MQTT_QOS_LEVEL0, NULL, NULL, 0, &mqttPacket) == 0)

{

ESP8266_SendData(mqttPacket._data, mqttPacket._len);                        //上传平台

dataPtr = ESP8266_GetIPD(250);                                                                        //等待平台响应

if(dataPtr != NULL)

{

if(MQTT_UnPacketRecv(dataPtr) == MQTT_PKT_CONNACK)

{

switch(MQTT_UnPacketConnectAck(dataPtr))

{

case 0:printf("Tips:        连接成功\r\n");status = 0;break;

case 1:printf("WARN:        连接失败:协议错误\r\n");break;

case 2:printf("WARN:        连接失败:非法的clientid\r\n");break;

case 3:printf("WARN:        连接失败:服务器失败\r\n");break;

case 4:printf("WARN:        连接失败:用户名或密码错误\r\n");break;

case 5:printf("WARN:        连接失败:非法链接(比如token非法)\r\n");break;

default:printf("ERR:        连接失败:未知错误\r\n");break;

}

}

}

MQTT_DeleteBuffer(&mqttPacket);                                                                //删包

}

else

printf("WARN:        MQTT_PacketConnect Failed\r\n");

return status;

使用NodeMCU的软件讲解:这部分软件比较简单,采用lua语言配合nodemcu固件,设计两个定时器,初始化D1、D2、D5管脚作为LED驱动,A0管脚作为模拟量输入端口。一个一秒的定时器作为连接本地WIFI的定时,另一个是5秒定时器作为上传平台的数据之用。软件首先初始化各个管脚,然后连接WIFI网络,登录ONENET平台并进入透传模式并订阅相应的主题,然后进入5秒循环,循环内先采集LED管脚的电平、模拟量A0和模拟温湿度数据,按照平台要求的数据格式发布到平台,然后,检查平台返回的数据看是否需要有下发的指令要去执行。详见下面部分程序代码:--[[

GPIO0--3

GPIO1--10

GPIO2--4

GPIO3--9

GPIO4--2

GPIO5--1

GPIO9--11

GPIO10--12

GPIO12--6

GPIO13--7

GPIO14--5

GPIO15--8

GPIO16--0

]]

print("Set up wifi..SmartConfig....")            --MESSAGE HINT

--yixiashizidongpeiwangdaima--

wifi.setmode(wifi.STATION)         --SET THE WiFi_MODE TO STATION

cfg = {}                           --CREATE NEW TABLE

cfg.ssid = "203"              --SET WiFi NAME

cfg.pwd = "987654321"               --SET WiFi PASSWORD

wifi.sta.config(cfg)               --CONFIG WiFi

wifi.sta.connect()                 --CONNECT WiFi

--Timer Init

time1 = tmr.create()               --create timer1  connect to OneNET

time2 = tmr.create()               --create timer1  publice data to OneNET

---------------------

--Dht11Pin7 = 2                      --DHT11_BIN

b302180adee18dbec9617297da8982c7.gif1

LED0=1

LED1=5

LED2=2

---------------------

--Variable Init

Four = 0

DeviceID = "649760557"

AuthInfo = "12345"

ProductID = "385059"

BrokerHost = "183.230.40.39"

Port = 6002

---------------------

gpio.mode(LED0,gpio.OUTPUT)

gpio.write(LED0,gpio.LOW)

gpio.mode(LED1,gpio.OUTPUT)

gpio.write(LED1,gpio.LOW)

gpio.mode(LED2,gpio.OUTPUT)

gpio.write(LED2,gpio.HIGH)

-------------------------

if adc.force_init_mode(adc.INIT_ADC)

then

node.restart()

return -- don't bother continuing, the restart is scheduled

end

---------------------

function Reconnect()

if wifi.sta.getip() == nil then

print("disconnect,waiting for ip...")

wifi.setmode(wifi.STATION)

wifi.startsmart(1, function (wifi_ssid, wifi_password)

config={}

config.ssid= wifi_ssid

config.pwd= wifi_password

config.save=true

wifi.sta.config(config)

wifi.sta.autoconnect(1)

end)

wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)

print(wifi.sta.getip())

wifi.setmode(wifi.STATION)

print("success")

end)

else

time1:stop()

print("connect success!")

print("ip is "..wifi.sta.getip())

clientD = mqtt.Client(DeviceID, 120, ProductID, AuthInfo, 0)

clientD:connect(BrokerHost, Port, 0, function(client)        --connect device

print("Connect to success!")

end)

time2:alarm(500, tmr.ALARM_AUTO, Up_AC_Message)

end

end

function Up_AC_Message()

Four = Four + 1

if(Four%5 == 0)then

Four = 0

--status,temp,humi,temp_dec,humi_dec = dht.read11(Dht11Pin7)

--print("temp is:"..temp, "humi is:"..humi)

--HumiVar = humi

--TempVar = temp

if(gpio.read(LED2)==1)then

gpio.write(LED2,gpio.LOW)

else

gpio.write(LED2,gpio.HIGH)

end

data = {}

data.Temp = 22.3

data.Humi =52.8

data.LED0=gpio.read(LED0)            --LED0 green

data.LED1=gpio.read(LED1)             --LED1 red

data.MAC=wifi.sta.getmac()             --mac

data.Voltage=3000/1024*(adc.read(0))

ok,data_message = pcall(sjson.encode, data)

header = string.char(3, 0, string.len(data_message))..data_message

clientD:publish("$dp",header,0,0,function(client)

print(&quot

995ff6267d2dbb3bf3c2a96f7a5c89da.gifUBLISH WEATHER SUCCESS!")

end)

clientD:subscribe("$dp",0,function(client)

print("SUBSCRIBE SUCCESS!")

end)

clientD

113f16c2fc4ec26ea0272403847c7a32.gifn("message",function(client,topic,message)

print(message)

if message=="LED0:0" then  gpio.write(LED0,gpio.LOW)end

if message=="LED0:1" then  gpio.write(LED0,gpio.HIGH)end

if message=="LED1:0" then  gpio.write(LED1,gpio.LOW)end

if message=="LED1:1" then  gpio.write(LED1,gpio.HIGH)end

end)

end

if(wifi.sta.getip() == nil) then

time2:stop()

time1:start()

end

end

time1:alarm(1000, tmr.ALARM_AUTO, Reconnect)    --do Reconnect()

顺便说一下,制作nodemcu固件需要选择GPIO,TMR,WIFI,ADC,DHT,MQTT,SJSON,I2C几个模块,生成制作固件方法参见我另外一篇文章的相关资料。刚才要把几个上位机的控制图片传上来,但是提示帖子的容量有限度,所以,只能另外再写关于上位机创建产品增加设备和应用管理以及View2.0大屏显示的内容。明天再写,今天就先到这里。

3613d438ea0fca2d67355e6cd2fbc11d.gif

写的不好,请大家多多批评指正!!

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐