ESP8266與STM32串口數據交互(通過JSON)
在這篇文章中我會通過介紹ESP8266和STM32兩部分的代碼來實現兩塊MCU串口數據的交互,交互的數據格式採用的是JSON格式,ESP8266的開發環境使用的是arduino,STM32使用的是KEIL5。
ESP8266部分
ESP8266我會從以下三部分介紹:①ArduinoJson解析庫的安裝②串口接收的實現③ArduinoJson庫實現數據解析④ArduinoJson庫實現數據的打包發送
JSON解析庫的安裝
本篇博客中使用的JSON庫爲ArduinoJson
,版本是V5版本,目前最新的是V6版本,大家可以選擇V6版本安裝,只需看下示例,修改下我分享的工程即可。
串口接收的實現
在ESP8266程序中需要將usartEvent();
函數放到loop()
函數中。
/*串口數據接收*/
void usartEvent(){
comdata = "";
while (Serial.available())//時刻讀取硬件串口數據
{
comdata = Serial.readStringUntil('\n');//從串口緩存區讀取字符到一個字符串型變量,直至讀完或遇到某終止字符。
UserData(comdata);//進行JOSN數據解析
}
while (Serial.read() >= 0){
}//清除串口緩存
}
ArduinoJson庫實現數據解析
/*數據解析{status:true}*/
void UserData(String content){
StaticJsonDocument<200> doc;//申請JSON解析空間
DeserializationError error = deserializeJson(doc,content);
if (error) {
//解析錯誤
Serial.print(F("deserializeJson() failed: "));
return;
}
status= doc["status"];
}
ArduinoJson庫實現數據的打包發送
參照ArduinoJson庫的JsonGeneratorExample
工程即可得到以下代碼,當然也可採用serial.println()
函數進行格式化輸出。
StaticJsonDocument<200> doc;
doc["sensor"] = "gps";
doc["time"] = 1351824120;
serializeJsonPretty(doc, Serial);
STM32部分
STM32同ESP8266一樣,我會從以下四部分介紹:①JSON解析庫的安裝②串口接收的實現③JSON庫實現數據解析④JSON數據的打包發送
JSON解析庫的安裝
STM32上我採用的JSON解析庫是Jansson
,我會給大家提供這個庫的pack包,大家自動安裝即可。
串口接收的實現
STM32的串口中斷我才用的是串口空閒中斷,空閒中斷是接受數據後出現一個byte的高電平(空閒)狀態,就會觸發空閒中斷。
代碼實現如下:
需要定義的全局變量:u8 buf1_size = 0;//串口數據接收數量標記 bool data_change = 0;//串口接收完成/變化標誌位
void USART1_printf (char *fmt, ...){
char buffer[USART1_REC_LEN+1]; // 數據長度
u8 i = 0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buffer, USART1_REC_LEN+1, fmt, arg_ptr);
while ((i < USART1_REC_LEN) && (i < strlen(buffer))){
USART_SendData(USART1, (u8) buffer[i++]);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
va_end(arg_ptr);
}
void USART1_Init(u32 bound){
//串口1初始化
//GPIO端口設置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓輸出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶佔優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
//USART 初始化設置
USART_InitStructure.USART_BaudRate = bound;//一般設置爲9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長爲8位數據格式
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_Rx | USART_Mode_Tx; //收發模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啓ENABLE/關閉DISABLE串口接收中斷
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//開啓串口空閒中斷
USART_Cmd(USART1, ENABLE); //使能串口
}
void USART1_IRQHandler(void){
//串口1中斷服務程序(固定的函數名不能修改)
u8 clear = clear;
USART_ClearFlag(USART1,USART_FLAG_TC);
if(USART_GetITStatus(USART1,USART_IT_RXNE)!=Bit_RESET)//串口中斷髮生
{
if(data_change == 0)//重新接收
{
memset(USART1_RX_BUF,0,sizeof(USART1_RX_BUF));//清空整個接收數組
data_change = 1;//標誌位拉高
}
USART1_RX_BUF[buf1_size++]=USART1->DR;
}
else if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)!=Bit_RESET)//空閒中斷髮生
{
buf1_size = 0;
data_change = 0;//標誌位拉低,下次數據改變進入
data_sys = 1;//允許解析
clear = USART1->SR;//空閒中斷要讀這兩個寄存器
clear = USART1->DR;
USART_ClearITPendingBit(USART1,USART_IT_IDLE);//清除空閒中斷標誌位
}
}
JSON庫實現數據解析
JSON庫解析需要調用頭文件#include <jansson.h>
,bool類型變量使用需要#include "stdbool.h"
頭文件。
並且非常重要的是,在對大量數據進行解析的時候,需要將startup_stm32f10x_md.s
33的Stack_Size EQU 0x00000200
修改爲Stack_Size EQU 0x00000C00
,這一步驟是將STM32的堆棧增加,防止在解析的時候出現堆棧不夠用的情況。
#include <jansson.h>
bool led_status;
//開關燈JSON函數{"status":true}
//存在問題!!使用本函數解析後串口printf無法使用,建議使用USART1_printf函數實現發送
/**
* @brief Json解析函數
* @param none
* @return
* - 0 轉換成功
* - 1 轉換失敗
* @details
*/
uint8_t Jansson_Analysis(char *text)
{
json_error_t error;
json_t *root;
root = json_loads((const char*)text, 0, &error);
if(json_is_object(root))
{
status = json_object_get(root, "status");
if(json_is_true(status))
led_status = 1;
else if(json_is_false(status))
led_status = 0;
}
else
{
USART1_printf("root format error:%d-%s\r\n", error.line, error.text);
return 1;
}
json_decref(root);//釋放JSON空間
return 0;
}
/*
// string 類型的解析
name = (char *)json_string_value(json_object_get(root, "name"));
// int 類型的解析
age = json_integer_value(json_object_get(root, "age"));
// double 類型的解析
score = json_real_value(json_object_get(root, "score"));
// bool 類型的解析
status = json_object_get(root, "status");
if(json_is_true(status))
else if(json_is_false(status))
*/
JSON數據的打包發送
Jansson
包提供了一個json數據打包的函數,但此函數在打包過程中會佔用極大的片內空間,因此在這裏僅介紹給大家,不推薦大家使用,推薦大家使用的方法還是使用printf
函數進行格式化輸出。
/**
* @brief 將數據打包爲Json格式
* @param [in] status
* @return none
* @details
*/
void jansson_pack(bool state)
{
json_t *root;
char *out;
/* Build the JSON object */
root = json_pack("{sb}","status",status);
out = json_dumps(root, JSON_ENCODE_ANY);
printf("%s",out);
json_decref(root);//釋放JSON空間
free(out);//釋放JSON空間
}
/*
s 代表string類型
b 代表bool類型
d 代表int類型
f 代表float、double類型
*/
使用printf
格式化輸出只需要:
printf("{\"status\":%d}",status);
需要注意的是在使用格式化輸出函數輸出JSON數據時需要自己進行JSON格式的校驗和轉義。
推薦大家使用JSON在線視圖查看器.進行格式查驗和轉義。