-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConfig.h
More file actions
1746 lines (1684 loc) · 82.3 KB
/
Copy pathConfig.h
File metadata and controls
1746 lines (1684 loc) · 82.3 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
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026 Dobrev IT Ltd
//
// This file is part of RetiMesh Node.
//
// RetiMesh Node is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// RetiMesh Node is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with RetiMesh Node. If not, see <https://www.gnu.org/licenses/>.
// ============================================================================
// Config.h — compile-time configuration for the RetiMesh Node
//
// Everything here can be overridden from platformio.ini build_flags
// (every #define is guarded by #ifndef).
// ============================================================================
#pragma once
#include <Arduino.h>
// ---------------------------------------------------------------------------
// Board selection. One header per board under boards/ defines the pin map and
// the capability flags (HAS_SD, HAS_PMU, HAS_GPS, ...); everything below is
// guarded by #ifndef, so a board header wins and anything it leaves out falls
// back to the general default. Pass -DBOARD_TBEAM (or another BOARD_*) from
// platformio.ini; without one, the T3-S3 is assumed.
// ---------------------------------------------------------------------------
#if defined(BOARD_TBEAM)
#include "boards/tbeam.h"
#elif defined(BOARD_TBEAM_SUPREME)
#include "boards/tbeam_supreme.h"
#elif defined(BOARD_HELTEC_V3)
#include "boards/heltec_v3.h"
#elif defined(BOARD_HELTEC_WS)
#include "boards/heltec_ws.h"
#elif defined(BOARD_HELTEC_WB)
#include "boards/heltec_wb.h"
#elif defined(BOARD_HELTEC_WP)
#include "boards/heltec_wp.h"
#elif defined(BOARD_HELTEC_V4)
#include "boards/heltec_v4.h"
#elif defined(BOARD_T3S3_SX1280_PA)
#include "boards/t3s3_sx1280_pa.h"
#elif defined(BOARD_T3S3_SX1280)
#include "boards/t3s3_sx1280.h"
#elif defined(BOARD_TDECK)
#include "boards/tdeck.h"
#elif defined(BOARD_THINKNODE_M9)
#include "boards/thinknode_m9.h"
#else
#include "boards/t3s3.h"
#endif
// ---------------------------------------------------------------------------
// Local-link capabilities: what the PCB puts on its USB connector. These come
// from boards.json — the one registry of board facts — through
// tools/board_caps.py, which turns the env's "local_link" block into -D flags
// at build time and refuses to build when they contradict the framework's own
// USB flags in platformio.ini. The defaults below are what a build with no
// entry (the host-native test env) gets: nothing.
//
// BOARD_USB_NATIVE the MCU's own USB D+/D- reach the connector
// BOARD_USB_NCM ...and the silicon can present CDC-NCM there
// BOARD_USB_BRIDGE "CP2102", "CH9102", ... or "none"
// BOARD_BRIDGE_AUTO_RESET the bridge's DTR/RTS reach EN/IO0 (esptool resets it)
// BOARD_UART_NETWORK the UART behind the bridge may carry PPP
// BOARD_UART_INSTANCE which UART the bridge sits on (0 on every board so far)
// BOARD_UART_BAUDS the rates the registry lists for it, comma-separated
// BOARD_UART_MAX_BAUD the highest of them tried on this board's hardware
// ---------------------------------------------------------------------------
#ifndef BOARD_USB_NATIVE
#define BOARD_USB_NATIVE 0
#endif
#ifndef BOARD_USB_NCM
#define BOARD_USB_NCM 0
#endif
#ifndef BOARD_USB_BRIDGE
#define BOARD_USB_BRIDGE "none"
#endif
#ifndef BOARD_BRIDGE_AUTO_RESET
#define BOARD_BRIDGE_AUTO_RESET 0
#endif
#ifndef BOARD_UART_NETWORK
#define BOARD_UART_NETWORK 0
#endif
#ifndef BOARD_UART_INSTANCE
#define BOARD_UART_INSTANCE 0
#endif
#ifndef BOARD_UART_BAUDS
#define BOARD_UART_BAUDS 115200
#endif
#ifndef BOARD_UART_MAX_BAUD
#define BOARD_UART_MAX_BAUD 115200
#endif
// Whether this build drives USB networking: the board can present NCM and
// the OTG stack owns the peripheral (ARDUINO_USB_MODE 0, set by
// tools/board_caps.py from the same registry facts). One derived fact, so no
// file has to know both halves. See docs/local-link.md and UsbNcm.h.
#if BOARD_USB_NCM && defined(ARDUINO_USB_MODE) && (ARDUINO_USB_MODE == 0)
#define HAS_USB_NCM 1
#else
#define HAS_USB_NCM 0
#endif
// Whether this build drives PPP over the bridge UART: every board whose
// registry entry says the UART may carry it does, because the core's
// prebuilt lwIP carries the PPP client on every chip. See PppUart.h.
#if BOARD_UART_NETWORK
#define HAS_PPP 1
#else
#define HAS_PPP 0
#endif
// ---------------------------------------------------------------------------
// Serial byte-stream link (UartLink.h)
// ---------------------------------------------------------------------------
// A UART carrying HDLC frames and knowing nothing of Reticulum, so the serial
// RNS interface and the later remote-radio link can both sit on it rather than
// each writing a UART of their own (roadmap 2.2).
//
// Off unless a board names a free port, and deliberately no board does yet.
// Which UART is free is a per-board fact with three claimants already — the
// bridge UART is the console's and PPP's (BOARD_UART_INSTANCE, arbitrated in
// PppArbiter.h), and UART1 is the GNSS receiver's on every board that has one
// (Gps.cpp constructs HardwareSerial(1)) — and the pins for whatever is left
// are a wiring question a bench answers, not one this file can. Naming a port
// here without that answer would claim a link that does not exist.
//
// Costing nothing until then is the point: everything behind HAS_SERIAL_LINK
// compiles out, so the driver is infrastructure for issues 10 and 18 rather
// than weight every board carries. docs/serial.md says what enabling it needs.
#ifndef BOARD_SERIAL_UART
#define BOARD_SERIAL_UART -1 // -1 = this board has no free UART
#endif
#ifndef PIN_SERIAL_LINK_RX
#define PIN_SERIAL_LINK_RX -1
#endif
#ifndef PIN_SERIAL_LINK_TX
#define PIN_SERIAL_LINK_TX -1
#endif
#if BOARD_SERIAL_UART >= 0 && PIN_SERIAL_LINK_RX >= 0 && PIN_SERIAL_LINK_TX >= 0
#define HAS_SERIAL_LINK 1
#else
#define HAS_SERIAL_LINK 0
#endif
// The transmit queue's bound, in frames and in bytes. Both are checked on
// every push and either one running out is the queue being full: a link is as
// likely to be held up by many small frames as by a few large ones, and a
// bound that only counted one of them would be no bound at all in the other
// direction. Sized to hold a handful of full-MTU frames — enough to ride out a
// scheduling hiccup, not enough to hide a wire that has stopped working.
#ifndef SERIAL_LINK_TX_FRAMES
#define SERIAL_LINK_TX_FRAMES 8
#endif
#ifndef SERIAL_LINK_TX_BYTES
#define SERIAL_LINK_TX_BYTES (4 * RNS_MTU)
#endif
// The driver's own receive ring, given to the UART driver at install. What
// does not fit while the reader is behind is lost on the wire and the far end
// resynchronises, which is the same bargain PPP's ring makes above.
#ifndef SERIAL_LINK_RX_RING
#define SERIAL_LINK_RX_RING 2048
#endif
#ifndef SERIAL_LINK_TASK_STACK
#define SERIAL_LINK_TASK_STACK 3072
#endif
// PPP over the bridge UART (PppUart.h). The receive ring is the UART
// driver's own, sized here; what does not fit while the reader is behind is
// dropped and PPP retransmits — the radio never waits for the serial port.
// The transmit queue holds one largest frame (an escaped 1500-byte packet is
// a little over 3 KB) with room to spare; a frame that does not fit is
// dropped whole. The default speed is the console's, so that switching PPP
// on changes nothing a host already relies on; a faster one has to be
// qualified per board (boards.json uart.tested_max_baud).
#ifndef PPP_RX_RING_BYTES
#define PPP_RX_RING_BYTES 4096
#endif
#ifndef PPP_TX_QUEUE_BYTES
#define PPP_TX_QUEUE_BYTES 8192 // five frames at PPP's MTU; internal RAM
#endif
#ifndef PPP_BAUD_DEFAULT
#define PPP_BAUD_DEFAULT 115200
#endif
// There is no smaller pair for the console alone. A board that carries PPP
// carries these sizes from boot whether the switch is on or off, because the
// core fixes them before it installs the UART driver and they cannot be
// resized afterwards (main.cpp, PppUart.h). What the switch gives back is the
// interface and the reader task, not the ring.
// ---------------------------------------------------------------------------
// The maintenance console (Maintenance.h) and its network transport
// (ConsoleServer.h)
//
// The console over a socket is what lets a node be configured from a
// distance without a resident web server: measured on a Heltec Wireless
// Stick, the portal and the resolver beside it cost about 22 KB of
// byte-addressable RAM ("http + dns" in the boot bill, with mDNS billed
// separately at 6 368 B) against 272 B for the Reticulum TCP listener, on a
// board that finishes booting with about 27 KB. Same parser, same settings rules, same refusals
// as the cable — a second transport, not a second implementation.
// ---------------------------------------------------------------------------
#ifndef CONSOLE_TCP_PORT
#define CONSOLE_TCP_PORT 4243 // beside the Reticulum transport's 4242
#endif
// One caller at a time, and not a setting: the transport holds one client
// slot (ConsoleServer.cpp), so a second would be dead RAM and a macro that
// raised it would be a knob that turns nothing. This is a configuration
// channel rather than a service — two operators changing settings at once is
// a race nobody asked for.
// One for whatever a network transport brings, and one kept back for a caller
// the transport authenticated itself — today that is a command arriving as an
// LXMF message. They are not interchangeable: the TCP console claims its slot
// on connect, before any password, so a single client sitting idle on the
// access point would otherwise deny the one recovery path a node has when its
// cable is dead (RnsAdmin.h, Maintenance.h).
#define MAINT_NET_SESSIONS 2
#define MAINT_RESERVED_SESSIONS 1
// Guessing the admin password over the air. Counted for the node, not the
// connection (Maintenance.h).
#ifndef MAINT_AUTH_MAX_FAILURES
#define MAINT_AUTH_MAX_FAILURES 3
#endif
#ifndef MAINT_AUTH_LOCKOUT_MS
#define MAINT_AUTH_LOCKOUT_MS 30000
#endif
// ---------------------------------------------------------------------------
// Firmware version — single-sourced from the git tag by CI
// (PLATFORMIO_BUILD_FLAGS=-DFW_VERSION=\"v1.2.3\"). A build CI did not version
// asks git instead (tools/fw_version.py): `git describe --always --dirty`, so
// a node can say which commit it runs and a bench of them can be told apart.
// "dev" survives only where git cannot answer at all — a source tarball, or a
// container without the binary.
// ---------------------------------------------------------------------------
#ifndef FW_VERSION
#define FW_VERSION "dev"
#endif
#define FW_NAME "RetiMesh Node"
// ---------------------------------------------------------------------------
// Wi-Fi SoftAP
// ---------------------------------------------------------------------------
// SSID = AP_SSID_PREFIX + "-" + last three octets of the factory MAC,
// e.g. "retimesh-A1B2C3", so several nodes in range never collide.
// Define AP_SSID to force a fixed name instead.
#ifndef AP_SSID_PREFIX
#define AP_SSID_PREFIX "retimesh"
#endif
#ifndef AP_PASSWORD
#define AP_PASSWORD "" // empty = open network
#endif
// SoftAP WPA3 (SAE) needs ESP-IDF 5, which the core 3.x toolchain brings. The
// gate stays on the IDF version rather than being assumed true because the
// earlier core 2.x (IDF 4.4) rejected the auth mode outright (verified:
// esp_wifi_set_config -> ESP_ERR_INVALID_ARG, AP stays WPA2), and a build on
// such a core must go on saying so rather than offering what it cannot do.
#include <esp_idf_version.h>
#define WPA3_SOFTAP_SUPPORTED (ESP_IDF_VERSION_MAJOR >= 5)
// Default security when no setting is stored: 0 open, 1 WPA2,
// 2 WPA2+WPA3 mixed, 3 WPA3-only. Ignored unless AP_PASSWORD has >= 8 chars.
#ifndef AP_SECURITY_DEFAULT
#define AP_SECURITY_DEFAULT 1
#endif
#ifndef AP_CHANNEL
#define AP_CHANNEL 6
#endif
#ifndef AP_MAX_STATIONS
#define AP_MAX_STATIONS 8
#endif
// SoftAP beacon interval, in 802.11 time units (1 TU = 1.024 ms, so 400 TU is
// about 410 ms). The Arduino core hardcodes 100 TU inside WiFi.softAP() — ten
// beacons a second at the lowest basic rate, around the clock, usually for
// nobody — and rebuilds that config on every call, which is why the value is
// applied by a read-modify-write after softAP() (WifiManager::startAccessPoint)
// and lives nowhere else. 4x less beacon airtime and current; the cost is a
// phone taking a moment longer to see the network in a scan list. A build
// default rather than a runtime setting, deliberately.
#ifndef WIFI_AP_BEACON_TU
#define WIFI_AP_BEACON_TU 400
#endif
// Default Wi-Fi TX power ceiling, dBm — wifi.tx_power's default, one global
// ceiling for the AP and the station together (the chip has one radio). 14 is
// plenty for the AP-as-maintenance-portal case of a phone at arm's length; a
// node genuinely bridging a LAN at range turns it up (setting bounds 2-20,
// SettingsRules).
#ifndef WIFI_TX_POWER_DBM
#define WIFI_TX_POWER_DBM 14
#endif
// How many AP beacon intervals a dozing station sleeps between wakes —
// wifi.sta_listen_interval's default, which is also the driver's own default.
// The driver consults it only under WIFI_PS_MAX_MODEM (the battery profile).
#ifndef WIFI_STA_LISTEN_INTERVAL
#define WIFI_STA_LISTEN_INTERVAL 3
#endif
// AP idle auto-off — wifi.ap_idle_off / wifi.ap_idle_minutes' defaults. With
// the switch on, an access point that has stood empty for the configured
// minutes is taken down until something wakes it: the button, SET
// links.wifi_ap on or WIFI ON at the console (either wakes even when the
// switch is already on — but WIFI ON writes both Wi-Fi switches, so on a
// node that keeps its station off it also restarts), or an admin message.
// Off by default, deliberately: the AP is usually the only management path
// on an unattended node, and a policy bug that keeps it down is a site
// visit — the operator opts in per node. Bounds (1-1440 min) live in
// SettingsRules.
// What kind of node this is, when nobody has said: 0 not set, 1 carried,
// 2 transport (Power::Role). Not set is deliberately the default and is
// deliberately not guessed from the board class — a handheld on a desk and a
// relay on a mast are the same board, and the only difference is what its
// operator did with it. A node that has never been told behaves exactly as
// every node did before the setting existed, which is what makes the upgrade
// a no-change. Bounds live in SettingsRules; a board or a future preset may
// override this, and later firmware may add roles above 2.
#ifndef NODE_ROLE_DEFAULT
#define NODE_ROLE_DEFAULT 0
#endif
#ifndef AP_IDLE_OFF_DEFAULT
#define AP_IDLE_OFF_DEFAULT 0
#endif
#ifndef AP_IDLE_MINUTES
#define AP_IDLE_MINUTES 10
#endif
// How long a runtime access-point teardown holds off after the change that
// asked for it, so the reply — an HTTP 200 to a browser on the AP itself, a
// console OK — leaves before the link it rides goes away. The same job
// RESTART_ACK_DELAY_MS does for a restart's acknowledgement, folded into the
// one teardown path (WifiManager's convergence) so no caller can shorten it.
// Invariant (WifiManager.h holds the static_assert): the grace must exceed
// the idle-gate cadence (_apIdleGate, 1 s), so at least one policy ask lands
// inside every grace — that ask is what re-reads the station count and lets a
// station that associated during the grace cancel the staged teardown. At
// 700 ms the teardown beat the next ask in a healthy loop and cut such a
// station off; 1200 ms still reads as instant and covers the async reply
// many times over.
#ifndef AP_STOP_GRACE_MS
#define AP_STOP_GRACE_MS 1200
#endif
// Admin password protecting the settings API/page (HTTP Basic Auth,
// user "admin"). Change it from the settings page; stored in NVS.
#ifndef ADMIN_PASSWORD_DEFAULT
#define ADMIN_PASSWORD_DEFAULT "retimesh"
#endif
#define NVS_NAMESPACE "retimesh"
// ---------------------------------------------------------------------------
// Diagnostics (see Diag.h). The boot counter lives in its own NVS namespace so
// a settings reset does not erase the restart history a soak run is built on.
// ---------------------------------------------------------------------------
#define DIAG_NVS_NAMESPACE "retimesh-diag" // max 15 chars
#define DIAG_STACK_WARN_B 768 // headroom below this: name the task
#define DIAG_HEAP_WARN_B 20480 // internal heap low-water below this
// A classic ESP32 splits its internal memory, and much of what the heap
// reports is 32-bit-only IRAM that cannot hold a buffer or a stack. With PSRAM
// there is somewhere else to put things; without it, the byte-addressable half
// is all a node has, and on a Wireless Stick that is about 213 KB against
// 200 KB spent before it has finished starting.
//
// A class rather than a board: today it selects the Wireless Stick alone — the
// Wireless Bridge is the same silicon with 8 MB of PSRAM, and the T-Beam's
// board file brings 4 MB — and it will select the next board like it without
// anybody remembering to add one.
#if defined(CONFIG_IDF_TARGET_ESP32) && !defined(BOARD_HAS_PSRAM)
#define BOARD_DRAM_TIGHT 1
#else
#define BOARD_DRAM_TIGHT 0
#endif
// Whether a board starts mDNS by default. It answers <hostname>.local and
// advertises the RNS port: a convenience, not a service anything depends on,
// and 6 368 B of byte-addressable RAM measured with Diag::cost(). On a board
// with ten kilobytes left it is the largest single thing that can be declined
// while still being a node, so the tight ones start without it. It stays a
// setting either way (docs/hardware.md).
#ifndef MDNS_ENABLED_DEFAULT
#define MDNS_ENABLED_DEFAULT (BOARD_DRAM_TIGHT ? 0 : 1)
#endif
#ifndef MDNS_HOSTNAME
// Fallback only. The name a node actually answers to is derived from its
// access-point name — see WifiManager::deriveHostname() — so that two nodes
// on one network do not both claim the same one. This is what is used when
// an operator has renamed the AP to something with no usable characters in
// it at all.
#define MDNS_HOSTNAME "retimesh"
#endif
// AP address 10.42.0.1 — "42" as a nod to the transport port.
#define AP_IP IPAddress(10, 42, 0, 1)
#define AP_NETMASK IPAddress(255, 255, 255, 0)
// ---------------------------------------------------------------------------
// Network services
// ---------------------------------------------------------------------------
#ifndef HTTP_PORT
#define HTTP_PORT 80
#endif
#ifndef RNS_TCP_PORT
#define RNS_TCP_PORT 4242
#endif
#ifndef RNS_MAX_CLIENTS
// Two on a board with no room to spare: past the cap a client is refused,
// which an operator sees and which costs the node nothing (see
// RetiTransportServer). Contrast AUTOIF_MAX_PEERS below, which evicts.
#define RNS_MAX_CLIENTS (BOARD_DRAM_TIGHT ? 2 : 4) // simultaneous Sideband/RNS peers
#endif
// ---------------------------------------------------------------------------
// RNS AutoInterface (IPv6 link-local peering on the access point and the LAN)
// ---------------------------------------------------------------------------
#ifndef HAS_AUTOINTERFACE
#define HAS_AUTOINTERFACE 1
#endif
#ifndef AUTOIF_GROUP_ID
#define AUTOIF_GROUP_ID "reticulum" // RNS default group id
#endif
// One slot per peer, and a peer is a link-local address rather than a node: a
// neighbour reachable both over this node's access point and over the LAN
// occupies two. Eight was the count of a bench where every node was an AP
// client of the next; a room of them on one LAN needs a slot each, in both
// directions, with the phones and laptops that are the point of the exercise.
#ifndef AUTOIF_MAX_PEERS
#define AUTOIF_MAX_PEERS 24
#endif
// The most interfaces Transport can hold at once: the radio, one per client on
// :4242, one per AutoInterface peer. Snapshot buffers are sized from this, so
// a listing shows every interface instead of the first few.
#define RNS_MAX_INTERFACES (1 + RNS_MAX_CLIENTS + AUTOIF_MAX_PEERS)
// An interface name has to hold the longest one this node builds,
// "WiFi/255.255.255.255:65535", with room to spare. RNS derives an
// interface's identity by hashing its name, so a name that gets truncated
// is two interfaces that Transport cannot tell apart.
#define INTERFACE_NAME_MAX 32
// ---------------------------------------------------------------------------
// Reticulum sizes
// ---------------------------------------------------------------------------
// RNS.Reticulum.MTU — the largest packet a Reticulum instance emits.
#define RNS_MTU 500
// Physical LoRa framing (RNode-compatible, see LoRaRadio.cpp):
// each RF frame is 1 header byte + up to 254 payload bytes.
#define LORA_FRAME_MAX 255
#define LORA_HEADER_LEN 1
#define LORA_FRAG_PAYLOAD (LORA_FRAME_MAX - LORA_HEADER_LEN) // 254
#define LORA_FLAG_SPLIT 0x01
#define LORA_SEQ_UNSET 0xFF
// ---------------------------------------------------------------------------
// LoRa radio on SPI. The pin map comes from the board header; these are the
// fallbacks for a board that does not define one. Both the SX1262 lines
// (DIO1/BUSY) and the SX127x line (DIO0) exist because LoRaRadio::begin()
// probes for either transceiver.
// ---------------------------------------------------------------------------
#ifndef LORA_SPI_BUS
#define LORA_SPI_BUS FSPI // general-purpose bus on the ESP32-S3
#endif
#ifndef PIN_LORA_SCK
#define PIN_LORA_SCK 5
#endif
#ifndef PIN_LORA_MISO
#define PIN_LORA_MISO 3
#endif
#ifndef PIN_LORA_MOSI
#define PIN_LORA_MOSI 6
#endif
#ifndef PIN_LORA_CS
#define PIN_LORA_CS 7
#endif
#ifndef PIN_LORA_RST
#define PIN_LORA_RST 8
#endif
#ifndef PIN_LORA_BUSY
#define PIN_LORA_BUSY 34
#endif
#ifndef PIN_LORA_DIO1
#define PIN_LORA_DIO1 33
#endif
#ifndef PIN_LORA_DIO0
#define PIN_LORA_DIO0 9 // SX127x only
#endif
// PHY defaults — the running values live in NVS (see Settings.h) and are
// editable from the settings page. They MUST match every other node on
// the channel (including real RNodes — configure the RNode side alike).
#ifndef RF_FREQ_MHZ
// 869.525, not 869.410. The 10 % sub-band is 869.4-869.65, and a channel is
// not a point: at the default 125 kHz, 869.410 spans 869.3475-869.4725 and
// so reaches into the unallocated gap below 869.4. The node's own limiter
// then holds it to 0.1 % and says the channel is not allocated to this kind
// of device — the firmware was already warning about the default it shipped
// with. 869.525 sits centred in the 10 % band with room either side.
#define RF_FREQ_MHZ 869.525 // EU868 SRD, centred in the 10 % sub-band
#endif
#ifndef RF_BW_KHZ
#define RF_BW_KHZ 125.0
#endif
#ifndef RF_SF
#define RF_SF 8
#endif
#ifndef RF_CR
#define RF_CR 5 // 4/5
#endif
#ifndef RF_TX_DBM
#define RF_TX_DBM 7 // SX1262 max 22, SX1276 max 17
#endif
#ifndef RF_PREAMBLE_SYMS
#define RF_PREAMBLE_SYMS 18 // RNode's LORA_PREAMBLE_SYMBOLS_MIN
#endif
// Duty-cycled receive: let the transceiver sleep between preamble samples
// instead of listening continuously. Off by default and deliberately so — a
// transport node's one job is to be listening, and the saving is worth having
// only where the channel makes the sleep long enough to be real. It is an
// SX1262-only mode (RadioCaps::Caps::rxDutyCycle) and at the shipped
// SF8/125 kHz channel it does not engage at all; docs/configuration.md gives
// the rule for when it does.
#ifndef RF_RX_DUTY_CYCLE
#define RF_RX_DUTY_CYCLE 0
#endif
// 0x12 is the classic SX127x "private network" sync word; RadioLib
// translates it to the equivalent SX126x two-byte value (0x1424).
#ifndef RF_SYNCWORD
#define RF_SYNCWORD 0x12
#endif
// T3-S3 SX1262 modules have a TCXO fed from DIO3. Set to 0.0 for
// plain-crystal modules (begin() fails with -706/-707 when this is wrong).
// Set by a board header carrying a 2.4 GHz module; see LoRaRadio::begin().
#ifndef RF_MODEM_SX1280
#define RF_MODEM_SX1280 0
#endif
// Set by a board header carrying an LR1110. Declared rather than probed, for
// the same reason the SX1280 is and one more: this part will not receive or
// transmit at all until it is told how its antenna switch is wired, and that
// table is a fact about the board (LR11X0_RF_SWITCH_TABLE below). A probe that
// found the chip and stopped there would report a working radio that is deaf.
#ifndef RF_MODEM_LR1110
#define RF_MODEM_LR1110 0
#endif
// Boards with a transmit/receive switch in front of the antenna name its pins;
// RadioLib steers them. Without one the radio is wired straight through.
#ifndef HAS_RF_SWITCH
#define HAS_RF_SWITCH 0
#endif
// An amplified front end that has to be powered and steered before the radio
// can do anything (src/radio/LoRaFem.h). Distinct from HAS_RF_SWITCH, which is
// a bare transmit/receive switch RadioLib drives by itself, and from HAS_PA,
// which only says an amplifier is in the path.
#ifndef HAS_LORA_FEM
#define HAS_LORA_FEM 0
#endif
// True where the board puts a power amplifier after the transceiver. It does
// not change what the chip may be driven at — the driver's own maximum still
// applies — but it does change what leaves the antenna, which is the operator's
// to account for. Reported through the API so the settings page can say so.
#ifndef HAS_PA
#define HAS_PA 0
#endif
// A single timed transmission at boot, to prove the IRQ line is the one the
// board actually uses. Off everywhere it is not needed: it costs airtime.
//
// Where it is on, it runs once per firmware image rather than once per boot:
// the verdict is kept in NVS under RADIO_NVS_NAMESPACE, keyed to the running
// binary itself (the ELF SHA-256 in its application descriptor, not the
// version string it prints), so a node brown-out looping on a flat battery
// does not re-pay a transmission its own image has already made. A new image
// proves itself again — the pin map the test is about is the image's. See
// src/radio/RadioSelfTestPolicy.h.
//
// Its own namespace, and the key belongs with it: this is not a setting (a
// settings reset must not clear it) and it is not restart history either.
#ifndef RADIO_SELFTEST_ON_BOOT
#define RADIO_SELFTEST_ON_BOOT 0
#endif
#define RADIO_NVS_NAMESPACE "retimesh-rf" // max 15 chars
#define RADIO_SELFTEST_NVS_KEY "irq"
#ifndef RF_TCXO_VOLTAGE
#define RF_TCXO_VOLTAGE 1.8
#endif
// The T3-S3 routes its RF switch from DIO2.
#ifndef RF_DIO2_AS_SWITCH
#define RF_DIO2_AS_SWITCH true
#endif
// ---------------------------------------------------------------------------
// microSD on its own SPI bus. Boards without a slot set HAS_SD 0 and the
// whole driver compiles out.
// ---------------------------------------------------------------------------
#ifndef HAS_SD
#define HAS_SD 1
#endif
#ifndef PIN_SD_MOSI
#define PIN_SD_MOSI 11
#endif
#ifndef PIN_SD_MISO
#define PIN_SD_MISO 2
#endif
#ifndef PIN_SD_SCK
#define PIN_SD_SCK 14
#endif
#ifndef PIN_SD_CS
#define PIN_SD_CS 13
#endif
// Which SPI host the card sits on. HSPI on every board that gives the slot
// wires of its own, which was every board until the T-Deck put the card, the
// panel and the radio on one bus and left no second host to put it on. A
// board that shares says so here; the driver reads it rather than assuming.
#ifndef SD_SPI_BUS
#define SD_SPI_BUS HSPI
#endif
#define SD_SPI_HZ 20000000
// How often the slot is looked at is no longer one number: it depends on what
// the last look found, and the ladder lives in sys/SdPollPolicy.h.
#define SD_PARTIAL_PERCENT 50 // volume < 50 % of the card => "partial"
#define SD_MOUNT_ATTEMPTS 6 // boot: the first mount after SPI init often fails
#define SD_MOUNT_RETRY_MS 100 // ~0.6 s of retries before giving up
// Web-layer snapshots of the Reticulum path table. Each row is read back
// through microStore (flash or SD), so this is deliberately unhurried.
#define SNAPSHOT_INTERVAL_MS 5000
#define SNAPSHOT_MAX_PATHS 64
// How often dead paths are swept out, which is a slower clock than the reading
// is refreshed on: the reading is capped and cheap, the sweep walks the whole
// table. It is also the ceiling the pass scheduler is held to — a pass carrying
// a minute's clock has to come round at least once a minute or that clock is
// not honoured at all (Rns::nextIntervalMs) — so the firmware and the host
// tests that drive that scheduler read the same figure from here rather than
// each writing 60000 out for itself.
#define SNAPSHOT_SWEEP_INTERVAL_MS 60000
#define SD_LOG_MAX_BYTES (1024UL * 1024UL)
// ---------------------------------------------------------------------------
// Display — SSD1306 128x64 on I2C by default (0x3C; the board names the pins)
// ---------------------------------------------------------------------------
#ifndef HAS_DISPLAY
#define HAS_DISPLAY 1
#endif
// What kind of glass, which is a different question from how big. The size
// decides the layout; the kind decides which driver is built and what an
// update costs — an e-paper panel of the same dimensions is not a slower
// OLED, it is a panel that has to be told how rarely it may be written.
// A board declares this in its own header; anything that does not say has
// the OLED every board so far has had.
#define DISPLAY_KIND_OLED 1
#define DISPLAY_KIND_EINK 2
// A colour TFT, which is neither of the others in the way that matters here:
// an update costs a burst of SPI rather than a kilobyte of I2C or a second of
// the panel's life, and the panel has a backlight — so "off" is a real state
// that saves real current, unlike an e-paper film that keeps its image.
#define DISPLAY_KIND_TFT 3
#ifndef DISPLAY_KIND
#define DISPLAY_KIND DISPLAY_KIND_OLED
#endif
// Which OLED controller, for a board whose DISPLAY_KIND is the OLED. Not the
// same question as the kind: both parts here are 128x64 monochrome panels on
// I2C, drawn with the same GFX calls, and they differ in how the buffer
// reaches the glass. The SH1106 has 132 columns of RAM with the panel wired to
// the middle 128 and no horizontal addressing mode, so the SSD1306's driver
// writes it two columns out of place and wraps the rest. The probe cannot tell
// them apart: an acknowledgement at whatever address the board names says a
// part is there and nothing about which controller it is. So the board says —
// both the controller here and the address in OLED_ADDR, which differs
// between boards and, on the T-Beam Supreme, is not the usual one.
#define OLED_CONTROLLER_SSD1306 1
#define OLED_CONTROLLER_SH1106 2
#ifndef OLED_CONTROLLER
#define OLED_CONTROLLER OLED_CONTROLLER_SSD1306
#endif
// The panel's pair, which a board with an OLED names in its own header. There
// is no default pair, and -1 is what "no such bus" reads as — I2cReg::mainBus()
// does not start a bus on pin -1 and hosts() does not list one. The default used to be 18/17, the T3-S3's
// pair, and the two boards that name none inherited it: the Wireless Bridge,
// which has no panel, and the Wireless Paper, whose panel is on SPI. Through
// PIN_I2C_* below that handed each a general bus on pins it uses for something
// else, and the first STATUS after boot scans that bus — on the Bridge, GPIO 18
// is the radio's chip select and GPIO 17 the D0WDQ6's PSRAM clock, and the scan
// panicked the node into a restart loop that came out of it with no PSRAM.
#ifndef PIN_OLED_SDA
#define PIN_OLED_SDA -1
#endif
#ifndef PIN_OLED_SCL
#define PIN_OLED_SCL -1
#endif
#if HAS_DISPLAY && DISPLAY_KIND == DISPLAY_KIND_OLED && (PIN_OLED_SDA < 0 || PIN_OLED_SCL < 0)
#error "an OLED board must name its panel's pins (PIN_OLED_SDA/PIN_OLED_SCL) in its header; a board with no panel sets HAS_DISPLAY 0"
#endif
// The board's general-purpose I2C — the bus that carries whatever is not the
// panel: a charger, an accelerometer, a touch controller, a keyboard. Most
// boards here have no such bus and only the panel's, and on those the two
// names are the same pair of wires: one bus, named twice. Boards that route a
// second pair say so in their own header.
//
// Named unconditionally, and not only where something sits on it, because
// I2cReg::mainBus() is what starts it and that has to compile everywhere —
// including on the boards where nothing ever calls it.
#ifndef PIN_I2C_SDA
#define PIN_I2C_SDA PIN_OLED_SDA
#endif
#ifndef PIN_I2C_SCL
#define PIN_I2C_SCL PIN_OLED_SCL
#endif
// Nothing may start this bus on a pin the board has already given to something
// that dies when it is clocked: the radio's SPI and control lines, and on a
// classic ESP32 with PSRAM the PSRAM's chip select and clock — GPIO 16 and 17
// on the D0WD parts, read from the core's sdkconfig rather than restated here.
// Wire.begin() takes the pins over from whatever held them, so a clash is not
// a bus with nothing on it; it is the radio or the heap failing underneath a
// console command. Refused here, so a header that clashes does not compile.
#define RM_PIN_IS_RADIO(p) ((p) >= 0 && ((p) == PIN_LORA_SCK || (p) == PIN_LORA_MISO || \
(p) == PIN_LORA_MOSI || (p) == PIN_LORA_CS || (p) == PIN_LORA_RST || \
(p) == PIN_LORA_DIO0 || (p) == PIN_LORA_DIO1 || (p) == PIN_LORA_BUSY))
#if RM_PIN_IS_RADIO(PIN_I2C_SDA) || RM_PIN_IS_RADIO(PIN_I2C_SCL)
#error "PIN_I2C_SDA/PIN_I2C_SCL name one of the radio's pins; a board with no general I2C bus leaves them -1"
#endif
#if defined(CONFIG_IDF_TARGET_ESP32) && defined(BOARD_HAS_PSRAM) && defined(CONFIG_D0WD_PSRAM_CLK_IO) && \
(PIN_I2C_SDA == CONFIG_D0WD_PSRAM_CS_IO || PIN_I2C_SDA == CONFIG_D0WD_PSRAM_CLK_IO || \
PIN_I2C_SCL == CONFIG_D0WD_PSRAM_CS_IO || PIN_I2C_SCL == CONFIG_D0WD_PSRAM_CLK_IO)
#error "PIN_I2C_SDA/PIN_I2C_SCL name the PSRAM's chip select or clock (CONFIG_D0WD_PSRAM_CS_IO/CLK_IO)"
#endif
#undef RM_PIN_IS_RADIO
// Panels on a switched rail need it brought up before they are probed, and it
// is active low on every board that has one so far.
#ifndef HAS_DISPLAY_VEXT
#define HAS_DISPLAY_VEXT 0
#endif
#ifndef PIN_OLED_RST
#define PIN_OLED_RST -1
#endif
#ifndef OLED_ADDR
#define OLED_ADDR 0x3C
#endif
#ifndef OLED_ROTATION
#define OLED_ROTATION 0 // 0..3, quarter turns
#endif
// Panel geometry. A board with a different panel says so here and everything
// downstream follows from it — see DisplayLayout.h, which turns these into the
// text grid, the chrome and the refresh interval each page is drawn against.
// An SSD1306 reports nothing about its own size, so the board is what knows.
#ifndef DISPLAY_WIDTH
#define DISPLAY_WIDTH 128
#endif
#ifndef DISPLAY_HEIGHT
#define DISPLAY_HEIGHT 64
#endif
// Panels too small for the full page set. A macro as well as the constexpr in
// DisplayLayout.h because the page enum itself has to change, not only what is
// drawn — a page that cannot work on this panel should not be in the cycle at
// all, costing a button press to reach and another to leave.
#if DISPLAY_WIDTH < 96
#define DISPLAY_COMPACT 1
#else
#define DISPLAY_COMPACT 0
#endif
// BOOT button (GPIO 0, active low) doubles as the display navigation key:
// short press = next page, long press = blank/wake the panel.
#ifndef PIN_BUTTON
#define PIN_BUTTON 0
#endif
// A second button, where the case has one. Boards without it say nothing and
// everything that reads it compiles out.
#ifndef HAS_BUTTON2
#define HAS_BUTTON2 0
#endif
#ifndef PIN_BUTTON2
#define PIN_BUTTON2 -1
#endif
// A piezo sounder on a PWM channel, where one is fitted.
// What makes the sound. Two very different parts answer the same two events: a
// piezo on a PWM pin, and a speaker behind an I2S amplifier. A board names
// which it has — they share no pin, no peripheral and no way of being driven,
// so probing would be two drivers' worth of guessing to save a board one line.
#define BUZZER_KIND_PWM 1
#define BUZZER_KIND_I2S 2
#ifndef HAS_BUZZER
#define HAS_BUZZER 0
#endif
#ifndef BUZZER_KIND
#define BUZZER_KIND BUZZER_KIND_PWM
#endif
// Whether the sounder starts switched on, which is decided by what it costs.
// A piezo is a PWM channel and a timer — free, so it ships on and the node
// keeps chirping as it always did. A speaker behind an I2S amplifier is a task
// and a DMA ring, about 5.4 KB of internal RAM, which on the board that has one
// is a quarter of what it has spare and enough to stop the portal serving. That
// ships off, and turning it on is a choice made knowing the trade.
// Task stacks that were guessed generously and have now been measured.
//
// Both were 8 KB, which is what you write when you do not know. What the
// console's STACKS command reports is the least each has ever had left, and on
// two boards over a day's use they never came close — but a high-water mark is
// "worst seen", never "worst possible", so these are cut with the margin the
// consequence deserves rather than to the observation.
//
// The card's poller: 1.6 KB used of 8 KB, and the biggest thing on its stack is
// the 512-byte sector it reads to notice a card being pulled. Its deepest path
// is not that, though — it is a format, which runs on this task and which
// nothing has exercised while anybody was measuring. FatFS takes its format
// work buffer from the heap (ff_memalloc, ffconf.h) so that part is not stack,
// but the call depth beneath it is unmeasured.
//
// And this stack has a history, recorded where it was raised: at 4 KB the FAT
// layer tripped the canary on core 0 — mount probes and a rename on log
// rotation, which are not what idles here and are exactly what a measurement
// taken while nothing is happening will miss. The probe path has been rewritten
// since and the figure today is 1.6 KB, but "it crashed at 4 KB once" outranks
// "it uses 1.6 KB this week": one is a bound, the other is a sample.
//
// So 6 KB, not the 4 the measurement alone would allow. Half as much again as
// the size that is known to have failed, nearly four times what has been seen,
// and it still gives 2 KB back. Getting this wrong costs somebody a corrupted
// card in the middle of formatting it.
#ifndef SD_TASK_STACK
#define SD_TASK_STACK 6144
#endif
// The radio: 2.6 KB used of 8 KB. Worth knowing that the packet does not set
// this — the transmit frame is a member of LoRaRadio, not a local, so the size
// of a LoRa frame never lands on this stack. What lands on it is call depth:
// RadioLib, the SPI transaction under it, and the receive path pushing into the
// ring. That is not derivable the way a buffer is, so the margin is empirical:
// 6 KB leaves more spare than the whole of what has ever been used.
//
// It is also the stack that has moved. It read 1.7 KB used this morning and
// 2.6 KB by the evening on the same firmware, which is the argument for
// watching STACKS over a soak rather than trusting one afternoon's figure.
#ifndef RADIO_TASK_STACK
#define RADIO_TASK_STACK 6144
#endif
#ifndef SOUND_ENABLED_DEFAULT
#if BUZZER_KIND == BUZZER_KIND_I2S
#define SOUND_ENABLED_DEFAULT 0
#else
#define SOUND_ENABLED_DEFAULT 1
#endif
#endif
// Percent. Only the speaker can act on it: a piezo is driven at the one
// amplitude its PWM channel produces, so the setting is offered where it means
// something and refused where it does not.
#ifndef SOUND_VOLUME_DEFAULT
#define SOUND_VOLUME_DEFAULT 40
#endif
// The I2S speaker's pins, meaningless on a PWM board.
#ifndef PIN_I2S_BCLK
#define PIN_I2S_BCLK -1
#endif
#ifndef PIN_I2S_LRCLK
#define PIN_I2S_LRCLK -1
#endif
#ifndef PIN_I2S_DOUT
#define PIN_I2S_DOUT -1
#endif
#ifndef PIN_BUZZER
#define PIN_BUZZER -1
#endif
// A capacitive touch layer over the panel. Two controllers are driven, and
// they are not alike enough to probe for: the CHSC6X holds one report and
// answers only while a finger is down, the GT911 is register-mapped and
// answers always. A board names which it carries.
// A clock the board keeps running for itself. Off unless a board says
// otherwise: a node without one counts from the epoch until its receiver has a
// fix, and every board here did that until the first one turned up carrying a
// PCF8563. The pins default to the board's general I2C, which is where such a
// part sits on every board that has one — it is a slow device with nothing to
// gain from a bus of its own.
#ifndef HAS_RTC
#define HAS_RTC 0
#endif
#ifndef RTC_ADDR
#define RTC_ADDR 0x51 // the PCF8563's, and it is not strappable
#endif
#ifndef PIN_RTC_SDA
#define PIN_RTC_SDA PIN_I2C_SDA
#endif
#ifndef PIN_RTC_SCL
#define PIN_RTC_SCL PIN_I2C_SCL
#endif
#define TOUCH_KIND_CHSC6X 1
#define TOUCH_KIND_GT911 2
#ifndef HAS_TOUCH
#define HAS_TOUCH 0
#endif
#ifndef TOUCH_KIND
#define TOUCH_KIND TOUCH_KIND_CHSC6X
#endif
// Whether the controller already reports in the frame the panel is shown in.
// Most do not: they answer in the glass's own portrait coordinates and the
// shell turns each point the same way it turned the picture. One board's
// controller is configured for the mounted orientation and needs no turning at
// all, and turning it anyway sends every tap to a different corner — which
// looks exactly like a touch layer that is not working.
#ifndef TOUCH_PRE_ROTATED
#define TOUCH_PRE_ROTATED 0
#endif
// Whether the controller counts a coordinate from the opposite edge to the
// panel. A mirrored axis is not a rotation and cannot be corrected by one: it
// puts every press at the reflection of the finger, so the top half of the
// glass presses the bottom half's controls and the middle very nearly works.
// It is also invisible without measuring, because the shell has no way to know
// where the finger really was.
#ifndef TOUCH_MIRROR_X
#define TOUCH_MIRROR_X 0
#endif
#ifndef TOUCH_MIRROR_Y
#define TOUCH_MIRROR_Y 0
#endif
// A physical keyboard, where the board has one. Not a matrix this firmware
// scans — on every board here it is a second microcontroller that scans it
// and answers on I2C, so what varies is the address and the bus, not the
// wiring. See src/ui/Keypad.h.
#ifndef HAS_KEYPAD
#define HAS_KEYPAD 0
#endif
// How the controller answers. Two protocols, and no way to tell them apart
// safely by trying: a bare read against a register-addressed part returns
// whatever register its pointer happens to be sitting on.
// BARE — read one byte; it is the key, or zero for none.
// REG8 — write the key register, repeated start, read one byte.
#define KEYPAD_KIND_BARE 1
#define KEYPAD_KIND_REG8 2
#ifndef KEYPAD_KIND
#define KEYPAD_KIND KEYPAD_KIND_BARE
#endif
#ifndef KEYPAD_ADDR
#define KEYPAD_ADDR 0x55
#endif
#ifndef KEYPAD_KEY_REG
#define KEYPAD_KEY_REG 0x01
#endif
// The rate the keyboard's own controller brings its slave up at, where that
// differs from the bus it shares. On a board where the keyboard has a bus to
// itself this is that bus's whole speed.
#ifndef KEYPAD_HZ
#define KEYPAD_HZ I2C_HZ
#endif
#ifndef PIN_KEYPAD_LED
#define PIN_KEYPAD_LED -1
#endif
#ifndef PIN_KEYPAD_SDA
#define PIN_KEYPAD_SDA PIN_I2C_SDA
#endif
#ifndef PIN_KEYPAD_SCL
#define PIN_KEYPAD_SCL PIN_I2C_SCL
#endif
#ifndef PIN_KEYPAD_INT
#define PIN_KEYPAD_INT -1
#endif
// A trackball: four direction lines, active low, one edge per detent. The
// click is PIN_BUTTON, because on the board that has one they are the same
// pin. Read in Keypad.cpp alongside the keyboard, since both are navigation.
#ifndef HAS_TRACKBALL
#define HAS_TRACKBALL 0
#endif
#ifndef PIN_TRACKBALL_UP
#define PIN_TRACKBALL_UP -1
#endif
#ifndef PIN_TRACKBALL_DOWN
#define PIN_TRACKBALL_DOWN -1
#endif
#ifndef PIN_TRACKBALL_LEFT
#define PIN_TRACKBALL_LEFT -1
#endif
#ifndef PIN_TRACKBALL_RIGHT
#define PIN_TRACKBALL_RIGHT -1
#endif
// How the panel's backlight is driven. A plain LED on a PWM channel on every
// board so far; the T-Deck puts a one-wire dimmer chip there instead, whose
// brightness is stepped by pulses rather than set by a duty cycle. See
// TftPanel.cpp — the difference is entirely inside applyBacklight().
#define BACKLIGHT_KIND_PWM 1
#define BACKLIGHT_KIND_AW9364 2
#ifndef BACKLIGHT_KIND
#define BACKLIGHT_KIND BACKLIGHT_KIND_PWM
#endif
// Whether the backlight lights when its pin is low. A board that sinks the
// LED's return through the transistor rather than driving its gate inverts the
// duty cycle, and nothing else about it changes.
#ifndef BACKLIGHT_ACTIVE_LOW
#define BACKLIGHT_ACTIVE_LOW 0
#endif