一、MCU与云端之间的传输
(1)题目要求中提到MCU需要上传给云端的信息有:饮水机编号、饮水机的状态、报警信息、校园卡卡号、系统时间以及取水时的水流量,我们可以设计一个传输的数据帧,而这个数据帧中包含了上述所有的信息,并且其中的每种数据可以设定不同的标志位,方便后台方面进行处理,例如:
b01(饮水机编号)|1(0表示复位、1表示正常运行、2表示暂停使用)|#(#表示无异常、E表示Error、W表示warning)|FFFFFFFF(8位十六进制数表示校园卡的卡号,没有校园卡时可用FFFFFFFF来代替)|02:20:59(严格按照时分秒的形式传输数据)|0(0表示没有流水量,非0表示有不同的流水量)
上面这段数据帧可以以一定的时间间隔去上传给云端,那云端如何得知用户在何时接了水呢,当校园卡卡号不为FFFFFFFF且流水量非0时,提取满足这两条数据的数据帧中的时间即可,当流水量归0且校园卡卡号复位时代表接水结束
同样的我们也可以设计一条数据帧来进行云端对MCU的控制,首先我们需要知道云端需要给MCU传输什么数据,研读任务书可知云端需要改变MCU的状态,调整至复位、正常、暂停状态,以及发送系统时间辅助MCU调整时钟,以及通过云端设定饮水机加热的温度,那么我们可以设定一条这样的数据帧:@1201203025,加上‘@’的作用是进行辅助定位数据帧位置的效果,当找到@之后的第一位表示设定的MCU的运行状态0-复位1-正常2-暂停,紧随其后的六位数据分别代表时分秒,最后三位数据代表设定的温度,设定范围是0-999
通过设计以上两种数据帧即可完成MCU和云端之间的通讯,我们也就完成了物联网最基本的数据传输功能
(2)在数据传输的过程中遇到的问题
A、一开始进行数据解析时,可能是由于场地内的WIFI太过于密集导致了信道相互干扰,在两个小时的时间里解析出来的数据不对,一直重复ABC,但是后来不知道怎么回事突然就好了
B、由于我使用的是非透传的方式通过WIFI模块传输数据,需要实时计算本次传输的字节数,有时候可能因为某种不知名的原因(可能是PCB没有辅铜的原因,导致电流传输的不稳定导致程序出了问题)导致计算的字节数出错
二、MCU内部的功能控制
(1)本次竞赛题目为饮水机,主要需要实现的功能是自动加水、水位报警、合法校园卡控制饮水,水质检测以及云端对于饮水机状态的控制
当饮水机收到云端发送来的复位指令时,stm32实现软重启功能,收到正常指令时饮水机才会开始运行,当收到暂停指令时,可以将程序困在某一段中,也可以跳转至初始化页面,而水位的检测功能可以使用两个按键来模拟,当按下K1时表示超过高水位,当按下K2时表示低于低水位,只需要对应调整PWM的占空比即可,当K2按下时检测到K1按键按下即表示水位出现异常,则将ERROR标志位置1,发出0.4秒循环鸣叫,并向云端上传报错消息,需要注意的是这个功能在实际运用中属于是全自动的,所以不需要刷校园卡来解除该功能的锁定,水质的检测可以通过水温、电压的检测来计算,同时应用flash来保存云端设定的温度,需要注意的是这个功能也是全自动的也不需要刷校园卡来解除该功能的锁定,接下来需要制作的功能便是饮水机的取水功能、取水流量的计数以及上传断网期间的数据,根据现实生活中饮水机的应用可知,取水应该是在刷了校园卡之后才能让取水按键凑效,没有刷校园卡之前应该无视按键的取水功能,我们可以通过一个小小的标志位即可实现该功能,刷了RFID之后将标志位置1,取水完毕后将标志位归零
对于断网期间消息的保存功能,我们可以定义一个数组,检测TCP连接状态,当处于TCP失联状态时每隔一定时间将数据保存在数组中,当恢复TCP连接时将数组中的数据以较快的速度上上传至云端即可
(2)对于断网保存消息功能实现过程中遇到的问题
A、一开始我的打算是用一种较快的速度上传至云端,由于我上传的数据是保存在定义的一个结构体中,所以我断网期间保存数据的数组也是一个结构体数组,可能是占用空间太大,导致每次只能上传数组首元素的数据,后来改成较慢的数据上传即可完成断网消息上传
三、巡检装置
巡检装置需要在不经过云端的情况下获取饮水机的数据,目的是方便维修人员在饮水机断网状态下检测饮水机的状态,我的思路是使用另外一个WIFI模块来组建一个局域网,巡检装置通过连接这个局域网来建立与饮水机之间的通讯,实现数据的传输,我们可以直接用发送的云端的数据帧格式发送给巡检装置,再到巡检装置进行数据解析即可
以下是饮水机main.c代码
#include "stm32f10x.h" // Device header
#include "esp8266.h"
#include "oled.h"
#include "key.h"
#include "led.h"
#include "ad.h"
#include "PWM.h"
#include "string.h"
#include "timer2.h"
#include "speed.h"
#include "RC522.h"
#include "delay.h"
#include "uart1.h"
#include "stdio.h"
#include "uart2.h"
#include "flash.h"
#include "ds18b20.h"
typedef struct{
u8 RunSta;//单车状态 0-复位 1-开始 2-暂停
u8 h;
u8 m;
u8 s;
u8 Temp;
} infomation;
infomation Info;
infomation Mas[50];
u8 pMas=0;
int flag_speed = 0,Speed = 0,led = 0,beed = 0,led_time = 0,beez_time = 0,esp_time = 0,
led_flag = 0,beez_flag = 0,rfid_time = 0,oled_time = 0;
char Tx[255],Blue[60],usart1_flag = 0;
int AD2Value = 0;
int tor_speed = 0,tor_speed_temp = 0,AD1Value = 0,key_num = 0,flag = 0,tcpState = 0;
u8 Pled_flag=0;
u8 pInfo = 0; //0-无状态信息 1-报警信息(水质不合格) 2-错误(水位不正常)
u8 s_time_flag=0,Water_Num_flag=0,Pled_time_flag=0; //计时标志位
u16 s_time=0,Pled_time=0; //饮水时间
char info[3] = {'#','W','E'}; //报警信息
u16 Water_Num=0;
u8 Water_clo=0,Pled=0;
unsigned char pSnr[4] = {0xFF,0xFF,0xFF,0xFF};
u8 istate=0;
void Some_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
OLED_Init(); //B8 B9
UART1_Init();
UART3_Init();
WIFI_Init();
ESP8266_Init(); //向串口1发送信息 A9--TX A10--RX
OLED_ShowString(1,1,"ESP is OK");
Delay_ms(500);
OLED_ShowString(1,1," ");
Key_Init(); //B11为1,B1为2,A5为3,C14为4
LED_Init(); //B12左转向灯,B14右转向灯,B15喇叭(高电平有效),B13光敏灯
AD1_Init(); //A0为测量电压
AD2_Init(); //B1为测量电压
PWM_Init(); //A8PWM输出口 tim2输出ch1通道
Timer_Init(); //计时
RFID_Init(); //A3-RST A2-MISO A1-MOSI A0-SCK C15-SDA
DS18B20_Init(); //A2测温
}
void ToYunData(infomation toInfo,unsigned char pSnr[4],u8 tate)
{
sprintf(Tx,"b01 %d %c %x%x%x%x %d:%d:%d %d",toInfo.RunSta,info[tate],pSnr[0],pSnr[1],pSnr[2],pSnr[3],toInfo.h,toInfo.m,toInfo.s,Water_Num);
ESP8266_SendData((unsigned char *)Tx,strlen(Tx));
}
u8 readData(void)
{
u8 find=0;
char *Pos=esp8266_buf;
// OLED_ShowString(3,1,(char*)Pos);
// OLED_ShowString(3,1,Pos);
for(u16 i = 0 ; i < 1000 ; i++)
{
if(*Pos == '@')
{
find=1;
break;
}
else
Pos++;
}
if(find) //Pos指向@
{
Info.RunSta = Pos[1]-'0';
while(*Pos!='|')
Pos++;
//获取时分秒
Info.h=((Pos[1]-'0')*10)+(Pos[2]-'0'); //Pos指向|
Info.m=((Pos[3]-'0')*10)+(Pos[4]-'0');
Info.s=((Pos[5]-'0')*10)+(Pos[6]-'0');
Info.Temp=((Pos[7]-'0')*100)+((Pos[8]-'0')*10)+(Pos[9]-'0');//Pos指向|
if(Info.Temp<100)
FLASH_W(FLASH_START_ADDR,Info.Temp); //将温度存放至Flash
Delay_ms(50);
Info.Temp = FLASH_R(FLASH_START_ADDR);
OLED_ShowString(2,1," ");
sprintf(Tx,"setTH:%d",Info.Temp);
OLED_ShowString(2,1,Tx);
}
ESP8266_Clear(); //清空数组
if(find)
return 0;
else
return 1;
}
void SoftReset(void)
{
__set_FAULTMASK(1); // 关闭所有中端
NVIC_SystemReset(); // 复位
}
//第一个饮水机ID 状态 异常信息(W\E) RFID(FFFFFFFF) 饮水时间 饮水量
int main(void)
{
Some_Init();
Info.RunSta=0;
Info.Temp=FLASH_R(FLASH_START_ADDR);
TIM_SetCounter(TIM3,0);
unsigned char pTagType[4];
loop:
istate=0;
OLED_Clear();
OLED_ShowString(1,1,"Waite Open");
sprintf(Tx,"setTH:%d",Info.Temp);
OLED_ShowString(2,1,Tx);
while(1) //等待接收b01 open指令
{
rfid_time = 0;
if(Info.RunSta==1)
break;
if(usart1_flag == 0)
readData();
}
sprintf(Tx,"TIME:%2d:%2d:%2d",Info.h,Info.m,Info.s);
OLED_ShowString(1,1,Tx);
OLED_ShowString(4,1,"CLOSE");
s_time_flag=1;
OLED_ShowString(3,1," ");
OLED_ShowString(4,1,"BEGIN");
while(1)
{
//查询是否关闭
if(usart1_flag == 0)
{
readData();
if(Info.RunSta==0)
SoftReset(); //重启32
if(Info.RunSta==2)
{
if(tor_speed || led)
{
PWM_SetCompare1(0);
tor_speed = 0; //关闭电机
led_flag = 0;
led = 0; //关闭方向灯
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
}
GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET);
beez_flag = 0;beez_time = 0;beed=0;
OLED_ShowString(4,1,"CLOSE");
goto loop;
}
}
if(rfid_time >= 10)
{
if(PcdRequest(0x52,pTagType) == MI_OK) //寻找所有卡片
{
if(PcdAnticoll(pSnr) == MI_OK)
{
if(PcdSelect(pSnr) == MI_OK)
{
WaitCardOff();
Water_clo=1;
esp_time = 0;
}
}
}
}
// AD1Value = AD1_GetValue();
// AD2Value = AD2_GetValue();
// if(AD1Value > 1000)
// GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_SET);
// else
// GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_RESET);
key_num = Get_Key();
switch(key_num)
{
case 1:{ //负数代表温水 //正数代表热水
if(led == -1 || led == -2)
{
led = 0;
Water_clo=0;
for(u8 i = 0 ; i < 4 ; i ++)
pSnr[i] = 0xFF;
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
}
else
{
if(Water_clo)
{
led = -1;
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
}
}
}break;
case 2:{
if(led == 1 || led == 2)
{
led = 0;
Water_clo=0;
for(u8 i = 0 ; i < 4 ; i ++)
pSnr[i] = 0xFF;
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
}
else
{
if(Water_clo)
{
led = 1;
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
}
}
}break;
case 5: //水位不正常
{
OLED_ShowString(3,1,"ERRO");
istate=2; //报警ERRO
beed=1; //错误鸣叫
}break;
}
if(beed<=0)
{
if(key_num==3) PWM_SetCompare1(0);
if(key_num==4) PWM_SetCompare1(80);
}
if(led && Water_clo)
{
Water_Num_flag=1;
}
else
{
Water_Num_flag=0;
Water_Num=0;
}
if(led)
{
if(led == 1)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_SET);
led = 2;
led_time = 0;
led_flag = 1;
}
else if(led == -1)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_SET);
led = -2;
led_time = 0;
led_flag = 1;
}
if(led_time >= 7)
{
if(led == 2)
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
else if(led == -2)
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
}
if(led_time >= 10)
{
if(led == 2)
led = 1;
else if(led == -2)
led = -1;
led_flag = 0;
}
}
if(Pled)
{
Pled_time_flag=1;
if(Pled==1)
{
if(Pled_time>2)
{
Pled_time=0;
if(++Pled_flag==2) Pled_flag=0;
GPIO_WriteBit(GPIOB,GPIO_Pin_13,(BitAction)Pled_flag);
}
}
if(Pled==2)
{
if(Pled_time>5)
{
Pled_time=0;
if(++Pled_flag==2) Pled_flag=0;
GPIO_WriteBit(GPIOB,GPIO_Pin_13,(BitAction)Pled_flag);
}
}
}
else
Pled_time_flag=0;
if(beed)
{
if(beed == 1)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_SET);
beed = 2;
beez_time = 0;
beez_flag = 1;
}
else if(beed == 2)
{
if(beez_time >= 4)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET);
}
if(beez_time >= 10)
{
beed = 1;
beez_flag = 0;
}
}
if(beed == -1)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_SET);
beed = -2;
beez_time = 0;
beez_flag = 1;
}
else if(beed == -2)
{
if(beez_time >= 4)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET);
}
if(beez_time >= 10)
{
beed = -1;
beez_flag = 0;
}
}
}
if(oled_time >= 20)
{
sprintf(Tx,"TIME:%2d:%2d:%2d ",Info.h,Info.m,Info.s);
OLED_ShowString(1,1,Tx);
short tem = DS18B20_Get_Temp();
OLED_ShowSignedNum(2,10,tem,2);
if(tem<Info.Temp)
Pled = 1; //1是加热
else if(tem>Info.Temp)
Pled = 2;
else if(tem==Info.Temp)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_RESET);
Pled = 0;
}
}
key_num = 0;
}
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET)
{
if(usart1_flag)
usart1_flag--;
if(led_flag)
led_time++;
if(beez_flag)
beez_time++;
if(s_time_flag)
{
s_time++;
if(s_time==10)
{
s_time=0;
Info.s++;
if(Info.s>59) {Info.m++;Info.s=0;}
if(Info.m>59) {Info.h++;Info.m=0;}
if(Info.h>23) {Info.h=0;}
}
}
if(Water_Num_flag)
{
Water_Num++;
if(Water_Num>100)
{
led = 0;
Water_clo=0;
for(u8 i = 0 ; i < 4 ; i ++)
pSnr[i] = 0xFF;
GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
}
}
if(Pled_time_flag)
Pled_time++;
esp_time++;
oled_time++;
rfid_time++;
if(esp_time == 10)
{
if(ESP8266_TCP())
{
if(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT")==0)
{
OLED_ShowString(4,7,"TCP OK ");
ESP8266_Clear();
}
else
{
Mas[pMas].h = Info.h;
Mas[pMas].m = Info.m;
Mas[pMas].s = Info.s;
pMas++;
OLED_ShowNum(3,1,pMas,1);
tcpState = 0;
ESP8266_Clear();
OLED_ShowString(4,7,"TCP ERRO");
}
}
else
{
if(pMas)
{
static u8 p = 0;
if(++p < pMas)
ToYunData(Mas[p],pSnr,istate);
else
pMas = 0;
}
else
{
tcpState = 1;
ToYunData(Info,pSnr,istate);
OLED_ShowString(4,7,"TCP OK ");
}
}
}
if(esp_time >= 12)
{
memset(Blue, 0, sizeof(Blue));
if(strstr((const char *)bulete,"@")!=NULL)
{
if(tcpState == 0)
{
if(Info.RunSta == 1)
sprintf(Blue,"|ERR#Run &4%4d",AD1Value);
else
sprintf(Blue,"|ERR#Close&4%4d",AD1Value);
}
else
{
if(Info.RunSta == 1)
sprintf(Blue,"|OK #Run &4%4d",AD1Value);
else
sprintf(Blue,"|OK #Close&4%4d",AD1Value);
}
WIFI_SendData((unsigned char *)Blue,strlen(Blue));
UART3_Clear();
}
esp_time = 0;
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
}
void USART1_IRQHandler(void)
{
char Rx_data;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //?????ж?
{
Rx_data = USART1->DR;
if(esp8266_cnt >= 999) esp8266_cnt = 0; //???????????
esp8266_buf[esp8266_cnt++] = Rx_data;
usart1_flag = 2;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
void USART3_IRQHandler(void)
{
char Rx_data;
if(USART_GetITStatus(USART3,USART_IT_RXNE) != RESET) //?????ж?
{
Rx_data = USART3->DR;
if(bulete_cnt >= 999) bulete_cnt = 0; //???????????
bulete[bulete_cnt++] = Rx_data;
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line12) == SET)
{
flag_speed++;
EXTI_ClearITPendingBit(EXTI_Line12);
}
}
完整工程代码上传资源,感兴趣的可以自取
新人小白总结赛后经验,大神勿喷