25#include "driver/gpio.h"
26#include "driver/i2c.h"
27#include "esp_adc/adc_oneshot.h"
35static const char*
TAG =
"GPIO_CMD";
42void send(
const char* line) { std::printf(
"%s\n", line); }
43void sendf(
const char* fmt, ...)
47 std::vprintf(fmt, ap);
60void cmd_mode(
const char* args)
62 char dir_buf[16] = {0};
63 char pull_buf[16] =
"none";
64 unsigned long pin_l = 0;
65 int n = std::sscanf(args,
"%lu %15s %15s", &pin_l, dir_buf, pull_buf);
66 if (n < 2) { send(
"ERR usage_pin_mode_pull");
return; }
67 uint8_t pin =
static_cast<uint8_t
>(pin_l);
68 if (!is_allowed(pin)) { send(
"ERR pin_not_allowed");
return; }
71 if (std::strcmp(dir_buf,
"in") == 0) mode = GPIO_MODE_INPUT;
72 else if (std::strcmp(dir_buf,
"out") == 0) mode = GPIO_MODE_OUTPUT;
73 else if (std::strcmp(dir_buf,
"out_od") == 0) mode = GPIO_MODE_OUTPUT_OD;
74 else { send(
"ERR mode_in_out_out_od");
return; }
76 gpio_pull_mode_t pull = GPIO_FLOATING;
77 if (std::strcmp(pull_buf,
"pull_up") == 0) pull = GPIO_PULLUP_ONLY;
78 else if (std::strcmp(pull_buf,
"pull_down") == 0) pull = GPIO_PULLDOWN_ONLY;
81 cfg.pin_bit_mask = 1ULL << pin;
82 cfg.intr_type = GPIO_INTR_DISABLE;
84 cfg.pull_up_en = pull == GPIO_PULLUP_ONLY ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
85 cfg.pull_down_en = pull == GPIO_PULLDOWN_ONLY ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE;
86 if (gpio_config(&cfg) != ESP_OK) { send(
"ERR gpio_config");
return; }
90void cmd_write(
const char* args)
92 unsigned long pin_l = 0;
94 if (std::sscanf(args,
"%lu %d", &pin_l, &level) != 2) { send(
"ERR usage_pin_level");
return; }
95 uint8_t pin =
static_cast<uint8_t
>(pin_l);
96 if (!is_allowed(pin)) { send(
"ERR pin_not_allowed");
return; }
97 if (gpio_set_level(
static_cast<gpio_num_t
>(pin), level ? 1 : 0) != ESP_OK) {
98 send(
"ERR set_level");
return;
103void cmd_read(
const char* args)
105 unsigned long pin_l = 0;
106 if (std::sscanf(args,
"%lu", &pin_l) != 1) { send(
"ERR usage_pin");
return; }
107 uint8_t pin =
static_cast<uint8_t
>(pin_l);
108 if (!is_allowed(pin)) { send(
"ERR pin_not_allowed");
return; }
109 sendf(
"%d", gpio_get_level(
static_cast<gpio_num_t
>(pin)));
112void cmd_release(
const char* args)
114 unsigned long pin_l = 0;
115 if (std::sscanf(args,
"%lu", &pin_l) != 1) { send(
"ERR usage_pin");
return; }
116 uint8_t pin =
static_cast<uint8_t
>(pin_l);
117 if (!is_allowed(pin)) { send(
"ERR pin_not_allowed");
return; }
118 gpio_reset_pin(
static_cast<gpio_num_t
>(pin));
122void gpioDispatch(
const char* args)
124 if (!args || !*args) { send(
"ERR missing_subcmd");
return; }
125 while (*args ==
' ') args++;
126 const char* space = std::strchr(args,
' ');
127 size_t sub_len = space ?
static_cast<size_t>(space - args) : std::strlen(args);
128 const char* rest = space ? space + 1 :
"";
130 auto eq = [&](
const char* w) {
131 return std::strlen(w) == sub_len && strncasecmp(args, w, sub_len) == 0;
134 if (eq(
"LIST")) cmd_list();
135 else if (eq(
"MODE")) cmd_mode(rest);
136 else if (eq(
"WRITE")) cmd_write(rest);
137 else if (eq(
"READ")) cmd_read(rest);
138 else if (eq(
"RELEASE")) cmd_release(rest);
139 else send(
"ERR unknown_subcmd");
144struct AdcMap { uint8_t pin; adc_channel_t ch; };
145constexpr AdcMap ADC1_PINS[] = {
146 { 2, ADC_CHANNEL_1 }, { 3, ADC_CHANNEL_2 },
147 { 4, ADC_CHANNEL_3 }, { 5, ADC_CHANNEL_4 },
148 { 6, ADC_CHANNEL_5 }, { 7, ADC_CHANNEL_6 },
149 { 9, ADC_CHANNEL_8 },
152bool pin_to_adc(uint8_t pin, adc_channel_t& ch_out) {
153 for (
const auto& m : ADC1_PINS) {
154 if (m.pin == pin) { ch_out = m.ch;
return true; }
159void adcDispatch(
const char* args)
161 if (!args || strncasecmp(args,
"READ", 4) != 0) { send(
"ERR usage_ADC_READ_pin");
return; }
162 while (*args && *args !=
' ') args++;
163 while (*args ==
' ') args++;
164 unsigned long pin_l = 0;
165 if (std::sscanf(args,
"%lu", &pin_l) != 1) { send(
"ERR usage_pin");
return; }
166 uint8_t pin =
static_cast<uint8_t
>(pin_l);
169 if (!pin_to_adc(pin, ch)) { send(
"ERR pin_no_adc1");
return; }
171 adc_oneshot_unit_handle_t unit;
172 adc_oneshot_unit_init_cfg_t unit_cfg{};
173 unit_cfg.unit_id = ADC_UNIT_1;
174 unit_cfg.ulp_mode = ADC_ULP_MODE_DISABLE;
175 if (adc_oneshot_new_unit(&unit_cfg, &unit) != ESP_OK) { send(
"ERR adc_unit");
return; }
177 adc_oneshot_chan_cfg_t chan_cfg{};
178 chan_cfg.atten = ADC_ATTEN_DB_12;
179 chan_cfg.bitwidth = ADC_BITWIDTH_DEFAULT;
180 if (adc_oneshot_config_channel(unit, ch, &chan_cfg) != ESP_OK) {
181 send(
"ERR adc_chan_config");
182 adc_oneshot_del_unit(unit);
186 if (adc_oneshot_read(unit, ch, &raw) != ESP_OK) { send(
"ERR adc_read"); }
187 else sendf(
"raw=%d", raw);
188 adc_oneshot_del_unit(unit);
191void i2cDispatch(
const char* args)
193 if (!args || strncasecmp(args,
"SCAN", 4) != 0) { send(
"ERR usage_I2C_SCAN_bus");
return; }
194 while (*args && *args !=
' ') args++;
195 while (*args ==
' ') args++;
196 unsigned long bus_l = 1;
197 std::sscanf(args,
"%lu", &bus_l);
198 if (bus_l == 0) { send(
"ERR bus0_reserved");
return; }
200 i2c_port_t port =
static_cast<i2c_port_t
>(bus_l);
202 for (uint8_t addr = 0x08; addr < 0x78; ++addr) {
203 i2c_cmd_handle_t cmd = i2c_cmd_link_create();
204 i2c_master_start(cmd);
205 i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE,
true);
206 i2c_master_stop(cmd);
207 esp_err_t err = i2c_master_cmd_begin(port, cmd, pdMS_TO_TICKS(20));
208 i2c_cmd_link_delete(cmd);
210 sendf(
"0x%02X", addr);
214 if (!any) send(
"(none)");
220constexpr i2c_port_t SAO_I2C_PORT =
static_cast<i2c_port_t
>(1);
222void sao_read(
const char* args)
224 unsigned long offset = 0, count = 0;
225 if (std::sscanf(args,
"%lu %lu", &offset, &count) != 2) {
226 send(
"ERR usage_offset_count");
return;
228 if (count == 0 || count > 256) { send(
"ERR count_1_256");
return; }
230 uint8_t reg[2] = {
static_cast<uint8_t
>(offset >> 8),
static_cast<uint8_t
>(offset & 0xff) };
231 uint8_t buf[256] = {0};
232 i2c_cmd_handle_t cmd = i2c_cmd_link_create();
233 i2c_master_start(cmd);
234 i2c_master_write_byte(cmd, (
SAO_EEPROM_ADDR << 1) | I2C_MASTER_WRITE,
true);
235 i2c_master_write(cmd, reg,
sizeof(reg),
true);
236 i2c_master_start(cmd);
237 i2c_master_write_byte(cmd, (
SAO_EEPROM_ADDR << 1) | I2C_MASTER_READ,
true);
238 i2c_master_read(cmd, buf, count, I2C_MASTER_LAST_NACK);
239 i2c_master_stop(cmd);
240 esp_err_t err = i2c_master_cmd_begin(SAO_I2C_PORT, cmd, pdMS_TO_TICKS(200));
241 i2c_cmd_link_delete(cmd);
242 if (err != ESP_OK) { send(
"ERR i2c");
return; }
245 for (
size_t i = 0; i < count; ++i) std::snprintf(hex + i * 2, 3,
"%02X", buf[i]);
246 hex[count * 2] =
'\0';
250void sao_write(
const char* args)
252 unsigned long offset = 0;
254 if (std::sscanf(args,
"%lu %n", &offset, &consumed) != 1 || consumed == 0) {
255 send(
"ERR usage_offset_hex");
return;
257 const char* hex = args + consumed;
258 size_t hex_len = std::strlen(hex);
259 if (hex_len == 0 || hex_len % 2 != 0) { send(
"ERR hex_len");
return; }
260 size_t bytes = hex_len / 2;
261 if (bytes > 128) { send(
"ERR max_128");
return; }
264 for (
size_t i = 0; i < bytes; ++i) {
266 if (h < 0 || l < 0) { send(
"ERR hex");
return; }
267 data[i] =
static_cast<uint8_t
>((h << 4) | l);
270 uint8_t reg[2] = {
static_cast<uint8_t
>(offset >> 8),
static_cast<uint8_t
>(offset & 0xff) };
271 i2c_cmd_handle_t cmd = i2c_cmd_link_create();
272 i2c_master_start(cmd);
273 i2c_master_write_byte(cmd, (
SAO_EEPROM_ADDR << 1) | I2C_MASTER_WRITE,
true);
274 i2c_master_write(cmd, reg,
sizeof(reg),
true);
275 i2c_master_write(cmd, data, bytes,
true);
276 i2c_master_stop(cmd);
277 esp_err_t err = i2c_master_cmd_begin(SAO_I2C_PORT, cmd, pdMS_TO_TICKS(200));
278 i2c_cmd_link_delete(cmd);
279 if (err != ESP_OK) { send(
"ERR i2c");
return; }
283void saoDispatch(
const char* args)
285 if (!args || !*args) { send(
"ERR missing_subcmd");
return; }
286 while (*args ==
' ') args++;
287 const char* space = std::strchr(args,
' ');
288 size_t sub_len = space ?
static_cast<size_t>(space - args) : std::strlen(args);
289 const char* rest = space ? space + 1 :
"";
291 auto eq = [&](
const char* w) {
292 return std::strlen(w) == sub_len && strncasecmp(args, w, sub_len) == 0;
295 if (eq(
"EEPROM_READ")) sao_read(rest);
296 else if (eq(
"EEPROM_WRITE")) sao_write(rest);
297 else send(
"ERR usage_SAO_EEPROM_READ_WRITE");
305 reg.registerCommand({
"GPIO",
"GPIO LIST/MODE/WRITE/READ/RELEASE on user pins", gpioDispatch,
CMD_MODULE,
true});
306 reg.registerCommand({
"ADC",
"ADC READ <pin>", adcDispatch,
CMD_MODULE,
true});
307 reg.registerCommand({
"I2C",
"I2C SCAN <bus>", i2cDispatch,
CMD_MODULE,
true});
308 reg.registerCommand({
"SAO",
"SAO EEPROM_READ <off> <count> | EEPROM_WRITE <off> <hex>", saoDispatch,
CMD_MODULE,
true});
309 LOG_I(
TAG,
"GPIO/ADC/I2C/SAO serial commands registered");
Hex-digit decoding shared by the plugin_manager source files.
Single source of truth for plugin-accessible GPIO pins.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_I(tag, fmt,...)
bool isAllowed(uint8_t pin)
constexpr size_t ALLOWED_COUNT
constexpr uint8_t ALLOWED[]
void registerGpioSerialCommands()
static const char * CMD_MODULE
int hex_val(char c)
Convert a single hex digit to its numeric value.
ICommandRegistry & getCommandRegistry()
Returns singleton command-registry interface.