【DigKey好物畅想】乐鑫esp32c61基础开发
提起esp32c61大家肯定会想到他的卖点wifi,本篇文章带领大家利用C61和一个oled屏幕来制作一个通过wifi来设置阈值让其来检测区间电压,他的量程是0-3.3V,我们可以将其通过WiFi发送阈值,例如大于0V小于1V变成蓝色,大于1V小于2V变成绿色,大于2V小于3V变为黄色。
1.启动
必备硬件Á
-
ESP32-C61-DEVKITC-1-N8R2
-
USB-A 转 USB-C 数据线
-
电脑(Windows、Linux 或 macOS)
-
一块oled屏幕
-
一个电位计
里面有预先烧录程序按下boot后就会出现灯光切换的效果
现场效果如图
之后就可以来实现我们的通过ADC来判断再随便找个电位计来调节
2.实现
我这里用在得捷上买的4883作为屏幕,大家可以去上面搜,也可以自己随便找个屏幕,屏幕驱动代码最好自己写一遍,这样不容易出问题,我之前碰到过因为杜邦线太垃圾搞得我和人家屏幕一样但烧进去的代码照样运行不了的问题
所以现在明确我们需要一个接口作为PWM发送出电压,一个任意的ADC接口来连接通过wifi来设置阈值,最后来判断
我这边使用的是这个wifi调试助手,大家也可以用自己喜欢的(我是自己用惯了)
https://github.com/SGXNll/Android-WiFi-debugging-software-WiFi-e4a-
代码展示
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include “freertos/FreeRTOS.h”
#include “freertos/task.h”
#include “esp_log.h”
#include “nvs_flash.h”
#include “esp_wifi.h”
#include “esp_event.h”
#include “esp_http_server.h”
#include “driver/gpio.h”
#include “driver/spi_master.h”
#include “esp_adc/adc_oneshot.h”
#include “esp_lcd_panel_io.h”
#include “esp_lcd_panel_vendor.h”
#include “esp_lcd_panel_ops.h”
static const char *TAG = “COLOR_CTRL”;
#define ADC_UNIT ADC_UNIT_1
#define ADC_CHANNEL ADC_CHANNEL_4
#define LCD_HOST SPI2_HOST
#define PIN_NUM_MOSI 7
#define PIN_NUM_CLK 6
#define PIN_NUM_CS 8
#define PIN_NUM_DC 2
#define PIN_NUM_RST 3
#define WIFI_SSID “YOUR_WIFI_SSID”
#define WIFI_PASS “YOUR_WIFI_PASSWORD”
typedef struct {
int min_mv;
int max_mv;
uint16_t color;
} VoltageRule;
#define MAX_RULES 3
VoltageRule g_rules[MAX_RULES] = {
{0, 1000, 0x001F},
{1000, 2000, 0x07E0},
{2000, 3000, 0xFFE0}
};
adc_oneshot_unit_handle_t adc_handle;
static void wifi_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();
} 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, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
}
}
void wifi_init_sta(void) {
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL);
wifi_config_t wifi_config = { .sta = { .ssid = WIFI_SSID, .password = WIFI_PASS } };
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start();
}
esp_err_t set_rule_handler(httpd_req_t *req) {
char buf\[128\];
if (httpd_req_get_url_query_str(req, buf, sizeof(buf)) == ESP_OK) {
char param\[16\];
int id = -1;
if (httpd_query_key_value(buf, "id", param, sizeof(param)) == ESP_OK) id = atoi(param);
if (id >= 0 && id < MAX_RULES) {
if (httpd_query_key_value(buf, "min", param, sizeof(param)) == ESP_OK)
g_rules\[id\].min_mv = atoi(param);
if (httpd_query_key_value(buf, "max", param, sizeof(param)) == ESP_OK)
g_rules\[id\].max_mv = atoi(param);
if (httpd_query_key_value(buf, "color", param, sizeof(param)) == ESP_OK)
g_rules\[id\].color = (uint16_t)atoi(param);
httpd_resp_send(req, "Rule Updated OK", HTTPD_RESP_USE_STRLEN);
} else {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid Rule ID");
}
}
return ESP_OK;
}
void start_webserver(void) {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK) {
httpd_uri_t set_uri = { .uri = "/set_rule", .method = HTTP_GET, .handler = set_rule_handler };
httpd_register_uri_handler(server, &set_uri);
}
}
uint16_t get_color_from_voltage(int mv) {
for (int i = 0; i < MAX_RULES; i++) {
if (mv >= g_rules\[i\].min_mv && mv < g_rules\[i\].max_mv) {
return g_rules\[i\].color;
}
}
return 0x0000;
}
void init_adc() {
adc_oneshot_unit_init_cfg_t init_config = { .unit_id = ADC_UNIT };
adc_oneshot_new_unit(&init_config, &adc_handle);
adc_oneshot_chan_cfg_t config = { .bitwidth = ADC_BITWIDTH_DEFAULT, .atten = ADC_ATTEN_DB_12 };
adc_oneshot_config_channel(adc_handle, ADC_CHANNEL, &config);
}
void app_main(void) {
nvs_flash_init();
wifi_init_sta();
start_webserver();
init_adc();
spi_bus_config_t buscfg = {
.sclk_io_num = PIN_NUM_CLK, .mosi_io_num = PIN_NUM_MOSI, .miso_io_num = -1,
.quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 240\*135\*2+8
};
spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = PIN_NUM_DC, .cs_gpio_num = PIN_NUM_CS, .pclk_hz = 40\*1000\*1000,
.lcd_cmd_bits = 8, .lcd_param_bits = 8, .spi_mode = 0, .trans_queue_depth = 10,
};
esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle);
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = PIN_NUM_RST, .rgb_endian = LCD_RGB_ENDIAN_RGB, .bits_per_pixel = 16 };
esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle);
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
esp_lcd_panel_invert_color(panel_handle, true);
esp_lcd_panel_swap_xy(panel_handle, true);
esp_lcd_panel_mirror(panel_handle, false, true);
esp_lcd_panel_set_gap(panel_handle, 40, 52);
esp_lcd_panel_disp_on_off(panel_handle, true);
uint16_t \*buf = heap_caps_malloc(240 \* 135 \* 2, MALLOC_CAP_DMA);
uint16_t current_disp_color = 0xFFFF;
while (1) {
int adc_raw;
adc_oneshot_read(adc_handle, ADC_CHANNEL, &adc_raw);
int voltage_mv = adc_raw \* 3300 / 4095;
uint16_t target_color = get_color_from_voltage(voltage_mv);
if (target_color != current_disp_color) {
uint16_t swap_color = (target_color >> 8) | (target_color << 8);
for (int i = 0; i < 240 \* 135; i++) buf\[i\] = swap_color;
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 240, 135, buf);
current_disp_color = target_color;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
实现效果
我们可以通过这个屏幕来实时判断电压的范围
原理逻辑在我我们刚开始的思路
开发心得与测试体会
在完成这一开发任务后,我总结了以下几点关于嵌入式开发与调试的实战心得:
1. 硬件连接的“隐形坑”
在测试过程中,我深刻体会到硬件质量对软件调试的影响 。即使逻辑代码完全正确,如果使用的杜邦线质量不佳,也可能导致屏幕无法正常驱动或数据传输中断 。
启发:在排查 Bug 时,不能仅盯着代码,硬件层面的物理连接往往是第一道关卡 。
2. 驱动代码的“自主权”
对于屏幕(如 4883 模块)的驱动,我建议尽可能亲手实现或重构底层驱动代码 。直接套用库文件虽然快捷,但在面对复杂的接线或特定型号差异时,自己编写的代码更易于定位问题,减少因“黑盒”库导致的未知错误 。
3. 软硬结合的灵活性
通过测试实现 WiFi 动态设置阈值 这一功能,赋予了硬件极高的灵活性 。传统的硬件检测逻辑往往是固定的,但引入 WiFi 调试助手后,我们可以在不重新烧录代码的情况下,远程修改系统判断逻辑 。
- 数学转换的准确性:在代码中,ADC 原始值通过公式
$$voltage\_mv = adc\_raw \times 3300 / 4095
转换为电压 。
- 状态同步:利用 Webserver 处理 HTTP 请求来更新全局规则结构体 g_rules,实现了控制端与执行端的即时同步 。
4. 实时性与交互体验
通过 vTaskDelay 控制采样周期(100ms),既保证了屏幕刷新不会过度占用 CPU,也确保了对电位计调节的快速响应 。看到屏幕随着旋钮转动而精准变色,这种**“感知-判断-反馈”**的闭环过程,正是嵌入式开发的魅力所在 。


