- void USART2_CFG(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure = {0};
- USART_InitTypeDef USART_InitStructure = {0};
- NVIC_InitTypeDef NVIC_InitStructure = {0};
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 , ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
- /* USART2 TX-->A.2 RX-->A.3 */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- USART_InitStructure.USART_BaudRate = 115200;
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
- USART_InitStructure.USART_Parity = USART_Parity_No;
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
- USART_Init(USART2, &USART_InitStructure);
- USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
- NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- USART_Cmd(USART2, ENABLE);
- }
并将指令发送到ESP8266和接收ESP266的数据
- void USART2_IRQHandler(void)
- {
- u8 uartR=0;
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
- {
- uartR= USART_ReceiveData(USART2);
- if(USART1_RX_STA<USART1_MAX) //还可以接收数据
- {
- usart1_rxbuf[USART1_RX_STA++]=uartR; //记录接收到的值
- } else
- {
- USART1_RX_STA=0; //计数归零
- }
- }
- }
初始化ESP8266的时候需要,配置ESP8266位STA模式并与路由器建立连接。
- uint8_t ESP8266_Init(void)
- {
- //清空发送和接收数组
- memset(usart1_txbuf,0,sizeof(usart1_txbuf));
- memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));
- ESP8266_ExitUnvarnishedTrans(); //退出透传
- Delay_Ms(500);
- ESP8266_ATSendString("AT+RST\r\n");
- Delay_Ms(2000);
- if(ESP8266_Check()==0) //使用AT指令检查ESP8266是否存在
- {
- return 0;
- }
- memset(usart1_rxbuf,0,sizeof(usart1_rxbuf)); //清空接收缓冲
- ESP8266_ATSendString("ATE1\r\n"); //关闭回显
- if(FindStr((char*)usart1_rxbuf,"OK",500)==0) //设置不成功
- {
- return 0;
- }
- return 1; //设置成功
- }
这里用到是strstr的指令,判断是否有我们要求的返回值。FindStr这里的代码是这样规划的。
通过多次演示,判断是否收到数据,如果没有收到数据或者不一样的数据,则判断不正确。
- uint8_t FindStr(char* dest,char* src,uint16_t retry_nms)
- {
- int retry_nms_t = retry_nms; //超时时间
- while(strstr((char*)usart1_rxbuf,src)== NULL && retry_nms_t)//等待串口接收完毕或超时退出
- {
- Delay_Ms(10);retry_nms_t--;
- }
- printf(usart1_rxbuf);
- if(retry_nms_t>=0) return 1;
- return 0;
- }
然后就是需要去连接WIFI模块了。需要提前写入路由器的名称和代码
- uint8_t ESP8266_ConnectAP(char* ssid,char* pswd)
- {
- uint8_t cnt=5;
- while(cnt--)
- {
- memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));
- ESP8266_ATSendString("AT+CWMODE_CUR=1\r\n"); //设置为STATION模式
- if(FindStr((char*)usart1_rxbuf,"OK",200) != 0)
- {
- break;
- }
- }
- if(cnt == 0)
- return 0;
- cnt=2;
- while(cnt--)
- {
- memset(usart1_txbuf,0,sizeof(usart1_txbuf));//清空发送缓冲
- memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));//清空接收缓冲
- sprintf((char*)usart1_txbuf,"AT+CWJAP_CUR="%s","%s"\r\n",ssid,pswd);//连接目标AP
- ESP8266_ATSendString((char*)usart1_txbuf);
- if(FindStr((char*)usart1_rxbuf,"GOT IP",8000)!=0) //连接成功且分配到IP
- {
- return 1;
- }
- }
- return 0;
- }
在编写代码之前,我们要先搭建好阿里云物联网平台,建立产品和设备。
要建立设备,设备名称随便起。
建立产品以后,需要建立的是物模型。因为我们只是做一个简单的LED的控制和状态显示,所以只添加了一个LED即可。
在产品里面就是需要建立设备了,这里设备名称随便起,但是必须是英文,后面需要用到的。
点击查看设备的三元素,填写到程序中,生产用户名 ID和密码。
我们接着回复编写我们的程序,连接阿里云。
这里需要的是阿里云的IP地址,https://help.aliyun.com/product/30520.html 具体的应用框架,可以看到这里。
- if(ESP8266_ConnectServer("TCP",ServerIP,1883)!=0)
- {
- printf("Conn to MQTT Server !\r\n");
- }
- else printf("Connto MQTT Fail!\r\n");
这里是生成三要素的代码。
- void AliIoT_Parameter_Init(void)
- {
- char temp[128]; //计算加密的时候,临时使用的缓冲区
- memset(ClientID,128,0); //客户端ID的缓冲区全部清零
- sprintf(ClientID,"%s|securemode=3,signmethod=hmacsha1|",DEVICENAME); //构建客户端ID,并存入缓冲区
- ClientID_len = strlen(ClientID); //计算客户端ID的长度
-
- memset(Username,128,0); //用户名的缓冲区全部清零
- sprintf(Username,"%s&%s",DEVICENAME,PRODUCTKEY); //构建用户名,并存入缓冲区
- Username_len = strlen(Username); //计算用户名的长度
-
- memset(temp,128,0); //临时缓冲区全部清零
- sprintf(temp,"clientId%sdeviceName%sproductKey%s",DEVICENAME,DEVICENAME,PRODUCTKEY); //构建加密时的明文
- utils_hmac_sha1(temp,strlen(temp),Passward,DEVICESECRE,DEVICESECRE_LEN); //以DeviceSecret为秘钥对temp中的明文,进行hmacsha1加密,结果就是密码,并保存到缓冲区中
- Passward_len = strlen(Passward); //计算用户名的长度
-
- memset(ServerIP,128,0);
- sprintf(ServerIP,"%s.iot-as-mqtt.cn-shanghai.aliyuncs.com",PRODUCTKEY); //构建服务器域名
- //sprintf(ServerIP,"106.54.182.59"); //构建服务器域名
- ServerPort = 1883;
至于如何使用MQTT连接 Broker,这里也不赘述了,有代码,直接应用即可。
- MQTT_Connect(ClientID, Username, Passward)
连接以后,可以看到,设备已经显示上线了。
我们在串口上也可以看到输出的日志。
这里是定时上传LED状态到阿里云物联网平台的。固定的json格式,不能修改,根据前面定义的物模型编写的。
- if(Connnect_flag==1) {//有客户端连接,发送到客户端
- sprintf(s_temp,"{"id":1,"params":{"led":%d},"version":"1.0","method":"thing.event.property.post"}",
- led_state);
- MQTT_PublishData(P_TOPIC_NAME,s_temp,0);
- }
这里显示的就是LED的状态。当前设备为关闭状态。
我们可以使用设备模式实现控制指令的下发。
当我们选择LED为关闭或者开启状态的时候,在单片机上就会收到这样的数据。
里面json数据"\"led\":1"或者"\"led\":0"便是对LED的状态控制。
我们在程序连判断一下,接收到数据是哪个,就可以对相应的LED进行控制了
- if(strstr(usart1_rxbuf+4,""led":1"))led_state=1;
- else if(strstr(usart1_rxbuf+4,""led":0"))led_state=0;
- if(led_state==0)GPIO_SetBits(GPIOE,GPIO_Pin_5);
- else GPIO_ResetBits(GPIOE,GPIO_Pin_5);
来看看开发板的验证吧。绿色框就是LED状态的变化。附上源代码