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
英文地址,請注明出處:http://en.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…