ESP32-C3_SMARTCAR

前言:
前面用stm32f103c8t6 rt-thread 写了个智能小车程序
这章用esp32-c3 重新来遍

1:环境
vscode+idf5.4
esp32-3c
找到一块MIN的底板 凑合用(138 cm左右)
一个L298N
一个船型开关,
一个6
65mm 2脚按钮
锂电池 18650
2 及电池盒(串联)
一个HC-RS04 超声波

2:接线及GPIO
超声波
#define TRIG_GPIO GPIO_NUM_6
#define ECHO_GPIO GPIO_NUM_7

PWM //如果接反,自行 修改 IN1 到IN4的 引脚 序号就可以,
这里已经用盖冒使能了,所以只要4个GPIO作为 PWM,.
也可以用 2个GPIO EN PWM ,再用4个GPIO 作为输出
#define IN1 GPIO_NUM_5
#define IN2 GPIO_NUM_4

#define IN3 GPIO_NUM_3
#define IN4 GPIO_NUM_2

// 按键引脚定义
#define KEY_GPIO GPIO_NUM_8

2节锂电池 直接 给L298N VS: 电机电压供应引脚,注意不是逻辑引脚,(逻辑引脚靠近 IN1-IN4)
接7-12V 电池,刚好 逻辑引脚可以输出 给MCU 供电,省了一个降压模块

超声波支架断了,用绳子绑下 凑合下
在这里插入图片描述

在这里插入图片描述

3:上代码

#include <stdio.h>
#include "driver/gpio.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_rom_sys.h"  // 添加此头文件以使用 esp_rom_delay_us()//pwm
#include "driver/ledc.h"//wifi
// WiFi配置
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#define WIFI_SSID "*****"
#define WIFI_PASSWORD "*******"
#define SERVER_HOST "192.168.1.3"
static EventGroupHandle_t s_wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;// 引脚定义
// #define TRIG_GPIO  GPIO_NUM_0
// #define ECHO_GPIO  GPIO_NUM_1#define TRIG_GPIO  GPIO_NUM_6
#define ECHO_GPIO  GPIO_NUM_7// 超时时间(毫秒),对应约5米距离
#define MEASURE_TIMEOUT_MS 300  //50m 左右,足够足够了// 超声波测量距离阈值(cm)
#define OBSTACLE_THRESHOLD 20
//#define TAG "test_sr04"
// 增加超时时间(对应约10米)// L298N电机驱动引脚定义
// #define IN1 GPIO_NUM_12
// #define IN2 GPIO_NUM_13
// #define IN3 GPIO_NUM_19
// #define IN4 GPIO_NUM_18
// #define IN1 GPIO_NUM_4
// #define IN2 GPIO_NUM_5
// #define IN3 GPIO_NUM_2
// #define IN4 GPIO_NUM_3#define IN1 GPIO_NUM_5
#define IN2 GPIO_NUM_4#define IN3 GPIO_NUM_3
#define IN4 GPIO_NUM_2// 按键引脚定义
#define KEY_GPIO GPIO_NUM_8// PWM配置参数
#define PWM_FREQ_HZ       1000    // 电机PWM频率(1kHz)
#define PWM_RESOLUTION    LEDC_TIMER_8_BIT  // 8位分辨率(0-255)
#define PWM_TIMER         LEDC_TIMER_0      // 定时器编号
#define PWM_CH_IN1        LEDC_CHANNEL_0
#define PWM_CH_IN2        LEDC_CHANNEL_1
#define PWM_CH_IN3        LEDC_CHANNEL_2
#define PWM_CH_IN4        LEDC_CHANNEL_3// 任务句柄
static TaskHandle_t auto_nav_task_handle = NULL;// 状态变量
static volatile bool is_running = false;
static volatile bool key_pressed = false;// 日志标签
static const char *TAG = "esp32c3_car";// 事件队列
QueueHandle_t ultrasonic_event_queue = NULL;// 测量状态
typedef enum {MEASURE_IDLE,WAITING_ECHO_START,WAITING_ECHO_END
} measure_state_t;// 事件类型
typedef enum {ECHO_RISING_EDGE,ECHO_FALLING_EDGE,MEASURE_TIMEOUT
} ultrasonic_event_t;typedef struct 
{int64_t  curtick;ultrasonic_event_t  event;}ultrasonic_queue_t;// 测量数据结构
typedef struct {int64_t start_time;int64_t end_time;measure_state_t state;esp_timer_handle_t timeout_timer;
} ultrasonic_measure_t;// 全局测量实例
ultrasonic_measure_t measure;volatile bool     g_carstate = false; // false 停止  true 运行
//////PWM///////////////////////////////////////////////////////
// PWM初始化函数
void motor_pwm_init() {{gpio_config_t motor_lock_conf = {.pin_bit_mask = (1ULL << IN1) | (1ULL << IN2) | (1ULL << IN3) | (1ULL << IN4),.mode = GPIO_MODE_OUTPUT,.pull_up_en = GPIO_PULLUP_DISABLE,.pull_down_en = GPIO_PULLDOWN_ENABLE,  // 启用内部下拉.intr_type = GPIO_INTR_DISABLE};gpio_config(&motor_lock_conf);// 强制拉低所有引脚gpio_set_level(IN1, 0);gpio_set_level(IN2, 0);gpio_set_level(IN3, 0);gpio_set_level(IN4, 0);// 添加硬件级延时(不可中断)esp_rom_delay_us(5000);  // 5ms延时确保电平稳定ESP_LOGI(TAG, "IN1-IN4 LOW FINISH");}//LEDC_TIMER_13_BIT// 配置PWM定时器ledc_timer_config_t timer_cfg = {.speed_mode       = LEDC_LOW_SPEED_MODE,//高速模式-LEDC_LOW_SPEED_MODE,低速模式-LEDC_SPEED_MODE_MAX.timer_num        = PWM_TIMER,  //通道的定时器源(0 -> LEDC_TIMER_MAX - 1).duty_resolution  = PWM_RESOLUTION, // LEDC通道占空比分辨率.freq_hz          = PWM_FREQ_HZ,    //LEDC定时器频率(赫兹)0到2^(8)-1 =>0->255 // 0->(2^(PWM_RESOLUTION) -1).clk_cfg          = LEDC_AUTO_CLK};ESP_ERROR_CHECK(ledc_timer_config(&timer_cfg));// 配置4个PWM通道(IN1-IN4)ledc_channel_config_t ch_cfg[] = {{.gpio_num   = IN1,.speed_mode = LEDC_LOW_SPEED_MODE,.channel    = PWM_CH_IN1,.timer_sel  = PWM_TIMER, //选择通道的定时器源(0 - LEDC_TIMER_MAX - 1).duty       = 0, //LEDC通道占空比,占空比设置范围是[0, (2^占空比分辨率)].hpoint     = 0},{.gpio_num   = IN2,.speed_mode = LEDC_LOW_SPEED_MODE,.channel    = PWM_CH_IN2,.timer_sel  = PWM_TIMER,.duty       = 0,.hpoint     = 0},{.gpio_num   = IN3,.speed_mode = LEDC_LOW_SPEED_MODE,.channel    = PWM_CH_IN3,.timer_sel  = PWM_TIMER,.duty       = 0,.hpoint     = 0},{.gpio_num   = IN4,.speed_mode = LEDC_LOW_SPEED_MODE,.channel    = PWM_CH_IN4,.timer_sel  = PWM_TIMER,.duty       = 0,.hpoint     = 0}};for (int i = 0; i < 4; i++) {ESP_ERROR_CHECK(ledc_channel_config(&ch_cfg[i]));}ESP_LOGI(TAG, "电机PWM初始化完成");
}// 设置PWM占空比
static void set_pwm_duty(ledc_channel_t ch, uint8_t duty) {// duty=0;ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, ch, duty));ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, ch));
}// 电机控制函数(带PWM调速)
void motor_forward(uint8_t speed) {g_carstate  = true;set_pwm_duty(PWM_CH_IN1, speed);set_pwm_duty(PWM_CH_IN2, 0);set_pwm_duty(PWM_CH_IN3, speed);set_pwm_duty(PWM_CH_IN4, 0);
}void motor_backward(uint8_t speed) {g_carstate  = true;set_pwm_duty(PWM_CH_IN1, 0);set_pwm_duty(PWM_CH_IN2, speed);set_pwm_duty(PWM_CH_IN3, 0);set_pwm_duty(PWM_CH_IN4, speed);
}void motor_turn_left(uint8_t speed) {g_carstate  = true;// set_pwm_duty(PWM_CH_IN1, 0);// set_pwm_duty(PWM_CH_IN2, 0);// set_pwm_duty(PWM_CH_IN3, speed);// set_pwm_duty(PWM_CH_IN4, 0);set_pwm_duty(PWM_CH_IN1, 0);set_pwm_duty(PWM_CH_IN2, 0);set_pwm_duty(PWM_CH_IN3, speed);set_pwm_duty(PWM_CH_IN4, 0);}void motor_turn_spin_eft(uint8_t speed) {g_carstate  = true;set_pwm_duty(PWM_CH_IN1, 0);set_pwm_duty(PWM_CH_IN2, speed);set_pwm_duty(PWM_CH_IN3, speed);set_pwm_duty(PWM_CH_IN4, 0);
}void motor_turn_right(uint8_t speed) {g_carstate  = true;// set_pwm_duty(PWM_CH_IN1, speed);// set_pwm_duty(PWM_CH_IN2, 0);// set_pwm_duty(PWM_CH_IN3, 0);// set_pwm_duty(PWM_CH_IN4, 0);set_pwm_duty(PWM_CH_IN1, speed);set_pwm_duty(PWM_CH_IN2, 0);set_pwm_duty(PWM_CH_IN3, 0);set_pwm_duty(PWM_CH_IN4, 0);
}void motor_turn_spin_right(uint8_t speed) {g_carstate  = true;set_pwm_duty(PWM_CH_IN1, speed);set_pwm_duty(PWM_CH_IN2, 0);set_pwm_duty(PWM_CH_IN3, 0);set_pwm_duty(PWM_CH_IN4, speed);
}void motor_stop() {g_carstate  = false;set_pwm_duty(PWM_CH_IN1, 0);set_pwm_duty(PWM_CH_IN2, 0);set_pwm_duty(PWM_CH_IN3, 0);set_pwm_duty(PWM_CH_IN4, 0);
}
///////////////////////////////////////////////////////////////
//按键
// 按键中断处理
static QueueHandle_t gpio_evt_queue = NULL;
static bool  b_keypress = false;
//static volatile bool is_running = false;
static void IRAM_ATTR key_isr_handler(void* arg) {uint32_t gpio_num = (uint32_t) arg;int level = gpio_get_level(gpio_num);if (level == 0) {b_keypress = true;} else {if (b_keypress) {b_keypress = false;xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);}}
}// 按键处理任务
static void key_task(void* arg) {uint32_t io_num;while (1) {if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {if (io_num == KEY_GPIO) {is_running = !is_running;if (!is_running) {// motor_stop();} else {vTaskResume(auto_nav_task_handle);}ESP_LOGI(TAG, "statechange: %s", is_running ? "running" : "stop");}}}
}/////////////////////////////////////////////////////////////
// 中断服务程序
static void IRAM_ATTR echo_isr_handler(void* arg) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;// ultrasonic_event_t event;ultrasonic_queue_t  event;// 获取准确的边缘类型uint32_t gpio_num = (uint32_t)arg;int level = gpio_get_level(gpio_num);event.curtick = esp_timer_get_time();if (level) {event.event = ECHO_RISING_EDGE;} else {event.event = ECHO_FALLING_EDGE;}// 发送事件到队列xQueueSendFromISR(ultrasonic_event_queue, &event, &xHigherPriorityTaskWoken);if (xHigherPriorityTaskWoken) {portYIELD_FROM_ISR();}
}// 超时定时器回调
static void timer_timeout_callback(void* arg) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;ultrasonic_queue_t event ;event.event = MEASURE_TIMEOUT;event.curtick = esp_timer_get_time();// 发送超时事件xQueueSendFromISR(ultrasonic_event_queue, &event, &xHigherPriorityTaskWoken);if (xHigherPriorityTaskWoken) {portYIELD_FROM_ISR();}
}// 初始化超声波模块
void ultrasonic_init() {// 配置Trig为输出gpio_config_t trig_conf = {.pin_bit_mask = (1ULL << TRIG_GPIO),.mode = GPIO_MODE_OUTPUT,.pull_up_en = GPIO_PULLUP_DISABLE,.pull_down_en = GPIO_PULLDOWN_DISABLE,.intr_type = GPIO_INTR_DISABLE};gpio_config(&trig_conf);gpio_set_level(TRIG_GPIO, 0);// 配置Echo为输入,启用双边沿中断gpio_config_t echo_conf = {.pin_bit_mask = (1ULL << ECHO_GPIO),.mode = GPIO_MODE_INPUT,.pull_up_en = GPIO_PULLUP_DISABLE,.pull_down_en = GPIO_PULLDOWN_ENABLE,.intr_type = GPIO_INTR_ANYEDGE};gpio_config(&echo_conf);// 创建事件队列ultrasonic_event_queue = xQueueCreate(10, sizeof(ultrasonic_queue_t));// 安装GPIO中断服务(使用特定核心)gpio_install_isr_service(ESP_INTR_FLAG_IRAM);// 配置中断处理程序(传递GPIO编号作为参数)gpio_isr_handler_add(ECHO_GPIO, echo_isr_handler, (void*)ECHO_GPIO);// 初始化测量状态measure.state = MEASURE_IDLE;measure.timeout_timer = NULL;
}// 启动一次测量
void ultrasonic_start_measurement() {if (measure.state != MEASURE_IDLE) {printf("Measurement already in progress\n");return;}// 确保ECHO引脚空闲(低电平)if (gpio_get_level(ECHO_GPIO) != 0) {printf("WARNING: ECHO not idle before measurement!\n");return;}// 发送10us脉冲gpio_set_level(TRIG_GPIO, 1);esp_rom_delay_us(15);gpio_set_level(TRIG_GPIO, 0);// 设置测量状态measure.state = WAITING_ECHO_START;// 创建超时定时器const esp_timer_create_args_t timeout_timer_args = {.callback = &timer_timeout_callback,.arg = NULL,.name = "timeout_timer"};ESP_ERROR_CHECK(esp_timer_create(&timeout_timer_args, &measure.timeout_timer));ESP_ERROR_CHECK(esp_timer_start_once(measure.timeout_timer, MEASURE_TIMEOUT_MS * 1000));
}// 处理超声波事件
float ultrasonic_process_events() {ultrasonic_event_t event;ultrasonic_queue_t eventmain ;float distance = -1.0;// 等待事件(最多100ms)if (xQueueReceive(ultrasonic_event_queue, &eventmain, pdMS_TO_TICKS(100))) {event = eventmain.event ;switch (measure.state) {case WAITING_ECHO_START:if (event == ECHO_RISING_EDGE) {measure.start_time = eventmain.curtick;//esp_timer_get_time();measure.state = WAITING_ECHO_END;//  ESP_LOGI(TAG,"Rising edge detected[%lld]",eventmain.curtick);} else if (event == MEASURE_TIMEOUT) {ESP_LOGI(TAG,"Timeout waiting for rising edge. Echo level: %d->%lld", gpio_get_level(ECHO_GPIO),eventmain.curtick);measure.state = MEASURE_IDLE;//这里可能是空旷地,声波返回不了distance = 99;//默认99cm //给个默认距离好了}break;case WAITING_ECHO_END:if (event == ECHO_FALLING_EDGE) {measure.end_time = eventmain.curtick;//esp_timer_get_time();uint32_t duration = measure.end_time - measure.start_time;//us 1s=340 1ms=0.34m  1us// 有效距离检查(2cm - 4m)if (duration > 117 && duration < 23529) {distance = (duration * 0.034) / 2.0; //(因为1米=100厘米,所以340米/秒 = 34000厘米/秒 = 34厘米/毫秒 = 0.034厘米/微秒)//   ESP_LOGI(TAG,"Falling edge detected[%lld]",eventmain.curtick);} else {ESP_LOGI(TAG,"Invalid duration: %d us\n",(int) duration);}measure.state = MEASURE_IDLE;} else if (event == MEASURE_TIMEOUT) {ESP_LOGI(TAG,"Timeout waiting for falling edge. Echo level: %d->%lld", gpio_get_level(ECHO_GPIO),eventmain.curtick);measure.state = MEASURE_IDLE;}break;default:ESP_LOGI(TAG,"Unexpected event in state: %d\n", (int)(measure.state));break;}// 清理定时器if (measure.timeout_timer && (event == ECHO_FALLING_EDGE || event == MEASURE_TIMEOUT)) {esp_timer_stop(measure.timeout_timer);esp_timer_delete(measure.timeout_timer);measure.timeout_timer = NULL;}} else {ESP_LOGI(TAG,"No event received in ultrasonic_process_events\n");}return distance;
}// 诊断函数:检查GPIO状态
void check_gpio_states() {ESP_LOGI(TAG,"TRIG state: %d, ECHO state: %d\n", gpio_get_level(TRIG_GPIO),gpio_get_level(ECHO_GPIO));
}// 手动触发中断(用于测试)
void simulate_echo_signal() {ESP_LOGI(TAG,"Simulating ECHO signal...\n");// 模拟上升沿gpio_set_level(ECHO_GPIO, 1);vTaskDelay(pdMS_TO_TICKS(1));// 模拟下降沿(实际距离对应5cm)gpio_set_level(ECHO_GPIO, 0);
}//const float  u8_Percent = 100/80 ;
//3.7*2  锂电池  空占比 要少小点,不然速度太快了,速度可以自行调整
const uint8_t NORMAL_SPEED = 100;    // 正常速度(~60%)150
const uint8_t TURN_SPEED = 100;      // 转向速度(~40%)100
const uint8_t BACK_SPEED = 120;      // 后退速度(~50%)120
// 自动寻路任务
static void auto_navigation_task(void* arg) {int16_t measured_distance = 0;int16_t  flag_run = 0;int64_t  loop_start =0;int64_t  loop_end =0;#define  CHECK_STATE()  if(!is_running) continue;while (1) {if (is_running) {flag_run=0;loop_start = esp_timer_get_time();ultrasonic_start_measurement();// 等待测量完成// vTaskDelay(pdMS_TO_TICKS(30)); //额外增加的 34厘米/毫秒 //30ms = 30 *34 = 1m //不要加,要实时取消息处理//     // 处理事件直到测量完成或超时float distance = -1.0;while (measure.state != MEASURE_IDLE) {distance = ultrasonic_process_events();vTaskDelay(pdMS_TO_TICKS(1)); // 防止任务阻塞}if(distance >0.0001f){measured_distance =  distance ;}else{measured_distance = 0;}//   measured_distance =30;//   ESP_LOGI(TAG,"Distance: %d cm\n", measured_distance);//  measured_distance =0;if (measured_distance <= 0 ) {//|| measured_distance > 5000// 测量错误,停止flag_run =1;motor_stop();vTaskDelay(pdMS_TO_TICKS(500));} else if (measured_distance < OBSTACLE_THRESHOLD) {if(g_carstate){motor_stop();vTaskDelay(pdMS_TO_TICKS(500));}CHECK_STATE()// 检测到障碍物,后退并转向motor_backward(BACK_SPEED);vTaskDelay(pdMS_TO_TICKS(500));CHECK_STATE()motor_stop();vTaskDelay(pdMS_TO_TICKS(500));CHECK_STATE()// 随机转向if (rand() % 2 == 0) {flag_run =2;motor_turn_left(TURN_SPEED);} else {flag_run =3;motor_turn_right(TURN_SPEED);}vTaskDelay(pdMS_TO_TICKS(500));CHECK_STATE()//////////////////////////////////////motor_stop();vTaskDelay(pdMS_TO_TICKS(500));} else {// 无障碍物,前进flag_run =4;motor_forward(NORMAL_SPEED);vTaskDelay(pdMS_TO_TICKS(10)); //额外增加的}loop_end = esp_timer_get_time();//  motor_stop();// vTaskDelay(pdMS_TO_TICKS(500));ESP_LOGI(TAG,"Distance: %d cm %d(%lld->%lld)", measured_distance,flag_run,loop_start,loop_end);} else if(g_carstate){motor_stop();vTaskDelay(pdMS_TO_TICKS(500)); //}else{vTaskSuspend(NULL);vTaskDelay(pdMS_TO_TICKS(100));}}
}
//wifi
// WiFi事件处理函数
static void event_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data) {if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {esp_wifi_connect();} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {esp_wifi_connect();xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);ESP_LOGW(TAG, "WiFi disconnect,try again...");} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;ESP_LOGI(TAG, "get IP address: " IPSTR, IP2STR(&event->ip_info.ip));xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);}
}// WiFi初始化////////////////////////////////////////////////////////
void wifi_init_sta(void) {s_wifi_event_group = xEventGroupCreate();ESP_ERROR_CHECK(esp_netif_init());ESP_ERROR_CHECK(esp_event_loop_create_default());esp_netif_create_default_wifi_sta();wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));wifi_config_t wifi_config = {.sta = {.ssid = WIFI_SSID,.password = WIFI_PASSWORD,},};ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));ESP_ERROR_CHECK(esp_wifi_start());ESP_LOGI(TAG, "WiFi init finish,connect %s...", WIFI_SSID);
}void init_wifi(){esp_err_t ret = nvs_flash_init();if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase());ret = nvs_flash_init();}ESP_ERROR_CHECK(ret);wifi_init_sta();xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT,pdFALSE, pdTRUE, portMAX_DELAY);
}
/////////////////////////////////////////////////////////////////////////////
void  test_ultrasonic(){// int64_t  loop_start = esp_timer_get_time();ultrasonic_start_measurement();// 等待测量完成// vTaskDelay(pdMS_TO_TICKS(30)); //额外增加的 34厘米/毫秒 //30ms = 30 *34 = 1m //不要加,要实时取消息处理//     // 处理事件直到测量完成或超时float distance = -1.0;while (measure.state != MEASURE_IDLE) {distance = ultrasonic_process_events();vTaskDelay(pdMS_TO_TICKS(1)); // 防止任务阻塞} ESP_LOGI(TAG,"test_ultrasonic: %.2f cm", distance);
}//
void test_montor(){{test_ultrasonic();}//测试小车状态for(int i=0;i<2;i++){ESP_LOGI(TAG, "motor_forward");for(int j=0;j<10;j++){motor_forward(NORMAL_SPEED);vTaskDelay(pdMS_TO_TICKS(500)); //motor_stop();vTaskDelay(pdMS_TO_TICKS(1500)); //}ESP_LOGI(TAG, "motor_backward");for(int j=0;j<3;j++){motor_backward(BACK_SPEED);vTaskDelay(pdMS_TO_TICKS(500)); //motor_stop();vTaskDelay(pdMS_TO_TICKS(1500)); //}ESP_LOGI(TAG, "motor_turn_left");for(int j=0;j<3;j++){motor_turn_left(TURN_SPEED);vTaskDelay(pdMS_TO_TICKS(500)); //motor_stop();vTaskDelay(pdMS_TO_TICKS(1500)); //}ESP_LOGI(TAG, "motor_turn_right");for(int j=0;j<3;j++){motor_turn_right(TURN_SPEED);vTaskDelay(pdMS_TO_TICKS(500)); //motor_stop();vTaskDelay(pdMS_TO_TICKS(1500)); //}{test_ultrasonic();}}}
////////////////////////////////////////////////////////////////////////////////
void app_main() {// 1. 初始化PWM并立即停止电机motor_pwm_init();motor_stop();   // 确保所有占空比为0// 2. 初始化WiFi(如果不需要可以去掉)init_wifi();// 3. 初始化超声波ultrasonic_init();// 4. 配置按键引脚(非PWM的GPIO)// 配置按键引脚为输入,并设置中断gpio_config_t key_conf = {.pin_bit_mask = (1ULL << KEY_GPIO),.mode = GPIO_MODE_INPUT,.pull_up_en = GPIO_PULLUP_ENABLE,.pull_down_en = GPIO_PULLDOWN_DISABLE,.intr_type = GPIO_INTR_ANYEDGE};gpio_config(&key_conf);// 5. 创建按键事件队列并添加中断处理gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));gpio_isr_handler_add(KEY_GPIO, key_isr_handler, (void*)KEY_GPIO);// 6. 创建任务xTaskCreate(key_task, "key_task", 2048, NULL, 5, NULL);if(0){vTaskDelay(pdMS_TO_TICKS(2000)); //
//          const uint8_t NORMAL_SPEED = 150;    // 正常速度(~60%)
// const uint8_t TURN_SPEED = 100;      // 转向速度(~40%)
// const uint8_t BACK_SPEED = 120;      // 后退速度(~50%)test_montor();}xTaskCreate(auto_navigation_task, "auto_nav_task", 4096, NULL, 4, &auto_nav_task_handle);ESP_LOGI(TAG, "System initialization complete");
}

4: 测试结果 如果对你又帮助,麻烦点个赞,加个关注
问题,挂电池的那边 重,有点不平衡,电池没地方装了,没办法了
其他的 传感器度没地方装啊,底板太小了
在这里插入图片描述

测试视频

esp3c-c3_smartcar

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93694.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消费者API

目录独立消费者案例&#xff08;订阅主题&#xff09;独立消费者案例&#xff08;订阅分区&#xff09;消费者组案例独立消费者案例&#xff08;订阅主题&#xff09; package com.tsg.kafka.consumer;import org.apache.kafka.clients.consumer.ConsumerConfig; import org.ap…

C# NX二次开发:操作按钮控件Button和标签控件Label详解

大家好&#xff0c;今天介绍ug二次开发过程中的一个叫操作按钮的控件&#xff0c;这个控件在块UI编辑器中可以使用。 ​ Button这个控件的属性和方法如下所示&#xff1a; namespace NXOpen.BlockStyler { public class Label : UIBlock { protected intern…

Vue.prototype 的作用

在 Vue.js 中&#xff0c;Vue.prototype 是用来向所有 Vue 实例添加属性或方法的机制。通过它添加的属性或方法可以在所有 Vue 组件实例中通过 this 访问。主要作用添加全局方法或属性&#xff1a;可以在所有组件中使用的工具方法或常量扩展 Vue 功能&#xff1a;添加 Vue 本身…

Javaee 多线程 --进程和线程之间的区别和联系

文章目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnable(接口)&#xff0c;重写run继承Thread,重写run,但是使用匿名内部类实现Runnable(接口)&#xff0c;重写run&#xff0c;但是使用匿名内部类使用lambda表达式请说明Thread类中run和…

企业如何让内部视频仅限指定域名播放,确保视频不被泄露?

在数字化办公时代&#xff0c;企业内部的培训视频、产品演示或机密会议录像等敏感内容&#xff0c;一旦被非法传播或泄露&#xff0c;可能带来严重的商业风险。如何确保这些视频只能在公司官网或指定域名播放&#xff0c;防止被恶意下载、盗链或二次传播&#xff1f;今天介绍一…

端口映射原理操作详解教程:实现外网访问内网服务,本地路由器端口映射公网ip和软件端口映射域名2种方法

端口映射作为一种不同网络间通信的关键网络技术&#xff0c;在远程访问和内外网连接服务需求日益增长的如今&#xff0c;理解端口映射的原理和设置方法是确保网络服务可用性的必要技能。本文将深入探讨端口映射的基本概念、路由器端口映射设置步骤以及无公网IP用端口映射软件映…

【PyTorch】多对象分割项目

对象分割任务的目标是找到图像中目标对象的边界。实际应用例如自动驾驶汽车和医学成像分析。这里将使用PyTorch开发一个深度学习模型来完成多对象分割任务。多对象分割的主要目标是自动勾勒出图像中多个目标对象的边界。 对象的边界通常由与图像大小相同的分割掩码定义&#xf…

SSH 使用密钥登录服务器

用这种方法远程登陆服务器的时候无需手动输入密码 具体步骤 客户端通过 ssh-keygen 生成公钥和私钥 ssh-keygen -t rsa 生成的时候会有一系列问题&#xff0c;根据自己的需要选择就行。生成的结果为两个文件&#xff1a; 上传公钥至服务器&#xff0c;上述两个文件一般在客户…

MySQL 8.4 企业版启用TDE功能和表加密

一、系统环境操作系统&#xff1a;Ubuntu 24.04 数据库:8.4.4-commercial for Linux on x86_64 (MySQL Enterprise Server - Commercial)二、安装TDE组件前提&#xff1a;检查组件文件是否存在ls /usr/lib/mysql/plugin/component_keyring_encrypted_file.so1.配置全局清单文件…

【Altium designer】导出的原理图PDF乱码异常的解决方法

一、有些电源名字无法显示或器件丢失 解决办法 (1)首先AD18以及以上的新版本AD不存在该问题。 (2)其次AD17以及更旧版本的AD很可能遇到该问题,参考如下博客笔记进行操作即可: 大致的操作如下:DXP → Preferences → Schematic → Options里面“Render Text with GDI+”…

4.Ansible自动化之-部署文件到主机

4 - 部署文件到受管主机 实验环境 先通过以下命令搭建基础环境&#xff08;创建工作目录、配置 Ansible 环境和主机清单&#xff09;&#xff1a; # 在控制节点&#xff08;controller&#xff09;上创建web目录并进入&#xff0c;作为工作目录 [bqcontroller ~]$ mkdir web &a…

Vuex的使用

Vuex 超详细使用教程&#xff08;从入门到精通&#xff09;一、Vuex 是什么&#xff1f;Vuex 是专门为 Vue.js 设计的状态管理库&#xff0c;它采用集中式存储管理应用的所有组件的状态。简单来说&#xff0c;Vuex 就是一个"全局变量仓库"&#xff0c;所有组件都可以…

pytorch 数据预处理,加载,训练,可视化流程

流程定义自定义数据集类定义训练和验证的数据增强定义模型、损失函数和优化器训练循环&#xff0c;包括验证训练可视化整个流程模型评估高级功能扩展混合精度训练​分布式训练​{:width“50%” height“50%”} 定义自定义数据集类 # #1. 自定义数据集类 # class CustomImageD…

Prompt工程:OCR+LLM文档处理的精准制导系统

在PDF OCR与大模型结合的实际应用中&#xff0c;很多团队会发现一个现象&#xff1a;同样的OCR文本&#xff0c;不同的Prompt设计会产生截然不同的提取效果。有时候准确率能达到95%&#xff0c;有时候却只有60%。这背后的关键就在于Prompt工程的精细化程度。 &#x1f3af; 为什…

RecSys:粗排模型和精排特征体系

粗排 在推荐系统链路中&#xff0c;排序阶段至关重要&#xff0c;通常分为召回、粗排和精排三个环节。粗排作为精排前的预处理阶段&#xff0c;需要在效果和性能之间取得平衡。 双塔模型 后期融合&#xff1a;把用户、物品特征分别输入不同的神经网络&#xff0c;不对用户、…

spring声明式事务,finally 中return对事务回滚的影响

finally 块中使用 return 是一个常见的编程错误&#xff0c;它会&#xff1a; 跳过正常的事务提交流程。吞掉异常&#xff0c;使错误处理失效 导致不可预测的事务行为Java 中 finally 和 return 的执行机制&#xff1a;1. finally 块的基本特性 在 Java 中&#xff0c;finally …

WPF 打印报告图片大小的自适应(含完整示例与详解)

目标&#xff1a;在 FlowDocument 报告里&#xff0c;根据 1~6 张图片的数量&#xff0c; 自动选择 2 行 3 列 的最佳布局&#xff1b;在只有 1、2、4 张时保持“占满感”&#xff0c;打印清晰且不变形。规则一览&#xff1a;1 张 → 占满 23&#xff08;大图居中&#xff09;…

【AI大模型前沿】百度飞桨PaddleOCR 3.0开源发布,支持多语言、手写体识别,赋能智能文档处理

系列篇章&#x1f4a5; No.文章1【AI大模型前沿】深度剖析瑞智病理大模型 RuiPath&#xff1a;如何革新癌症病理诊断技术2【AI大模型前沿】清华大学 CLAMP-3&#xff1a;多模态技术引领音乐检索新潮流3【AI大模型前沿】浙大携手阿里推出HealthGPT&#xff1a;医学视觉语言大模…

迅为RK3588开发板Android12 制作使用系统签名

在 Android 源码 build/make/target/product/security/下存放着签名文件&#xff0c;如下所示&#xff1a;将北京迅为提供的 keytool 工具拷贝到 ubuntu 中&#xff0c;然后将 Android11 或 Android12 源码build/make/target/product/security/下的 platform.pk8 platform.x509…

Day08 Go语言学习

1.安装Go和Goland 2.新建demo项目实践语法并使用git实践版本控制操作 2.1 Goland配置 路径**&#xff1a;** GOPATH workspace GOROOT golang 文件夹&#xff1a; bin 编译后的可执行文件 pkg 编译后的包文件 src 源文件 遇到问题1&#xff1a;运行 ‘go build awesomeProject…