Skip to content

Commit ac88a3d

Browse files
committed
Merge branch 'main' into dev
2 parents 6a24829 + 7dff5b6 commit ac88a3d

File tree

157 files changed

+18378
-196
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

157 files changed

+18378
-196
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/**
2+
**************************************************
3+
*
4+
* @file BluetoothSerial.ino
5+
* @brief Example showing how to use Bluetooth Serial on Inkplate 10
6+
* with an LVGL-based UI that safely renders incoming text.
7+
*
8+
* HOW TO USE
9+
* - Upload this example to Inkplate 10.
10+
* - Pair your phone with the device in Bluetooth settings (device name: "Inkplate10").
11+
* - Open a Serial Bluetooth Terminal app (or similar) and connect to "Inkplate10".
12+
* - Anything you type in the app will be shown on the Inkplate screen.
13+
* - Anything you type in the Arduino Serial Monitor will be sent to the phone.
14+
*
15+
*
16+
* For info on how to quickly get started with Inkplate 10 visit
17+
* https://soldered.com/documentation/inkplate/10/overview/
18+
*
19+
* @authors Soldered
20+
* @date November 2025
21+
**************************************************
22+
*/
23+
24+
25+
#include <Inkplate-LVGL.h>
26+
#include <BluetoothSerial.h>
27+
28+
static const char *btDeviceName = "Inkplate10";
29+
30+
Inkplate inkplate(INKPLATE_1BIT);
31+
BluetoothSerial SerialBT;
32+
33+
// ---------- Font helpers (use LV_FONT_DEFAULT only) ----------
34+
static const lv_font_t* font_big() { return LV_FONT_DEFAULT; }
35+
static const lv_font_t* font_small() { return LV_FONT_DEFAULT; }
36+
37+
// ---------- LVGL helpers ----------
38+
static inline int dispW() {
39+
lv_display_t *d = lv_display_get_default();
40+
return lv_display_get_horizontal_resolution(d);
41+
}
42+
static inline int dispH() {
43+
lv_display_t *d = lv_display_get_default();
44+
return lv_display_get_vertical_resolution(d);
45+
}
46+
47+
static int lineHeight(const lv_font_t *f) {
48+
#if LVGL_VERSION_MAJOR >= 9
49+
return lv_font_get_line_height(f);
50+
#else
51+
// Approximate default font line height for LVGL v8 if API is unavailable
52+
return 18;
53+
#endif
54+
}
55+
56+
static void make_opaque(lv_obj_t *obj) {
57+
lv_obj_set_style_bg_color(obj, lv_color_hex(0xFFFFFF), 0);
58+
lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, 0);
59+
lv_obj_set_style_border_width(obj, 0, 0);
60+
}
61+
62+
// ---------- UI elements ----------
63+
static lv_obj_t *label_title = nullptr;
64+
static lv_obj_t *label_info = nullptr;
65+
static lv_obj_t *panel_log = nullptr;
66+
static lv_obj_t *label_log = nullptr;
67+
68+
// Spacing
69+
static const int INFO_LINE_SPACE = 4;
70+
static const int LOG_LINE_SPACE = 6;
71+
static const int topMargin = 8;
72+
static const int leftMargin = 8;
73+
static const int rightMargin = 8;
74+
75+
// Log state
76+
static String logText;
77+
static int maxLines = 0;
78+
static int usedLines = 0;
79+
80+
static void ui_create() {
81+
// Screen background
82+
lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
83+
84+
// Title
85+
label_title = lv_label_create(lv_screen_active());
86+
lv_obj_set_style_text_color(label_title, lv_color_hex(0x000000), 0);
87+
lv_obj_set_style_text_font(label_title, font_big(), 0);
88+
lv_label_set_text(label_title, "Bluetooth Serial Example");
89+
lv_obj_set_pos(label_title, leftMargin, topMargin);
90+
make_opaque(label_title);
91+
92+
// Instructions
93+
label_info = lv_label_create(lv_screen_active());
94+
lv_obj_set_style_text_color(label_info, lv_color_hex(0x000000), 0);
95+
lv_obj_set_style_text_font(label_info, font_small(), 0);
96+
lv_label_set_long_mode(label_info, LV_LABEL_LONG_WRAP);
97+
lv_obj_set_style_text_line_space(label_info, INFO_LINE_SPACE, 0);
98+
int infoY = topMargin + lineHeight(font_big()) + 8;
99+
lv_obj_set_pos(label_info, leftMargin, infoY);
100+
lv_obj_set_width(label_info, dispW() - leftMargin - rightMargin);
101+
lv_label_set_text(label_info,
102+
"Pair your phone and open a Serial Bluetooth Terminal.\n"
103+
"Messages from phone appear below.\n"
104+
"USB Serial -> phone is also forwarded.\n");
105+
make_opaque(label_info);
106+
107+
// Ensure layout is computed before measuring info height
108+
lv_timer_handler();
109+
#if LVGL_VERSION_MAJOR >= 9
110+
lv_obj_update_layout(label_info);
111+
#endif
112+
int infoBottom = lv_obj_get_y(label_info) + lv_obj_get_height(label_info);
113+
114+
// Opaque log panel below the instructions
115+
panel_log = lv_obj_create(lv_screen_active());
116+
make_opaque(panel_log);
117+
lv_obj_set_style_pad_all(panel_log, 0, 0);
118+
lv_obj_set_style_border_width(panel_log, 0, 0);
119+
int panelX = leftMargin;
120+
int panelY = infoBottom + 10;
121+
int panelW = dispW() - leftMargin - rightMargin;
122+
int panelH = dispH() - panelY - topMargin;
123+
lv_obj_set_pos(panel_log, panelX, panelY);
124+
lv_obj_set_size(panel_log, panelW, panelH);
125+
126+
// Log label inside the panel
127+
label_log = lv_label_create(panel_log);
128+
lv_obj_set_style_text_color(label_log, lv_color_hex(0x000000), 0);
129+
lv_obj_set_style_text_font(label_log, font_small(), 0);
130+
lv_label_set_long_mode(label_log, LV_LABEL_LONG_WRAP);
131+
lv_obj_set_style_text_line_space(label_log, LOG_LINE_SPACE, 0);
132+
lv_obj_set_pos(label_log, 0, 0);
133+
lv_obj_set_width(label_log, panelW);
134+
135+
// Compute how many lines fit into the panel
136+
int effLineH = lineHeight(font_small()) + LOG_LINE_SPACE;
137+
maxLines = (effLineH > 0) ? (panelH / effLineH) : 0;
138+
if (maxLines < 3) maxLines = 3; // safety minimum
139+
}
140+
141+
static void ui_reset_log_full_refresh() {
142+
// Rebuild the entire UI and perform a full refresh (avoids ghosting)
143+
lv_obj_clean(lv_screen_active());
144+
logText = "";
145+
usedLines = 0;
146+
ui_create();
147+
148+
lv_tick_inc(10);
149+
lv_timer_handler();
150+
inkplate.display(); // FULL
151+
}
152+
153+
static void log_append_char(char c) {
154+
if (c == '\r') return;
155+
156+
if (c == '\n') {
157+
logText += '\n';
158+
usedLines++;
159+
} else {
160+
logText += c;
161+
}
162+
163+
if (usedLines >= maxLines) {
164+
ui_reset_log_full_refresh();
165+
return;
166+
}
167+
168+
lv_label_set_text(label_log, logText.c_str());
169+
170+
// Partial refresh for faster updates; opaque panel removes old pixels
171+
lv_tick_inc(5);
172+
lv_timer_handler();
173+
inkplate.partialUpdate();
174+
}
175+
176+
void setup() {
177+
Serial.begin(115200);
178+
inkplate.begin(LV_DISP_RENDER_MODE_PARTIAL);
179+
180+
ui_create();
181+
182+
// Initial full refresh to clear any boot artifacts
183+
lv_tick_inc(20);
184+
lv_timer_handler();
185+
inkplate.display();
186+
187+
// Start Bluetooth
188+
if (!SerialBT.begin(btDeviceName)) {
189+
const char *err = "Bluetooth init ERROR";
190+
for (const char *p = err; *p; ++p) log_append_char(*p);
191+
log_append_char('\n');
192+
} else {
193+
const char *ok1 = "BT ready. Pair and connect.";
194+
for (const char *p = ok1; *p; ++p) log_append_char(*p);
195+
log_append_char('\n');
196+
log_append_char('\n'); // extra spacing before user messages
197+
}
198+
}
199+
200+
void loop() {
201+
// USB Serial -> Bluetooth
202+
while (Serial.available()) {
203+
char c = (char)Serial.read();
204+
SerialBT.write((uint8_t)c);
205+
}
206+
207+
// Bluetooth -> screen + echo to USB Serial
208+
while (SerialBT.available()) {
209+
char c = (char)SerialBT.read();
210+
Serial.write((uint8_t)c);
211+
log_append_char(c);
212+
}
213+
214+
lv_tick_inc(5);
215+
lv_timer_handler();
216+
delay(20);
217+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/**
2+
**************************************************
3+
*
4+
* @file Qwiic.ino
5+
* @brief Example demonstrating easyC (I2C) communication between Inkplate 6 and
6+
* a Soldered BME280 or BME680 environmental sensor, using the LVGL library
7+
* for display rendering.
8+
*
9+
* This example reads temperature, humidity, and pressure data from a
10+
* Soldered BME280 sensor connected via the easyC interface and displays
11+
* it on the Inkplate 6 e-paper screen using LVGL elements.
12+
*
13+
* Note: Both BME280 and BME680 sensors are supported by the same
14+
* Soldered BME280/BME680 library. In this example, the BME280 is used,
15+
* as defined in the code. The sensor communicates over easyC (I2C)
16+
* using the default 0x76 address.
17+
*
18+
* For setup instructions and more information about Inkplate 6, visit:
19+
* https://soldered.com/documentation/inkplate/6/overview/
20+
*
21+
* @hardware Inkplate 6COLOR (ESP32-based e-paper display)
22+
* @sensors Soldered BME280 (or BME680) via easyC connector
23+
* @library Soldered BME280/BME680 Gas Sensor Arduino Library
24+
* https://github.com/SolderedElectronics/Soldered-BME280-BME680-Gas-Sensor-Arduino-Library
25+
*
26+
* @authors Soldered
27+
* @date November 2025
28+
**************************************************
29+
*/
30+
31+
32+
#include <Inkplate-LVGL.h>
33+
#include <BME280-SOLDERED.h> // Soldered BME280/BME680 library (easyC/I2C)
34+
35+
// Inkplate in 1-bit mode (fast partial updates)
36+
Inkplate inkplate(INKPLATE_1BIT);
37+
BME280 bme280; // easyC/I2C (Soldered uses 0x76)
38+
39+
// Optional temperature calibration offset
40+
const float TEMPERATURE_OFFSET = 0.0f;
41+
42+
// UI elements
43+
static lv_obj_t *panel_data;
44+
static lv_obj_t *lbl_title;
45+
static lv_obj_t *lbl_temp;
46+
static lv_obj_t *lbl_hum;
47+
static lv_obj_t *lbl_press;
48+
static lv_obj_t *lbl_warn;
49+
50+
// Refresh policy (do a FULL every ~20 partials)
51+
static int partialCount = 0;
52+
static const int FULL_REFRESH_EVERY = 20;
53+
54+
// Helpers
55+
static inline int dispW() { auto *d = lv_display_get_default(); return lv_display_get_horizontal_resolution(d); }
56+
static inline int dispH() { auto *d = lv_display_get_default(); return lv_display_get_vertical_resolution(d); }
57+
58+
static void make_opaque(lv_obj_t *obj) {
59+
lv_obj_set_style_bg_color(obj, lv_color_hex(0xFFFFFF), 0);
60+
lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, 0);
61+
lv_obj_set_style_border_width(obj, 0, 0);
62+
}
63+
64+
// Minimal LVGL UI (no GFX icons to keep it LVGL-only)
65+
static void ui_create() {
66+
lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0xFFFFFF), LV_PART_MAIN);
67+
68+
lbl_title = lv_label_create(lv_screen_active());
69+
lv_label_set_text(lbl_title, "Qwiic • BME280");
70+
lv_obj_set_style_text_color(lbl_title, lv_color_hex(0x000000), 0);
71+
lv_obj_set_pos(lbl_title, 8, 8);
72+
make_opaque(lbl_title);
73+
74+
panel_data = lv_obj_create(lv_screen_active());
75+
make_opaque(panel_data);
76+
lv_obj_set_style_pad_all(panel_data, 0, 0);
77+
lv_obj_set_style_border_width(panel_data, 0, 0);
78+
lv_obj_set_pos(panel_data, 0, 40);
79+
lv_obj_set_size(panel_data, dispW(), dispH() - 80);
80+
81+
lbl_temp = lv_label_create(panel_data);
82+
lv_obj_set_style_text_color(lbl_temp, lv_color_hex(0x000000), 0);
83+
lv_obj_set_pos(lbl_temp, 24, 40);
84+
85+
lbl_hum = lv_label_create(panel_data);
86+
lv_obj_set_style_text_color(lbl_hum, lv_color_hex(0x000000), 0);
87+
lv_obj_set_pos(lbl_hum, 24, 140);
88+
89+
lbl_press = lv_label_create(panel_data);
90+
lv_obj_set_style_text_color(lbl_press, lv_color_hex(0x000000), 0);
91+
lv_obj_set_pos(lbl_press, 24, 240);
92+
93+
lbl_warn = lv_label_create(panel_data); // shown only if sensor looks unresponsive
94+
lv_obj_set_style_text_color(lbl_warn, lv_color_hex(0x000000), 0);
95+
lv_obj_set_pos(lbl_warn, 24, 320);
96+
lv_label_set_text(lbl_warn, "");
97+
}
98+
99+
static void update_readings() {
100+
// Read sensor
101+
float tC = bme280.readTemperature() + TEMPERATURE_OFFSET; // °C
102+
float hum = bme280.readHumidity() / 10.0f; // library returns x10
103+
float hPa = bme280.readPressure() * 10.0f; // hPa (original style)
104+
105+
// Basic sanity check (some libs return 0 or extreme values if not connected)
106+
bool looks_bad = (hPa <= 0.0f) || (hum < 0.0f || hum > 100.0f) || (tC < -40.0f || tC > 85.0f);
107+
108+
char bufT[32], bufH[32], bufP[32];
109+
snprintf(bufT, sizeof(bufT), "Temperature: %.1f *C", tC);
110+
snprintf(bufH, sizeof(bufH), "Humidity: %.0f %%", hum);
111+
snprintf(bufP, sizeof(bufP), "Pressure: %.0f hPa", hPa);
112+
113+
lv_label_set_text(lbl_temp, bufT);
114+
lv_label_set_text(lbl_hum, bufH);
115+
lv_label_set_text(lbl_press, bufP);
116+
117+
if (looks_bad) {
118+
lv_label_set_text(lbl_warn, "Warning: sensor readings look invalid.\nCheck Qwiic cable and sensor power.");
119+
} else {
120+
lv_label_set_text(lbl_warn, "");
121+
}
122+
}
123+
124+
void setup() {
125+
Serial.begin(115200);
126+
127+
// LVGL + Inkplate in PARTIAL mode
128+
inkplate.begin(LV_DISP_RENDER_MODE_PARTIAL);
129+
130+
// Init BME280 (void return in this library)
131+
bme280.begin();
132+
133+
ui_create();
134+
update_readings();
135+
136+
// Initial FULL refresh
137+
lv_tick_inc(20);
138+
lv_timer_handler();
139+
inkplate.display();
140+
}
141+
142+
void loop() {
143+
update_readings();
144+
145+
lv_tick_inc(10);
146+
lv_timer_handler();
147+
inkplate.display(); // FULL
148+
149+
150+
delay(60000); // ~60 s between readings
151+
}

0 commit comments

Comments
 (0)