STM32+EC600E 4G模块 与华为云平台通信
前言
由于在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_IRQHandlervoid 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()
{//10msTimer2Config(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
握手测试 正常返回:OKAT<value>
1开启0关闭,开启关闭回显 正常返回:OKAT+CPIN?
检测SIM卡是否插入 正常返回:+CPIN: READY OKAT+CREG?
查询网络的注册状态 正常返回: +CREG: 1,1 OKAT+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;// 控制LEDif (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指令失败后续处理
- 优化云平台下发数据处理,
- 可实现接收云平台数据后做响应数据,
- 可实现灯状态主动上报
- ……
代码水平不足,上述可能有表达错误, 代码仅供参考!!!!
相关文章:
STM32+EC600E 4G模块 与华为云平台通信
前言 由于在STM32巡回研讨会上淘了一块EC600E4G模块以及刚办完电信卡多了两张副卡,副卡有流量刚好可以用一下,试想着以后画一块ESP32板子搭配这个4G模块做个随身WIFI,目前先用这个模块搭配STM32玩一下云平顺便记录一下。 实验目的 实现STM…...
进行性核上性麻痹患者,饮食 “稳” 健康
进行性核上性麻痹作为一种复杂且罕见的神经系统退行性疾病,给患者的身体机能和日常生活带来严重挑战。在积极接受专业治疗的同时,合理的饮食安排对于维持患者营养状况、缓解症状及提升生活质量起着关键作用。以下为患者提供一些健康饮食建议。 首先&…...
【数据结构 · 初阶】- 顺序表
目录 一、线性表 二、顺序表 1.实现动态顺序表 SeqList.h SeqList.c Test.c 问题 经验:free 出问题,2种可能性 解决问题 (2)尾删 (3)头插,头删 (4)在 pos 位…...
NHANES指标推荐:aMED
文章题目:The moderating effect of alternate Mediterranean diet on the association between sedentary behavior and insomnia in postmenopausal women DOI:10.3389/fnut.2024.1516334 中文标题:替代性地中海饮食对绝经后女性久坐行为与…...
ngx_cycle_modules
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_cycle_modules-CSDN博客 定义在 src/core/ngx_module.c ngx_int_t ngx_cycle_modules(ngx_cycle_t *cycle) {/** create a list of modules to be used for this cycle,* copy static modules to it*/cycle->modules ngx_pcalloc(…...
Spring Cloud 远程调用
4.OpenFeign的实现原理是什么? 在使用OpenFeign的时候,主要关心两个注解,EnableFeignClients和FeignClient。整体的流程分为以下几个部分: 启用Feign代理,通过在启动类上添加EnableFeignClients注解,开启F…...
YOLO学习笔记 | YOLOv8环境搭建全流程指南(2025.4)
===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== YOLOv8环境搭建 一、环境准备与工具配置1. Conda虚拟环境搭建2. CUDA与…...
使用Apache POI(Java)创建docx文档和表格
1、引入poi 依赖组件 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.0.0</version> </dependency> <dependency><groupId>org.apache.poi</groupId>&…...
力扣 — — 最长公共子序列
力扣 — — 最长公共子序列 最长公共子序列 题源:1143. 最长公共子序列 - 力扣(LeetCode) 题目: 分析: 一道经典的题目:最长公共子序列(LCS) 题目大意:求两个字符串的最长公共序列。 算法&…...
当一个 HTTP 请求发往 Kubernetes(K8s)部署的微服务时,整个过程流转时怎样的?
以下是一个简单的示意图来展示这个过程: 1. 请求发起 客户端(可以是浏览器、移动应用或者其他服务)发起一个 HTTP 请求到目标微服务的地址。这个地址可以是服务的域名、IP 地址或者 Kubernetes 服务的 ClusterIP、NodePort 等。 2. 外部流量…...
C#核心学习(十五)面向对象--关联知识点(1)命名空间
目录 一、命名空间基本概念:代码的"虚拟文件夹" 二 、命名空间的普通使用 三 、不同命名空间中相互使用 需要引用命名空间或指明出处 四、命名空间可以包裹命名空间(嵌套命名空间使用) 五、 关于修饰类的访问修饰符 一、命名空…...
淘宝商品数据实时抓取 API 开发指南:从接口申请到数据解析实战
一、引言 在当今电商蓬勃发展的时代,淘宝作为国内电商巨头,其平台上汇聚了海量商品信息。对于电商从业者、数据分析爱好者以及众多依赖淘宝商品数据开展业务的企业而言,能够实时获取淘宝商品数据具有极高价值。例如,电商运营者…...
【嵌入式硬件】LAN9253说明书(中文版)
目录 1.介绍 1.1总体介绍 1.2模式介绍 1.2.1微控制器模式: 1.2.2 扩展模式 1.2.3 数字IO模式 1.2.4 各模式图 2.引脚说明 2.1 引脚总览 2.2 引脚描述 2.2.1 LAN端口A引脚 2.2.2 LAN端口B引脚 2.2.3 LAN端口A和、B电源和公共引脚 2.2.4 SPI/SQI PINS 2.2.5 分布式时…...
蓝桥杯-蓝桥幼儿园(Java-并查集)
并查集的核心思想 并查集主要由两个操作构成: Find:查找某个元素所在集合的根节点。并查集的特点是,每个元素都指向它自己的父节点,根节点的父节点指向它自己。查找过程中可以通过路径压缩来加速后续的查找操作,即将路…...
C++蓝桥杯填空题(攻克版)
片头 嗨~小伙伴们,咱们继续攻克填空题,先把5分拿到手~ 第1题 数位递增的数 这道题,需要我们计算在整数 1 至 n 中有多少个数位递增的数。 什么是数位递增的数呢?一个正整数如果任何一个数位不大于右边相邻的数位。比如…...
JS 构造函数实现封装性
通过构造函数实现封装性,构造函数生成的对象独立存在互不影响 创建实例对象时,其中函数的创建会浪费内存...
以太网供电(PoE)交换机:为音频和视频系统赋能的多面利器
近年来,物联网(IoT)视频设备的普及浪潮正以稳健的步伐持续推进。诸如摄像机、支持视频功能的办公自动化系统等物联网视频设备,凭借其远程会议支持、安全性强化以及便捷性提升等诸多优势,赢得了市场的广泛青睐。以太联Intellinet,作…...
《深度剖析分布式软总线:软时钟与时间同步机制探秘》
在分布式系统不断发展的进程中,设备间的协同合作变得愈发紧密和复杂。为了确保各个设备在协同工作时能够有条不紊地进行,就像一场精准的交响乐演出,每个乐器都要在正确的时间奏响音符,分布式软总线中的软时钟与时间同步机制应运而…...
一站式云分账系统!智能虚拟户分账系统成电商合规“刚需”
电商智能分账解决:电商一站式破解多平台资金管理难题集中管理分账,分账后秒到,并为针对电商行业三大核心痛点提供高效应对策略: 1. 票据合规困境 智能对接上下游交易数据流,构建自动化票据协同机制,有效规…...
服务器加空间失败 growpart /dev/vda 1
[rootecm-2c5 ~]# growpart /dev/vda 1 unexpected output in sfdisk --version [sfdisk,来自 util-linux 2.23.2] [rootecm-2c5 ~]# xfs_info /dev/vda1 meta-data/dev/vda1 isize512 agcount21, agsize1310656 blks sectsz512 attr2, projid32bit1 crc1 finobt0…...
慢查询解决思路
1. 复现问题 慢查询的出现是常态还是偶尔?是否在业务允许范围内? "不要过早优化,先 Make it work / right,再 Make it fast。" 建议先将查询语句及其触发条件记录下来,便于后续测试、分析和对比。 2. 定位问题 2.1 单机数据库: explain查询执行计划 数据库默…...
数组 array
1、数组定义 是一种用于存储多个相同类型数据的存储模型。 2、数组格式 (1)数据类型[ ] 变量名(比较常见这种格式) 例如: int [ ] arr0,定义了一个int类型的数组,数组名是arr0; &am…...
linux命令六
逻辑卷 作用: 整合分散空间 空间支持扩大 步骤一:建立卷组(VG) 格式:vgcreate 卷组名 设备路径……. 如果分区不是卷组,则会先调用pvcreat 组建物理卷,再将其组建成组卷 Successfully:成功 example:例子 在man帮助中可以使用examp…...
深度学习总结(8)
模型工作流程 模型由许多层链接在一起组成,并将输入数据映射为预测值。随后,损失函数将这些预测值与目标值进行比较,得到一个损失值,用于衡量模型预测值与预期结果之间的匹配程度。优化器将利用这个损失值来更新模型权重。 下面是…...
基于docker搭建redis集群环境
在redis目录下创建redis-cluster目录,创建docker-compose.yml文化和generate.sh文件 【配置generate.sh文件】 for port in $(seq 1 9); \ do \ mkdir -p redis${port}/ touch redis${port}/redis.conf cat << EOF > redis${port}/redis.conf port 6379 …...
OpenHarmony 5.0版本视频硬件编解码适配
一、简介 Codec HDI(Hardware Device Interface)对上层媒体服务提供视频编解码的驱动能力接口,主要功能有获取组件编解码能力,创建、销毁编解码器对象,启停编解码器操作,编解码处理等。 Codec HDI 2.0接口…...
deepseek热度已过?
DeepSeek的热度并没有消退,以下是具体表现: 用户使用量和下载量方面 • 日活跃用户量增长:DeepSeek已经成为目前最快突破3000万日活跃用户量的应用程序。 • 应用商店下载量:1月26日,DeepSeek最新推出的AI聊天机器人…...
分治-归并系列一>翻转对
目录 题目:解析:策略一: 代码:策略二: 代码: 题目: 链接: link 这题和逆序对区别点就是,要找到前一个元素是后一个元素的2倍 先找到目标值再,继续堆排序 解析࿱…...
FFMPEG大文件视频分割传输教程,微信不支持1G文件以上
如下是一个2.77g的文件分割教程 . 前言 FFmpeg 是一个用于处理视频、音频等多媒体文件的开源工具包。它支持几乎所有的多媒体格式转换、剪辑和编辑,是开发者和多媒体工作者必备的工具。本文详细讲解如何在 Windows 系统上安装 FFmpeg 并进行基本配置。 2. 下载 FF…...
从数据海洋中“淘金”——数据挖掘的魔法与实践
从数据海洋中“淘金”——数据挖掘的魔法与实践 在这个数据飞速膨胀的时代,每天产生的数据量可以用“天文数字”来形容。如果将数据比作金矿,那么数据挖掘(Data Mining)就是在数据的海洋中挖掘黄金的技术。作为一门结合统计学、机…...
