简述:使用ESP32实现在同一局域网内通过UDP进行通信及控制。
功能:ESP32通过电容屏控制及显示CH32V307开发板LED
的状态。
详细资料:请关注公众号《跳跃的影子》并发送“ ESP32 和 CH32V307 UDP通信 ”获取。
CH32V307
硬件说明
- 系统框图
- 网口不需要
phy
芯片,支持10M通信。
- 网口不需要
- 开发板正面
- 左侧
type-c
为供电口、下载口及串口。- 开关:只有通过左侧
type-c
供电时才受控制。 - 按键:复位按键、用户按键。
- 右侧
- 两
type-c
。 - 网口。
- 两
- LED
- 两按键间的两个LED灯只连接到了排针上,未连接芯片,需要跳线。
- 左侧
- 开发板背面。
软件说明
- 开发环境使用
RT-Thread Studio
(2.2.4)。- RT-Thread_Source_Code:4.1.0
- CH32V307V-R1:1.0.8
- RISC-V-GCC-WCH:8.2.0
- 使能
ETH
。
- 使能网络接口设备及TCP/IP。
void udpserv(void)
{
int sock;
int bytes_read;
char *recv_data;
rt_uint32_t addr_len;
struct sockaddr_in server_addr, client_addr;
/* 分配接收用的数据缓冲 */
recv_data = rt_malloc(1024);
if (recv_data == RT_NULL)
{
/* 分配内存失败,返回 */
rt_kprintf("No memory\n");
return;
}
/* 创建一个socket,类型是SOCK_DGRAM,UDP类型 */
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
rt_kprintf("Socket error\n");
/* 释放接收用的数据缓冲 */
rt_free(recv_data);
return;
}
/* 初始化服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5000); /* _端口_ */
server_addr.sin_addr.s_addr = INADDR_ANY;
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/* 绑定socket到服务端地址 */
if (bind(sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr))
== -1)
{
/* 绑定地址失败 */
rt_kprintf("Bind error\n");
/* 释放接收用的数据缓冲 */
rt_free(recv_data);
return;
}
addr_len = sizeof(struct sockaddr);
rt_kprintf("\nUDPServer Waiting for client on port 5000...\n");
while (1)
{
/* 从sock中收取最大1024字节数据 (堵塞接收) */
bytes_read = recvfrom(sock, recv_data, 1024, 0,(struct sockaddr *) &client_addr, &addr_len);
/* UDP不同于TCP,它基本不会出现收取的数据失败的情况,除非设置了超时等待 */
recv_data[bytes_read] = '\0'; /* 把末端清零 */
/* 返回led状态 */
if(strcmp(recv_data, "led0_check") == 0){
rt_kprintf("led0_check \n");
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0) == 1){
sendto(sock, "led0_ack0", 9, 0,
(struct sockaddr *) &client_addr, sizeof(struct sockaddr));
rt_kprintf("led0_ack0 \n");
}else if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0) == 0){
sendto(sock, "led0_ack1", 9, 0,
(struct sockaddr *) &client_addr, sizeof(struct sockaddr));
rt_kprintf("led0_ack1 \n");
}
}
/* led0 */
else if (strcmp(recv_data, "led0_off") == 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0);
sendto(sock, "led0_ack0", 9, 0,
(struct sockaddr *) &client_addr, sizeof(struct sockaddr));
rt_kprintf("led0 off success \n");
}
else if (strcmp(recv_data, "led0_on") == 0)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
sendto(sock, "led0_ack1", 9, 0,
(struct sockaddr *) &client_addr, sizeof(struct sockaddr));
rt_kprintf("led0 on success \n");
}
/* pwm */
else if (strncmp(recv_data, "led1:", 5) == 0)
{
//rt_kprintf("%s \n", recv_data+5);
TIM1->CH1CVR=atoi(recv_data+5);
}
/* 退出 */
else if (strcmp(recv_data, "exit") == 0) /* 如果接收数据是exit,退出 */
{
lwip_close(sock);
/* 释放接收用的数据缓冲 */
rt_free(recv_data);
break;
}
/* 其他数据直接返回 */
else
{
/* 输出接收的数据 */
rt_kprintf("\n(%s, %d) said : ", inet_ntoa(client_addr.sin_addr), ntohs(server_addr.sin_port));
rt_kprintf("%s", recv_data);
/* 回发 */
sendto(sock, recv_data, bytes_read, 0,
(struct sockaddr *) &client_addr, sizeof(struct sockaddr));
}
}
return;
}
MSH_CMD_EXPORT(udpserv, udpserv sample);
- 下载选项
- Chip Mem:连接上芯片后,选择
192K ROM + 128K RAM
后点击Set
。 - 文件选择
.hex
。
- Chip Mem:连接上芯片后,选择
ESP32
硬件说明
- 裸板正面。
- 2.0寸(240*320)电容触摸屏。
- 右上角:sht30温湿度传感器、电源指示灯。
- 右下角:MPU6050、按键。
- 裸板背面。
- 左侧:ESP32。
- 右侧:供电口、下载口及调试口。
- 3D打印外壳正面。
- 通电LCD显示效果。
软件说明
-
VS Code
- ESP-IDF:4.3
- FreeRTOS
- LIWP
- LVGL:7.9.0
- GUI-Guider1.3.0-GA
-
使用
Kconfig
文件给配置菜单添加相关配置项。
- 打开配置菜单。
menu "IP Configuration"
choice EXAMPLE_IP_MODE
prompt "IP Version"
depends on EXAMPLE_SOCKET_IP_INPUT_STRING
help
Example can use either IPV4 or IPV6.
config EXAMPLE_IPV4
bool "IPV4"
config EXAMPLE_IPV6
bool "IPV6"
select EXAMPLE_CONNECT_IPV6
endchoice
config EXAMPLE_IPV4_ADDR
string "IPV4 Address"
default "192.168.0.165"
depends on EXAMPLE_IPV4
help
IPV4 address to which the client example will send data.
config EXAMPLE_IPV6_ADDR
string "IPV6 Address"
default "FE80::30AD:E57B:C212:68AD"
depends on EXAMPLE_IPV6
help
IPV6 address to which the client example will send data.
config EXAMPLE_PORT
int "Port"
range 0 65535
default 3333
help
The remote port to which the client example will send data.
choice EXAMPLE_SOCKET_IP_INPUT
prompt "Socket example source"
default EXAMPLE_SOCKET_IP_INPUT_STRING
help
Selects the input source of the IP used in the example.
config EXAMPLE_SOCKET_IP_INPUT_STRING
bool "From string"
config EXAMPLE_SOCKET_IP_INPUT_STDIN
bool "From stdin"
endchoice
endmenu
- UDP主要逻辑代码。
static void udp_client_task(void *pvParameters)
{
char rx_buffer[128];
char host_ip[] = HOST_IP_ADDR;
int addr_family = 0;
int ip_protocol = 0;
ESP_LOGI(TAG, "udp start!");
while (1) {
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
socklen_t socklen = sizeof(dest_addr);
sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, PORT);
sendto(sock, "led0_check", 10, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); /* 查询状态 */
vTaskDelay(1000 / portTICK_PERIOD_MS);
while (1) {
/* 接收函数 */
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&dest_addr, &socklen);
rx_buffer[len] = 0; /* 接收最后一字节为结束符 */
/* 处理函数 */
/* led0 */
if (strcmp(rx_buffer, "led0_ack0") == 0)
{
lv_led_off(guider_ui.screen_led_1);
ESP_LOGI(TAG, "led0_off");
}
else if(strcmp(rx_buffer, "led0_ack1") == 0)
{
lv_led_on(guider_ui.screen_led_1);
ESP_LOGI(TAG, "led0_on");
}
else
{
ESP_LOGI(TAG, "Received %d from %s:", len, inet_ntoa(dest_addr.sin_addr)); /* 接收长度,接收IP */
ESP_LOGI(TAG, "%s", rx_buffer);
}
/* 退出循环 */
if (strncmp(rx_buffer, "OK: ", 4) == 0) {
ESP_LOGI(TAG, "Received expected message, reconnecting");
break;
}
/* 延时 */
vTaskDelay(500 / portTICK_PERIOD_MS);
}
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}
- 在
events_init.c
中添加lvgl相关的事件驱动。
/*
* Copyright 2022 NXP
* SPDX-License-Identifier: MIT
*/
#include "events_init.h"
#include <stdio.h>
#include "lvgl/lvgl.h"
#if !LV_USE_GUIDER_SIMULATOR
#include "lwip/sockets.h"
static unsigned int counter = 0;
extern struct sockaddr_in dest_addr;
extern int sock;
#endif
void events_init(lv_ui *ui)
{
}
static void screen_led0_onevent_handler(lv_obj_t * obj, lv_event_t event)
{
switch (event)
{
case LV_EVENT_PRESSED:
{
#if !LV_USE_GUIDER_SIMULATOR
sendto(sock, "led0_on", 7, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
#endif
}
break;
default:
break;
}
}
static void screen_led0_offevent_handler(lv_obj_t * obj, lv_event_t event)
{
switch (event)
{
case LV_EVENT_PRESSED:
{
#if !LV_USE_GUIDER_SIMULATOR
sendto(sock, "led0_off", 8, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
#endif
}
break;
default:
break;
}
}
static void screen_slider_1event_handler(lv_obj_t * obj, lv_event_t event)
{
switch (event)
{
case LV_EVENT_VALUE_CHANGED:
{
#if !LV_USE_GUIDER_SIMULATOR
static char buf[20]={0};
sprintf(buf, "led1:%03d", lv_slider_get_value(guider_ui.screen_slider_1));
sendto(sock, buf, 8, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
#endif
}
break;
default:
break;
}
}
void events_init_screen(lv_ui *ui)
{
lv_obj_set_event_cb(ui->screen_led0_on, screen_led0_onevent_handler);
lv_obj_set_event_cb(ui->screen_led0_off, screen_led0_offevent_handler);
lv_obj_set_event_cb(ui->screen_slider_1, screen_slider_1event_handler);
}
评论区