ESP32 UART select解析json数据,上位机控制LED灯实验
前言:
本实验的目的主要是通过上位机通过UART来控制ESP32端的LED的点亮以及熄灭,整个项目逻辑比较简单,整体架构如下:
上位机(PC)主要是跑在PC端的一个软件,主要作用包含:
1)串口相关配置,串口号,波特率等配置,串口动态识别,打开,关闭串口等
2)发送json指令来控制ESP32 LED等点亮跟熄灭
3)显示ESP32吐出来的log
下位机(ESP32)主要就是ESP32开发板,主要包含作用如下:
1)LED灯的驱动
2)UART select监听上位机发送的指令,从而来控制LED灯的点亮/熄灭
3)发送log到上位机
整个工程链接:esp32_study/2_peripheral/4_pc_control_led at main · sj15712795029/esp32_study · GitHub
一. ESP32下位机端
1. LED、UART select方式
LED驱动我们在前面已经介绍,链接如下:一灯大师,点亮ESP32的LED_esp32点亮led-CSDN博客
UART select方式我们在前面已经介绍,链接如下:
详解ESP32使用select函数来监听串口数据-CSDN博客
2. json基础
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript的一个子集,但独立于语言,广泛用于Web应用中的数据交换。
a. JSON的基本结构
JSON数据由键值对组成,主要有两种结构:
对象(Object):
-
- 用花括号
{}
包裹,表示一个无序的键值对集合。 - 键值对之间用逗号
,
分隔。 - 键和值之间用冒号
:
分隔。 - 键必须是字符串,值可以是字符串、数字、布尔值、数组、对象或
null
。
- 用花括号
示例:
{"name": "Alice","age": 25,"isStudent": false,"address": {"city": "Beijing","postalCode": "100000"},"hobbies": ["reading", "traveling"]
}
数组(Array):
-
- 用方括号
[]
包裹,表示一个有序的值集合。 - 值之间用逗号
,
分隔。 - 值可以是字符串、数字、布尔值、数组、对象或
null
。
- 用方括号
示例:
[{"name": "Alice","age": 25},{"name": "Bob","age": 30}
]
b. JSON的数据类型
JSON支持以下几种数据类型:
- 字符串(String):用双引号
""
包裹的文本。 - 数字(Number):整数或浮点数。
- 布尔值(Boolean):
true
或false
。 - 数组(Array):有序的值集合。
- 对象(Object):无序的键值对集合。
- null:表示空值。
c. JSON的示例
以下是一个包含多种数据类型的JSON示例:
{"name": "Alice","age": 25,"isStudent": false,"scores": [95, 89, 78],"contact": {"email": "alice@example.com","phone": "123-456-7890"},"graduated": null
}
d. JSON的特点
- 简洁:JSON格式简洁,易于阅读和编写。
- 跨语言:JSON独立于编程语言,几乎所有主流语言都支持JSON的解析和生成。
- 轻量级:JSON数据格式比XML更轻量,适合网络传输。
e. JSON的使用场景
- Web API:JSON常用于Web API的数据交换格式。
- 配置文件:许多应用程序使用JSON格式来存储配置信息。
- 数据存储:NoSQL数据库(如MongoDB)使用JSON格式存储数据。
3. json编程
在了解了json的基础后(其实很简单哈,就是格式),我们首先要做的第一件事情就是约定下上位机跟下位机的通讯格式,我自己定出来一个哈·,如下:
以LED_ON为例,发送的格式如下:
{"FUNC":"HW","OPERATE":"LED_ON","PARAM1":null,"PARAM2":null,"PARAM3":null,"PARAM4":null,"PARAM5":null,"PARAM6":null}
后面的PARAM是为了其他硬件功能的扩展,我们在LED灯中没有用到
在ESP IDF中已经集成了json的库,就是cJson,只需要引用这个头文件即可以使用其中的API
#include "cJSON.h"
我们先来看下核心代码
/* 解析上位机的json格式 */cJSON* parse_json = cJSON_Parse((const char *)shell_string);if(!parse_json){ESP_LOGE(TAG, "Not specific json format:%s\n",shell_string);goto exit;}uint8_t* func_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"FUNC"))->valuestring;uint8_t* operate_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"OPERATE"))->valuestring;uint8_t* para1 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM1"))->valuestring;uint8_t* para2 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM2"))->valuestring;uint8_t* para3 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM3"))->valuestring;uint8_t* para4 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM4"))->valuestring;uint8_t* para5 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM5"))->valuestring;uint8_t* para6 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM6"))->valuestring;ESP_UNUSED(para1);ESP_UNUSED(para2);ESP_UNUSED(para3);ESP_UNUSED(para4);ESP_UNUSED(para5);ESP_UNUSED(para6);if(!func_value || !operate_value){ESP_LOGE(TAG, "Not specific json format:%s\n",shell_string);goto exit;}if(strcmp((const char *)func_value,"HW") == 0){if(strcmp((const char *)operate_value,"LED_ON") == 0){ESP_LOGD(TAG, "UART PARSE DEBUG:operate LED_ON\n");LED_ON;goto exit;}if(strcmp((const char *)operate_value,"LED_OFF") == 0){ESP_LOGD(TAG, "UART PARSE DEBUG:operate LED_OFF\n");LED_OFF;goto exit;}}
这里的核心作用就是解析了json格式,来控制了LED灯的点亮系列,其中部分API的使用如下:
cJSON
是一个轻量级的C语言库,用于解析和生成JSON数据。它提供了一系列函数来操作JSON对象和数组。以下是你代码中使用到的 cJSON
函数及其作用的详细介绍:
cJSON_Parse
cJSON* cJSON_Parse(const char *value);
- 作用:将JSON格式的字符串解析为
cJSON
对象。 - 参数:
-
value
:要解析的JSON字符串。
- 返回值:
-
- 成功时返回一个指向
cJSON
对象的指针。 - 失败时返回
NULL
。
- 成功时返回一个指向
cJSON_GetObjectItem
cJSON* cJSON_GetObjectItem(const cJSON *object, const char *string);
- 作用:从
cJSON
对象中获取指定键对应的值。 - 参数:
-
object
:要查找的cJSON
对象。string
:要查找的键名。
- 返回值:
-
- 成功时返回一个指向
cJSON
对象的指针。 - 如果键不存在或对象不是JSON对象,返回
NULL
。
- 成功时返回一个指向
cJSON
结构体
cJSON
结构体用于表示JSON数据。常用的字段包括:
valuestring
:如果cJSON
对象表示一个字符串,该字段存储字符串的值。valueint
:如果cJSON
对象表示一个整数,该字段存储整数的值。valuedouble
:如果cJSON
对象表示一个浮点数,该字段存储浮点数的值。type
:表示cJSON
对象的类型(如字符串、数字、对象、数组等)。
cJSON_Delete
void cJSON_Delete(cJSON *item);
- 作用:释放
cJSON
对象及其所有子对象的内存。 - 参数:
-
item
:要释放的cJSON
对象。
代码解析
你的代码片段展示了如何使用 cJSON
库解析JSON字符串并提取其中的值。以下是代码的详细解析:
解析JSON字符串:
cJSON* parse_json = cJSON_Parse((const char *)shell_string);
将 shell_string
解析为 cJSON
对象 parse_json
。
提取JSON对象中的值:
uint8_t* func_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"FUNC"))->valuestring;
uint8_t* operate_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"OPERATE"))->valuestring;
uint8_t* para1 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM1"))->valuestring;
uint8_t* para2 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM2"))->valuestring;
uint8_t* para3 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM3"))->valuestring;
uint8_t* para4 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM4"))->valuestring;
uint8_t* para5 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM5"))->valuestring;
uint8_t* para6 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM6"))->valuestring;
使用 cJSON_GetObjectItem
从 parse_json
中提取键为 "FUNC"
、"OPERATE"
、"PARAM1"
到 "PARAM6"
的值,并将其转换为 uint8_t*
类型。
示例JSON
假设 shell_string
包含以下JSON字符串:
{"FUNC": "start","OPERATE": "run","PARAM1": "value1","PARAM2": "value2","PARAM3": "value3","PARAM4": "value4","PARAM5": "value5","PARAM6": "value6"
}
解析后,func_value
将指向 "start"
,operate_value
将指向 "run"
,para1
到 para6
将分别指向 "value1"
到 "value6"
。
所有代码如下(包含LED灯的控制,UART的select解析):
/* UART Select ExampleThis example code is in the Public Domain (or CC0 licensed, at your option.)Unless required by applicable law or agreed to in writing, thissoftware is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES ORCONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <sys/fcntl.h>
#include <sys/errno.h>
#include <sys/unistd.h>
#include <sys/select.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/uart_vfs.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "cJSON.h"#define LED_PIN 17
#define LED_PIN_SEL (1<<LED_PIN)/* Operate LED on/off */
#define LED_OFF gpio_set_level(LED_PIN, 1)
#define LED_ON gpio_set_level(LED_PIN, 0)void bsp_led_init()
{gpio_config_t io_conf;io_conf.intr_type = GPIO_INTR_DISABLE;io_conf.mode = GPIO_MODE_OUTPUT;io_conf.pin_bit_mask = LED_PIN_SEL;io_conf.pull_down_en = 0;io_conf.pull_up_en = 0;gpio_config(&io_conf);
}static const char* TAG = "uart_select_example";#define UART_JSON_BUFFER_SIZE 512
char uart_json_buf[UART_JSON_BUFFER_SIZE] = {0};void uart_receive_parse(uint8_t *shell_string)
{cJSON* parse_json = cJSON_Parse((const char *)shell_string);if(!parse_json){ESP_LOGE(TAG, "Not specific json format:%s\n",shell_string);goto exit;}uint8_t* func_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"FUNC"))->valuestring;uint8_t* operate_value = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"OPERATE"))->valuestring;uint8_t* para1 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM1"))->valuestring;uint8_t* para2 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM2"))->valuestring;uint8_t* para3 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM3"))->valuestring;uint8_t* para4 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM4"))->valuestring;uint8_t* para5 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM5"))->valuestring;uint8_t* para6 = (uint8_t*)((cJSON *)cJSON_GetObjectItem(parse_json,"PARAM6"))->valuestring;ESP_UNUSED(para1);ESP_UNUSED(para2);ESP_UNUSED(para3);ESP_UNUSED(para4);ESP_UNUSED(para5);ESP_UNUSED(para6);if(!func_value || !operate_value){ESP_LOGE(TAG, "Not specific json format:%s\n",shell_string);goto exit;}if(strcmp((const char *)func_value,"HW") == 0){if(strcmp((const char *)operate_value,"LED_ON") == 0){ESP_LOGD(TAG, "UART PARSE DEBUG:operate LED_ON\n");LED_ON;goto exit;}if(strcmp((const char *)operate_value,"LED_OFF") == 0){ESP_LOGD(TAG, "UART PARSE DEBUG:operate LED_OFF\n");LED_OFF;goto exit;}}if(strcmp((const char *)shell_string,"shop220811498.taobao.com") == 0)ESP_LOGD(TAG, "welcome to use our stm32f1 camera wifi board\n");elseESP_LOGD(TAG, "UART PARSE ERR:HW_ERR_SHELL_NO_CMD\n");exit:cJSON_Delete(parse_json);return;
}static void uart_select_task(void *arg)
{if (uart_driver_install(UART_NUM_0, 2 * 1024, 0, 0, NULL, 0) != ESP_OK) {ESP_LOGE(TAG, "Driver installation failed");vTaskDelete(NULL);}uart_config_t uart_config = {.baud_rate = 115200,.data_bits = UART_DATA_8_BITS,.parity = UART_PARITY_DISABLE,.stop_bits = UART_STOP_BITS_1,.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,.source_clk = UART_SCLK_DEFAULT,};uart_param_config(UART_NUM_0, &uart_config);while (1) {int fd;if ((fd = open("/dev/uart/0", O_RDWR)) == -1) {ESP_LOGE(TAG, "Cannot open UART");vTaskDelay(5000 / portTICK_PERIOD_MS);continue;}// We have a driver now installed so set up the read/write functions to use driver also.uart_vfs_dev_use_driver(UART_NUM_0);while (1) {int s;fd_set rfds;struct timeval tv = {.tv_sec = 5,.tv_usec = 0,};FD_ZERO(&rfds);FD_SET(fd, &rfds);s = select(fd + 1, &rfds, NULL, NULL, &tv);if (s < 0) {ESP_LOGE(TAG, "Select failed: errno %d (%s)", errno, strerror(errno));break;} else if (s == 0) {ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");} else {if (FD_ISSET(fd, &rfds)) {memset(uart_json_buf,0,UART_JSON_BUFFER_SIZE);if (read(fd, uart_json_buf, UART_JSON_BUFFER_SIZE) > 0) {// Note: Only one character was read even the buffer contains more. The other characters will// be read one-by-one by subsequent calls to select() which will then return immediately// without timeout.uart_receive_parse((uint8_t *)uart_json_buf);} else {ESP_LOGE(TAG, "UART read error");break;}} else {ESP_LOGE(TAG, "No FD has been set in select()");break;}}}close(fd);}vTaskDelete(NULL);
}void app_main(void)
{bsp_led_init();xTaskCreate(uart_select_task, "uart_select_task", 4 * 1024, NULL, 5, NULL);
}
二.PC上位机端
1. 上位机介绍
整个上位机比较简单,分为几个区:
1)串口配置区,包括串口的参数,串口的打开,关闭以及UI上体现不出来的支持串口热插拔扫描等
2)LED测试区,主要是发送控制LED灯点亮/熄灭的功能
3)串口调试区,因为我们是通过串口来发送指令,所以ESP32下位机的log也会通过这个串口吐出来,所以要做一个类似其他串口工具的串口调试区用来输出log
上位机的概念知识我们就不在这里介绍了,因为比较庞大,我是用winform来编写的上位机,有非常多的空间,对于写这种搓搓的上位机已经够用了,我们直接贴代码吧
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using Newtonsoft.Json;namespace led_control
{public partial class Form1 : Form{string hw_func = "HW";string operate_led_on = "LED_ON";string operate_led_off = "LED_OFF";const int PRINTFING_LOG = 0;const int PARSEING_JSON = 1;int serial_recv_status = PRINTFING_LOG;string recv_json_str;public const int WM_DEVICE_CHANGE = 0x219;public const int DBT_DEVICEARRIVAL = 0x8000;public const int DBT_DEVICE_REMOVE_COMPLETE = 0x8004;public Form1(){InitializeComponent();System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;}private void Form1_Load(object sender, EventArgs e){search_add_serial_port();cb_serial_baudrate.SelectedIndex = 0;}/* 串口搜索 */private void search_add_serial_port(){/* 更新串口按键状态 *//*------串口界面参数设置------*/int has_port = 0;//检查是否含有串口string[] str = SerialPort.GetPortNames();//添加串口foreach (string s in str){has_port++;if (!cb_serial_port.Items.Contains(s))cb_serial_port.Items.Add(s);}if (has_port == 0){return;}//设置默认串口选项cb_serial_port.SelectedIndex = 0;}protected override void WndProc(ref Message m){if (m.Msg == WM_DEVICE_CHANGE) // 捕获USB设备的拔出消息WM_DEVICECHANGE{switch (m.WParam.ToInt32()){case DBT_DEVICE_REMOVE_COMPLETE: // USB拔出 {b_serial_open.Enabled = true;b_serial_close.Enabled = false;}break;case DBT_DEVICEARRIVAL: // USB插入获取对应串口名称 {search_add_serial_port();}break;}}base.WndProc(ref m);}private void b_serial_send_Click(object sender, EventArgs e){if (t_data_send.Text == "")MessageBox.Show("发送内容为空", "错误提示");else{if (serialPort1.IsOpen)serialPort1.Write(t_data_send.Text);elseMessageBox.Show("先打开串口", "错误提示");}}private void b_serial_open_Click(object sender, EventArgs e){if (!serialPort1.IsOpen)//串口处于关闭状态{try{if (cb_serial_port.SelectedIndex == -1){MessageBox.Show("Error: 无效的端口,请重新选择", "Error");return;}string strSerialName = cb_serial_port.SelectedItem.ToString();serialPort1.PortName = strSerialName;//串口号serialPort1.BaudRate = Convert.ToInt32(cb_serial_baudrate.SelectedItem.ToString());//波特率serialPort1.DataBits = 8;//数据位serialPort1.StopBits = StopBits.One;serialPort1.Parity = Parity.None;//打开串口serialPort1.Open();b_serial_open.Enabled = false;b_serial_close.Enabled = true;}catch (System.Exception ex){MessageBox.Show("Error:" + ex.Message, "Error");return;}}}private void b_serial_close_Click(object sender, EventArgs e){if (serialPort1.IsOpen)//串口处于关闭状态{serialPort1.Close();//关闭串口b_serial_open.Enabled = true;b_serial_close.Enabled = false;}}private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e){if (serialPort1.IsOpen){try{string recv_data_string;int recv_data_count = serialPort1.BytesToRead;while (serialPort1.BytesToRead > 0){recv_data_string = serialPort1.ReadLine();if (recv_data_string.Contains("{") || (serial_recv_status == PARSEING_JSON)){serial_recv_status = PARSEING_JSON;recv_json_str += recv_data_string;if (recv_data_string.Contains("}")){serial_recv_status = PRINTFING_LOG;
#if CONSOLE_DEBUGConsole.WriteLine(recv_json_str);
#endifjson_status status = JsonConvert.DeserializeObject<json_status>(recv_json_str);json_status_recv_parse(status);recv_json_str = "";//return;}}else{t_data_recv.AppendText(recv_data_string + '\r' + '\n');}}}catch (Exception ex){return;}}}private void b_led_on_Click(object sender, EventArgs e){json_construction_send(hw_func, operate_led_on, null, null, null, null, null, null);}private void b_led_off_Click(object sender, EventArgs e){json_construction_send(hw_func, operate_led_off, null, null, null, null, null, null);}private void json_construction_send(string func, string operate, string param1, string param2, string param3, string param4, string param5, string param6){json_commmand cmd = new json_commmand();cmd.FUNC = func;cmd.OPERATE = operate;cmd.PARAM1 = param1;cmd.PARAM2 = param2;cmd.PARAM3 = param3;cmd.PARAM4 = param4;cmd.PARAM5 = param5;cmd.PARAM6 = param6;string json_cmd = JsonConvert.SerializeObject(cmd);
#if CONSOLE_DEBUGConsole.WriteLine(json_cmd);
#endifif (serialPort1.IsOpen){serialPort1.WriteLine(json_cmd);}}private void json_status_recv_parse(json_status status){
#if CONSOLE_DEBUGConsole.WriteLine("----------json_status_recv_parse-------------");Console.WriteLine("json func:" + status.FUNC);Console.WriteLine("json operate:" + status.OPERATE);Console.WriteLine("json status:" + status.STATUS);Console.WriteLine("json param1:" + status.PARAM1);Console.WriteLine("json param2:" + status.PARAM2);Console.WriteLine("json param3:" + status.PARAM3);Console.WriteLine("json param4:" + status.PARAM4);Console.WriteLine("json param5:" + status.PARAM5);Console.WriteLine("----------json_status_recv_parse end--------");
#endif}private void b_serial_send_Click_1(object sender, EventArgs e){if (t_data_send.Text == "")MessageBox.Show("发送内容为空", "错误提示");else{if (serialPort1.IsOpen)serialPort1.Write(t_data_send.Text);elseMessageBox.Show("先打开串口", "错误提示");}}}/* JSON定义 */public class json_commmand{public string FUNC { get; set; }public string OPERATE { get; set; }public string PARAM1 { get; set; }public string PARAM2 { get; set; }public string PARAM3 { get; set; }public string PARAM4 { get; set; }public string PARAM5 { get; set; }public string PARAM6 { get; set; }}public class json_status{public string FUNC { get; set; }public string OPERATE { get; set; }public string STATUS { get; set; }public string PARAM1 { get; set; }public string PARAM2 { get; set; }public string PARAM3 { get; set; }public string PARAM4 { get; set; }public string PARAM5 { get; set; }public string PARAM6 { get; set; }}
}
2. 发送json数据来控制LED灯
每个语言基本都有自己的json库,我们c#用的是:using Newtonsoft.Json;
点击LED ON(蓝色图标)触发的动作如下:
string hw_func = "HW";
string operate_led_on = "LED_ON";private void b_led_on_Click(object sender, EventArgs e)
{json_construction_send(hw_func, operate_led_on, null, null, null, null, null, null);
}
点击LED OFF(红色图标)触发的动作如下:
string hw_func = "HW";
string operate_led_off = "LED_OFF";private void b_led_off_Click(object sender, EventArgs e)
{json_construction_send(hw_func, operate_led_off, null, null, null, null, null, null);
}
其中json_construction_send函数如下 :
private void json_construction_send(string func, string operate, string param1, string param2, string param3, string param4, string param5, string param6)
{json_commmand cmd = new json_commmand();cmd.FUNC = func;cmd.OPERATE = operate;cmd.PARAM1 = param1;cmd.PARAM2 = param2;cmd.PARAM3 = param3;cmd.PARAM4 = param4;cmd.PARAM5 = param5;cmd.PARAM6 = param6;string json_cmd = JsonConvert.SerializeObject(cmd);
#if CONSOLE_DEBUGConsole.WriteLine(json_cmd);
#endifif (serialPort1.IsOpen){serialPort1.WriteLine(json_cmd);}
}
可以看到这个也是遵循我们指定的json格式,上位机跟下位机的格式是一致的
我们就来看下这个函数,非常简单
创建 JSON 命令对象:
json_commmand cmd = new json_commmand();
这里创建了一个 json_commmand
类的实例 cmd
。json_commmand
类应该是一个自定义类,用于存储命令的各个部分。
设置命令属性:
cmd.FUNC = func;
cmd.OPERATE = operate;
cmd.PARAM1 = param1;
cmd.PARAM2 = param2;
cmd.PARAM3 = param3;
cmd.PARAM4 = param4;
cmd.PARAM5 = param5;
cmd.PARAM6 = param6;
将传入的参数赋值给 cmd 对象的相应属性。
将对象序列化为 JSON 字符串:
string json_cmd = JsonConvert.SerializeObject(cmd);
使用 JsonConvert.SerializeObject
方法将 cmd
对象序列化为 JSON 格式的字符串。JsonConvert
是 Newtonsoft.Json
库中的一个类,用于处理 JSON 数据的序列化和反序列化。
通过串口发送 JSON 命令:
if (serialPort1.IsOpen)
{serialPort1.WriteLine(json_cmd);
}
相关文章:

ESP32 UART select解析json数据,上位机控制LED灯实验
前言: 本实验的目的主要是通过上位机通过UART来控制ESP32端的LED的点亮以及熄灭,整个项目逻辑比较简单,整体架构如下: 上位机(PC)主要是跑在PC端的一个软件,主要作用包含: 1)串口相关配置&…...

K8S 集群搭建——cri-dockerd版
目录 一、工作准备 1.配置主机名 2.配置hosts解析 3.配置免密登录(只需要在master上操作) 4.时间同步(每台节点都要做,必做,否则可能会因为时间不同步导致集群初始化失败) 5.关闭系统防火墙 6.配置…...
基于Python的电商销售数据分析与可视化系统实
一、系统架构设计 1.1系统流程图 #mermaid-svg-Pdo9oZWrVHNuOoTT {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Pdo9oZWrVHNuOoTT .error-icon{fill:#552222;}#mermaid-svg-Pdo9oZWrVHNuOoTT .error-text{fill:#5…...

学习笔记:Python网络编程初探之基本概念(一)
一、网络目的 让你设备上的数据和其他设备上进行共享,使用网络能够把多方链接在一起,然后可以进行数据传递。 网络编程就是,让在不同的电脑上的软件能够进行数据传递,即进程之间的通信。 二、IP地址的作用 用来标记唯一一台电脑…...
高效处理 List<T> 集合:更新、查找与优化技巧
引言 在日常开发中,List<T> 是我们最常用的数据结构之一。无论是批量更新数据、查找特定项还是进行复杂的集合操作,掌握 List<T> 的高级用法可以显著提高代码的效率和可读性。本文将详细介绍如何使用 List<T> 进行批量更新、查找匹配项以及优化性能的方法…...

HTML5(Web前端开发笔记第一期)
p.s.这是萌新自己自学总结的笔记,如果想学习得更透彻的话还是请去看大佬的讲解 目录 三件套标签标题标签段落标签文本格式化标签图像标签超链接标签锚点链接默认链接地址 音频标签视频标签 HTML基本骨架综合案例->个人简介列表表格表单input标签单选框radio上传…...
Windows控制台函数:标准输入输出流交互函数GetStdHandle()
目录 什么是 GetStdHandle? 它长什么样? 怎么用它? 它跟 std::cout 有什么不一样? GetStdHandle 是一个 Windows API 函数,用于获取标准输入、标准输出或标准错误设备的句柄。它定义在 Windows 的核心头文件 <…...
Vue3 中 Computed 用法
Computed 又被称作计算属性,用于动态的根据某个值或某些值的变化,来产生对应的变化,computed 具有缓存性,当无关值变化时,不会引起 computed 声明值的变化。 产生一个新的变量并挂载到 vue 实例上去。 vue3 中 的 com…...
常见的三种锁
一、互斥锁 互斥锁 Mutex 保证在任意时刻只有一个线程能够进入被保护的临界区。当一个线程获取到互斥锁后,其他线程若要进入临界区就会被阻塞,直到该线程释放锁。 互斥锁是一种阻塞锁,当线程无法获取到锁时,会进入阻塞状态。 应…...

离线文本转语音库pyttsx3(目前接触到的声音效果最好的,基本上拿来就能用)
在现代应用程序中,文本转语音(Text-to-Speech, TTS)技术越来越受到重视。无论是为视力障碍人士提供帮助,还是为教育和娱乐应用增添趣味,TTS 都能发挥重要作用。今天,我们将介绍一个简单易用的 Python 库——…...

LeetCode Hot100刷题——反转链表(迭代+递归)
206.反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2: 输入:head [1,2] 输出:[2,1]示例 3&#…...
JJJ:linux sysfs相关
文章目录 1.sysfs(属性)文件的创建、读、写1.1 创建流程1.2 open流程1.3 read流程 2.补充2.1 sysfs下常见目录介绍2.2 属性相关2.2.1 简介2.2.2 attribute文件的创建 2.3 sysfs目录如何创建的 1.sysfs(属性)文件的创建、读、写 1…...

Leetcode 刷题记录 06 —— 矩阵
本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答。 目录 01 矩阵置零 方法一:标记数组 方法二:两个标记变量 02 螺旋矩阵…...

什么样的物联网框架适合开展共享自助KTV唱歌项目?
现在物联网的广泛应用,也让更多用户们看到了它的实力,也使得共享经济遍地开花。其中共享自助唱歌设备也备受欢迎,那么适合开展共享自助KTV唱歌项目的物联网框架都应具备哪些特点呢? 智能化与自动化管理 物联网技术在共享KTV中的应…...
【Academy】HTTP Host 标头攻击 ------ HTTP Host header attacks
HTTP Host 标头攻击 ------ HTTP Host header attacks 1. 什么是 HTTP Host 标头?2. 什么是 HTTP Host 标头攻击?3. HTTP Host 标头漏洞是如何产生的?4. 如何测试 HTTP Host 标头漏洞4.1 提供任意 Host 标头4.2 检查有缺陷的验证4.3 发送不明…...

Web基础:HTML快速入门
HTML基础语法 HTML(超文本标记语言) 是用于创建网页内容的 标记语言,通过定义页面的 结构和内容 来告诉浏览器如何呈现网页。 超文本(Hypertext) 是一种通过 链接(Hyperlinks) 将不同文本、图像…...
技术领域,有许多优秀的博客和网站
在技术领域,有许多优秀的博客和网站为开发者、工程师和技术爱好者提供了丰富的学习资源和行业动态。以下是一些常用的技术博客和网站,涵盖了编程、软件开发、数据科学、人工智能、网络安全等多个领域: 1. 综合技术博客 1.1 Medium 网址: ht…...

k8s概念及k8s集群部署(Centos7)
Centos7部署k8s集群 部署之前,先简单说下k8s是个啥: 一、k8s简介: k8s,全称:kubernetes,它可以看作是一个分布式系统支撑平台。k8s的作用: 1、故障自愈: k8s这个玩意可以监控容器…...
《DeepSeek-V3:动态温度调节算法,开启推理新境界!》
在人工智能领域不断探索的征程中,DeepSeek-V3以其卓越的创新技术,尤其是动态温度调节算法,成为了备受瞩目的焦点。这项算法犹如一把神奇的钥匙,巧妙地开启了推理速度与精度动态平衡的大门,为大语言模型的发展开辟了新的…...

Python从入门到精通1:FastAPI
引言 在现代 Web 开发中,API 是前后端分离架构的核心。FastAPI 凭借其高性能、简洁的语法和自动文档生成功能,成为 Python 开发者的首选框架。本文将从零开始,详细讲解 FastAPI 的核心概念、安装配置、路由设计、请求处理以及实际应用案例&a…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...