前言
由于在STM32巡回研讨会上淘了一块EC600E4G模块以及刚办完电信卡多了两张副卡,副卡有流量刚好可以用一下,试想着以后画一块ESP32板子搭配这个4G模块做个随身WIFI,目前先用这个模块搭配STM32玩一下云平顺便记录一下。
实验目的
实现STM32通过4G模块与华为云进行通信,完成简单的LED控制。
硬件准备
材料清单
主控芯片
STM32F103ZET6 V3战舰
通信模块
移远的 EC600E
SIM卡
电信卡(支持4G网络)
天线 4G天线
模块原装
杜邦线
若干
硬件连接示意图
本人使用了串口2,串口可自行随意选择
STM32F103ZET6 <---> EC600E
PA2(TX) <-----> RX
PA3(RX) <-----> TX
5V <-----> VCC
GND <-----> GND
实验思路与步骤
- 可以现将4G模块接TTL与电脑进行测试,使用串口助手等工具检测是否通信成功(发送AT 看是否能收到OK)
- 尝试将4G模块与STM32串口连接,使用STM32发送AT指令是否能接收成功
- 依据EC600E指导手册查找配置相关的AT指令,逐步配置4G模块
- 找一个云平台注册账号并创建服务以及设备,记录下自己的mqtt设备接入地址、以及mqtt连接参数(三元组)
- 在stm32上编写代码解析云平台下发的数据信息以及完成指定动作(开、关LED)
代码实现
串口通信配置
本人使用的是串口2,采用串口空闲中断+DMA+定时器超时判断;由于参考其他博主,了解到4G模块以及wifi模块接收数据不是特别连续以及会超时,担心空闲中断+ dma会导致数据丢失故再加了个定时器做超时判断;(目前工作上使用的GD32,好像GD32的某款中串口的空闲中断可以配置超时时间,没去试过,感觉还是挺方便的)
bsp_usart.c
#include "string.h"
#include "bsp_usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "comm_drv.h"
uint8_t uart2Recbuffer1[UART_RX_BUFFER_SIZE];
uint8_t uart2Recbuffer2[UART_RX_BUFFER_SIZE];
uint8_t *pActiveBuf = uart2Recbuffer1;
uint8_t *pReadyBuf = uart2Recbuffer2;
uint16_t g_ec600RecLen = 0;
uint16_t g_uart2RecStartFlag = 0;
extern SemaphoreHandle_t EC600RecSem;
static void Usart1Config(int baudRate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = baudRate;
// 配置 针数据字长
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_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
static void Usart2Config(int baudRate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
DMA_InitTypeDef DMA_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(USART_4G_MODULE_GPIO_CLK, ENABLE);
RCC_APB1PeriphClockCmd(USART_4G_MODULE_CLK, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* 配置串口的 NVIC设置*/
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART_4G_MODULE_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(USART_4G_MODULE_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART_4G_MODULE_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(USART_4G_MODULE_RX_GPIO_PORT, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = baudRate;
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_Rx | USART_Mode_Tx;
USART_Init(USART_4G_MODULE, &USART_InitStructure);
// 配置DMA1通道6(USART2_RX)
DMA_DeInit(DMA1_Channel6);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)pActiveBuf;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = UART_RX_BUFFER_SIZE;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; // 循环模式
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel6, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel6, ENABLE);
// 使能USART2 DMA接收和空闲中断
USART_DMACmd(USART_4G_MODULE, USART_DMAReq_Rx, ENABLE);
USART_ITConfig(USART_4G_MODULE, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(USART_4G_MODULE, ENABLE);
}
void UsartInit()
{
Usart1Config(DEBUG_USART_BAUDRATE);
Usart2Config(USART_4G_MODULE_BAUDRATE);
}
void GetUsart2Data()
{
BaseType_t xHigherPriorityTaskWoken;
DMA_Cmd(DMA1_Channel6, DISABLE);
DMA_ClearFlag(DMA1_FLAG_GL3 | DMA1_FLAG_TC3 | DMA1_FLAG_HT3 | DMA1_FLAG_TE3);
g_ec600RecLen = UART_RX_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6);
if (g_ec600RecLen > 0)
{
pReadyBuf = pActiveBuf;
}
DMA_SetCurrDataCounter(DMA1_Channel6, UART_RX_BUFFER_SIZE);
DMA_Cmd(DMA1_Channel6, ENABLE);
xSemaphoreGiveFromISR(EC600RecSem,&xHigherPriorityTaskWoken);
}
void Usart2SendStr(char *str)
{
int i = 0;
do
{
Usart_SendByte(USART_4G_MODULE, *(str + i));
i++;
} while(*(str + i)!='\0');
while(USART_GetFlagStatus(USART_4G_MODULE,USART_FLAG_TC)==RESET);
}
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
USART_SendData(pUSARTx,ch);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
}
// USART2空闲中断处理
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART_4G_MODULE, USART_IT_IDLE) != RESET)
{
if (!g_uart2RecStartFlag)
{
USART_ReceiveData(USART_4G_MODULE);
TIM_Cmd(TIM2, ENABLE);
}
else
{
USART_ReceiveData(USART_4G_MODULE);
TIM_SetCounter(TIM2, 0);
}
}
USART_ClearITPendingBit(USART2, USART_IT_IDLE);
}
bsp_usart.h
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include <stdio.h>
#define UART_RX_BUFFER_SIZE 256
// 串口1-USART1
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler
// 串口2-USART2
#define USART_4G_MODULE USART2
#define USART_4G_MODULE_CLK RCC_APB1Periph_USART2
#define USART_4G_MODULE_APBxClkCmd RCC_APB1PeriphClockCmd
#define USART_4G_MODULE_BAUDRATE 115200
#define USART_4G_MODULE_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define USART_4G_MODULE_TX_GPIO_PORT GPIOA
#define USART_4G_MODULE_TX_GPIO_PIN GPIO_Pin_2
#define USART_4G_MODULE_RX_GPIO_PORT GPIOA
#define USART_4G_MODULE_RX_GPIO_PIN GPIO_Pin_3
#define USART_IRQ USART2_IRQn
#define USART_IRQHandler USART2_IRQHandler
void UsartInit(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);
void Usart2SendStr(char *str);
void GetUsart2Data(void);
extern uint16_t g_ec600RecLen;
extern uint8_t *pReadyBuf;
#endif /* __USART_H */
采用定时器2做超时判断(10ms)
#include "bsp_timer.h"
#include "bsp_usart.h"
static void Timer2Config(uint16_t pre ,int arr)
{
TIM_TimeBaseInitTypeDef TIM_InitStruct = {0};
NVIC_InitTypeDef NVIC_InitStruct = {0};
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InitStruct.TIM_Prescaler = pre - 1;
TIM_InitStruct.TIM_Period = arr - 1;
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 7;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, DISABLE);
}
void TimerInit()
{
//10ms
Timer2Config(72,10000);
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
GetUsart2Data();
TIM_Cmd(TIM2, DISABLE);
}
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
}
AT指令配置
具体可参考 EC600E-CN&EC800E-CN AT 命令手册
AT
握手测试 正常返回:OK
AT<value>
1开启0关闭,开启关闭回显 正常返回:OK
AT+CPIN?
检测SIM卡是否插入 正常返回:+CPIN: READY OK
AT+CREG?
查询网络的注册状态 正常返回: +CREG: 1,1 OK
AT+CGREG?\r\n
查询PS状态, 正常返回: +CGREG: 0,1 OK
云平台参数获取
本人查找了腾讯云以及阿里云,里面的物联网平台都不免费使用了,所以最终选择了华为云,能白嫖就白嫖嘛。具体创建的步骤可参考其他博文。网上有很多。
- 获取mqtt设备接入地址
- 获取三元组
代码
typedef enum
{
EC600E_INIT_SUCCESS = 0,
EC600E_AT_ERROR,
EC600E_ATE_ERROR,
EC600E_CPIN_ERROR,
EC600E_CREG_ERROR,
EC600E_CGREG_ERROR,
EC600E_QMTOPEN_ERRORAT,
EC600E_QMTCONN_ERRORAT,
}EC600E_AT_ACK_STATUS;
typedef enum
{
AT_TEST = 0,
AT_ATE,
AT_CPIN,
AT_CREG,
AT_CGREG,
AT_QMTOPEN,
AT_QMTCONN,
}EC600E_AT_CMD;
typedef struct {
/* 要发送的AT命令 */
char *cmd;
/* 期望的应答数据,默认处理匹配到该字符串认为命令执行成功 */
char *rsp;
/* 得到应答的超时时间,达到超时时间为执行失败,单位ms*/
uint32_t timeoutMs;
} AtCmdInfo_t;
char g_mqttUrl[] = {"自己的mqttUrl"};
char g_mqttClientId[] = {"自己的mqttClientId"};
char g_mqttUserName[] = {"自己的mqttUserName"};
char g_mqttPassword[] = {"自己的mqttPasswordl"};
static AtCmdInfo_t g_connectMqttCmdTable[] = {
{
.cmd = "AT\r\n",
.rsp = "OK",
.timeoutMs = 500,
},
{
.cmd = "ATE0\r\n",
.rsp = "OK",
.timeoutMs = 500,
},
{
.cmd = "AT+CPIN?\r\n",
.rsp = "+CPIN: READY",
.timeoutMs = 500,
},
{
.cmd = "AT+CREG?\r\n",
.rsp = "+CREG: 0,1",
.timeoutMs = 500,
},
{
.cmd = "AT+CGREG?\r\n",
.rsp = "+CGREG: 0,1",
.timeoutMs = 500,
},
{
.cmd = "AT+QMTOPEN=0,\"%s\",1883\r\n",
.rsp = "OK",
.timeoutMs = 2000,
},
{
.cmd = "AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"\r\n",
.rsp = "OK",
.timeoutMs = 2000,
},
};
/***************************************************************
* @brief 发送数据至云平台
* @param
* @return
* @note
* @Sample usage:
**************************************************************/
void SendDataToCloud(char *data)
{
Usart2SendStr(data);
}
/***************************************************************
* @brief 发送AT指令并解析ACK
* @param
* @return
* @note
* @Sample usage:
**************************************************************/
static bool SendATCommand(char* cmd,char *ack,uint16_t timeOut)
{
BaseType_t err;
bool ret = true;
Usart2SendStr(cmd);
err = xSemaphoreTake(EC600RecSem,timeOut);
if (err == pdTRUE)
{
if(strstr((char*)pReadyBuf, ack))
{
printf("rec ok\r\n");
ret = true;
}
else if(strstr((char*)pReadyBuf, "ERROR"))
{
printf("rec error\r\n");
ret = false;
}
else
{
printf("rec --------\r\n");
ret = false;
}
}
else
{
printf("rec time out\r\n");
ret = false;
}
return ret;
}
/***************************************************************
* @brief EC600E模块初始化
* @param
* @return
* @note
* @Sample usage:
**************************************************************/
EC600E_AT_ACK_STATUS EC600EInit(void)
{
char cmdStrBuf[256] = {0};
EC600JsonMutex = xSemaphoreCreateMutex();
//测试AT通信
if(!SendATCommand(g_connectMqttCmdTable[AT_TEST].cmd,g_connectMqttCmdTable[AT_TEST].rsp,g_connectMqttCmdTable[AT_TEST].timeoutMs))
{
return EC600E_AT_ERROR;
}
vTaskDelay(1000);
//关闭回显
if(!SendATCommand(g_connectMqttCmdTable[AT_ATE].cmd,g_connectMqttCmdTable[AT_ATE].rsp,g_connectMqttCmdTable[AT_ATE].timeoutMs))
{
return EC600E_ATE_ERROR;
}
vTaskDelay(1000);
// 查询SIM卡是否正常,返回ready则表示SIM卡正常
if(!SendATCommand(g_connectMqttCmdTable[AT_CREG].cmd,g_connectMqttCmdTable[AT_CREG].rsp,g_connectMqttCmdTable[AT_CREG].timeoutMs))
{
return EC600E_CPIN_ERROR;
}
vTaskDelay(1000);
// 查询模组是否注册上GSM网络
if(!SendATCommand(g_connectMqttCmdTable[AT_CGREG].cmd,g_connectMqttCmdTable[AT_CGREG].rsp,g_connectMqttCmdTable[AT_CGREG].timeoutMs))
{
return EC600E_CREG_ERROR;
}
vTaskDelay(1000);
sprintf(cmdStrBuf,g_connectMqttCmdTable[AT_QMTOPEN].cmd,g_mqttUrl);
if(!SendATCommand(cmdStrBuf,g_connectMqttCmdTable[AT_QMTOPEN].rsp,g_connectMqttCmdTable[AT_QMTOPEN].timeoutMs))
{
return EC600E_QMTOPEN_ERRORAT;
}
vTaskDelay(1000);
sprintf(cmdStrBuf,g_connectMqttCmdTable[AT_QMTCONN].cmd,g_mqttClientId,g_mqttUserName,g_mqttPassword);
if(!SendATCommand(cmdStrBuf,g_connectMqttCmdTable[AT_QMTCONN].rsp,g_connectMqttCmdTable[AT_QMTCONN].timeoutMs))
{
return EC600E_QMTCONN_ERRORAT;
}
return EC600E_INIT_SUCCESS;
}
经过以上配置4G模块就能连上云平台了,但是还未实现测试功能,需在云平台配置好相关指令下发,我这边是下发LED灯开关指令,指定value来确定LED亮灭
通过LedStatusChange这个值来决定LED亮灭;
其中下发的数据格式是json形式:
{"service_id":"connectTest","command_name":"LedControl","paras":{"LedStatusChange":1}}
可移植CJson库来解析json数据以及发送,网上stm32移植CJson库文章很多,可自行参考。注意(由于本人工程带Freertos,需要将cjson初始化函数内malloc free 等函数改成freertos的api)
数据解析:本人采用了简单的方式就是找到第一个‘{’然后将后面的数据使用Cjson的相关API进行解析,经供参考,建议优化,按个人需求来!!!
void ParseCloudCommand(const char *json_str)
{
cJSON *root = cJSON_Parse(json_str);
if (!root)
{
printf("Parse failed: %s\n", cJSON_GetErrorPtr());
return;
}
cJSON *paras1 = cJSON_GetObjectItem(root, "paras");
// 检查服务ID和命令名称
cJSON *service_id = cJSON_GetObjectItem(root, "service_id");
cJSON *command_name = cJSON_GetObjectItem(root, "command_name");
if (!cJSON_IsString(service_id) || !cJSON_IsString(command_name))
{
cJSON_Delete(root);
return;
}
// 确认是目标服务
if (strcmp(service_id->valuestring, "connectTest") != 0)
{
cJSON_Delete(root);
return;
}
// 处理命令
if (strcmp(command_name->valuestring, "LedControl") == 0)
{
cJSON *paras = cJSON_GetObjectItem(root, "paras");
if (paras && cJSON_IsObject(paras))
{
cJSON *led_status = cJSON_GetObjectItem(paras, "LedStatusChange");
if (cJSON_IsNumber(led_status))
{
int status = led_status->valueint;
// 控制LED
if (status)
{
LED0 = 1;
}
else
{
LED0 = 0;
}
}
}
}
cJSON_Delete(root);
}
// 生成成功响应
void send_success_response(int current_status)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "service_id", "connectTest");
cJSON_AddStringToObject(root, "command_name", "LedControl");
cJSON_AddStringToObject(root, "response_name", "LedControl_ACK");
cJSON_AddNumberToObject(root, "result_code", 200);
cJSON *paras = cJSON_CreateObject();
cJSON_AddNumberToObject(paras, "LedStatus", current_status);
cJSON_AddItemToObject(root, "paras", paras);
char *json_str = cJSON_PrintUnformatted(root);
SendDataToCloud(json_str); // 通过4G模块发送
cJSON_Delete(root);
vPortFree(json_str);
}
// 生成错误响应
void send_error_response(const char *error_code)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "service_id", "connectTest");
cJSON_AddStringToObject(root, "command_name", "LedControl");
cJSON_AddStringToObject(root, "response_name", "LedControl_ACK");
cJSON_AddNumberToObject(root, "result_code", 500);
cJSON *paras = cJSON_CreateObject();
cJSON_AddStringToObject(paras, "error_detail", error_code);
cJSON_AddItemToObject(root, "paras", paras);
char *json_str = cJSON_PrintUnformatted(root);
SendDataToCloud(json_str);
cJSON_Delete(root);
vPortFree(json_str);
}
void EC600RecTask(void *parameter)
{
uint8_t tempBuf[256];
while (1)
{
if (g_startRec)
{
xSemaphoreTake(EC600RecSem,portMAX_DELAY);
xSemaphoreTake(EC600JsonMutex, portMAX_DELAY);
memcpy(tempBuf,pReadyBuf,sizeof(tempBuf));
xSemaphoreGive(EC600JsonMutex);
char *json_start = strchr((char *)tempBuf, '{');
if (json_start != NULL)
{
// 去除可能的引号或空格
ParseCloudCommand(json_start);
}
}
vTaskDelay(500);
}
}
总结
通过以上代码就能初步实现云平台通过4G模块控制STM32LED灯亮灭,可继续进行拓展。
优化点:
- 优化4G模块初始配置,考虑AT指令失败后续处理
- 优化云平台下发数据处理,
- 可实现接收云平台数据后做响应数据,
- 可实现灯状态主动上报
- ……
代码水平不足,上述可能有表达错误, 代码仅供参考!!!!