mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-23 03:00:56 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13806cce93 | ||
|
|
e7eee0995a | ||
|
|
5687bd09c6 | ||
|
|
f3db895832 | ||
|
|
dd2ffe5d14 | ||
|
|
ec10e784e1 | ||
|
|
649faa1d93 | ||
|
|
669f96b367 | ||
|
|
996821d18e | ||
|
|
2e172b019e | ||
|
|
3df05cd5c1 | ||
|
|
8bb85cdc69 | ||
|
|
9f6e23754c | ||
|
|
5a7cfdffb3 | ||
|
|
caafddfdfa | ||
|
|
ee0e31be97 | ||
|
|
f1da6469a3 | ||
|
|
68e57dd3a7 | ||
|
|
2504311671 | ||
|
|
aa29315624 | ||
|
|
e3bcb87cf0 | ||
|
|
2530dc44c7 | ||
|
|
70a8fe30b7 | ||
|
|
30e538e5ed | ||
|
|
58dbc3c702 | ||
|
|
210c904604 | ||
|
|
27fdab7c8d | ||
|
|
ba3e1abb5e | ||
|
|
ce194e2162 | ||
|
|
595166db8e | ||
|
|
38d9e34a66 | ||
|
|
c1e0977db3 | ||
|
|
b4f32e7645 | ||
|
|
6bb0c95c95 | ||
|
|
4e958c9230 | ||
|
|
64cf1890f2 | ||
|
|
6a857b00db | ||
|
|
6d60a061bc | ||
|
|
6a09ddef18 | ||
|
|
d48e803b7b | ||
|
|
9061b3d8c3 | ||
|
|
2839539c6c | ||
|
|
f77a1798f3 | ||
|
|
664b558a36 | ||
|
|
c1865f127c | ||
|
|
3a69539192 | ||
|
|
cfcaf28ace | ||
|
|
d9e93f3944 | ||
|
|
60470211e5 | ||
|
|
c5851a4a0c | ||
|
|
0c0b2446b7 | ||
|
|
ce9352fd23 | ||
|
|
375ae5fe77 | ||
|
|
665d35196d | ||
|
|
9757f9ae53 | ||
|
|
e45d0c4dcf | ||
|
|
5c9f22bc18 | ||
|
|
d8287e9cdb | ||
|
|
c66e064f42 | ||
|
|
364fc84aaa | ||
|
|
fe4f86bc84 | ||
|
|
982b2e33ff | ||
|
|
8190098bb8 | ||
|
|
c99411311b | ||
|
|
da8a048dce | ||
|
|
b4de495154 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -59,6 +59,7 @@
|
|||||||
"cfsr",
|
"cfsr",
|
||||||
"descs",
|
"descs",
|
||||||
"ocrypto",
|
"ocrypto",
|
||||||
"protobufs"
|
"protobufs",
|
||||||
|
"wifi"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -24,14 +24,17 @@ We currently support three models of radios.
|
|||||||
|
|
||||||
- TTGO T-Beam
|
- TTGO T-Beam
|
||||||
|
|
||||||
- [T-Beam V1.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (Recommended)
|
- [T-Beam V1.0 w/ NEO-6M - special Meshtastic version](https://www.aliexpress.com/item/4001178678568.html) (Includes built-in OLED display and they have **preinstalled** the meshtastic software)
|
||||||
- [T-Beam V1.0 w/ NEO-6M](https://www.aliexpress.com/item/33050391850.html)
|
- [T-Beam V1.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS)
|
||||||
- 3D printable cases
|
- 3D printable cases
|
||||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
||||||
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
|
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
|
||||||
|
|
||||||
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
|
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
|
||||||
|
|
||||||
|
- 3D printable case
|
||||||
|
- [TTGO LORA32 v1](https://www.thingiverse.com/thing:3385109)
|
||||||
|
|
||||||
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
|
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
|
||||||
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
|
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
|
||||||
|
|
||||||
@@ -47,6 +50,8 @@ Getting a version that includes a screen is optional, but highly recommended.
|
|||||||
|
|
||||||
Prebuilt binaries for the supported radios are available in our [releases](https://github.com/meshtastic/Meshtastic-esp32/releases). Your initial installation has to happen over USB from your Mac, Windows or Linux PC. Once our software is installed, all future software updates happen over bluetooth from your phone.
|
Prebuilt binaries for the supported radios are available in our [releases](https://github.com/meshtastic/Meshtastic-esp32/releases). Your initial installation has to happen over USB from your Mac, Windows or Linux PC. Once our software is installed, all future software updates happen over bluetooth from your phone.
|
||||||
|
|
||||||
|
Be **very careful** to install the correct load for your board. In particular the popular 'T-BEAM' radio from TTGO is not called 'TTGO-Lora' (that is a different board). So don't install the 'TTGO-Lora' build on a TBEAM, it won't work correctly.
|
||||||
|
|
||||||
Please post comments on our [group chat](https://meshtastic.discourse.group/) if you have problems or successes.
|
Please post comments on our [group chat](https://meshtastic.discourse.group/) if you have problems or successes.
|
||||||
|
|
||||||
### Installing from a GUI - Windows and Mac
|
### Installing from a GUI - Windows and Mac
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
export VERSION=0.7.8
|
export VERSION=0.7.9
|
||||||
@@ -39,6 +39,7 @@ This software is 100% open source and developed by a group of hobbyist experimen
|
|||||||
|
|
||||||
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.
|
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.
|
||||||
|
|
||||||
|
- 06/24/2020 - 0.7.x Now with over 1000 android users, over 600 people using the radios and translated into 13 languages. Fairly stable and we are working through bugs to get to 1.0.
|
||||||
- 06/04/2020 - 0.6.7 Beta releases of both the application and the device code are released. Features are fairly solid now with a sizable number of users.
|
- 06/04/2020 - 0.6.7 Beta releases of both the application and the device code are released. Features are fairly solid now with a sizable number of users.
|
||||||
- 04/28/2020 - 0.6.0 [Python API](https://pypi.org/project/meshtastic/) released. Makes it easy to use meshtastic devices as "zero config / just works" mesh transport adapters for other projects.
|
- 04/28/2020 - 0.6.0 [Python API](https://pypi.org/project/meshtastic/) released. Makes it easy to use meshtastic devices as "zero config / just works" mesh transport adapters for other projects.
|
||||||
- 04/20/2020 - 0.4.3 Pretty solid now both for the android app and the device code. Many people have donated translations and code. Probably going to call it a beta soon.
|
- 04/20/2020 - 0.4.3 Pretty solid now both for the android app and the device code. Many people have donated translations and code. Probably going to call it a beta soon.
|
||||||
@@ -63,8 +64,9 @@ If you'd like to help with development, the source code is [on github](https://g
|
|||||||
|
|
||||||
## Supported hardware
|
## Supported hardware
|
||||||
|
|
||||||
We currently support two brands of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4000119152086.html) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most people should buy the T-Beam and a 18650 battery (total cost less than \$35). Make
|
We currently support two brands of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4001178678568.html) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most people should buy the T-Beam and a 18650 battery (total cost less than \$35). Also, the version of the T-Beam we link to is shipped with Meshtastic **preinstalled** by TTGO, so you don't have to install it yourself.
|
||||||
sure to buy the frequency range which is legal for your country. For the USA, you should buy the 915MHz version. Getting a version that include a screen is optional, but highly recommended.
|
|
||||||
|
Make sure to buy the frequency range which is legal for your country. For the USA, you should buy the 915MHz version. Getting a version that include a screen is optional, but highly recommended.
|
||||||
|
|
||||||
Instructions for installing prebuilt firmware can be found [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md).
|
Instructions for installing prebuilt firmware can be found [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md).
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,17 @@
|
|||||||
|
|
||||||
You probably don't care about this section - skip to the next one.
|
You probably don't care about this section - skip to the next one.
|
||||||
|
|
||||||
- bluetooth toggle enable stress test, we are not properly restarting our connect
|
- do device release (after testing BLE fixes) https://github.com/meshtastic/Meshtastic-device/pull/218#event-3475395948
|
||||||
- make new android release
|
- some channel names don't work - check for illegal symbols per forum report
|
||||||
- check in our modified arduino binaries
|
- auto invalidate old BLE handles
|
||||||
- post bug on esp32-arduino
|
- disable software update button after update finishes
|
||||||
- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors
|
- mention translations in main readme
|
||||||
|
- check BLE handle stability across sleep - stress test sleep/wake - btu_init_core calls gatt_init - which assigns handles global
|
||||||
|
- @feh123 Sony Xperia Z1 C6903 running Android 5.1.1
|
||||||
|
- first message sent is still doubled for some people
|
||||||
|
- Android frontend should refetch the android messages from backend service on Resume
|
||||||
- let users set arbitrary params in android
|
- let users set arbitrary params in android
|
||||||
|
- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors
|
||||||
- NRF52 BLE support
|
- NRF52 BLE support
|
||||||
|
|
||||||
# Medium priority
|
# Medium priority
|
||||||
@@ -58,6 +63,13 @@ Items after the first final candidate release.
|
|||||||
- report esp32 device code bugs back to the mothership via android
|
- report esp32 device code bugs back to the mothership via android
|
||||||
- change BLE bonding to something more secure. see comment by pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND)
|
- change BLE bonding to something more secure. see comment by pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND)
|
||||||
|
|
||||||
|
Changes related to wifi support on ESP32:
|
||||||
|
|
||||||
|
- iram space: https://esp32.com/viewtopic.php?t=8460
|
||||||
|
- set https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/external-ram.html spi ram bss
|
||||||
|
- figure out if iram or bluetooth classic caused ble problems
|
||||||
|
- post bug on esp32-arduino with BLE bug findings
|
||||||
|
|
||||||
# Spinoff project ideas
|
# Spinoff project ideas
|
||||||
|
|
||||||
- an open source version of https://www.burnair.ch/skynet/
|
- an open source version of https://www.burnair.ch/skynet/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
We build our own custom version of esp32-arduino, in order to get some fixes we've made but haven't yet been merged in master.
|
We build our own custom version of esp32-arduino, in order to get some fixes we've made but haven't yet been merged in master.
|
||||||
|
|
||||||
These are a set of currently unformatted notes on how to build and install them. Most developers should not care about this, because
|
These are a set of currently unformatted notes on how to build and install them. Most developers should not care about this, because
|
||||||
you'll automatically get our fixed libraries.
|
you'll automatically get our fixed libraries.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -12,5 +12,5 @@ you'll automatically get our fixed libraries.
|
|||||||
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
||||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||||
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
||||||
cp -ar components/arduino/ ~/.platformio/packages/framework-arduinoespressif32@src-fba9d33740f719f712e9f8b07da6ea13/
|
cp -ar components/arduino/* ~/.platformio/packages/framework-arduinoespressif32@src-fba9d33740f719f712e9f8b07da6ea13/
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -19,17 +19,18 @@ reliable messaging tasks (stage one for DSR):
|
|||||||
- DONE once an ack comes in, remove the packet from the retry list and deliver the ack to the original sender
|
- DONE once an ack comes in, remove the packet from the retry list and deliver the ack to the original sender
|
||||||
- DONE after three retries, deliver a no-ack packet to the original sender (i.e. the phone app or mesh router service)
|
- DONE after three retries, deliver a no-ack packet to the original sender (i.e. the phone app or mesh router service)
|
||||||
- DONE test one hop ack/nak with the python framework
|
- DONE test one hop ack/nak with the python framework
|
||||||
- Do stress test with acks
|
- DONE Do stress test with acks
|
||||||
|
|
||||||
dsr tasks
|
dsr tasks
|
||||||
|
|
||||||
- oops I might have broken message reception
|
- DONE oops I might have broken message reception
|
||||||
- DONE Don't use broadcasts for the network pings (close open github issue)
|
- DONE Don't use broadcasts for the network pings (close open github issue)
|
||||||
- DONE add ignoreSenders to radioconfig to allow testing different mesh topologies by refusing to see certain senders
|
- DONE add ignoreSenders to radioconfig to allow testing different mesh topologies by refusing to see certain senders
|
||||||
- test multihop delivery with the python framework
|
- DONE test multihop delivery with the python framework
|
||||||
|
|
||||||
optimizations / low priority:
|
optimizations / low priority:
|
||||||
|
|
||||||
|
- read this [this](http://pages.cs.wisc.edu/~suman/pubs/nadv-mobihoc05.pdf) paper and others and make our naive flood routing less naive
|
||||||
- read @cyclomies long email with good ideas on optimizations and reply
|
- read @cyclomies long email with good ideas on optimizations and reply
|
||||||
- Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
|
- Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
|
||||||
- make android app warn if firmware is too old or too new to talk to
|
- make android app warn if firmware is too old or too new to talk to
|
||||||
|
|||||||
18
linker/esp32.extram.bss.ld
Normal file
18
linker/esp32.extram.bss.ld
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/* This section is only included if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||||
|
is set, to link some sections to BSS in PSRAM */
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* external memory bss, from any global variable with EXT_RAM_ATTR attribute*/
|
||||||
|
.ext_ram.bss (NOLOAD) :
|
||||||
|
{
|
||||||
|
_ext_ram_bss_start = ABSOLUTE(.);
|
||||||
|
*(.ext_ram.bss*)
|
||||||
|
*libnet80211.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
*libpp.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
*liblwip.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
*libbt.a:(EXCLUDE_FILE (libbtdm_app.a) .dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ext_ram_bss_end = ABSOLUTE(.);
|
||||||
|
} > extern_ram_seg
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ debug_tool = jlink
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
||||||
SPI
|
SPI
|
||||||
; 1260 ; OneButton - not used yet
|
1260 ; OneButton library for non-blocking button debounce
|
||||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||||
https://github.com/meshtastic/arduino-fsm.git
|
https://github.com/meshtastic/arduino-fsm.git
|
||||||
@@ -84,10 +84,12 @@ src_filter =
|
|||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
debug_init_break = tbreak setup
|
debug_init_break = tbreak setup
|
||||||
build_flags =
|
build_flags =
|
||||||
${env.build_flags} -Wall -Wextra -Isrc/esp32
|
${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue
|
||||||
|
# Hmm - this doesn't work yet
|
||||||
|
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||||
lib_ignore = segger_rtt
|
lib_ignore = segger_rtt
|
||||||
platform_packages =
|
platform_packages =
|
||||||
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git
|
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#f26c4f96fefd13ed0ed042e27954f8aba6328f6b
|
||||||
|
|
||||||
; The 1.0 release of the TBEAM board
|
; The 1.0 release of the TBEAM board
|
||||||
[env:tbeam]
|
[env:tbeam]
|
||||||
@@ -96,6 +98,7 @@ board = ttgo-t-beam
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
https://github.com/meshtastic/AXP202X_Library.git
|
https://github.com/meshtastic/AXP202X_Library.git
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TBEAM_V10
|
${esp32_base.build_flags} -D TBEAM_V10
|
||||||
|
|
||||||
|
|||||||
2
proto
2
proto
Submodule proto updated: 72cbde93ff...ab281311c4
@@ -144,7 +144,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define I2C_SDA 21
|
#define I2C_SDA 21
|
||||||
#define I2C_SCL 22
|
#define I2C_SCL 22
|
||||||
|
|
||||||
#define BUTTON_PIN 38
|
#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam
|
||||||
|
#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed
|
||||||
|
|
||||||
#ifndef USE_JTAG
|
#ifndef USE_JTAG
|
||||||
#define RESET_GPIO 14
|
#define RESET_GPIO 14
|
||||||
@@ -213,6 +214,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#elif defined(TTGO_LORA_V1)
|
#elif defined(TTGO_LORA_V1)
|
||||||
// This string must exactly match the case used in release file names or the android updater won't work
|
// This string must exactly match the case used in release file names or the android updater won't work
|
||||||
#define HW_VENDOR "ttgo-lora32-v1"
|
#define HW_VENDOR "ttgo-lora32-v1"
|
||||||
|
#undef GPS_RX_PIN
|
||||||
|
#undef GPS_TX_PIN
|
||||||
|
#define GPS_RX_PIN 36
|
||||||
|
#define GPS_TX_PIN 37
|
||||||
|
|
||||||
#define I2C_SDA 4 // I2C pins for this board
|
#define I2C_SDA 4 // I2C pins for this board
|
||||||
#define I2C_SCL 15
|
#define I2C_SCL 15
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class DataCharacteristic : public CallbackCharacteristic
|
|||||||
crc.update(data, len);
|
crc.update(data, len);
|
||||||
Update.write(data, len);
|
Update.write(data, len);
|
||||||
updateActualSize += len;
|
updateActualSize += len;
|
||||||
|
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,8 +124,10 @@ class CRC32Characteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void bluetoothRebootCheck()
|
void bluetoothRebootCheck()
|
||||||
{
|
{
|
||||||
if (rebootAtMsec && millis() > rebootAtMsec)
|
if (rebootAtMsec && millis() > rebootAtMsec) {
|
||||||
|
DEBUG_MSG("Rebooting for update\n");
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ BLEService *createBatteryService(BLEServer *server)
|
|||||||
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
||||||
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||||
|
|
||||||
// I don't think we need to advertise this
|
// I don't think we need to advertise this? and some phones only see the first thing advertised anyways...
|
||||||
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
||||||
pBattery->start();
|
pBattery->start();
|
||||||
|
|
||||||
@@ -135,8 +135,8 @@ BLEService *createBatteryService(BLEServer *server)
|
|||||||
*/
|
*/
|
||||||
void updateBatteryLevel(uint8_t level)
|
void updateBatteryLevel(uint8_t level)
|
||||||
{
|
{
|
||||||
// Pretend to update battery levels - fixme do elsewhere
|
|
||||||
if (batteryLevelC) {
|
if (batteryLevelC) {
|
||||||
|
DEBUG_MSG("set BLE battery level %u\n", level);
|
||||||
batteryLevelC->setValue(&level, 1);
|
batteryLevelC->setValue(&level, 1);
|
||||||
batteryLevelC->notify();
|
batteryLevelC->notify();
|
||||||
}
|
}
|
||||||
@@ -215,7 +215,7 @@ class MySecurity : public BLESecurityCallbacks
|
|||||||
|
|
||||||
BLEServer *pServer;
|
BLEServer *pServer;
|
||||||
|
|
||||||
BLEService *pDevInfo, *pUpdate;
|
BLEService *pDevInfo, *pUpdate, *pBattery;
|
||||||
|
|
||||||
void deinitBLE()
|
void deinitBLE()
|
||||||
{
|
{
|
||||||
@@ -230,6 +230,9 @@ void deinitBLE()
|
|||||||
pUpdate->executeDelete();
|
pUpdate->executeDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pBattery->stop();
|
||||||
|
pBattery->executeDelete();
|
||||||
|
|
||||||
pDevInfo->stop();
|
pDevInfo->stop();
|
||||||
pDevInfo->executeDelete();
|
pDevInfo->executeDelete();
|
||||||
|
|
||||||
@@ -242,6 +245,7 @@ void deinitBLE()
|
|||||||
if (pUpdate != NULL)
|
if (pUpdate != NULL)
|
||||||
delete pUpdate;
|
delete pUpdate;
|
||||||
delete pDevInfo;
|
delete pDevInfo;
|
||||||
|
delete pBattery;
|
||||||
delete pServer;
|
delete pServer;
|
||||||
|
|
||||||
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
||||||
@@ -279,10 +283,9 @@ BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoo
|
|||||||
|
|
||||||
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
||||||
|
|
||||||
// We now let users create the battery service only if they really want (not all devices have a battery)
|
pBattery = createBatteryService(pServer);
|
||||||
// BLEService *pBattery = createBatteryService(pServer);
|
|
||||||
|
|
||||||
// #define BLE_SOFTWARE_UPDATE
|
#define BLE_SOFTWARE_UPDATE
|
||||||
#ifdef BLE_SOFTWARE_UPDATE
|
#ifdef BLE_SOFTWARE_UPDATE
|
||||||
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
||||||
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
||||||
|
|||||||
@@ -35,5 +35,8 @@ BLECharacteristic *addBLECharacteristic(BLECharacteristic *c);
|
|||||||
/// Add a characteristic that we will delete when we restart
|
/// Add a characteristic that we will delete when we restart
|
||||||
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c);
|
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c);
|
||||||
|
|
||||||
|
/// Given a level between 0-100, update the BLE attribute
|
||||||
|
void updateBatteryLevel(uint8_t level);
|
||||||
|
|
||||||
/// Any bluetooth objects you allocate _must_ come from this pool if you want to be able to call deinitBLE()
|
/// Any bluetooth objects you allocate _must_ come from this pool if you want to be able to call deinitBLE()
|
||||||
extern SimpleAllocator btPool;
|
extern SimpleAllocator btPool;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
|
||||||
bool bluetoothOn;
|
bool bluetoothOn;
|
||||||
@@ -75,6 +76,15 @@ void readPowerStatus()
|
|||||||
powerStatus.haveBattery = axp.isBatteryConnect();
|
powerStatus.haveBattery = axp.isBatteryConnect();
|
||||||
if (powerStatus.haveBattery) {
|
if (powerStatus.haveBattery) {
|
||||||
powerStatus.batteryVoltageMv = axp.getBattVoltage();
|
powerStatus.batteryVoltageMv = axp.getBattVoltage();
|
||||||
|
// If the AXP192 returns a valid battery percentage, use it
|
||||||
|
if (axp.getBattPercentage() >= 0) {
|
||||||
|
powerStatus.batteryChargePercent = axp.getBattPercentage();
|
||||||
|
} else {
|
||||||
|
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
||||||
|
// In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in power.h
|
||||||
|
powerStatus.batteryChargePercent = clamp((int)(((powerStatus.batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100);
|
||||||
|
}
|
||||||
|
DEBUG_MSG("Battery %dmV %d%%\n", powerStatus.batteryVoltageMv, powerStatus.batteryChargePercent);
|
||||||
}
|
}
|
||||||
powerStatus.usb = axp.isVBUSPlug();
|
powerStatus.usb = axp.isVBUSPlug();
|
||||||
powerStatus.charging = axp.isChargeing();
|
powerStatus.charging = axp.isChargeing();
|
||||||
@@ -205,15 +215,6 @@ uint32_t axpDebugRead()
|
|||||||
Periodic axpDebugOutput(axpDebugRead);
|
Periodic axpDebugOutput(axpDebugRead);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Per @spattinson
|
|
||||||
* MIN_BAT_MILLIVOLTS seems high. Typical 18650 are different chemistry to LiPo, even for LiPos that chart seems a bit off, other
|
|
||||||
* charts put 3690mV at about 30% for a lipo, for 18650 i think 10% remaining iis in the region of 3.2-3.3V. Reference 1st graph
|
|
||||||
* in [this test report](https://lygte-info.dk/review/batteries2012/Samsung%20INR18650-30Q%203000mAh%20%28Pink%29%20UK.html)
|
|
||||||
* looking at the red line - discharge at 0.2A - he gets a capacity of 2900mah, 90% of 2900 = 2610, that point in the graph looks
|
|
||||||
* to be a shade above 3.2V
|
|
||||||
*/
|
|
||||||
#define MIN_BAT_MILLIVOLTS 3250 // millivolts. 10% per https://blog.ampow.com/lipo-voltage-chart/
|
|
||||||
|
|
||||||
/// loop code specific to ESP32 targets
|
/// loop code specific to ESP32 targets
|
||||||
void esp32Loop()
|
void esp32Loop()
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
void perhapsSetRTC(const struct timeval *tv);
|
void perhapsSetRTC(const struct timeval *tv);
|
||||||
void perhapsSetRTC(struct tm &t);
|
void perhapsSetRTC(struct tm &t);
|
||||||
|
|
||||||
|
// Generate a string representation of DOP
|
||||||
|
const char *getDOPString(uint32_t dop);
|
||||||
|
|
||||||
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
|
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
|
||||||
uint32_t getTime();
|
uint32_t getTime();
|
||||||
|
|
||||||
@@ -31,6 +34,8 @@ class GPS : public Observable<void *>
|
|||||||
public:
|
public:
|
||||||
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
||||||
int32_t altitude = 0;
|
int32_t altitude = 0;
|
||||||
|
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use)
|
||||||
|
|
||||||
bool isConnected = false; // Do we have a GPS we are talking to
|
bool isConnected = false; // Do we have a GPS we are talking to
|
||||||
|
|
||||||
virtual ~GPS() {}
|
virtual ~GPS() {}
|
||||||
|
|||||||
@@ -53,9 +53,13 @@ void NEMAGPS::loop()
|
|||||||
latitude = toDegInt(loc.lat);
|
latitude = toDegInt(loc.lat);
|
||||||
longitude = toDegInt(loc.lng);
|
longitude = toDegInt(loc.lng);
|
||||||
}
|
}
|
||||||
|
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
|
||||||
|
if(reader.hdop.isValid()) {
|
||||||
|
dop = reader.hdop.value();
|
||||||
|
}
|
||||||
|
|
||||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude);
|
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
|
||||||
|
|
||||||
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
||||||
if (hasValidLocation)
|
if (hasValidLocation)
|
||||||
|
|||||||
@@ -99,12 +99,12 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||||
*/
|
*/
|
||||||
struct tm t;
|
struct tm t;
|
||||||
t.tm_sec = ublox.getSecond();
|
t.tm_sec = ublox.getSecond(0);
|
||||||
t.tm_min = ublox.getMinute();
|
t.tm_min = ublox.getMinute(0);
|
||||||
t.tm_hour = ublox.getHour();
|
t.tm_hour = ublox.getHour(0);
|
||||||
t.tm_mday = ublox.getDay();
|
t.tm_mday = ublox.getDay(0);
|
||||||
t.tm_mon = ublox.getMonth() - 1;
|
t.tm_mon = ublox.getMonth(0) - 1;
|
||||||
t.tm_year = ublox.getYear() - 1900;
|
t.tm_year = ublox.getYear(0) - 1900;
|
||||||
t.tm_isdst = false;
|
t.tm_isdst = false;
|
||||||
perhapsSetRTC(t);
|
perhapsSetRTC(t);
|
||||||
}
|
}
|
||||||
@@ -112,10 +112,11 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
|
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
|
||||||
{
|
{
|
||||||
// we only notify if position has changed
|
// we only notify if position has changed
|
||||||
latitude = ublox.getLatitude();
|
latitude = ublox.getLatitude(0);
|
||||||
longitude = ublox.getLongitude();
|
longitude = ublox.getLongitude(0);
|
||||||
altitude = ublox.getAltitude() / 1000; // in mm convert to meters
|
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
|
||||||
DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude);
|
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
|
||||||
|
DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d, pdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
|
||||||
|
|
||||||
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
|
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
|
||||||
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
|
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0
|
|||||||
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
|
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
|
||||||
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
|
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
|
||||||
|
|
||||||
const
|
const uint8_t imgUSB[] PROGMEM = { 0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C };
|
||||||
|
const uint8_t imgPower[] PROGMEM = { 0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08, 0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22 };
|
||||||
|
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C };
|
||||||
|
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
|
||||||
|
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
||||||
|
|
||||||
#include "icon.xbm"
|
#include "icon.xbm"
|
||||||
|
|
||||||
// We now programmatically draw our compass
|
// We now programmatically draw our compass
|
||||||
|
|||||||
76
src/main.cpp
76
src/main.cpp
@@ -38,10 +38,12 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
#include <OneButton.h>
|
||||||
// #include <driver/rtc_io.h>
|
// #include <driver/rtc_io.h>
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
#include "BluetoothUtil.h"
|
#include "BluetoothUtil.h"
|
||||||
|
#include "WiFi.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "RF95Interface.h"
|
#include "RF95Interface.h"
|
||||||
@@ -124,7 +126,42 @@ static uint32_t ledBlinker()
|
|||||||
|
|
||||||
Periodic ledPeriodic(ledBlinker);
|
Periodic ledPeriodic(ledBlinker);
|
||||||
|
|
||||||
|
// Prepare for button presses
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
OneButton userButton;
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
OneButton userButtonAlt;
|
||||||
|
#endif
|
||||||
|
void userButtonPressed() {
|
||||||
|
powerFSM.trigger(EVENT_PRESS);
|
||||||
|
}
|
||||||
|
void userButtonPressedLong(){
|
||||||
|
screen.adjustBrightness();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_ESP32
|
||||||
|
void initWifi()
|
||||||
|
{
|
||||||
|
strcpy(radioConfig.preferences.wifi_ssid, "geeksville");
|
||||||
|
strcpy(radioConfig.preferences.wifi_password, "xxx");
|
||||||
|
if (radioConfig.has_preferences) {
|
||||||
|
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||||
|
|
||||||
|
if (*wifiName) {
|
||||||
|
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||||
|
if (radioConfig.preferences.wifi_ap_mode) {
|
||||||
|
// DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
|
||||||
|
} else {
|
||||||
|
// WiFi.mode(WIFI_MODE_STA);
|
||||||
|
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
|
||||||
|
// WiFi.begin(wifiName, wifiPsw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
DEBUG_MSG("Not using WIFI\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
@@ -161,8 +198,14 @@ void setup()
|
|||||||
|
|
||||||
// Buttons & LED
|
// Buttons & LED
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
userButton = OneButton(BUTTON_PIN, true, true);
|
||||||
digitalWrite(BUTTON_PIN, 1);
|
userButton.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
||||||
|
userButtonAlt.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
#endif
|
#endif
|
||||||
#ifdef LED_PIN
|
#ifdef LED_PIN
|
||||||
pinMode(LED_PIN, OUTPUT);
|
pinMode(LED_PIN, OUTPUT);
|
||||||
@@ -213,6 +256,11 @@ void setup()
|
|||||||
|
|
||||||
service.init();
|
service.init();
|
||||||
|
|
||||||
|
#ifndef NO_ESP32
|
||||||
|
// Must be after we init the service, because the wifi settings are loaded by NodeDB (oops)
|
||||||
|
initWifi();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SX1262_ANT_SW
|
#ifdef SX1262_ANT_SW
|
||||||
// make analog PA vs not PA switch on SX1262 eval board work properly
|
// make analog PA vs not PA switch on SX1262 eval board work properly
|
||||||
pinMode(SX1262_ANT_SW, OUTPUT);
|
pinMode(SX1262_ANT_SW, OUTPUT);
|
||||||
@@ -295,24 +343,10 @@ void loop()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
// if user presses button for more than 3 secs, discard our network prefs and reboot (FIXME, use a debounce lib instead of
|
userButton.tick();
|
||||||
// this boilerplate)
|
#endif
|
||||||
static bool wasPressed = false;
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt.tick();
|
||||||
if (!digitalRead(BUTTON_PIN)) {
|
|
||||||
if (!wasPressed) { // just started a new press
|
|
||||||
DEBUG_MSG("pressing\n");
|
|
||||||
|
|
||||||
// doLightSleep();
|
|
||||||
// esp_pm_dump_locks(stdout); // FIXME, do this someplace better
|
|
||||||
wasPressed = true;
|
|
||||||
|
|
||||||
powerFSM.trigger(EVENT_PRESS);
|
|
||||||
}
|
|
||||||
} else if (wasPressed) {
|
|
||||||
// we just did a release
|
|
||||||
wasPressed = false;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||||
@@ -334,8 +368,6 @@ void loop()
|
|||||||
screen.debug()->setNodeNumbersStatus(nodeDB.getNumOnlineNodes(), nodeDB.getNumNodes());
|
screen.debug()->setNodeNumbersStatus(nodeDB.getNumOnlineNodes(), nodeDB.getNumNodes());
|
||||||
screen.debug()->setChannelNameStatus(channelSettings.name);
|
screen.debug()->setChannelNameStatus(channelSettings.name);
|
||||||
screen.debug()->setPowerStatus(powerStatus);
|
screen.debug()->setPowerStatus(powerStatus);
|
||||||
// TODO(#4): use something based on hdop to show GPS "signal" strength.
|
|
||||||
screen.debug()->setGPSStatus(gps->isConnected ? (gps->hasLock() ? "GPS ok" : "No Sats") : "No GPS");
|
|
||||||
|
|
||||||
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
||||||
// i.e. don't just keep spinning in loop as fast as we can.
|
// i.e. don't just keep spinning in loop as fast as we can.
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "power.h"
|
||||||
|
#include "BluetoothUtil.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
|
||||||
|
|
||||||
/*
|
/*
|
||||||
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
|
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
|
||||||
@@ -280,6 +282,8 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
|||||||
sendToMesh(p);
|
sendToMesh(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int MeshService::onGPSChanged(void *unused)
|
int MeshService::onGPSChanged(void *unused)
|
||||||
{
|
{
|
||||||
// DEBUG_MSG("got gps notify\n");
|
// DEBUG_MSG("got gps notify\n");
|
||||||
@@ -298,6 +302,10 @@ int MeshService::onGPSChanged(void *unused)
|
|||||||
pos.time = getValidTime();
|
pos.time = getValidTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include our current battery voltage in our position announcement
|
||||||
|
pos.battery_level = powerStatus.batteryChargePercent;
|
||||||
|
updateBatteryLevel(pos.battery_level);
|
||||||
|
|
||||||
// We limit our GPS broadcasts to a max rate
|
// We limit our GPS broadcasts to a max rate
|
||||||
static uint32_t lastGpsSend;
|
static uint32_t lastGpsSend;
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
NodeDB nodeDB;
|
NodeDB nodeDB;
|
||||||
|
|
||||||
// we have plenty of ram so statically alloc this tempbuf (for now)
|
// we have plenty of ram so statically alloc this tempbuf (for now)
|
||||||
DeviceState devicestate;
|
EXT_RAM_ATTR DeviceState devicestate;
|
||||||
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
||||||
RadioConfig &radioConfig = devicestate.radio;
|
RadioConfig &radioConfig = devicestate.radio;
|
||||||
ChannelSettings &channelSettings = radioConfig.channel_settings;
|
ChannelSettings &channelSettings = radioConfig.channel_settings;
|
||||||
@@ -103,13 +103,16 @@ void NodeDB::resetRadioConfig()
|
|||||||
crypto->setKey(channelSettings.psk.size, channelSettings.psk.bytes);
|
crypto->setKey(channelSettings.psk.size, channelSettings.psk.bytes);
|
||||||
|
|
||||||
// temp hack for quicker testing
|
// temp hack for quicker testing
|
||||||
|
// devicestate.no_save = true;
|
||||||
|
if (devicestate.no_save) {
|
||||||
|
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE *****\n");
|
||||||
|
|
||||||
/*
|
// Sleep quite frequently to stress test the BLE comms, broadcast position every 6 mins
|
||||||
radioConfig.preferences.screen_on_secs = 30;
|
radioConfig.preferences.screen_on_secs = 30;
|
||||||
radioConfig.preferences.wait_bluetooth_secs = 30;
|
radioConfig.preferences.wait_bluetooth_secs = 30;
|
||||||
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
||||||
radioConfig.preferences.ls_secs = 60;
|
radioConfig.preferences.ls_secs = 60;
|
||||||
*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeDB::installDefaultDeviceState()
|
void NodeDB::installDefaultDeviceState()
|
||||||
@@ -257,32 +260,36 @@ void NodeDB::loadFromDisk()
|
|||||||
void NodeDB::saveToDisk()
|
void NodeDB::saveToDisk()
|
||||||
{
|
{
|
||||||
#ifdef FS
|
#ifdef FS
|
||||||
auto f = FS.open(preftmp, FILE_O_WRITE);
|
if (!devicestate.no_save) {
|
||||||
if (f) {
|
auto f = FS.open(preftmp, FILE_O_WRITE);
|
||||||
DEBUG_MSG("Writing preferences\n");
|
if (f) {
|
||||||
|
DEBUG_MSG("Writing preferences\n");
|
||||||
|
|
||||||
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
||||||
|
|
||||||
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
||||||
|
|
||||||
devicestate.version = DEVICESTATE_CUR_VER;
|
devicestate.version = DEVICESTATE_CUR_VER;
|
||||||
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
||||||
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
||||||
// FIXME - report failure to phone
|
// FIXME - report failure to phone
|
||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
|
} else {
|
||||||
|
// Success - replace the old file
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
// brief window of risk here ;-)
|
||||||
|
if (!FS.remove(preffile))
|
||||||
|
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
||||||
|
if (!FS.rename(preftmp, preffile))
|
||||||
|
DEBUG_MSG("Error: can't rename new pref file\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Success - replace the old file
|
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
||||||
f.close();
|
|
||||||
|
|
||||||
// brief window of risk here ;-)
|
|
||||||
if (!FS.remove(preffile))
|
|
||||||
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
|
||||||
if (!FS.rename(preftmp, preffile))
|
|
||||||
DEBUG_MSG("Error: can't rename new pref file\n");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
DEBUG_MSG("ERROR filesystem not implemented\n");
|
DEBUG_MSG("ERROR filesystem not implemented\n");
|
||||||
|
|||||||
@@ -101,8 +101,9 @@ typedef struct _RadioConfig_UserPreferences {
|
|||||||
uint32_t sds_secs;
|
uint32_t sds_secs;
|
||||||
uint32_t ls_secs;
|
uint32_t ls_secs;
|
||||||
uint32_t min_wake_secs;
|
uint32_t min_wake_secs;
|
||||||
bool keep_all_packets;
|
char wifi_ssid[33];
|
||||||
bool promiscuous_mode;
|
char wifi_password[64];
|
||||||
|
bool wifi_ap_mode;
|
||||||
pb_size_t ignore_incoming_count;
|
pb_size_t ignore_incoming_count;
|
||||||
uint32_t ignore_incoming[3];
|
uint32_t ignore_incoming[3];
|
||||||
} RadioConfig_UserPreferences;
|
} RadioConfig_UserPreferences;
|
||||||
@@ -187,6 +188,7 @@ typedef struct _DeviceState {
|
|||||||
bool has_rx_text_message;
|
bool has_rx_text_message;
|
||||||
MeshPacket rx_text_message;
|
MeshPacket rx_text_message;
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
|
bool no_save;
|
||||||
} DeviceState;
|
} DeviceState;
|
||||||
|
|
||||||
typedef struct _FromRadio {
|
typedef struct _FromRadio {
|
||||||
@@ -241,10 +243,10 @@ typedef struct _ToRadio {
|
|||||||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
|
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
|
||||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""}
|
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""}
|
||||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
|
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
|
||||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
|
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0}
|
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0}
|
||||||
#define DebugString_init_default {""}
|
#define DebugString_init_default {""}
|
||||||
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
||||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||||
@@ -257,10 +259,10 @@ typedef struct _ToRadio {
|
|||||||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
|
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
|
||||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""}
|
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""}
|
||||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
|
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
|
||||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
|
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0}
|
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0}
|
||||||
#define DebugString_init_zero {""}
|
#define DebugString_init_zero {""}
|
||||||
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
||||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||||
@@ -308,8 +310,9 @@ typedef struct _ToRadio {
|
|||||||
#define RadioConfig_UserPreferences_sds_secs_tag 9
|
#define RadioConfig_UserPreferences_sds_secs_tag 9
|
||||||
#define RadioConfig_UserPreferences_ls_secs_tag 10
|
#define RadioConfig_UserPreferences_ls_secs_tag 10
|
||||||
#define RadioConfig_UserPreferences_min_wake_secs_tag 11
|
#define RadioConfig_UserPreferences_min_wake_secs_tag 11
|
||||||
#define RadioConfig_UserPreferences_keep_all_packets_tag 100
|
#define RadioConfig_UserPreferences_wifi_ssid_tag 12
|
||||||
#define RadioConfig_UserPreferences_promiscuous_mode_tag 101
|
#define RadioConfig_UserPreferences_wifi_password_tag 13
|
||||||
|
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 102
|
#define RadioConfig_UserPreferences_ignore_incoming_tag 102
|
||||||
#define RouteDiscovery_route_tag 2
|
#define RouteDiscovery_route_tag 2
|
||||||
#define User_id_tag 1
|
#define User_id_tag 1
|
||||||
@@ -351,6 +354,7 @@ typedef struct _ToRadio {
|
|||||||
#define DeviceState_receive_queue_tag 5
|
#define DeviceState_receive_queue_tag 5
|
||||||
#define DeviceState_version_tag 8
|
#define DeviceState_version_tag 8
|
||||||
#define DeviceState_rx_text_message_tag 7
|
#define DeviceState_rx_text_message_tag 7
|
||||||
|
#define DeviceState_no_save_tag 9
|
||||||
#define FromRadio_packet_tag 2
|
#define FromRadio_packet_tag 2
|
||||||
#define FromRadio_my_info_tag 3
|
#define FromRadio_my_info_tag 3
|
||||||
#define FromRadio_node_info_tag 4
|
#define FromRadio_node_info_tag 4
|
||||||
@@ -456,8 +460,9 @@ X(a, STATIC, SINGULAR, UINT32, mesh_sds_timeout_secs, 8) \
|
|||||||
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
|
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
|
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, keep_all_packets, 100) \
|
X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, promiscuous_mode, 101) \
|
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
|
||||||
|
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
|
||||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 102)
|
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 102)
|
||||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||||
@@ -498,7 +503,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
|||||||
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
||||||
X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, version, 8)
|
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
||||||
|
X(a, STATIC, SINGULAR, BOOL, no_save, 9)
|
||||||
#define DeviceState_CALLBACK NULL
|
#define DeviceState_CALLBACK NULL
|
||||||
#define DeviceState_DEFAULT NULL
|
#define DeviceState_DEFAULT NULL
|
||||||
#define DeviceState_radio_MSGTYPE RadioConfig
|
#define DeviceState_radio_MSGTYPE RadioConfig
|
||||||
@@ -592,11 +598,11 @@ extern const pb_msgdesc_t ManufacturingData_msg;
|
|||||||
#define SubPacket_size 274
|
#define SubPacket_size 274
|
||||||
#define MeshPacket_size 313
|
#define MeshPacket_size 313
|
||||||
#define ChannelSettings_size 60
|
#define ChannelSettings_size 60
|
||||||
#define RadioConfig_size 157
|
#define RadioConfig_size 253
|
||||||
#define RadioConfig_UserPreferences_size 93
|
#define RadioConfig_UserPreferences_size 188
|
||||||
#define NodeInfo_size 132
|
#define NodeInfo_size 132
|
||||||
#define MyNodeInfo_size 110
|
#define MyNodeInfo_size 110
|
||||||
#define DeviceState_size 5305
|
#define DeviceState_size 5403
|
||||||
#define DebugString_size 258
|
#define DebugString_size 258
|
||||||
#define FromRadio_size 322
|
#define FromRadio_size 322
|
||||||
#define ToRadio_size 316
|
#define ToRadio_size 316
|
||||||
|
|||||||
15
src/power.h
15
src/power.h
@@ -1,5 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per @spattinson
|
||||||
|
* MIN_BAT_MILLIVOLTS seems high. Typical 18650 are different chemistry to LiPo, even for LiPos that chart seems a bit off, other
|
||||||
|
* charts put 3690mV at about 30% for a lipo, for 18650 i think 10% remaining iis in the region of 3.2-3.3V. Reference 1st graph
|
||||||
|
* in [this test report](https://lygte-info.dk/review/batteries2012/Samsung%20INR18650-30Q%203000mAh%20%28Pink%29%20UK.html)
|
||||||
|
* looking at the red line - discharge at 0.2A - he gets a capacity of 2900mah, 90% of 2900 = 2610, that point in the graph looks
|
||||||
|
* to be a shade above 3.2V
|
||||||
|
*/
|
||||||
|
#define MIN_BAT_MILLIVOLTS 3250 // millivolts. 10% per https://blog.ampow.com/lipo-voltage-chart/
|
||||||
|
|
||||||
|
#define BAT_MILLIVOLTS_FULL 4100
|
||||||
|
#define BAT_MILLIVOLTS_EMPTY 3500
|
||||||
|
|
||||||
namespace meshtastic
|
namespace meshtastic
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -9,6 +22,8 @@ struct PowerStatus {
|
|||||||
bool haveBattery;
|
bool haveBattery;
|
||||||
/// Battery voltage in mV, valid if haveBattery is true
|
/// Battery voltage in mV, valid if haveBattery is true
|
||||||
int batteryVoltageMv;
|
int batteryVoltageMv;
|
||||||
|
/// Battery charge percentage, either read directly or estimated
|
||||||
|
int batteryChargePercent;
|
||||||
/// Whether USB is connected
|
/// Whether USB is connected
|
||||||
bool usb;
|
bool usb;
|
||||||
/// Whether we are charging the battery
|
/// Whether we are charging the battery
|
||||||
|
|||||||
184
src/screen.cpp
184
src/screen.cpp
@@ -31,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
||||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
||||||
@@ -54,6 +55,8 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
|||||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||||
static char btPIN[16] = "888888";
|
static char btPIN[16] = "888888";
|
||||||
|
|
||||||
|
uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C };
|
||||||
|
|
||||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
// draw an xbm image.
|
// draw an xbm image.
|
||||||
@@ -64,20 +67,33 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
display->setFont(ArialMT_Plain_16);
|
display->setFont(ArialMT_Plain_16);
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
|
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
|
||||||
|
display->setFont(ArialMT_Plain_10);
|
||||||
|
const char *region = xstr(HW_VERSION);
|
||||||
|
if(*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
|
||||||
|
region += 4;
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%s", xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
|
||||||
|
display->drawString(SCREEN_WIDTH - 20, 0, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||||
display->setFont(ArialMT_Plain_16);
|
display->setFont(ArialMT_Plain_16);
|
||||||
display->drawString(64 + x, 2 + y, "Bluetooth");
|
display->drawString(64 + x, y, "Bluetooth");
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(ArialMT_Plain_10);
|
||||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT + y, "Enter this code");
|
display->drawString(64 + x, FONT_HEIGHT + y + 2, "Enter this code");
|
||||||
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
|
||||||
display->setFont(ArialMT_Plain_24);
|
display->setFont(ArialMT_Plain_24);
|
||||||
display->drawString(64 + x, 22 + y, btPIN);
|
display->drawString(64 + x, 26 + y, btPIN);
|
||||||
|
|
||||||
|
display->setFont(ArialMT_Plain_10);
|
||||||
|
char buf[30];
|
||||||
|
const char *name = "Name: ";
|
||||||
|
strcpy(buf,name);
|
||||||
|
strcat(buf,getDeviceName());
|
||||||
|
display->drawString(64 + x, 48 + y, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the last text message we received
|
/// Draw the last text message we received
|
||||||
@@ -156,6 +172,47 @@ static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char
|
|||||||
return yo;
|
return yo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
|
||||||
|
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, PowerStatus *powerStatus) {
|
||||||
|
static const uint8_t powerBar[3] = { 0x81, 0xBD, 0xBD };
|
||||||
|
static const uint8_t lightning[8] = { 0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85 };
|
||||||
|
// Clear the bar area on the battery image
|
||||||
|
for (int i = 1; i < 14; i++) {
|
||||||
|
imgBuffer[i] = 0x81;
|
||||||
|
}
|
||||||
|
// If charging, draw a charging indicator
|
||||||
|
if (powerStatus->charging) {
|
||||||
|
memcpy(imgBuffer + 3, lightning, 8);
|
||||||
|
// If not charging, Draw power bars
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if(powerStatus->batteryChargePercent >= 25 * i) memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display->drawFastImage(x, y, 16, 8, imgBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw nodes status
|
||||||
|
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, int nodesOnline, int nodesTotal) {
|
||||||
|
char usersString[20];
|
||||||
|
sprintf(usersString, "%d/%d", nodesOnline, nodesTotal);
|
||||||
|
display->drawFastImage(x, y, 8, 8, imgUser);
|
||||||
|
display->drawString(x + 10, y - 2, usersString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw GPS status summary
|
||||||
|
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, GPS *gps) {
|
||||||
|
if(!gps->isConnected) { display->drawString(x, y - 2, "No GPS"); return; }
|
||||||
|
display->drawFastImage(x, y, 6, 8, gps->hasLock() ? imgPositionSolid : imgPositionEmpty );
|
||||||
|
if(!gps->hasLock()) { display->drawString(x + 8, y - 2, "No sats"); return; }
|
||||||
|
if(gps->dop <= 100) { display->drawString(x + 8, y - 2, "Ideal"); return; }
|
||||||
|
if(gps->dop <= 200) { display->drawString(x + 8, y - 2, "Exc."); return; }
|
||||||
|
if(gps->dop <= 500) { display->drawString(x + 8, y - 2, "Good"); return; }
|
||||||
|
if(gps->dop <= 1000) { display->drawString(x + 8, y - 2, "Mod."); return; }
|
||||||
|
if(gps->dop <= 2000) { display->drawString(x + 8, y - 2, "Fair"); return; }
|
||||||
|
if(gps->dop > 0) { display->drawString(x + 8, y - 2, "Poor"); return; }
|
||||||
|
}
|
||||||
|
|
||||||
/// Ported from my old java code, returns distance in meters along the globe
|
/// Ported from my old java code, returns distance in meters along the globe
|
||||||
/// surface (by magic?)
|
/// surface (by magic?)
|
||||||
static float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
|
static float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
|
||||||
@@ -295,6 +352,30 @@ static bool hasPosition(NodeInfo *n)
|
|||||||
static size_t nodeIndex;
|
static size_t nodeIndex;
|
||||||
static int8_t prevFrame = -1;
|
static int8_t prevFrame = -1;
|
||||||
|
|
||||||
|
// Draw the compass and arrow pointing to location
|
||||||
|
static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
||||||
|
{
|
||||||
|
// display->drawXbm(compassX, compassY, compass_width, compass_height,
|
||||||
|
// (const uint8_t *)compass_bits);
|
||||||
|
|
||||||
|
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||||
|
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||||
|
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
||||||
|
|
||||||
|
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
points[i]->rotate(headingRadian);
|
||||||
|
points[i]->scale(COMPASS_DIAM * 0.6);
|
||||||
|
points[i]->translate(compassX, compassY);
|
||||||
|
}
|
||||||
|
drawLine(display, tip, tail);
|
||||||
|
drawLine(display, leftArrow, tip);
|
||||||
|
drawLine(display, rightArrow, tip);
|
||||||
|
|
||||||
|
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert an integer GPS coords to a floating point
|
/// Convert an integer GPS coords to a floating point
|
||||||
#define DegD(i) (i * 1e-7)
|
#define DegD(i) (i * 1e-7)
|
||||||
|
|
||||||
@@ -328,7 +409,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
const char *username = node->has_user ? node->user.long_name : "Unknown Name";
|
const char *username = node->has_user ? node->user.long_name : "Unknown Name";
|
||||||
|
|
||||||
static char signalStr[20];
|
static char signalStr[20];
|
||||||
snprintf(signalStr, sizeof(signalStr), "Signal: %.0f", node->snr);
|
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
||||||
|
|
||||||
uint32_t agoSecs = sinceLastSeen(node);
|
uint32_t agoSecs = sinceLastSeen(node);
|
||||||
static char lastStr[20];
|
static char lastStr[20];
|
||||||
@@ -339,15 +420,17 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
else
|
else
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||||
|
|
||||||
static float simRadian;
|
|
||||||
simRadian += 0.1; // For testing, have the compass spin unless both
|
|
||||||
// locations are valid
|
|
||||||
|
|
||||||
static char distStr[20];
|
static char distStr[20];
|
||||||
*distStr = 0; // might not have location data
|
*distStr = 0; // might not have location data
|
||||||
float headingRadian = simRadian;
|
float headingRadian;
|
||||||
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
|
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
|
||||||
if (ourNode && hasPosition(ourNode) && hasPosition(node)) {
|
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||||
|
drawColumns(display, x, y, fields);
|
||||||
|
|
||||||
|
// coordinates for the center of the compass/circle
|
||||||
|
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
|
||||||
|
|
||||||
|
if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node
|
||||||
Position &op = ourNode->position, &p = node->position;
|
Position &op = ourNode->position, &p = node->position;
|
||||||
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
if (d < 2000)
|
if (d < 2000)
|
||||||
@@ -360,35 +443,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
|
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
|
||||||
headingRadian = bearingToOther - myHeading;
|
headingRadian = bearingToOther - myHeading;
|
||||||
} else {
|
drawCompass(display, compassX, compassY, headingRadian);
|
||||||
|
} else { // direction to node is unknown so display question mark
|
||||||
// Debug info for gps lock errors
|
// Debug info for gps lock errors
|
||||||
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
||||||
|
|
||||||
|
display->drawString(compassX - FONT_HEIGHT/4, compassY - FONT_HEIGHT/2, "?");
|
||||||
|
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
|
||||||
drawColumns(display, x, y, fields);
|
|
||||||
|
|
||||||
// coordinates for the center of the compass
|
|
||||||
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
|
|
||||||
// display->drawXbm(compassX, compassY, compass_width, compass_height,
|
|
||||||
// (const uint8_t *)compass_bits);
|
|
||||||
|
|
||||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
|
||||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
|
||||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
|
||||||
|
|
||||||
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
points[i]->rotate(headingRadian);
|
|
||||||
points[i]->scale(COMPASS_DIAM * 0.6);
|
|
||||||
points[i]->translate(compassX, compassY);
|
|
||||||
}
|
|
||||||
drawLine(display, tip, tail);
|
|
||||||
drawLine(display, leftArrow, tip);
|
|
||||||
drawLine(display, rightArrow, tip);
|
|
||||||
|
|
||||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@@ -456,6 +518,9 @@ void Screen::setup()
|
|||||||
// Store a pointer to Screen so we can get to it from static functions.
|
// Store a pointer to Screen so we can get to it from static functions.
|
||||||
ui.getUiState()->userData = this;
|
ui.getUiState()->userData = this;
|
||||||
|
|
||||||
|
// Set the utf8 conversion function
|
||||||
|
dispdev.setFontTableLookupFunction(customFontTableLookup);
|
||||||
|
|
||||||
// Add frames.
|
// Add frames.
|
||||||
static FrameCallback bootFrames[] = {drawBootScreen};
|
static FrameCallback bootFrames[] = {drawBootScreen};
|
||||||
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
|
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
|
||||||
@@ -648,35 +713,38 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
// The coordinates define the left starting point of the text
|
// The coordinates define the left starting point of the text
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
|
||||||
char usersStr[20];
|
|
||||||
char channelStr[20];
|
char channelStr[20];
|
||||||
char batStr[20];
|
|
||||||
char gpsStr[20];
|
|
||||||
{
|
{
|
||||||
LockGuard guard(&lock);
|
LockGuard guard(&lock);
|
||||||
snprintf(usersStr, sizeof(usersStr), "Users %d/%d", nodesOnline, nodesTotal);
|
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
||||||
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
|
||||||
if (powerStatus.haveBattery) {
|
|
||||||
// TODO: draw a battery icon instead of letter "B".
|
|
||||||
int batV = powerStatus.batteryVoltageMv / 1000;
|
|
||||||
int batCv = (powerStatus.batteryVoltageMv % 1000) / 10;
|
|
||||||
snprintf(batStr, sizeof(batStr), "B %01d.%02dV%c%c", batV, batCv, powerStatus.charging ? '+' : ' ',
|
|
||||||
powerStatus.usb ? 'U' : ' ');
|
|
||||||
} else {
|
|
||||||
snprintf(batStr, sizeof(batStr), "%s", powerStatus.usb ? "USB" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gpsStatus.empty()) {
|
// Display power status
|
||||||
snprintf(gpsStr, sizeof(gpsStr), "%s", gpsStatus.c_str());
|
if (powerStatus.haveBattery) drawBattery(display, x, y + 2, imgBattery, &powerStatus); else display->drawFastImage(x, y + 2, 16, 8, powerStatus.usb ? imgUSB : imgPower);
|
||||||
} else {
|
// Display nodes status
|
||||||
gpsStr[0] = '\0'; // Just show empty string.
|
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodesOnline, nodesTotal);
|
||||||
}
|
// Display GPS status
|
||||||
|
drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gps);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *fields[] = {batStr, gpsStr, usersStr, channelStr, nullptr};
|
const char *fields[] = {channelStr, nullptr};
|
||||||
uint32_t yo = drawRows(display, x, y, fields);
|
uint32_t yo = drawRows(display, x, y + FONT_HEIGHT, fields);
|
||||||
|
|
||||||
display->drawLogBuffer(x, yo);
|
display->drawLogBuffer(x, yo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
|
||||||
|
void Screen::adjustBrightness(){
|
||||||
|
if (brightness == 254)
|
||||||
|
{
|
||||||
|
brightness = 0;
|
||||||
|
} else {
|
||||||
|
brightness++;
|
||||||
|
}
|
||||||
|
int width = brightness / 1.984375;
|
||||||
|
dispdev.drawRect( 0, 30, 128, 4);
|
||||||
|
dispdev.fillRect(0, 30, width, 4);
|
||||||
|
dispdev.display();
|
||||||
|
dispdev.setBrightness(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace meshtastic
|
} // namespace meshtastic
|
||||||
|
|||||||
35
src/screen.h
35
src/screen.h
@@ -117,6 +117,10 @@ class Screen : public PeriodicTask
|
|||||||
/// Handles a button press.
|
/// Handles a button press.
|
||||||
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
|
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
|
||||||
|
|
||||||
|
// Implementation to Adjust Brightness
|
||||||
|
void adjustBrightness();
|
||||||
|
int brightness = 150;
|
||||||
|
|
||||||
/// Starts showing the Bluetooth PIN screen.
|
/// Starts showing the Bluetooth PIN screen.
|
||||||
//
|
//
|
||||||
// Switches over to a static frame showing the Bluetooth pairing screen
|
// Switches over to a static frame showing the Bluetooth pairing screen
|
||||||
@@ -149,6 +153,37 @@ class Screen : public PeriodicTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Overrides the default utf8 character conversion, to replace empty space with question marks
|
||||||
|
static char customFontTableLookup(const uint8_t ch) {
|
||||||
|
// UTF-8 to font table index converter
|
||||||
|
// Code form http://playground.arduino.cc/Main/Utf8ascii
|
||||||
|
static uint8_t LASTCHAR;
|
||||||
|
static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters
|
||||||
|
|
||||||
|
if (ch < 128) { // Standard ASCII-set 0..0x7F handling
|
||||||
|
LASTCHAR = 0;
|
||||||
|
SKIPREST = false;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t last = LASTCHAR; // get last char
|
||||||
|
LASTCHAR = ch;
|
||||||
|
|
||||||
|
switch (last) { // conversion depnding on first UTF8-character
|
||||||
|
case 0xC2: { SKIPREST = false; return (uint8_t) ch; }
|
||||||
|
case 0xC3: { SKIPREST = false; return (uint8_t) (ch | 0xC0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to strip out prefix chars for two-byte char formats
|
||||||
|
if (ch == 0xC2 || ch == 0xC3 || ch == 0x82) return (uint8_t) 0;
|
||||||
|
|
||||||
|
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the rest of it
|
||||||
|
if (SKIPREST) return (uint8_t) 0;
|
||||||
|
SKIPREST = true;
|
||||||
|
|
||||||
|
return (uint8_t) 191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't stick to standard EASCII codes)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a handle to the DebugInfo screen.
|
/// Returns a handle to the DebugInfo screen.
|
||||||
//
|
//
|
||||||
// Use this handle to set things like battery status, user count, GPS status, etc.
|
// Use this handle to set things like battery status, user count, GPS status, etc.
|
||||||
|
|||||||
7
src/utils.h
Normal file
7
src/utils.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// C++ v17+ clamp function, limits a given value to a range defined by lo and hi
|
||||||
|
template<class T>
|
||||||
|
constexpr const T& clamp( const T& v, const T& lo, const T& hi ) {
|
||||||
|
return (v < lo) ? lo : (hi < v) ? hi : v;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user