首先实现与小车的信息交互功能。
把信息交互分为2个部分,小车端,主机控制端。

首先假设一下使用场景:
小车独立开始运行测试的时候,就没办法通过串口接收到小车的运行信息了。
在小车调试中,在小车旁边可以遥控器给小车发送简单指令,比如设置小车的速度,探测距离。小车将运行结果通过OLED模块显示出来。
在小车运行中,人就没办法在旁边干预了,就通过WiFi网络发送接收信息。
一般情况小车应用都是比较复杂的,数据上传PC才能处理。在有些实验场景下,是没有固定的WiFi网络提供的,没法给小车预设WiFi信息。比如参与一个竞赛,临时做一些适应场地的设置。
做了一堆尝试,最后确定下来这样一个结构。

我有一个模块作为控制端的接收和发送,开启WiFi 站点模式,通过串口与计算机连接。为下一步连接多台小车做准备。
PC机通过串口与控制端模块连接,发送指令控制这个模块的发送接收信息。
小车通过遥控器开启AP 热点模式,开启网络UDP服务端模式,接收到信息就对发送来信息的地址回传信息。
主要的代码
Hi3863 WiFi AP 热点开启
cpp
/**
*
* wifi ap
*
* 2025 12 21
*
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"
#include "cmsis_os2.h"
#include "wifi_event.h"
#include "wifi_device.h"
#include "wifi_device_config.h"
#include "wifi_hotspot.h"
#include "wifi_hotspot_config.h"
#include "err.h"
#include "lwip/netifapi.h"
#include "lwip/ip_addr.h"
#include "ap_start.h"
static struct netif *ap_netif = NULL; // 网络接口
// static char ap_name[10] = "Didi"; // ssid
// static char ap_pass[10] = "123456789"; // key
// 1.1 Softap 状态
static void WiFi_Event_Softap_State(int32_t state)
{
osal_printk(" [WiFi_Event_Softap_State] \r\n");
osal_printk(" softap state : %d \r\n", state);
}
// 1.2 Softap 加入
static void WiFi_Event_Softap_Join(const wifi_sta_info_stru *info)
{
unused(info);
osal_printk(" [WiFi_Event_Softap_Join] \r\n");
}
// 1.3 Softap 离开
static void WiFi_Event_Softap_State_Leave(const wifi_sta_info_stru *info)
{
unused(info);
osal_printk(" [WiFi_Event_Softap_State_Leave] \r\n");
}
// 1. WiFi 事件回调
static wifi_event_stru WiFi_Event_Callback = {
.wifi_event_softap_state_changed = WiFi_Event_Softap_State,
.wifi_event_softap_sta_join = WiFi_Event_Softap_Join,
.wifi_event_softap_sta_leave = WiFi_Event_Softap_State_Leave,
};
// ap 启动
uint8_t AP_Start(char *ap_name, char *ap_pass, char *ap_addr)
{
// 1. 注册 WiFi 事件回调
if (wifi_register_event_cb(&WiFi_Event_Callback) != ERRCODE_SUCC)
{
osal_printk(" [AP_Start] 1. wifi_register_event_cb FAILURE! \r\n");
return 0;
}
// 等待WiFi初始完成
while (wifi_is_wifi_inited() == 0)
{
(void)osDelay(10);
}
// ap 扩展配置
softap_config_advance_stru ap_config_advance = {0};
ap_config_advance.beacon_interval = 100;
ap_config_advance.dtim_period = 2;
ap_config_advance.gi = 0;
ap_config_advance.group_rekey = 86400;
ap_config_advance.protocol_mode = 4;
ap_config_advance.hidden_ssid_flag = 1;
// 2.1 配置SoftAP
if (wifi_set_softap_config_advance(&ap_config_advance) != ERRCODE_SUCC)
{
osal_printk(" [AP_Start] 2.1 wifi_set_softap_config_advance FAILURE! \r\n");
return 0;
}
// ap 配置
softap_config_stru ap_config = {0};
(void)memcpy_s(ap_config.ssid, sizeof(ap_config.ssid), ap_name, strlen(ap_name));
(void)memcpy_s(ap_config.pre_shared_key, WIFI_MAX_KEY_LEN, ap_pass, strlen(ap_pass));
ap_config.security_type = 3;
ap_config.channel_num = 13;
ap_config.wifi_psk_type = 0;
// 2.2 开启SoftAP
if (wifi_softap_enable(&ap_config) != ERRCODE_SUCC)
{
osal_printk(" [AP_Start] 2.2 wifi_softap_enable FAILURE! \r\n");
return 0;
}
// 网口名 默认值
char ifname[WIFI_IFNAME_MAX_SIZE + 1] = "ap0";
// 3.1 获取网络接口
ap_netif = netif_find(ifname);
if (ap_netif == NULL)
{
osal_printk(" [AP_Start] 3.1 netif_find FAILURE! \r\n");
return 0;
}
ip4_addr_t local_ipaddr;
ip4_addr_t local_netmask;
ip4_addr_t local_gw;
IP4_ADDR(&local_ipaddr, 192, 168, 55, 1);
IP4_ADDR(&local_netmask, 255, 255, 255, 0);
IP4_ADDR(&local_gw, 192, 168, 55, 2);
// 3.2 设置地址
if (netifapi_netif_set_addr(ap_netif, &local_ipaddr, &local_netmask, &local_gw) != ERR_OK)
{
osal_printk(" [AP_Start] 3.2 netifapi_netif_set_addr FAILURE! \r\n");
return 0;
}
// 3.3 dhcp 启动
if (netifapi_dhcps_start(ap_netif, NULL, 0) != ERR_OK)
{
osal_printk(" [AP_Start] 3.3 netifapi_dhcps_start FAILURE! \r\n");
return 0;
}
// 查看分配的IP地址
ip4_addr_t ap_ipaddr; // ip 地址
ip4_addr_t ap_netmask; // 掩码
ip4_addr_t ap_gw; // 网关
IP4_ADDR(&ap_ipaddr, 0, 0, 0, 0);
IP4_ADDR(&ap_netmask, 0, 0, 0, 0);
IP4_ADDR(&ap_gw, 0, 0, 0, 0);
(void)netifapi_netif_get_addr(ap_netif, &ap_ipaddr, &ap_netmask, &ap_gw);
// 打印地址信息
// osal_printk("\r\n ---- [ ap netif addr ] ---- \r\n");
// osal_printk(" ap ip: %s \r\n", ip4addr_ntoa(&ap_ipaddr));
// osal_printk(" ap gw: %s \r\n", ip4addr_ntoa(&ap_gw));
// osal_printk(" ap netmask: %s \r\n", ip4addr_ntoa(&ap_netmask));
// osal_printk(" ---- ---- ---- ---- \r\n");
// 回传 地址
strcpy(ap_addr, ip4addr_ntoa(&ap_ipaddr));
osal_printk(" [AP_Start] SUCCESS. \r\n");
return 1;
}
hi3863 WiFi STA 站点模式 开启
cpp
/**
*
* wifi sta
*
* 2025 12 21
*
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"
#include "cmsis_os2.h"
#include "wifi_event.h"
#include "wifi_device.h"
#include "wifi_device_config.h"
#include "wifi_hotspot.h"
#include "wifi_hotspot_config.h"
#include "err.h"
#include "lwip/netifapi.h"
#include "lwip/ip_addr.h"
#include "sta_start.h"
static uint8_t conn_stat = 0; // 连接状态
static uint8_t scan_stat = 0; // 扫描状态
struct netif *sta_netif = NULL; // 网络接口
// 连接状态改变 回调
static void WiFi_Conn_State_Changed_Callback(int32_t state, const wifi_linked_info_stru *info, int32_t reason_code)
{
(void)info;
(void)reason_code;
osal_printk(" [ WiFi_Conn_State_Changed_Callback ] state = %d \r\n", state);
if(state != 0)
{
conn_stat = 1;
}
return;
}
// 扫描状态改变 回调
static void WiFi_Scan_State_Changed_Callback(int32_t state, int32_t size)
{
(void)state;
(void)size;
osal_printk(" [ WiFi_Scan_State_Changed_Callback ] state = %d \r\n", state);
scan_stat = 1;
return;
}
// WiFi 事件 回调
static wifi_event_stru WiFi_Event_Callback = {
.wifi_event_connection_changed = WiFi_Conn_State_Changed_Callback, // 连接状态改变
.wifi_event_scan_state_changed = WiFi_Scan_State_Changed_Callback, // 扫描状态改变
};
// sta 启动
uint8_t STA_Start(char *ap_name, char *ap_pass, char *sta_addr)
{
// 1. 注册事件回调
if(wifi_register_event_cb(&WiFi_Event_Callback) != ERRCODE_SUCC)
{
osal_printk(" [STA_Start] 1. wifi_register_event_cb FAILURE! \n");
return 0;
}
// 获取 WiFi 设备初始化状态
while(wifi_is_wifi_inited() != 1)
{
(void)osDelay(20);
}
// 2.1 开启 STA
if(wifi_sta_enable() != ERRCODE_SUCC)
{
osal_printk(" [STA_Start] 2.1 wifi_sta_enable FAILURE! \n");
return 0;
}
// 2.2 进行全信道基础扫描
if(wifi_sta_scan() != ERRCODE_SUCC)
{
osal_printk(" [STA_Start] 2.2 wifi_sta_scan FAILURE! \n");
return 0;
}
// 等待 扫描状态改变
while(scan_stat != 1)
{
(void)osDelay(20);
}
// 最大扫描数
uint32_t ap_num = 16;
// 扫描信息大小
uint32_t scan_info_size = sizeof(wifi_scan_info_stru) * ap_num;
// 2.3 分配存储空间
wifi_scan_info_stru *result = osal_kmalloc(scan_info_size, OSAL_GFP_ATOMIC);
// 分配 失败
if(result == NULL)
{
osal_printk(" [STA_Start] 2.3 osal_kmalloc FAILURE! \n");
return 0;
}
// 空间清零
memset_s(result, scan_info_size, 0, scan_info_size);
// 2.4 获取sta扫描结果
if(wifi_sta_get_scan_info(result, &ap_num) != ERRCODE_SUCC)
{
osal_kfree(result);
osal_printk(" [STA_Start] 2.4 wifi_sta_get_scan_info FAILURE! \n");
return 0;
}
uint8_t ap_find = 0; // ap 查找 结果
uint8_t idx; // 序号
// 检索 热点名
for(idx = 0; idx < ap_num; idx++)
{
osal_printk(" result[idx].ssid = %s \r\n", result[idx].ssid);
if(strlen(ap_name) == strlen(result[idx].ssid))
{
if(memcmp(ap_name, result[idx].ssid, strlen(ap_name)) == 0)
{
ap_find = 1;
break;
}
}
}
// 2.5 未检索到 热点
if(ap_find == 0)
{
osal_kfree(result);
osal_printk(" [STA_Start] 2.5 ap_find == 0 FAILURE! \n");
return 0;
}
// 连接 AP 的 STA 配置信息
wifi_sta_config_stru sta_config = {0};
memcpy_s(sta_config.ssid, WIFI_MAX_SSID_LEN, ap_name, strlen(ap_name));
memcpy_s(sta_config.bssid, WIFI_MAC_LEN, result[idx].bssid, WIFI_MAC_LEN);
memcpy_s(sta_config.pre_shared_key, WIFI_MAX_KEY_LEN, ap_pass, strlen(ap_pass)); // 密钥
sta_config.security_type = result[idx].security_type; // 安全类型。
sta_config.ip_type = DHCP; // IP的分配类型
osal_kfree(result);
// 连接到 WiFi AP 热点
if(wifi_sta_connect(&sta_config) != ERRCODE_SUCC)
{
osal_printk(" [STA_Start] 2.6 wifi_sta_connect FAILURE! \n");
return 0;
}
// 等待 连接状态改变
while(conn_stat != 1)
{
(void)osDelay(20);
}
// 网络 接口 名
char if_name[WIFI_IFNAME_MAX_SIZE + 1] = "wlan0";
// 3.1 获取 网络接口信息
sta_netif = netifapi_netif_find(if_name);
// 网络接口信息 为空
if(sta_netif == NULL)
{
osal_printk(" [STA_Start] 3.1 netifapi_netif_find FAILURE! \n");
return 0;
}
// 3.2 开启 DHCP
if(netifapi_dhcp_start(sta_netif) != ERR_OK)
{
osal_printk(" [STA_Start] 3.2 netifapi_dhcp_start FAILURE! \n");
return 0;
}
// 等待协商完成
while(netifapi_dhcp_is_bound(sta_netif) != ERR_OK)
{
(void)osDelay(20);
}
// 查看分配的IP地址
ip4_addr_t sta_ipaddr; // ip 地址
ip4_addr_t sta_netmask; // 掩码
ip4_addr_t sta_gw; // 网关
IP4_ADDR(&sta_ipaddr, 0, 0, 0, 0);
IP4_ADDR(&sta_netmask, 0, 0, 0, 0);
IP4_ADDR(&sta_gw, 0, 0, 0, 0);
(void)netifapi_netif_get_addr(sta_netif, &sta_ipaddr, &sta_netmask, &sta_gw);
// 打印地址信息
// ip_addr_t addr;
osal_printk("\r\n ---- [ sta netif addr ] ---- \r\n");
osal_printk(" sta ip: %s \r\n", ip4addr_ntoa(&sta_ipaddr));
osal_printk(" sta netmask: %s \r\n", ip4addr_ntoa(&sta_netmask));
osal_printk(" sta gw: %s \r\n", ip4addr_ntoa(&sta_gw));
osal_printk(" ---- ---- ---- ---- \r\n");
// 回传 地址
strcpy(sta_addr, ip4addr_ntoa(&sta_ipaddr));
osal_printk(" [STA_Start] 0. SUCCESS. \n");
return 1;
}
网络 UDP Server 端
cpp
/**
*
* hi3863
*
* UDP网络通讯 服务端
*
* 2025 12 21
*
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"
#include "cmsis_os2.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "lwip/err.h"
#include "udp_server.h"
static int server_socket = -1;
static struct sockaddr_in client_addr;
static socklen_t client_addr_size = sizeof(client_addr);
// udp server 创建
uint8_t UDP_Server_Create(char *server_ip, int server_port)
{
// 1. 创建 socket
server_socket = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if(server_socket == -1)
{
osal_printk(" [UDP_Server_Create] 1. lwip_socket FAILURE! \r\n");
return 0;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
server_addr.sin_addr.s_addr = inet_addr(server_ip);
// 2. 绑定 网络 信息
if(lwip_bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
osal_printk(" [UDP_Server_Create] 2. lwip_bind FAILURE! \r\n");
return 0;
}
osal_printk(" [UDP_Server_Create] SUCCESS. \r\n");
return 1;
}
// udp 接收 数据
uint8_t UDP_Server_Recv_Data(uint8_t *recv_buff, uint8_t buff_len)
{
ssize_t len = lwip_recvfrom(server_socket, recv_buff, (size_t)buff_len, MSG_DONTWAIT, (struct sockaddr *)&client_addr, &client_addr_size);
if(len == -1)
{
return 0;
}
osal_printk(" UDP Recv Data : %d \r\n", len);
return (uint8_t)len;
}
// udp 发送 数据
uint8_t UDP_Server_Send_Data(uint8_t *send_buff, uint8_t buff_len)
{
ssize_t len = lwip_sendto(server_socket, send_buff, (size_t)buff_len, MSG_DONTWAIT, (struct sockaddr *)&client_addr, client_addr_size);
if(len == -1)
{
return 0;
}
osal_printk(" UDP Send Data : %d \r\n", len);
return (uint8_t)len;
}
// udp server 关闭
void UDP_Server_Close(void)
{
if(server_socket != -1)
{
lwip_close(server_socket);
osDelay(20);
server_socket = -1;
osal_printk(" [UDP_Server_Close ] \r\n");
}
}
网络 UDP Client 端
cpp
/**
*
* hi3863
*
* UDP网络通讯 客户端
*
* 2025 12 20
*
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"
#include "cmsis_os2.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "lwip/err.h"
#include "udp_client.h"
static int client_socket = -1;
static struct sockaddr_in server_addr;
static socklen_t server_addr_size = sizeof(server_addr);
// udp client 创建
uint8_t UDP_Client_Create(char *server_ip, int server_port)
{
// 1. 创建 socket
client_socket = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if(client_socket == -1)
{
osal_printk(" [UDP_Client_Create] 1. lwip_socket FAILURE! \r\n");
return 0;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
server_addr.sin_addr.s_addr = inet_addr(server_ip);
server_addr_size = sizeof(server_addr);
osal_printk(" [UDP_Client_Create] 0. SUCCESS. \r\n");
return 1;
}
// udp 接收 数据
uint8_t UDP_Client_Recv_Data(uint8_t *recv_buff, uint8_t buff_len)
{
ssize_t len = lwip_recvfrom(client_socket, recv_buff, (size_t)buff_len, MSG_DONTWAIT, NULL, 0);
if(len == -1)
{
return 0;
}
osal_printk(" UDP Recv Data : %d \r\n", len);
return (uint8_t)len;
}
// udp 发送 数据
uint8_t UDP_Client_Send_Data(uint8_t *send_buff, uint8_t buff_len)
{
ssize_t len = lwip_sendto(client_socket, send_buff, (size_t)buff_len, MSG_DONTWAIT, (struct sockaddr *)&server_addr, server_addr_size);
if(len == -1)
{
return 0;
}
osal_printk(" UDP Send Data : %d \r\n", len);
return (uint8_t)len;
}
// udp 关闭 socket
void UDP_Client_Close(void)
{
if(client_socket != -1)
{
lwip_close(client_socket);
osDelay(20);
client_socket = -1;
}
osal_printk(" [UDP_Client_Close] \r\n");
}
hi3863 串口通信
cpp
/**
*
* hi3863
*
* 串口通讯
*
* 2025 12 21
*
**/
#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"
#include "watchdog.h"
#include "pinctrl.h"
#include "gpio.h"
#include "uart.h"
#include "uart_comm.h"
#define BAUD_RATE 115200 // 波特率
#define DATA_BITS UART_DATA_BIT_8 // uart_data_bit_t UART数据位为8-bit
#define STOP_BITS UART_STOP_BIT_1 // uart_stop_bit_t UART停止位为1-bit
#define PARITY UART_PARITY_NONE // uart_parity_t UART无奇偶校验
// 串口 接收 缓存
static uint8_t uart_buffer[UART_BUFF_SIZE] = {0};
// 串口 接收 标志 接收数据的长度
static uint16_t uart_rx_len = 0;
// 串口 接收 回调
static void UART_RX_Callback(const void *buffer, uint16_t length, bool error)
{
unused(error);
if (buffer == NULL || length == 0)
{
return;
}
// 接收到 length 个数据
uart_rx_len = length;
}
// 串口 初始化 成功 返回 0 失败 返回 错误代码
uint8_t UART_Pin_Init(void)
{
// 设置引脚复用模式
uapi_pin_set_mode(UART_TX_PIN, PIN_MODE_1);
uapi_pin_set_mode(UART_RX_PIN, PIN_MODE_1);
// UART PIN 配置
uart_pin_config_t pin_config = {
.tx_pin = UART_TX_PIN, // 发送引脚
.rx_pin = UART_RX_PIN, // 接收引脚
.cts_pin = PIN_NONE, // 发送就绪引脚
.rts_pin = PIN_NONE // 接收就绪引脚
};
// UART 基本属性定义
uart_attr_t attr = {
.baud_rate = BAUD_RATE, // UART 波特率
.data_bits = DATA_BITS, // UART 数据位
.stop_bits = STOP_BITS, // UART 停止位
.parity = PARITY // UART 奇偶校验位
};
// UART 缓存数据结构定义
uart_buffer_config_t buffer_config = {
.rx_buffer = uart_buffer, // 接收数据的 buffer 指针
.rx_buffer_size = UART_BUFF_SIZE // 接收 Buffer 的长度
};
// 1. 去初始化指定的串口 在初始化前先去初始化
if(uapi_uart_deinit(UART_BUS_ID) != ERRCODE_SUCC)
{
osal_printk(" [UART_Pin_Init] 1. uapi_uart_deinit FAILURE! \r\n");
return 0;
}
// 2. 初始化指定的串口
if(uapi_uart_init(UART_BUS_ID, &pin_config, &attr, NULL, &buffer_config) != ERRCODE_SUCC)
{
osal_printk(" [UART_Pin_Init] 2. uapi_uart_init FAILURE! \n");
return 0;
}
// 3. 注册接收回调函数
if(uapi_uart_register_rx_callback(UART_BUS_ID, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE, 1, UART_RX_Callback) != ERRCODE_SUCC)
{
osal_printk(" [UART_Pin_Init] 3. uapi_uart_register_rx_callback FAILURE! \n");
return 0;
}
osal_printk(" [UART_Pin_Init] 0. SUCCESS. \r\n");
return 1;
}
// 串口 去初始化
void UART_Pin_Deinit(void)
{
(void)uapi_uart_deinit(UART_BUS_ID);
uapi_pin_set_mode(UART_TX_PIN, HAL_PIO_FUNC_GPIO);
uapi_pin_set_mode(UART_RX_PIN, HAL_PIO_FUNC_GPIO);
}
// 串口 读取收到数据 有数据 返回 数据长度 没有数据 返回 0
uint8_t UART_Read_Data(uint8_t *buff, uint8_t size)
{
uint8_t len = (uint8_t)uart_rx_len;
if(len > 0)
{
(void)memcpy_s(buff, (size_t)size, uart_buffer, (size_t)len);
uart_rx_len = 0;
memset(uart_buffer, 0, UART_BUFF_SIZE);
return len;
}
return 0;
}
// 串口 发送数据 成功 返回 发送数据长度 失败 返回 0
uint8_t UART_Send_Data(uint8_t *buff, uint8_t size, uint8_t boot)
{
uint8_t data[UART_BUFF_SIZE + 2] = {0};
memcpy_s(data, (UART_BUFF_SIZE + 2), buff, (size_t)size);
uint32_t len = (uint32_t)size;
if(boot == 1)
{
data[size] = '\r';
data[size + 1] = '\n';
len += 2;
}
int32_t ret = uapi_uart_write(UART_BUS_ID, data, len, 0);
if(ret > 0)
{
return (uint8_t)ret;
}
return 0;
}
其他模块代码我前面都有写,这里不复述了。
注意说明一点,WiFi AP 模式,设置密码长度 最少要 8 位。

运行起来到是很流畅,但是肯定还有没发现的错误。
代码太多了,我浏览一遍都要花好长时间,代码写的也乱,也没法做详细的说明。
到最后做一个最小集成版,发出来。
然后下篇更新,小车动力部分。
Hi3863 Lite os 的 PWM非常有意思,感觉挺高级的,我这没见过什么世面,感觉非常好的功能。
下篇讲。