DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
gpio_map.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file gpio_map.cpp
6 * @brief GPIO pin-mapper direction names, JSON serializer, control parser, and
7 * the ESP32 digital read / write helpers.
8 *
9 * The serializer and the `pin=&level=` parser are pure (host-tested); the digital
10 * I/O uses the Arduino API on ESP32 and is a no-op on host builds. No server
11 * dependency lives here - the route is in gpio_map_routes.cpp.
12 */
13
15
16#if DETWS_ENABLE_GPIO_MAP
17
18#include <stdarg.h>
19#include <stdio.h>
20#include <string.h>
21
22const char *detws_gpio_dir_name(DetwsGpioDir dir)
23{
24 switch (dir)
25 {
26 case DetwsGpioDir::DETWS_GPIO_IN:
27 return "in";
28 case DetwsGpioDir::DETWS_GPIO_IN_PULLUP:
29 return "in_pullup";
30 case DetwsGpioDir::DETWS_GPIO_IN_PULLDOWN:
31 return "in_pulldown";
32 case DetwsGpioDir::DETWS_GPIO_OUT:
33 return "out";
34 default:
35 return "in";
36 }
37}
38
39static int json_append(char *out, size_t cap, size_t *pos, const char *fmt, ...)
40{
41 if (*pos >= cap)
42 return -1;
43 va_list ap;
44 va_start(ap, fmt);
45 int w = vsnprintf(out + *pos, cap - *pos, fmt, ap);
46 va_end(ap);
47 if (w < 0 || (size_t)w >= cap - *pos)
48 return -1;
49 *pos += (size_t)w;
50 return 0;
51}
52
53int detws_gpio_json(const DetwsGpioPin *pins, uint8_t count, char *out, size_t cap)
54{
55 if (!out || cap == 0)
56 return 0;
57 out[0] = '\0';
58 if (!pins)
59 return 0;
60 size_t pos = 0;
61 if (json_append(out, cap, &pos, "{\"pins\":[") != 0)
62 return 0;
63 for (uint8_t i = 0; i < count; i++)
64 {
65 const DetwsGpioPin *p = &pins[i];
66 if (json_append(out, cap, &pos, "%s{\"pin\":%u,\"label\":\"%s\",\"dir\":\"%s\",\"level\":%u}", i ? "," : "",
67 (unsigned)p->pin, p->label ? p->label : "", detws_gpio_dir_name(p->dir),
68 p->level ? 1u : 0u) != 0)
69 return 0;
70 }
71 if (json_append(out, cap, &pos, "]}") != 0)
72 return 0;
73 return (int)pos;
74}
75
76// Read the decimal integer that follows "name=" in a form-encoded body. Returns
77// false if the field is absent or has no digits.
78static bool form_field_uint(const char *body, size_t len, const char *name, unsigned *out)
79{
80 size_t nlen = strnlen(name, len + 1);
81 for (size_t i = 0; i + nlen + 1 <= len; i++)
82 {
83 bool at_field = (i == 0) || body[i - 1] == '&';
84 if (!at_field || memcmp(body + i, name, nlen) != 0 || body[i + nlen] != '=')
85 continue;
86 size_t j = i + nlen + 1;
87 if (j >= len || body[j] < '0' || body[j] > '9')
88 return false;
89 unsigned v = 0;
90 for (; j < len && body[j] >= '0' && body[j] <= '9'; j++)
91 v = v * 10 + (unsigned)(body[j] - '0');
92 *out = v;
93 return true;
94 }
95 return false;
96}
97
98bool detws_gpio_parse_set(const char *body, size_t len, uint8_t *pin, uint8_t *level)
99{
100 if (!body || !pin || !level)
101 return false;
102 unsigned p;
103 unsigned l;
104 if (!form_field_uint(body, len, "pin", &p) || !form_field_uint(body, len, "level", &l))
105 return false;
106 *pin = (uint8_t)p;
107 *level = l ? 1 : 0;
108 return true;
109}
110
111bool detws_gpio_is_output(const DetwsGpioPin *pins, uint8_t count, uint8_t pin)
112{
113 if (!pins)
114 return false;
115 for (uint8_t i = 0; i < count; i++)
116 if (pins[i].pin == pin && pins[i].dir == DetwsGpioDir::DETWS_GPIO_OUT)
117 return true;
118 return false;
119}
120
121#ifdef ARDUINO
122
123#include <Arduino.h>
124
125void detws_gpio_begin_pins(const DetwsGpioPin *pins, uint8_t count)
126{
127 if (!pins)
128 return;
129 for (uint8_t i = 0; i < count; i++)
130 {
131 switch (pins[i].dir)
132 {
133 case DetwsGpioDir::DETWS_GPIO_OUT:
134 pinMode(pins[i].pin, OUTPUT);
135 break;
136 case DetwsGpioDir::DETWS_GPIO_IN_PULLUP:
137 pinMode(pins[i].pin, INPUT_PULLUP);
138 break;
139 case DetwsGpioDir::DETWS_GPIO_IN_PULLDOWN:
140 pinMode(pins[i].pin, INPUT_PULLDOWN);
141 break;
142 default:
143 pinMode(pins[i].pin, INPUT);
144 break;
145 }
146 }
147}
148
149void detws_gpio_read(DetwsGpioPin *pins, uint8_t count)
150{
151 if (!pins)
152 return;
153 for (uint8_t i = 0; i < count; i++)
154 pins[i].level = (uint8_t)(digitalRead(pins[i].pin) ? 1 : 0);
155}
156
157void detws_gpio_write(uint8_t pin, uint8_t level)
158{
159 digitalWrite(pin, level ? HIGH : LOW);
160}
161
162#else // host build - no GPIO
163
164void detws_gpio_begin_pins(const DetwsGpioPin *, uint8_t)
165{
166}
167
168void detws_gpio_read(DetwsGpioPin *, uint8_t)
169{
170}
171
172void detws_gpio_write(uint8_t, uint8_t)
173{
174}
175
176#endif // ARDUINO
177
178#endif // DETWS_ENABLE_GPIO_MAP
Browser GPIO pin-mapper / diagnostics (DETWS_ENABLE_GPIO_MAP).