ESP32S3智能小车开发板【直流电机马达驱动】
本文介绍了一款基于ESP32S3核心板的低成本智能小车开发方案。该开发板集成数字麦克风、扬声器、四路马达控制和摄像头接口,采用ESP32S3-N16R8主控芯片,支持3.7V锂电池供电。系统采用Go语言开发的Fyne框架作为上位机界面,通过WebSocket实现通信。硬件驱动部分展示了TB6612电机驱动器的PWM控制代码,以及WebSocket协议解析指令的方法。最终实现了一个可通过上位机控制运
项目背景
大模型的浪潮还在汹涌的向前,伴随着大模型的浪潮,很多基于大模型的各种玩具类也迎来白热化的阶段,小编今天推荐一款低成本的核心板,可以基于这款开发版快速的打造一台包含智能对话的玩具小车。
硬件介绍
这款开发板包含:一路数字MIC,一路扬声器喇叭输出,可以同时控制四路直流马达,支持OV2610摄像头,供电支持18650 3.7V供电,电池充电保护,主控采用的是ESP32S3-N16R8所有的GPIO都已经引出,可以基于这款开发版,进行多种个性化应用的开发。https://item.taobao.com/item.htm?ft=t&id=965144824762

功能流程图

整体的控制流程
上位机
上位机使用GO语言Fyne框架开发,Fyne,这是一款基于 Go 语言开发的开源 GUI 框架。Fyne 旨在为开发者提供一种简单、现代且一致的用户界面体验,支持在 Windows、macOS、Linux 和 移动设备(iOS/Android) 上运行。 与其他 GUI 框架相比,Fyne 的最大特点是完全使用 Go 语言开发,结合了 Go 的简洁和高效特性,非常适合快速构建跨平台桌面和移动应用。

TB6612驱动
TB6612核心PWM的产生,使用的ESP-IDF LEDC模块,可以高精度的产生不同频率的PWM波形,通过不同频率的PWM,控制马达的转速,驱动的代码如下:
static void drv_gpio_init(int gpio, int level)
{
gpio_config_t gpio_conf = {
.pin_bit_mask = (1ULL << gpio), // 选择 GPIO
.mode = GPIO_MODE_OUTPUT, // 配置为输出模式
.pull_up_en = GPIO_PULLUP_DISABLE, // 关闭上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 关闭下拉
.intr_type = GPIO_INTR_DISABLE // 关闭中断
};
gpio_config(&gpio_conf);
gpio_set_level(gpio, level);
}
void esp32s3_bsp_motor_init(void)
{
ledc_timer_config_t motor_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = MOTOR_PWM_DUTY_RES,
.timer_num = MOTOR_PWM_TIMER,
.freq_hz = MOTOR_PWM_FREQUENCY_HZ, // Set output frequency at 4 kHz
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&motor_timer));
ledc_channel_config_t motor_channel_0 = {
.speed_mode = MOTOR_PWM_MODE,
.channel = MOTOR_PWM_A_CHANNEL,
.timer_sel = MOTOR_PWM_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = MOTOR_PWM_A_GPIO,
.duty = 0, // Set duty to 0%
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&motor_channel_0));
ledc_channel_config_t motor_channel_1 = {
.speed_mode = MOTOR_PWM_MODE,
.channel = MOTOR_PWM_B_CHANNEL,
.timer_sel = MOTOR_PWM_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = MOTOR_PWM_B_GPIO,
.duty = 0, // Set duty to 0%
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&motor_channel_1));
drv_gpio_init(MOTOR_A_CTL_GPIO_1, GPIO_LEVEL_LOW);
drv_gpio_init(MOTOR_A_CTL_GPIO_2, GPIO_LEVEL_LOW);
drv_gpio_init(MOTOR_B_CTL_GPIO_1, GPIO_LEVEL_LOW);
drv_gpio_init(MOTOR_B_CTL_GPIO_2, GPIO_LEVEL_LOW);
drv_gpio_init(MOTOR_C_CTL_GPIO_1, GPIO_LEVEL_LOW);
drv_gpio_init(MOTOR_C_CTL_GPIO_2, GPIO_LEVEL_LOW);
drv_gpio_init(MOTOR_D_CTL_GPIO_1, GPIO_LEVEL_LOW);
drv_gpio_init(MOTOR_D_CTL_GPIO_2, GPIO_LEVEL_LOW);
}
void esp32s3_bsp_set_gpio_level(int gpio, int level)
{
gpio_set_level(gpio, level);
}
void esp32s3_bsp_set_pwm_duty(int channel, int duty)
{
ESP_ERROR_CHECK(ledc_set_duty(MOTOR_PWM_MODE, channel, duty));
ESP_ERROR_CHECK(ledc_update_duty(MOTOR_PWM_MODE, channel));
}
Websoket客户端
通过websocket协议与上位机进行通信,解析上位机发送的指令,控制直流电机的速度,以及小车的方向,客户端的代码如下:
static void handle_motor_event(const char *event)
{
if (0 == strcmp(event,"stop")) {
app_motor_stop();
}
if (0 == strcmp(event,"left")) {
app_motor_left();
}
if (0 == strcmp(event,"right")) {
app_motor_right();
}
if (0 == strcmp(event,"forward")) {
app_motor_forward();
}
if (0 == strcmp(event,"backward")) {
app_motor_backward();
}
if (0 == strcmp(event,"break")) {
app_motor_break();
}
if (0 == strcmp(event,"forwardUp")) {
app_motor_forward_arm_up();
}
if (0 == strcmp(event,"forwardStop")) {
app_motor_forward_arm_stop();
}
if (0 == strcmp(event,"backwardUp")) {
app_motor_forward_arm_down();
}
if (0 == strcmp(event,"backwardStop")) {
app_motor_forward_arm_stop();
}
}
static void handle_pan_tilt_event(const char *event)
{
if (0 == strcmp(event,"up")) {
app_motor_backward_arm_up();
}
if (0 == strcmp(event,"down")) {
app_motor_backward_arm_down();
}
if (0 == strcmp(event,"stop")) {
app_motor_backward_arm_stop();
}
}
static void phrase_ws_recv_buffer(const char *buf)
{
char method[32] ={0};
char value[32] ={0};
cJSON *root = cJSON_Parse(buf);
if (NULL == root) {
ESP_LOGE(TAG, "Phrase mqtt recv buffer error");
return;
}
cJSON *filed_obj = cJSON_GetObjectItem(root,"method");
if (NULL != filed_obj) {
strncpy(method, filed_obj->valuestring, sizeof(method)-1);
}
filed_obj = cJSON_GetObjectItem(root,"value");
if (NULL != filed_obj) {
strncpy(value, filed_obj->valuestring, sizeof(value)-1);
}
if (0 == strcmp(method,"dirCtl")) {
handle_motor_event(value);
}
if (0 == strcmp(method,"ptzCtl")) {
handle_pan_tilt_event(value);
}
if (0 == strcmp(method,"fileSave")) {
strncpy(s_file_name,value,sizeof(s_file_name)-1);
}
if (0 == strcmp(method,"fileEnd")) {
memset(s_file_name, 0, sizeof(s_file_name));
if (NULL != s_save_file_fp) {
fclose(s_save_file_fp);
}
s_save_file_fp = NULL;
}
cJSON_Delete(root);
}
static void ws_tools_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
switch (event_id) {
case WEBSOCKET_EVENT_BEGIN:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_BEGIN");
break;
case WEBSOCKET_EVENT_CONNECTED:
s_camera_send_flag = 1;
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
break;
case WEBSOCKET_EVENT_DATA:
if (data->op_code == 0x08 && data->data_len == 2) {
ESP_LOGW(TAG, "Received closed message with code=%d", 256 * data->data_ptr[0] + data->data_ptr[1]);
}else {
if (data->data_len > 0) {
if (data->op_code == 0x1) {
memset(s_test_buf, 0, sizeof(s_test_buf));
memcpy(s_test_buf, data->data_ptr, data->data_len);
ESP_LOGI(TAG,"Receive Data %s",s_test_buf);
phrase_ws_recv_buffer(s_test_buf);
}
if (data->op_code == 0x2) {
save_data_file_to_spiffs(data->data_ptr,data->data_len);
}
}
}
break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
break;
case WEBSOCKET_EVENT_FINISH:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_FINISH");
break;
case WEBSOCKET_EVENT_CLOSED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CLOSED");
break;
}
}
void init_toos_ws_client()
{
s_send_pic = (struct camera_detach_pic_t *)malloc(sizeof(struct camera_detach_pic_t));
assert(s_send_pic);
esp_websocket_client_config_t websocket_cfg = {};
//websocket_cfg.uri = s_app_toos.ws_addr;
websocket_cfg.uri = APP_TOOLS_WS_ADDR;
websocket_cfg.reconnect_timeout_ms = 5000;
s_app_toos.ws_client = esp_websocket_client_init(&websocket_cfg);
esp_websocket_register_events(s_app_toos.ws_client, WEBSOCKET_EVENT_ANY, ws_tools_event_handler, (void *)s_app_toos.ws_client);
esp_websocket_client_start(s_app_toos.ws_client);
xTaskCreatePinnedToCore(camera_frame_send_task,"Camera Task", 4 * 1024, NULL, 5, NULL, 0);
}
代码仓库
https://gitee.com/acccode/smart-car
运行效果代码
智能小车
更多推荐



所有评论(0)