forked from crosspoint-reader/crosspoint-reader
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplatformio.ini
More file actions
637 lines (613 loc) · 29.5 KB
/
Copy pathplatformio.ini
File metadata and controls
637 lines (613 loc) · 29.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
[platformio]
default_envs = default
build_cache_dir = .cache
extra_configs = platformio.local.ini
[trailink]
version = 0.2.1
[base]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.37/platform-espressif32.zip
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
check_tool = cppcheck
; missingInclude (project headers) is suppressed alongside missingIncludeSystem: on a
; fresh CI checkout cppcheck has no resolved include paths, so it reports every
; project header as missing (~400 information-level lines) and fails the job.
check_flags = --enable=all --suppress=missingIncludeSystem --suppress=missingInclude --suppress=unusedFunction --suppress=unmatchedSuppression --suppress=*:*/.pio/* --inline-suppr
check_skip_packages = yes
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_upload.offset_address = 0x10000
build_flags =
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
-DEINK_DISPLAY_SINGLE_BUFFER_MODE=1
-DDISABLE_FS_H_WARNING=1
-DDESTRUCTOR_CLOSES_FILE=1
# Map is the only screen left that draws text (UI_10/UI_12/SMALL, always
# compiled below); no on-device path still reaches a reading font, so the
# 8 NotoSerif/NotoSans reader families (12/14/16/18pt x2, 4 styles each) are
# dead weight. OMIT_FONTS drops all of them except the always-on NotoSerif
# 14pt fallback (src/main.cpp), so Settings > Reader keeps a working preview
# font instead of a blank one.
-DOMIT_FONTS=1
# https://libexpat.github.io/doc/api/latest/#XML_GE
-DXML_GE=0
-DXML_CONTEXT_BYTES=1024
-std=gnu++2a
# Enable UTF-8 long file names in SdFat
-DUSE_UTF8_LONG_NAMES=1
# Increase PNG scanline buffer to support up to 2048px wide images
# Default is (320*4+1)*2=2562, we need more for larger images
-DPNG_MAX_BUFFERED_PIXELS=16416
-DFREEINK_NET_WOLFSSL=1
-DWOLFSSL_USER_SETTINGS
-DWOLFSSL_OPTIONS_H
-DWOLFSSL_CLIENT_EXAMPLE
-DWOLFSSL_TLS13
# MEMFIX-PORT: single-precision ECC (sp_c32.c). Without it every P-256 operation
# (TLS 1.3 key_share keygen, ECDHE, ECDSA cert verify) runs on fast-math bignums
# that WOLFSSL_SMALL_STACK heap-allocates at FP_MAX_BITS size -- tens of KB of
# temporaries, which OOMs (MP_MEM) at the ~50KB free heap a reading session
# leaves. SP uses fixed 256-bit arrays: a few KB, and several times faster.
# SP_SMALL trades the large precomputed point tables for smaller flash.
-DWOLFSSL_HAVE_SP_ECC
-DWOLFSSL_SP_SMALL
# Arduino-wolfSSL's user_settings.h enables TLS extensions, supported curves,
# HKDF, and RSA-PSS when WOLFSSL_TLS13 is set. Do not repeat those defines here:
# GCC reports every library translation unit as a macro redefinition.
-DHAVE_FFDHE_2048
-DHAVE_CURVE25519
-DHAVE_SNI
-Wno-bidi-chars
-Wl,--wrap=panic_print_backtrace,--wrap=panic_abort,--wrap=bootloader_common_check_efuse_blk_validity
-fno-exceptions
build_unflags =
-std=gnu++11
-fexceptions
; Board configuration
board_build.flash_mode = dio
board_build.flash_size = 16MB
board_build.partitions = partitions.csv
; MEMFIX-PORT: custom_sdkconfig heap reclamation (~32-37 KB). Rebuilds the
; Arduino core libs on first build (slower once, cached after; needs the CMake
; pin in platformio.local.ini on macOS).
;
; If an interrupted rebuild fails with "multiple definition of 'app_main'"
; (stale generated scaffold), clean it up with:
; rm -rf .dummy CMakeLists.txt sdkconfig.default sdkconfig.defaults .pio/build/default
; Do NOT use `git clean -fdX` — it deletes platformio.local.ini.
custom_sdkconfig =
; Task stack right-sizing from measured high-water marks (heap block map +
; per-task stack audit, July 2026): esp_timer used ~0.8 KB of 8 KB across
; every capture; the FreeRTOS timer service used ~0.5 KB
; of 4 KB. Neither runs TLS or app code. ~7 KB back to the heap.
CONFIG_ESP_TIMER_TASK_STACK_SIZE=4096
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2560
; Move the WiFi stack's non-critical hot paths out of IRAM into flash.
; On the C3, IRAM and DRAM share one SRAM pool, so the ~25-30 KB this
; frees lands directly in the heap — paid for with lower WiFi throughput
; during transfers (occasional sync/OTA use, not streaming: acceptable).
; IRAM cost is static, so the heap gain applies even with WiFi off.
CONFIG_ESP_WIFI_IRAM_OPT=n
CONFIG_ESP_WIFI_RX_IRAM_OPT=n
; Keep the Arduino wrappers for the removed cloud components (below) out of
; the core source list; all other bundled libraries default to enabled.
CONFIG_ARDUINO_SELECTIVE_COMPILATION=y
CONFIG_ARDUINO_SELECTIVE_RainMaker=n
CONFIG_ARDUINO_SELECTIVE_Insights=n
; ESP_SR (speech recognition) only exists in the S3 core; its wrapper includes
; ESP_I2S.h, which the isolated core rebuild can't resolve. Unused here anyway,
; so drop it or the sticky env fails to build.
CONFIG_ARDUINO_SELECTIVE_ESP_SR=n
; BLE sized for what this device is: one peripheral link to one phone, never a
; central, never scanning. Measured on hardware 2026-08-10 (docs/map-memory.md):
; BlePositionServer::begin() takes 64,544 bytes -- 86 % of the map screen's whole
; 75 KB heap cost, against 7,696 for the tile source. The stock Arduino config
; pays for 3 connections, central and observer roles (no NimBLEClient or
; NimBLEScan exists anywhere in this tree), a 100-entry scan-duplicate cache and
; 24x255 ACL buffers at an MTU of 256.
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
CONFIG_BT_NIMBLE_ROLE_CENTRAL=n
CONFIG_BT_NIMBLE_ROLE_OBSERVER=n
; setSecurityAuth(bonding=false) already refuses to bond (BlePositionServer.cpp),
; so the bond table and whitelist are dead structures.
CONFIG_BT_NIMBLE_MAX_BONDS=1
CONFIG_BT_NIMBLE_WHITELIST_SIZE=1
; One transfer at a time, one connection, 248-byte payloads: the stock pools are
; sized for a multi-link central. Host task high-water left 2,152 of 4,096 bytes
; free during a real 42 KB tile push (measured, same run).
CONFIG_BT_NIMBLE_ACL_BUF_COUNT=12
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=8
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=12
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096
; Controller: one advertising set plus one connection, and no scanning at all.
CONFIG_BT_CTRL_BLE_MAX_ACT=2
CONFIG_BT_CTRL_BLE_SCAN_DUPL=n
; Modem sleep: power the baseband and RF down between connection events
; instead of holding them up for the whole ride. Off in this tree until now
; (sdkconfig.defaults:993), which means the radio was never sleeping at all.
;
; Run 1 (docs/power-plan.md) measured 44.4 mA over 11.4 h with the map up and
; the phone linked, of which real work was 2.2 % of the day. The radio is one
; of the two big components of the rest. With the idle connection set --
; 30-50 ms interval, latency 4 (BlePositionServer.h) -- the radio needs to be
; awake roughly every 225 ms for a couple of milliseconds, so mode 1 turns a
; permanently powered baseband into a ~1 % duty cycle.
;
; MAIN_XTAL, not EXT_32K: no external 32.768 kHz crystal is selected
; (sdkconfig.defaults:1568) and whether the board even has one is still an
; open question. The EXT_32K path falls back to the main crystal with a
; warning anyway, and the 136 kHz internal RC is too inaccurate to hold a
; connection. Main XTAL costs "the crystal stays powered", which buys nothing
; back only once light sleep exists -- and it does not, CONFIG_PM_ENABLE is
; off.
;
; This does NOT license dropping the CPU below 80 MHz. APB follows the CPU
; onto the crystal below 80 (docs/power-management.md), and the controller's
; only defence against that is an esp_pm lock that compiles out without
; CONFIG_PM_ENABLE. Modem sleep and the CPU clock floor are separate rules.
CONFIG_BT_CTRL_MODEM_SLEEP=y
CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y
CONFIG_BT_CTRL_LPCLK_SEL_MAIN_XTAL=y
; Drop unused cloud components from the core rebuild. esp_insights/rainmaker
; require embedded server certs the lib builder can't generate
; ("https_server.crt.S not found"); this firmware uses none of them.
custom_component_remove =
espressif/esp_insights
espressif/esp_rainmaker
espressif/esp_diagnostics
espressif/esp_diag_data_store
espressif/esp_schedule
espressif/esp_rcp_update
espressif/esp_secure_cert_mgr
espressif/cbor
extra_scripts =
pre:scripts/patch_wolfssl.py
pre:scripts/build_html.py
pre:scripts/gen_i18n.py
pre:scripts/gen_mode_masks.py
pre:scripts/gen_mapstyle.py
pre:scripts/gen_marker_shape.py
pre:scripts/git_branch.py
pre:scripts/patch_jpegdec.py
post:scripts/register_unit_tests_target.py
post:scripts/register_map_preview_target.py
; Libraries
lib_deps =
BatteryMonitor=symlink://freeink-sdk/libs/hardware/BatteryMonitor
InputManager=symlink://freeink-sdk/libs/hardware/InputManager
EInkDisplay=symlink://freeink-sdk/libs/display/FreeInkDisplay
SDCardManager=symlink://freeink-sdk/libs/hardware/SDCardManager
BoardConfig=symlink://freeink-sdk/libs/hardware/BoardConfig
XteinkDetect=symlink://freeink-sdk/libs/hardware/XteinkDetect
PowerManager=symlink://freeink-sdk/libs/hardware/PowerManager
; Every env, not just the frontlight boards: src/main.cpp includes the header
; unconditionally, and the library is inert when the active profile has no
; frontlight (FREEINK_CAP_FRONTLIGHT=0 links stub bodies, no LEDC code).
FrontlightManager=symlink://freeink-sdk/libs/hardware/FrontlightManager
Rtc=symlink://freeink-sdk/libs/hardware/Rtc
Imu=symlink://freeink-sdk/libs/hardware/Imu
SecureNet=symlink://freeink-sdk/libs/network/SecureNet
FreeInkUI=symlink://freeink-sdk/libs/ui/FreeInkUI
Icons=symlink://freeink-sdk/libs/assets/Icons
bblanchon/ArduinoJson @ 7.4.2
ricmoo/QRCode @ 0.0.1
bitbank2/PNGdec @ 1.1.6
https://github.com/bitbank2/JPEGDEC.git#86282979224c8a32fd51e091ed5a35b0c699a52b
links2004/WebSockets @ 2.7.3
wolfssl/Arduino-wolfSSL @ 5.7.2
; Our pinned NimBLE-Arduino, named once here because two environments need the
; identical spec and they do not live on the same branch: `default` is here on
; `develop`, `t5s3pro` is on `release/lilygo-t5-s3-pro`. Spelling it twice is
; how one of them silently keeps the unpatched library through a merge.
;
; The fork is upstream 2.5.1 with exactly one line removed,
; `ble_npl_callout_deinit(&ble_hs_timer)` in `ble_hs_timer_reset()`. Upstream
; deinits the host timer callout while its event may already be queued, so the
; host task dequeues an event whose `fn` was memset to NULL and jumps to 0. It
; killed the device on four real map and sync-screen exits (BUG-033, T-233 in
; the parent repo; `docs/ble-deinit-crash.md` has the coredumps and the chain).
; Removing it leaks nothing: `ble_hs_deinit()` deinits that callout anyway,
; after the host has stopped and the queue is drained.
;
; Reported and offered upstream as h2zero/NimBLE-Arduino#1184 and #1185. Drop
; the fork and go back to a release spec once that merges and ships -- the fork
; carries nothing else, on purpose, so the diff to check is one line.
nimble_dep = NimBLE-Arduino=https://github.com/rfordinal/NimBLE-Arduino.git#63d87323622616310b1839a876ac2ed187a14f79
lib_ignore =
BLE
[env:default]
extends = base
; NimBLE-Arduino + FREEINK_CAP_BLE_PERIPHERAL are scoped to this dev env only
; (map/nav feature in progress, see docs/firmware-implementation-plan.md
; Phase 3 in the parent xteink repo) -- not yet added to gh_release/slim/
; sticky, matching BoardConfig.h's "opt-in feature, not board-derived"
; convention for other BLE capabilities.
lib_deps =
${base.lib_deps}
${base.nimble_dep}
build_flags =
${base.build_flags}
; TRAILINK_VERSION is set by scripts/git_branch.py (includes branch + short SHA)
-DFREEINK_DEVICE_X4=1
-DFREEINK_DEVICE_X3=1
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=2 ; Set log level to debug for development builds
-DFREEINK_CAP_BLE_PERIPHERAL=1
; Bench only: CMD:SETTING over the serial console. It writes persisted
; settings, so anyone holding the device over USB could turn on the rider's
; mobile-data spend (mapAutoSyncTiles, mapTileFreshnessMode) or print their
; exact position on the panel (mapDebugInfo). Stays out of every release env.
-DENABLE_SETTING_CMD=1
; Serial button injection (CMD:BUTTON, src/DebugInput.h). Bench only for the
; same reason as the line above: a press injector on a lost device is a thumb
; for whoever picked it up.
-DENABLE_BUTTON_CMD=1
; CMD:BATT, widened here from env:t5s3pro-only (main.cpp comment predicted
; this: "the flag is worth widening when that measurement comes up" -- X3
; carries a BQ27220 too). Read-only, no position/route/identity in the
; reply, so devel-only is about keeping it out of a stranger's build, not
; secrecy.
-DENABLE_BATT_CMD=1
# The grayscale test bench (Home > Preview) is OFF by default, in every
# environment. It is a lab instrument, not a rider's screen: a menu item on a
# device mounted to a handlebar is something to open by mistake at 90 km/h. Turn
# it on in platformio.local.ini when there is a panel question to answer:
# build_flags = ${env:default.build_flags}
# -DENABLE_PREVIEW_BENCH=1
# See src/activities/preview/PreviewActivity.h and docs/eink-grayscale.md.
[env:gh_release]
extends = base
build_flags =
${base.build_flags}
-DFREEINK_DEVICE_X4=1
-DFREEINK_DEVICE_X3=1
-DTRAILINK_VERSION=\"${trailink.version}\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=1 ; Set log level to info for release builds
[env:gh_release_rc]
extends = base
build_flags =
${base.build_flags}
-DFREEINK_DEVICE_X4=1
-DFREEINK_DEVICE_X3=1
-DTRAILINK_VERSION=\"${trailink.version}-rc+${sysenv.TRAILINK_RC_HASH}\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=1 ; Set log level to info for release candidate builds
[env:slim]
extends = base
build_flags =
${base.build_flags}
-DFREEINK_DEVICE_X4=1
-DFREEINK_DEVICE_X3=1
-DTRAILINK_VERSION=\"${trailink.version}-slim\"
; serial output is disabled in slim builds to save space
-UENABLE_SERIAL_LOG
; --- Seeed Sticky — ESP32-S3R8, 3.97" 800x480 SSD1677 + GT911 touch -----------
; Different MCU family than the C3 envs (one binary per family). The SDK
; auto-enables CAP_TOUCH (GT911) and the BQ27220 gauge for this device; PSRAM is
; intentionally left off (48KB framebuffer fits in DRAM, same as X4).
; pio run -e sticky -t upload
[env:sticky]
extends = base
board = esp32-s3-devkitc1-n16r8
board_build.mcu = esp32s3
build_flags =
${base.build_flags}
-DFREEINK_DEVICE_STICKY=1
; git_branch.py only injects TRAILINK_VERSION for the default env; set it here.
-DTRAILINK_VERSION=\"${trailink.version}-sticky\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=2
; Bench only, same reason as env:default.
-DENABLE_SETTING_CMD=1
; Serial button injection (CMD:BUTTON, src/DebugInput.h). Bench only for the
; same reason as the line above: a press injector on a lost device is a thumb
; for whoever picked it up.
-DENABLE_BUTTON_CMD=1
; --- Xteink X4 Pro ------------------------------------------------------------
; The S3 sibling of the C3 X4: SSD1677 panel, GT911 capacitive touch, a warm and
; cold frontlight, 8 MB PSRAM. Different MCU family from the X4 and X3, so it is
; a second binary and a second hardware pass, not a flag on `default`
; (BoardConfig.h: "all selected devices must share one MCU family").
;
; The frontlight is why this board is the reference device: it is mandatory for
; the product and the plain X4 does not have one (parent
; docs/devices/xteink-x4-pro.md).
;
; Mirrors the SDK's own `[env:x4pro]` in freeink-sdk/platformio.sample.ini, whose
; board line is matched against the OEM flash dump, not guessed:
; 16 MB flash, 8 MB PSRAM. The SDK's `docs/xteink-x4pro-support.md` has the pin
; evidence and its own Pending list.
[env:x4pro]
extends = base
board = esp32-s3-devkitc1-n16r8 ; 16 MB flash + 8 MB PSRAM, matches the X4 Pro dump
board_build.mcu = esp32s3
lib_deps =
${base.lib_deps}
${base.nimble_dep}
build_flags =
${base.build_flags}
-DFREEINK_DEVICE_X4PRO=1
-DBOARD_HAS_PSRAM
; The X4 Pro's SD is 1-bit SDMMC, so BoardConfig auto-enables FREEINK_SD_SDMMC
; and SdFat mounts an esp-idf block device instead of driving SPI itself. That
; needs SdFat's generic block-device interface, or SDCardManager.cpp does not
; compile: "cannot convert 'freeink::SdmmcBlockDevice*' to 'FsBlockDevice*'".
; BoardConfig.h says so at the FREEINK_SD_SDMMC definition; the SDK's own
; sample env for this board omits the flag and would hit the same error.
-DUSE_BLOCK_DEVICE_INTERFACE=1
; git_branch.py only injects TRAILINK_VERSION for the default env; set it here.
-DTRAILINK_VERSION=\"${trailink.version}-x4pro\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=2
-DFREEINK_CAP_BLE_PERIPHERAL=1
; Bench only, same reason as env:default: CMD:SETTING writes persisted settings
; and CMD:BUTTON is a thumb for whoever picks up a lost device.
-DENABLE_SETTING_CMD=1
-DENABLE_BUTTON_CMD=1
; Raw GT911 status log (CMD:TOUCHLOG, src/DebugTouchLog.h). Only on the two
; boards that have a GT911: it is the instrument T-266 step 1 needs, and it is
; read-only, but it blocks the loop for up to eight seconds, which in the hands
; of whoever picks up a lost device is a way to freeze the screen. Bench only.
-DENABLE_TOUCHLOG_CMD=1
; --- What a stranger gets: the X4 Pro build that is published ------------------
; env:x4pro above is the bench env and it carries three serial backdoors. This
; one is what goes on GitHub and into the browser flasher, and it is a separate
; env for one reason: the device gets lost or stolen, and while it is in someone
; else's hands they can plug into USB or talk to it over BLE, which share one
; unauthenticated command grammar (MapCommandParser.h, P3 and P5).
;
; So none of these three are set here, and adding one back is a decision, not an
; oversight:
;
; ENABLE_SETTING_CMD writes persisted settings. CMD:SETTING mapDebugInfo 1
; paints the rider's exact position on the panel, and
; mapAutoSyncTiles / mapTileFreshnessMode spend their
; mobile data. The write survives a reboot.
; ENABLE_BUTTON_CMD injects button presses. A thumb for whoever picked the
; device up, reaching every screen the rider can.
; ENABLE_TOUCHLOG_CMD read-only, but it blocks the loop for up to eight
; seconds, which is a way to freeze the screen.
;
; Measured, 2026-09-14: the published explorink-x4pro-v0.2.0-alpha image was
; built from env:x4pro and carries SETTING_OK: and BUTTON_OK in its strings.
; TOUCHLOG is absent there only because it did not exist yet, which is what this
; env exists to stop happening again.
;
; LOG_LEVEL is 1 (info) to match gh_release rather than 2 (debug); ENABLE_SERIAL_LOG
; stays on for the same reason it does in gh_release, so a rider can be asked for
; a boot log.
;
; pio run -e gh_release_x4pro
[env:gh_release_x4pro]
extends = base
board = esp32-s3-devkitc1-n16r8
board_build.mcu = esp32s3
lib_deps =
${base.lib_deps}
${base.nimble_dep}
build_flags =
${base.build_flags}
-DFREEINK_DEVICE_X4PRO=1
-DBOARD_HAS_PSRAM
; Same SdFat reason as env:x4pro: the X4 Pro's SD is 1-bit SDMMC.
-DUSE_BLOCK_DEVICE_INTERFACE=1
-DTRAILINK_VERSION=\"${trailink.version}-x4pro\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=1
-DFREEINK_CAP_BLE_PERIPHERAL=1
; --- LilyGo T5 E-Paper S3 Pro — ESP32-S3, 4.7" ED047TC1, 960x540, 16 gray -----
; The non-Xteink validation board (../../docs/devices/lilygo-t5-s3-pro.md).
; Everything board-specific is already in the SDK: BoardT5S3 supplies the
; parallel-bus pins, the PCA9535 + TPS65185 EPD power sequence and the user
; button hook. The panel has no on-glass controller, so LgfxEpdDriver wraps
; LovyanGFX's Panel_EPD out of m5stack/M5GFX. GT911 touch, the PWM frontlight
; (GPIO11) and the BQ27220/BQ25896 I2C gauge are all board-derived capabilities.
;
; PSRAM matters here and does not on sticky: LgfxEpdDriver keeps an 8-bit
; grayscale canvas there. The real board is an ESP32-S3-WROOM-1 N16R8, so the
; devkitc1-n16r8 definition (octal PSRAM, -DBOARD_HAS_PSRAM) is the stand-in.
;
; pio run -e t5s3pro -t upload
[env:t5s3pro]
extends = base
board = esp32-s3-devkitc1-n16r8
board_build.mcu = esp32s3
; The ATT MTU the device offers, and it decides the whole transfer rate: the
; central initiates the exchange but cannot get more than this
; (BlePositionServer.h, negotiatedMtu). At 256 a chunk carries 248 bytes of
; file; at 517 it carries 507, so this is a little over 2x on every transfer.
;
; 256 was NimBLE's stock default rather than a decision for this board. The
; number it was a decision for is the C3's memory: [base]'s BLE block above is
; sized "for one transfer at a time, one connection, 248-byte payloads" against
; 380 KB of DRAM and no PSRAM. **This board is an S3 with 8 MB of PSRAM**
; (CONFIG_SPIRAM=y), so that constraint is not this board's.
;
; Measured before: 3.9 kB/s on this link at MTU 256, 2026-09-03, a 33-square
; pre-trip batch. Both spellings are set because NimBLE-Arduino carries its own
; CONFIG_NIMBLE_* symbols alongside ESP-IDF's CONFIG_BT_NIMBLE_*, and
; sdkconfig.defaults holds both at 256.
custom_sdkconfig =
${base.custom_sdkconfig}
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=517
CONFIG_NIMBLE_ATT_PREFERRED_MTU=517
lib_deps =
${base.lib_deps}
BoardT5S3=symlink://freeink-sdk/libs/hardware/BoardT5S3
; Not optional here: HalPowerManager.cpp includes <esp_bt.h> unconditionally,
; and the isolated core rebuild only ships that header when something enables
; the BT controller. Same dependency the default env has -- and it has to stay
; the same one, which is why it is a reference and not a second spelling:
; this env lives on the release branch and `default` lives on `develop`, so
; two literals here is how one branch keeps the unpatched library through a
; merge. See `base.nimble_dep` for what the fork is and why.
${base.nimble_dep}
; 0.2.20 (freeink-sdk platformio.sample.ini) is not in the registry — only
; 0.1.17, 0.2.27 and 0.2.28 are. Panel_EPD/Bus_EPD need the 0.2.2x line.
m5stack/M5GFX @ 0.2.28
build_flags =
${base.build_flags}
-DFREEINK_DEVICE_LILYGO=1
-DFREEINK_LGFX_EPD_CONFIG=lilygoT5S3LgfxConfig
-DFREEINK_CAP_BLE_PERIPHERAL=1
; Bring-up only: CMD:LIGHT over the serial console. The frontlight has no UI
; yet, and this actuates the device, so it stays out of every release env.
-DENABLE_FRONTLIGHT_CMD=1
; Bring-up only: CMD:GNSS over the serial console (docs/gnss.md). Two reasons
; it stays out of every release env, not one -- it powers a radio rail shared
; with the LoRa part, and its reply is the rider's exact position.
-DENABLE_GNSS_CMD=1
; Bring-up only: CMD:BATT, the fuel gauge's voltage, SoC and average current on
; demand. It is the other half of a USB-meter power run -- a VBUS reading is the
; board plus the charger, and this is the number that gets subtracted (parent
; docs/usb-power-meter.md). Devel-only because a command with no UI behind it
; does not belong in a build a stranger flashes, not because it leaks anything.
-DENABLE_BATT_CMD=1
; Bring-up only: CMD:BLE, the radio up and down from the console. The bench
; needs a pair of states that differ by one thing, and until this the only way
; to have BLE running was to enter the map -- which also brings tiles, a
; renderer and a panel refresh with it. Devel-only because ON starts an
; unauthenticated command channel with nothing on the screen to say so.
-DENABLE_BLE_CMD=1
; Bring-up only: CMD:CHARGE, the BQ25896's charging switch and the BATFET
; questions behind it (T-251). This is the only route to a USB-meter reading
; that is board draw rather than board-plus-charger: charging off opens the
; BATFET and takes the cell out of the path, with no device opened.
;
; Devel-only for a stronger reason than the flags around it -- those are
; UI-less, this one actuates the charger. Both command channels are
; unauthenticated (parent docs/TODO.md, T-222), so in a release build it would
; hand anyone in BLE range a silent way to stop a rider's device charging.
-DENABLE_CHARGE_CMD=1
; Bring-up only: CMD:SDBUS, the bench instrument for BUG-037. Toggles the
; radio's chip select, its reset line and the shared GNSS/LoRa rail one at a
; time and reads a file back with a CRC, because the fix writes all three at
; once and no hardware run has separated them. Read-only against the card on
; purpose. Devel-only for two reasons: RAIL 1 powers a radio, and a CS left at
; 0 breaks the card until something puts it back.
-DENABLE_SDBUS_CMD=1
; Bring-up only: CMD:SETTING, the map's opt-in toggles from the serial console.
; This env used to get it through ENABLE_SERIAL_LOG; develop moved the gate to
; its own flag on 2026-09-02 because ENABLE_SERIAL_LOG is set in both release
; envs too, and develop has no t5s3pro env to re-declare it in. Bench needs it:
; testing autosync and freshness unattended means flipping them without hands.
-DENABLE_SETTING_CMD=1
; Serial button injection (CMD:BUTTON, src/DebugInput.h). Bring-up only, same
; reason as the flag above, and worth more on this board than on the X4: it has
; one user switch, so up/down/left/right have no thumb here at all.
-DENABLE_BUTTON_CMD=1
; Raw GT911 status log (CMD:TOUCHLOG, src/DebugTouchLog.h). Only on the two
; boards that have a GT911: it is the instrument T-266 step 1 needs, and it is
; read-only, but it blocks the loop for up to eight seconds, which in the hands
; of whoever picks up a lost device is a way to freeze the screen. Bench only.
-DENABLE_TOUCHLOG_CMD=1
; RX ring for the L76K, in bytes. The library's own default is 1024 and stays
; there, because lib/Gnss is board-agnostic and a C3 cannot afford this; the
; board that has the receiver is the board that pays for it.
;
; 8192 is picked from a measurement over REAL map tiles, 2026-09-01
; (docs/gnss.md). Measuring this over ground with no tile coverage
; understates it about fourfold -- a redraw there costs 481 ms and the same
; redraw over loaded tiles costs 2687 ms -- so the number has to come from a
; viewport that actually has data.
;
; The receiver emits 810 B/s. Over real tiles the whole zoom ladder ran with
; a worst render of 4072 ms and a worst main-loop block of 5760 ms, and at
; 8192 not one sentence was lost at any rung. 8192 covers 10.1 s.
;
; It does NOT cover everything: the maintainer reports redraws reaching 15 s
; in use, which is about 12.2 kB. That is deliberate rather than an oversight.
; A position source needs the LATEST fix, not the stream, and the phone sends
; at most one position per 7 s -- so a block past the ring costs about a
; second of position age and never a wrong position. Sizing for the rare 15 s
; case would spend 16 kB to buy nothing the product asks for.
; Gnss::ringOverflows() is what reports when it happens.
-DGNSS_RX_BUFFER_BYTES=8192
; git_branch.py only injects TRAILINK_VERSION for the default env; set it here.
-DTRAILINK_VERSION=\"${trailink.version}-t5s3pro\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=2
; --- Desktop simulator -------------------------------------------------------
; Native build of the firmware against the ExplorInk fork of the CrossPoint
; simulator: SDL2 window, no device. The fork exists because the simulator
; replaces lib/hal/ wholesale, so it is pinned to one firmware's HAL
; (explorink-simulator/FORKING.md).
;
; pio run -e simulator -t run_simulator
;
; Point [simdep] at a local checkout in platformio.local.ini while working on
; the simulator itself:
; [simdep]
; source = symlink://../../../firmware/explorink-simulator
; Three levels up, not two: a worktree lives at .worktrees/firmware/<topic>
; since 2026-08-23. An absolute path works too and is what a one-off local
; file usually wants.
[simdep]
source = https://github.com/rfordinal/explorink-simulator#explorink
[env:simulator]
platform = native
lib_ldf_mode = deep+
lib_compat_mode = off
build_src_filter =
+<*>
; Firmware-update code stays non-destructive in the simulator.
-<network/FirmwareFlasher.cpp>
-<network/OtaBootSwitch.cpp>
-<network/OtaUpdater.cpp>
-<platform/skip_efuse_blk_check.c>
build_flags =
-std=gnu++2a
!sdl2-config --cflags --libs
-lssl
-lcrypto
-Wno-deprecated-declarations
-Wno-narrowing
-DSIMULATOR
-DFREEINK_DEVICE_X4=1
; The real BLE peripheral code, compiled against the simulator's NimBLE shim.
; `BLE` stays in lib_ignore below: that entry ignores NimBLE-Arduino, and the
; shim supplies those headers instead. See docs/simulator.md, "BLE".
-DFREEINK_CAP_BLE_PERIPHERAL=1
-DCROSSPOINT_SIMULATOR_PROJECT_WEBSERVER
-DTRAILINK_VERSION=\"${trailink.version}-simulator\"
-DCROSSPOINT_VERSION=\"dev-simulator\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=2
; Bench only, same reason as env:default.
-DENABLE_SETTING_CMD=1
; Serial button injection (CMD:BUTTON, src/DebugInput.h). Bench only for the
; same reason as the line above: a press injector on a lost device is a thumb
; for whoever picked it up.
-DENABLE_BUTTON_CMD=1
-DEINK_DISPLAY_SINGLE_BUFFER_MODE=1
-DMINIZ_NO_ZLIB_COMPATIBLE_NAMES=1
-DOMIT_FONTS=1
-DXML_GE=0
-DXML_CONTEXT_BYTES=1024
-DUSE_UTF8_LONG_NAMES=1
-DPNG_MAX_BUFFERED_PIXELS=16416
-DDISABLE_FS_H_WARNING=1
-DDESTRUCTOR_CLOSES_FILE=1
-Isrc
lib_ignore = hal, PNGdec, JPEGDEC, WebSockets, BLE, SecureNet
extra_scripts =
pre:scripts/gen_i18n.py
pre:scripts/build_html.py
pre:scripts/gen_mode_masks.py
pre:scripts/gen_mapstyle.py
pre:scripts/gen_marker_shape.py
lib_deps =
simulator=${simdep.source}
; BoardConfig and XteinkDetect are deliberately absent: the simulator ships its
; own reduced BoardConfig.h and XteinkDetect.h so the native build never pulls
; in ESP32 GPIO or Wire headers. Device and panel controller are compile-time
; there, picked with the SIMULATOR_DEVICE_* / SIMULATOR_DISPLAY_* flags.
FreeInkUI=symlink://freeink-sdk/libs/ui/FreeInkUI
Icons=symlink://freeink-sdk/libs/assets/Icons
bblanchon/ArduinoJson @ 7.4.2
ricmoo/QRCode @ 0.0.1
links2004/WebSockets @ 2.7.3