Skip to content

Commit fd30bc8

Browse files
committed
v1.2.58
- Added support for ST7796 (320x480) display. - Added experimental *AutodetectDisplay* driver. - Implemented HTTP Basic Authentication in `NetHelper` (scripting) and the `CameraDisplay` activity. - Introduced `NetHelper.withCredentials(user, pass)` API for authentication. - Implemented response callback for API commands issued via terminal.
1 parent 10c1d09 commit fd30bc8

31 files changed

Lines changed: 709 additions & 241 deletions

README.md

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -195,26 +195,18 @@ command using the `/api/` prefix, or enter system commands prefixed by `#` chara
195195

196196
##### System configuration parameters list
197197

198-
| Key | Description | Default |
199-
|------------|----------------------------|------------------|
200-
| `sys-rb-n` | Factory reset button GPIO# | -1 (-1=not used) |
201-
| `sys-sl-n` | System status LED GPIO# | -1 (-1=not used) |
202-
| `io-typ01` | I/O Ch.1 type | |
203-
| `io-pin01` | I/O Ch.1 GPIO# | -1 (-1=not used) |
204-
| `io-typ02` | I/O Ch.2 type | |
205-
| `io-pin02` | I/O Ch.2 GPIO# | -1 (-1=not used) |
206-
| `io-typ03` | I/O Ch.3 type | |
207-
| `io-pin03` | I/O Ch.3 GPIO# | -1 (-1=not used) |
208-
| `io-typ04` | I/O Ch.4 type | |
209-
| `io-pin04` | I/O Ch.4 GPIO# | -1 (-1=not used) |
210-
| `io-typ05` | I/O Ch.5 type | |
211-
| `io-pin05` | I/O Ch.5 GPIO# | -1 (-1=not used) |
212-
| `io-typ06` | I/O Ch.6 type | |
213-
| `io-pin06` | I/O Ch.6 GPIO# | -1 (-1=not used) |
214-
| `io-typ07` | I/O Ch.7 type | |
215-
| `io-pin07` | I/O Ch.7 GPIO# | -1 (-1=not used) |
216-
| `io-typ08` | I/O Ch.8 type | |
217-
| `io-pin08` | I/O Ch.8 GPIO# | -1 (-1=not used) |
198+
| Key | Description | Default |
199+
|------------|-----------------------------------------------------|------------------|
200+
| `sys-rb-n` | Factory reset button GPIO# | -1 (-1=not used) |
201+
| `sys-sl-n` | System status LED GPIO# | -1 (-1=not used) |
202+
| `io-typ01` | I/O Ch.1 type | |
203+
| `io-pin01` | I/O Ch.1 GPIO# | -1 (-1=not used) |
204+
| `io-typ02` | I/O Ch.2 type | |
205+
| `io-pin02` | I/O Ch.2 GPIO# | -1 (-1=not used) |
206+
| ... | (channels 3 through 8 follow the same pattern) | ... |
207+
208+
209+
**IO Channel Type (`io-typ0*`) can be one of the following: 'Switch','Light' or 'Dimmer'.**
218210

219211
*Example **setting** configuration from a terminal connected to the serial port of the device:*
220212

examples/smart-sensor-display/README.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,25 @@ Smart multi-sensor device with display. HomeGenie Mini UI example application.
1111
Besides the default system options and the *smart-sensor* options detailed in the `../smart-sensor/README.md` file,
1212
the `smart-sensor-display` firmware offers the following additional configuration settings:
1313

14-
| Key | Description | Default |
15-
|-------------|------------------------------------------------------------|----------|
16-
| `disp-typ` | Display driver ("GC9A01" or "ST7789") | "GC9A01" |
17-
| `disp-sclk` | DISPLAY_SCLK | |
18-
| `disp-mosi` | DISPLAY_MOSI | |
19-
| `disp-miso` | DISPLAY_MISO | |
20-
| `disp-dc` | DISPLAY_DC | |
21-
| `disp-cs` | DISPLAY_CS | |
22-
| `disp-rst` | DISPLAY_RST | |
23-
| `disp-bl` | DISPLAY_BL (backlight) | |
24-
| `dstch-prt` | TOUCH_PORT | |
25-
| `dstch-adr` | TOUCH_ADDRESS | |
26-
| `dstch-sda` | TOUCH_SDA | |
27-
| `dstch-scl` | TOUCH_SCL | |
28-
| `dstch-rst` | TOUCH_RST | |
29-
| `dstch-int` | TOUCH_INTERRUPT | |
30-
| `imus-typ` | Inertial Measurement Unit type ("QMI8658") | |
31-
| `imus-sda` | IMU SDA | |
32-
| `imus-scl` | IMU SCL | |
14+
| Key | Description | Default |
15+
|-------------|----------------------------------------------------------|----------|
16+
| `disp-typ` | Display driver ("GC9A01", "ST7789", "ST7796" or "AUTO") | "GC9A01" |
17+
| `disp-sclk` | DISPLAY_SCLK | |
18+
| `disp-mosi` | DISPLAY_MOSI | |
19+
| `disp-miso` | DISPLAY_MISO | |
20+
| `disp-dc` | DISPLAY_DC | |
21+
| `disp-cs` | DISPLAY_CS | |
22+
| `disp-rst` | DISPLAY_RST | |
23+
| `disp-bl` | DISPLAY_BL (backlight) | |
24+
| `dstch-prt` | TOUCH_PORT | |
25+
| `dstch-adr` | TOUCH_ADDRESS | |
26+
| `dstch-sda` | TOUCH_SDA | |
27+
| `dstch-scl` | TOUCH_SCL | |
28+
| `dstch-rst` | TOUCH_RST | |
29+
| `dstch-int` | TOUCH_INTERRUPT | |
30+
| `imus-typ` | Inertial Measurement Unit type ("QMI8658") | |
31+
| `imus-sda` | IMU SDA | |
32+
| `imus-scl` | IMU SCL | |
3333

3434
For default GPIO# values used by the display driver see `ui/drivers/RoundDisplay.h` and
3535
`ui/drivers/StandardDisplay.h` files.

examples/smart-sensor-display/configuration.h

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929
#include "defs.h"
3030

3131
#include <ui/Dashboard.h>
32-
#include <ui/drivers/RoundDisplay.h>
33-
#include <ui/drivers/StandardDisplay.h>
32+
#include <ui/drivers/GC9A01.h>
33+
#include <ui/drivers/ST7789.h>
34+
#include <ui/drivers/ST7796.h>
35+
#include <ui/drivers/AutodetectDisplay.h>
3436
#include <ui/activities/utilities/SystemInfoActivity.h>
3537

3638
#ifdef BOARD_HAS_PSRAM
@@ -64,7 +66,7 @@ namespace DisplayConfig = DisplayApi::Configuration;
6466
namespace DisplayOption = DisplayApi::Options;
6567

6668

67-
Dashboard* dashboard;
69+
Dashboard* dashboard = nullptr;
6870

6971

7072
// UI options update listener
@@ -148,7 +150,7 @@ void addDashboardActivities(String &list) {
148150
static int systemInfoCount = 0;
149151
if (elementName == "SystemInfo") {
150152
// single instance activity
151-
if (systemInfoCount++ == 0) {
153+
if (++systemInfoCount == 1) {
152154
auto systemInfo = new SystemInfoActivity();
153155
dashboard->addActivity(systemInfo);
154156
}
@@ -159,7 +161,7 @@ void addDashboardActivities(String &list) {
159161
static int sensorValuesCount = 0;
160162
if (elementName == "SensorValues") {
161163
// single instance activity
162-
if (sensorValuesCount++ == 0) {
164+
if (++sensorValuesCount == 1) {
163165
auto miniModule = HomeGenie::getInstance()->getDefaultModule();
164166
auto sensorValues = new SensorValuesActivity(miniModule);
165167
dashboard->addActivity(sensorValues);
@@ -171,7 +173,7 @@ void addDashboardActivities(String &list) {
171173
static int digitalClockCount = 0;
172174
if (elementName == "DigitalClock") {
173175
// single instance activity
174-
if (digitalClockCount++ == 0) {
176+
if (++digitalClockCount == 1) {
175177
auto digitalClock = new DigitalClockActivity();
176178
dashboard->addActivity(digitalClock);
177179
}
@@ -181,8 +183,9 @@ void addDashboardActivities(String &list) {
181183
// user-definable devices. Each button emits events
182184
// that can be automated using the device Scheduler.
183185
// It can be configured using HomeGenie Panel app.
186+
static int switchControlCount = 0;
184187
if (elementName == "SwitchControl") {
185-
String address = "D1";
188+
String address = "D" + String(++switchControlCount);
186189
if (elementOptions.length() > 0) {
187190
address = elementOptions;
188191
}
@@ -196,8 +199,9 @@ void addDashboardActivities(String &list) {
196199

197200
#ifndef DISABLE_LVGL
198201
// Similar to the SwitchControl but using LVGL
202+
static int levelControlCount = 0;
199203
if (elementName == "LevelControl") {
200-
String address = "M1";
204+
String address = "M" + String(++levelControlCount);
201205
if (elementOptions.length() > 0) {
202206
address = elementOptions;
203207
}
@@ -209,8 +213,9 @@ void addDashboardActivities(String &list) {
209213
#endif
210214
}
211215
// Color control
216+
static int colorControlCount = 0;
212217
if (elementName == "ColorControl") {
213-
String address = "H1";
218+
String address = "H" + String(++colorControlCount);
214219
if (elementOptions.length() > 0) {
215220
address = elementOptions;
216221
}
@@ -226,12 +231,12 @@ void addDashboardActivities(String &list) {
226231
// or local camera directly connected to the ESP32.
227232
static int cameraDisplayCount = 0;
228233
if (elementName == "CameraDisplay") {
229-
String address = "V1";
234+
String address = "V" + String(++cameraDisplayCount);
230235
if (elementOptions.length() > 0) {
231236
address = elementOptions;
232237
}
233238
auto cameraDisplay = new CameraDisplayActivity(address.c_str());
234-
if (cameraDisplayCount++ == 0) {
239+
if (cameraDisplayCount == 1) {
235240
// The first instance is reserved for
236241
// onboard ESP32 camera module if enabled
237242
if (Config::getSetting(Camera_Sensor::SensorType).equals("esp32-cam")) {
@@ -245,15 +250,15 @@ void addDashboardActivities(String &list) {
245250
static int analogClockCount = 0;
246251
if (elementName == "AnalogClock") {
247252
// single instance activity
248-
if (analogClockCount++ == 0) {
253+
if (++analogClockCount == 1) {
249254
auto analogClock = new AnalogClockActivity();
250255
dashboard->addActivity(analogClock);
251256
}
252257
}
253258
static int gaugeExampleCount = 0;
254259
if (elementName == "GaugeExample") {
255260
// single instance activity
256-
if (gaugeExampleCount++ == 0) {
261+
if (++gaugeExampleCount == 1) {
257262
auto gaugeExample = new GaugeExampleActivity();
258263
dashboard->addActivity(gaugeExample);
259264
}

examples/smart-sensor-display/smart-sensor-display.cpp

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,27 @@ void setup() {
4343
miniModule->setProperty(Implements::Scheduling, "true");
4444
miniModule->setProperty(Implements::Scheduling_ModuleEvents, "true");
4545

46-
DisplayDriver* displayDriver;
46+
DisplayDriver* displayDriver = nullptr;
4747
auto displayType = Config::getSetting("disp-typ");
4848
if (displayType.equals("ST7789")) {
49-
displayDriver = new UI::Drivers::StandardDisplay();
50-
} else {
51-
// fallback to "GC9A01"
52-
displayDriver = new UI::Drivers::RoundDisplay();
49+
// ST7789 320x240 display
50+
displayDriver = new UI::Drivers::ST7789();
51+
} else if (displayType.equals("ST7796")) {
52+
// ST7789 480x320 display
53+
displayDriver = new UI::Drivers::ST7796();
54+
} else if (displayType.equals("GC9A01")) {
55+
// GC9A01 240x240 round display
56+
displayDriver = new UI::Drivers::GC9A01();
57+
} else if (displayType.equals("AUTO")) {
58+
// fallback to autodetect
59+
displayDriver = new UI::Drivers::AutodetectDisplay();
5360
}
5461

55-
initDashboard(displayDriver, miniModule);
62+
if (displayDriver) {
63+
initDashboard(displayDriver, miniModule);
64+
} else {
65+
Serial.println("ERROR: display driver not set, could not initialize Dashboard.");
66+
}
5667

5768
if (Config::isDeviceConfigured()) {
5869

@@ -94,29 +105,38 @@ void setup() {
94105
}
95106
#endif
96107

97-
auto dashboardConfig = Config::getSetting(
98-
"dashboard",
108+
if (dashboard) {
109+
110+
auto dashboardConfig = Config::getSetting(
111+
"dashboard",
99112
#ifdef BOARD_HAS_PSRAM
100-
"SensorValues,CameraDisplay:V1,LevelControl:M1,ColorControl:H1,SwitchControl:D1,DigitalClock,"
101-
#ifdef LGFX_EXAMPLES
102-
"AnalogClock,GaugeExample,"
103-
#endif
104-
"SystemInfo"
113+
"SensorValues,CameraDisplay:V1,LevelControl:M1,ColorControl:H1,SwitchControl:D1,DigitalClock,"
114+
#ifdef LGFX_EXAMPLES
115+
"AnalogClock,GaugeExample,"
116+
#endif
117+
"SystemInfo"
105118
#else
106-
"SensorValues,SystemInfo"
119+
"SensorValues,SystemInfo"
107120
#endif
108-
);
109-
110-
// ----------------------------------------------------------------
111-
// NOTE: Activities can be added if free RAM remains above ~110KB.
112-
// This threshold accounts for scheduler and system task
113-
// memory requirements.
114-
// ----------------------------------------------------------------
115-
116-
// Add activities to UI
117-
addDashboardActivities(dashboardConfig);
121+
);
122+
// very basic consistency check
123+
dashboardConfig.trim();
124+
if (dashboardConfig.isEmpty()) {
125+
// ensure there is at least the SystemInfo activity
126+
dashboardConfig = "SystemInfo";
127+
}
128+
129+
// ----------------------------------------------------------------
130+
// NOTE: Activities can be added if free RAM remains above ~110KB.
131+
// This threshold accounts for scheduler and system task
132+
// memory requirements.
133+
// ----------------------------------------------------------------
134+
135+
// Add activities to UI
136+
addDashboardActivities(dashboardConfig);
137+
}
118138

119-
} else {
139+
} else if (dashboard) {
120140

121141
// On devices with default RAM, activating
122142
// Bluetooth will get most of the available RAM,

platformio.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ lib_ignore =
220220
ESP32_BleSerial
221221

222222
[env:smart-sensor-display]
223-
build_flags = ${env.build_flags} -I examples -I src -D ENABLE_UI -D ENABLE_SCREEN_SAVER -D DISABLE_BLUETOOTH -D DISABLE_LVGL
223+
build_flags = ${env.build_flags} -I examples -I src -D ENABLE_UI -D ENABLE_SCREEN_SAVER -D DISABLE_BLUETOOTH -D DISABLE_LVGL -D LGFX_AUTODETECT
224224
-D DEFAULT_CONFIG='{"sys-rb-n":"0","sys-sl-n":"-1","io-typ01":"Dimmer","io-pin01":"15","io-typ02":"Dimmer","io-pin02":"17","sdht-typ":"22","sdht-pin":"21","motn-typ":"switch","motn-pin":"16"}'
225225
build_src_filter = +<src> -<src/main.cpp> +<examples/smart-sensor> -<examples/smart-sensor/smart-sensor.cpp> +<examples/smart-sensor-display>
226226
board_build.partitions = ./src/partitions.csv
@@ -230,7 +230,7 @@ lib_deps = ${env:smart-sensor.lib_deps}
230230

231231
[env:smart-sensor-display-s3]
232232
board = esp32-s3-devkitc-1
233-
build_flags = ${env.build_flags} -I examples -I src -D ESP32_S3 -D ENABLE_UI -D ENABLE_SCREEN_SAVER
233+
build_flags = ${env.build_flags} -I examples -I src -D ESP32_S3 -D ENABLE_UI -D ENABLE_SCREEN_SAVER -D LGFX_AUTODETECT
234234
-D ESP32S3_DEV -D BOARD_HAS_PSRAM
235235
-D DEFAULT_CONFIG='{"sys-rb-n":"0","sys-sl-n":"-1", "motn-pms": "sleep", "disp-typ":"GC9A01","imus-typ":"QMI8658-dis","imus-sda":"6","imus-scl":"7","io-typ01":"Dimmer","io-pin01":"15","io-typ02":"Dimmer","io-pin02":"17","sdht-typ":"22","sdht-pin":"21","motn-typ":"switch","motn-pin":"16"}'
236236
build_src_filter = ${env:smart-sensor-display.build_src_filter}
@@ -249,7 +249,7 @@ lib_deps = ${env:smart-sensor.lib_deps}
249249

250250
[env:smart-sensor-display-s3-opi]
251251
board = esp32-s3-devkitc-1
252-
build_flags = ${env.build_flags} -I examples -I src -D ESP32_S3 -D ENABLE_UI -D ENABLE_SCREEN_SAVER -D ESP32_S3_LCD_CAM -D ESP_CAMERA_SUPPORTED
252+
build_flags = ${env.build_flags} -I examples -I src -D ESP32_S3 -D ENABLE_UI -D ENABLE_SCREEN_SAVER -D ESP32_S3_LCD_CAM -D ESP_CAMERA_SUPPORTED -D LGFX_AUTODETECT
253253
-D ESP32S3_DEV -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1
254254
-D DEFAULT_CONFIG='{"sys-rb-n":"0","sys-sl-n":"-1","disp-typ":"ST7789","imus-typ":"QMI8658","imus-sda":"48","imus-scl":"47","io-typ01":"Dimmer","io-pin01":"","io-typ02":"Dimmer","io-pin02":"","sdht-typ":"none","sdht-pin":"","motn-typ":"none","motn-pin":""}'
255255
build_src_filter = ${env:smart-sensor-display.build_src_filter}

src/HomeGenie.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,7 @@ namespace Service {
165165
if (cmd.startsWith("#")) {
166166
Config::handleConfigCommand(cmd);
167167
} else if (!cmd.isEmpty()) {
168-
// TODO: implement SerialCallback
169-
auto callback = DummyResponseCallback();
168+
auto callback = TerminalResponseCallback();
170169
onNetRequest(this, cmd.c_str(), &callback);
171170
}
172171
}

src/Utility.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,63 @@ void Utility::simpleJsonStringEscape(String& s) {
225225
}
226226
s = result;
227227
}
228+
229+
bool Utility::isNumeric(const char *s) {
230+
return strlen(s) > 0 && strspn(s, "0123456789.") == strlen(s);
231+
}
232+
233+
float Utility::roundDecimals(float value, unsigned int n_decimals) {
234+
float multiplier = std::pow(10.0f, static_cast<float>(n_decimals));
235+
return std::round(value * multiplier) / multiplier;
236+
}
237+
238+
Utility::UrlParts Utility::parseURL(const String &url) {
239+
UrlParts parts;
240+
parts.url = url;
241+
int protocolEndPos = url.indexOf("://");
242+
if (protocolEndPos != -1) {
243+
parts.protocol = url.substring(0, protocolEndPos);
244+
}
245+
// credentials lookup
246+
int atPos = (protocolEndPos != -1) ? url.indexOf('@', protocolEndPos + 3) : url.indexOf('@');
247+
if (atPos == -1) {
248+
return parts;
249+
}
250+
String protocolPart = (protocolEndPos != -1) ? url.substring(0, protocolEndPos + 3) : "";
251+
String credentials = url.substring(protocolPart.length(), atPos);
252+
String hostAndPath = url.substring(atPos + 1);
253+
// build clean URL
254+
parts.url = protocolPart + hostAndPath;
255+
// parse credentials
256+
int colonPos = credentials.indexOf(':');
257+
if (colonPos != -1) {
258+
parts.username = urlDecode(credentials.substring(0, colonPos));
259+
parts.password = urlDecode(credentials.substring(colonPos + 1));
260+
} else {
261+
parts.username = urlDecode(credentials);
262+
}
263+
return parts;
264+
}
265+
266+
String Utility::urlDecode(const String &str) {
267+
String encodedString = "";
268+
char c;
269+
char code0;
270+
char code1;
271+
for (int i = 0; i < str.length(); i++){
272+
c=str.charAt(i);
273+
if (c == '+') {
274+
encodedString += ' ';
275+
} else if (c == '%') {
276+
i++;
277+
code0=str.charAt(i);
278+
i++;
279+
code1=str.charAt(i);
280+
c = (hexToInt(code0) << 4) | hexToInt(code1);
281+
encodedString += c;
282+
} else {
283+
encodedString += c;
284+
}
285+
}
286+
return encodedString;
287+
}

0 commit comments

Comments
 (0)