Merge branch 'master' into stm32wl-wip

This commit is contained in:
Thomas Göttgens
2023-01-21 21:55:09 +01:00
committed by GitHub
co-authored by GitHub
322 changed files with 8054 additions and 8114 deletions
+4
View File
@@ -0,0 +1,4 @@
* text=auto eol=lf
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
*.{sh,[sS][hH]} text eol=lf
+5 -4
View File
@@ -8,16 +8,17 @@ plugins:
uri: https://github.com/trunk-io/plugins uri: https://github.com/trunk-io/plugins
lint: lint:
enabled: enabled:
- git-diff-check
- gitleaks@8.15.2
- clang-format@14.0.0
- prettier@2.8.3
disabled:
- shellcheck@0.9.0 - shellcheck@0.9.0
- shfmt@3.5.0 - shfmt@3.5.0
- oxipng@8.0.0 - oxipng@8.0.0
- actionlint@1.6.22 - actionlint@1.6.22
- git-diff-check
- gitleaks@8.15.2
- markdownlint@0.33.0 - markdownlint@0.33.0
- hadolint@2.12.0 - hadolint@2.12.0
- clang-format@14.0.0
- prettier@2.8.3
- svgo@3.0.2 - svgo@3.0.2
runtimes: runtimes:
enabled: enabled:
+5 -1
View File
@@ -1 +1,5 @@
cd protobufs && ..\nanopb-0.4.7\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated\meshtastic -I=..\protobufs meshtastic\*.proto cd protobufs && ..\nanopb-0.4.7\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated -I=..\protobufs ..\protobufs\meshtastic\*.proto
@REM cd ../src/mesh/generated/meshtastic
@REM sed -i 's/#include "meshtastic/#include "./g' *
@REM sed -i 's/meshtastic_//g' *
+3 -3
View File
@@ -10,10 +10,10 @@ echo "prebuilt binaries for your computer into nanopb-0.4.7"
cd protobufs cd protobufs
../nanopb-0.4.7/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated/ -I=../protobufs meshtastic/*.proto ../nanopb-0.4.7/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated/ -I=../protobufs meshtastic/*.proto
cd ../src/mesh/generated/meshtastic # cd ../src/mesh/generated/meshtastic
sed -i 's/#include "meshtastic/#include "./g' * # sed -i 's/#include "meshtastic/#include "./g' -- *
sed -i 's/meshtastic_//g' * # sed -i 's/meshtastic_//g' -- *
#echo "Regenerating protobuf documentation - if you see an error message" #echo "Regenerating protobuf documentation - if you see an error message"
#echo "you can ignore it unless doing a new protobuf release to github." #echo "you can ignore it unless doing a new protobuf release to github."
+6 -21
View File
@@ -7,12 +7,7 @@
"cpu": "cortex-m4", "cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA", "extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
"f_cpu": "64000000L", "f_cpu": "64000000L",
"hwids": [ "hwids": [["0x239A", "0x4405"]],
[
"0x239A",
"0x4405"
]
],
"usb_product": "TTGO_eink", "usb_product": "TTGO_eink",
"mcu": "nrf52840", "mcu": "nrf52840",
"variant": "eink0.1", "variant": "eink0.1",
@@ -30,19 +25,13 @@
"settings_addr": "0xFF000" "settings_addr": "0xFF000"
} }
}, },
"connectivity": [ "connectivity": ["bluetooth"],
"bluetooth"
],
"debug": { "debug": {
"jlink_device": "nRF52840_xxAA", "jlink_device": "nRF52840_xxAA",
"onboard_tools": [ "onboard_tools": ["jlink"],
"jlink"
],
"svd_path": "nrf52840.svd" "svd_path": "nrf52840.svd"
}, },
"frameworks": [ "frameworks": ["arduino"],
"arduino"
],
"name": "TTGO eink (Adafruit BSP)", "name": "TTGO eink (Adafruit BSP)",
"upload": { "upload": {
"maximum_ram_size": 248832, "maximum_ram_size": 248832,
@@ -50,12 +39,8 @@
"require_upload_port": true, "require_upload_port": true,
"speed": 115200, "speed": 115200,
"protocol": "jlink", "protocol": "jlink",
"protocols": [ "protocols": ["jlink", "nrfjprog", "stlink"]
"jlink",
"nrfjprog",
"stlink"
]
}, },
"url": "FIXME", "url": "FIXME",
"vendor": "TTGO" "vendor": "TTGO"
} }
+2 -6
View File
@@ -9,9 +9,7 @@
"product_line": "STM32WLE5xx" "product_line": "STM32WLE5xx"
}, },
"debug": { "debug": {
"default_tools": [ "default_tools": ["stlink"],
"stlink"
],
"jlink_device": "STM32WLE5CC", "jlink_device": "STM32WLE5CC",
"openocd_target": "stm32wlx", "openocd_target": "stm32wlx",
"svd_path": "STM32WLE5_CM4.svd" "svd_path": "STM32WLE5_CM4.svd"
@@ -22,9 +20,7 @@
"maximum_ram_size": 65536, "maximum_ram_size": 65536,
"maximum_size": 262144, "maximum_size": 262144,
"protocol": "cmsis-dap", "protocol": "cmsis-dap",
"protocols": [ "protocols": ["cmsis-dap"]
"cmsis-dap"
]
}, },
"url": "https://www.st.com/en/microcontrollers-microprocessors/stm32wl-series.html", "url": "https://www.st.com/en/microcontrollers-microprocessors/stm32wl-series.html",
"vendor": "ST" "vendor": "ST"
+4 -13
View File
@@ -19,16 +19,12 @@
"sd_fwid": "0x00B7" "sd_fwid": "0x00B7"
} }
}, },
"connectivity": [ "connectivity": ["bluetooth"],
"bluetooth"
],
"debug": { "debug": {
"jlink_device": "nRF52832_xxAA", "jlink_device": "nRF52832_xxAA",
"svd_path": "nrf52.svd" "svd_path": "nrf52.svd"
}, },
"frameworks": [ "frameworks": ["arduino"],
"arduino"
],
"name": "lora ISP4520", "name": "lora ISP4520",
"upload": { "upload": {
"maximum_ram_size": 65536, "maximum_ram_size": 65536,
@@ -36,13 +32,8 @@
"require_upload_port": true, "require_upload_port": true,
"speed": 115200, "speed": 115200,
"protocol": "nrfutil", "protocol": "nrfutil",
"protocols": [ "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"]
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
]
}, },
"url": "", "url": "",
"vendor": "PsiSoft" "vendor": "PsiSoft"
} }
+45 -66
View File
@@ -1,72 +1,51 @@
{ {
"build": { "build": {
"arduino": { "arduino": {
"ldscript": "nrf52840_s140_v6.ld" "ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[
"0x239A",
"0x8029"
],
[
"0x239A",
"0x0029"
],
[
"0x239A",
"0x002A"
],
[
"0x239A",
"0x802A"
]
],
"usb_product": "PCA10059",
"mcu": "nrf52840",
"variant": "nRF52840 Dongle",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
}, },
"connectivity": [ "core": "nRF5",
"bluetooth" "cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
], ],
"debug": { "usb_product": "PCA10059",
"jlink_device": "nRF52840_xxAA", "mcu": "nrf52840",
"svd_path": "nrf52840.svd" "variant": "nRF52840 Dongle",
"bsp": {
"name": "adafruit"
}, },
"frameworks": [ "softdevice": {
"arduino" "sd_flags": "-DS140",
], "sd_name": "s140",
"name": "nRF52840 Dongle", "sd_version": "6.1.1",
"upload": { "sd_fwid": "0x00B6"
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
}, },
"url": "https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle", "bootloader": {
"vendor": "Nordic Semiconductor" "settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "nRF52840 Dongle",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle",
"vendor": "Nordic Semiconductor"
} }
+42 -43
View File
@@ -1,47 +1,46 @@
{ {
"build": { "build": {
"arduino": { "arduino": {
"ldscript": "nrf52840_s140_v6.ld" "ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x4404"]],
"usb_product": "nrf52840dk",
"mcu": "nrf52840",
"variant": "pca10056",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
}, },
"connectivity": ["bluetooth"], "core": "nRF5",
"debug": { "cpu": "cortex-m4",
"jlink_device": "nRF52840_xxAA", "extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
"onboard_tools": ["jlink"], "f_cpu": "64000000L",
"svd_path": "nrf52840.svd" "hwids": [["0x239A", "0x4404"]],
"usb_product": "nrf52840dk",
"mcu": "nrf52840",
"variant": "pca10056",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
}, },
"frameworks": ["arduino"], "softdevice": {
"name": "A modified NRF52840-DK devboard (Adafruit BSP)", "sd_flags": "-DS140",
"upload": { "sd_name": "s140",
"maximum_ram_size": 248832, "sd_version": "6.1.1",
"maximum_size": 815104, "sd_fwid": "0x00B6"
"require_upload_port": true,
"speed": 115200,
"protocol": "jlink",
"protocols": ["jlink", "nrfjprog", "stlink"]
}, },
"url": "https://meshtastic.org/", "bootloader": {
"vendor": "Nordic Semi" "settings_addr": "0xFF000"
} }
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "A modified NRF52840-DK devboard (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"require_upload_port": true,
"speed": 115200,
"protocol": "jlink",
"protocols": ["jlink", "nrfjprog", "stlink"]
},
"url": "https://meshtastic.org/",
"vendor": "Nordic Semi"
}
+14 -30
View File
@@ -7,12 +7,7 @@
"cpu": "cortex-m4", "cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA", "extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
"f_cpu": "64000000L", "f_cpu": "64000000L",
"hwids": [ "hwids": [["0x239A", "0x4405"]],
[
"0x239A",
"0x4405"
]
],
"usb_product": "TTGO_eink", "usb_product": "TTGO_eink",
"mcu": "nrf52840", "mcu": "nrf52840",
"variant": "t-echo", "variant": "t-echo",
@@ -30,35 +25,24 @@
"settings_addr": "0xFF000" "settings_addr": "0xFF000"
} }
}, },
"connectivity": [ "connectivity": ["bluetooth"],
"bluetooth"
],
"debug": { "debug": {
"jlink_device": "nRF52840_xxAA", "jlink_device": "nRF52840_xxAA",
"onboard_tools": [ "onboard_tools": ["jlink"],
"jlink"
],
"svd_path": "nrf52840.svd" "svd_path": "nrf52840.svd"
}, },
"frameworks": [ "frameworks": ["arduino"],
"arduino"
],
"name": "TTGO eink (Adafruit BSP)", "name": "TTGO eink (Adafruit BSP)",
"upload": { "upload": {
"maximum_ram_size": 248832, "maximum_ram_size": 248832,
"maximum_size": 815104, "maximum_size": 815104,
"speed": 115200, "speed": 115200,
"protocol": "nrfutil", "protocol": "nrfutil",
"protocols": [ "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"jlink", "use_1200bps_touch": true,
"nrfjprog", "require_upload_port": true,
"nrfutil", "wait_for_upload_port": true
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
}, },
"url": "FIXME", "url": "FIXME",
"vendor": "TTGO" "vendor": "TTGO"
} }
+4 -13
View File
@@ -15,24 +15,15 @@
"f_cpu": "240000000L", "f_cpu": "240000000L",
"f_flash": "80000000L", "f_flash": "80000000L",
"flash_mode": "dio", "flash_mode": "dio",
"hwids": [ "hwids": [["0X303A", "0x1001"]],
[
"0X303A",
"0x1001"
]
],
"mcu": "esp32s3", "mcu": "esp32s3",
"variant": "tbeam-s3-core" "variant": "tbeam-s3-core"
}, },
"connectivity": [ "connectivity": ["wifi"],
"wifi"
],
"debug": { "debug": {
"openocd_target": "esp32s3.cfg" "openocd_target": "esp32s3.cfg"
}, },
"frameworks": [ "frameworks": ["arduino"],
"arduino"
],
"name": "LilyGo TBeam-S3-Core", "name": "LilyGo TBeam-S3-Core",
"upload": { "upload": {
"flash_size": "8MB", "flash_size": "8MB",
@@ -43,4 +34,4 @@
}, },
"url": "http://www.lilygo.cn/", "url": "http://www.lilygo.cn/",
"vendor": "LilyGo" "vendor": "LilyGo"
} }
+4 -14
View File
@@ -14,25 +14,15 @@
"f_cpu": "240000000L", "f_cpu": "240000000L",
"f_flash": "80000000L", "f_flash": "80000000L",
"flash_mode": "dio", "flash_mode": "dio",
"hwids": [ "hwids": [["0X303A", "0x1001"]],
[
"0X303A",
"0x1001"
]
],
"mcu": "esp32s3", "mcu": "esp32s3",
"variant": "tlora-t3s3-v1" "variant": "tlora-t3s3-v1"
}, },
"connectivity": [ "connectivity": ["wifi"],
"wifi"
],
"debug": { "debug": {
"openocd_target": "esp32s3.cfg" "openocd_target": "esp32s3.cfg"
}, },
"frameworks": [ "frameworks": ["arduino", "espidf"],
"arduino",
"espidf"
],
"name": "LilyGo TLora-T3S3-V1", "name": "LilyGo TLora-T3S3-V1",
"upload": { "upload": {
"flash_size": "4MB", "flash_size": "4MB",
@@ -44,4 +34,4 @@
}, },
"url": "http://www.lilygo.cn/", "url": "http://www.lilygo.cn/",
"vendor": "LilyGo" "vendor": "LilyGo"
} }
+4 -16
View File
@@ -1,6 +1,6 @@
{ {
"build": { "build": {
"arduino":{ "arduino": {
"ldscript": "esp32_out.ld" "ldscript": "esp32_out.ld"
}, },
"core": "esp32", "core": "esp32",
@@ -11,26 +11,14 @@
"mcu": "esp32", "mcu": "esp32",
"variant": "WisCore_RAK11200_Board" "variant": "WisCore_RAK11200_Board"
}, },
"connectivity": [ "connectivity": ["wifi", "bluetooth", "ethernet", "can"],
"wifi", "frameworks": ["arduino", "espidf"],
"bluetooth",
"ethernet",
"can"
],
"frameworks": [
"arduino",
"espidf"
],
"name": "WisCore RAK11200 Board", "name": "WisCore RAK11200 Board",
"upload": { "upload": {
"flash_size": "4MB", "flash_size": "4MB",
"maximum_ram_size": 327680, "maximum_ram_size": 327680,
"maximum_size": 4194304, "maximum_size": 4194304,
"protocols": [ "protocols": ["esptool", "espota", "ftdi"],
"esptool",
"espota",
"ftdi"
],
"require_upload_port": true, "require_upload_port": true,
"speed": 460800 "speed": 460800
}, },
+8 -30
View File
@@ -8,22 +8,10 @@
"extra_flags": "-DNRF52832_XXAA -DNRF52", "extra_flags": "-DNRF52832_XXAA -DNRF52",
"f_cpu": "64000000L", "f_cpu": "64000000L",
"hwids": [ "hwids": [
[ ["0x239A", "0x8029"],
"0x239A", ["0x239A", "0x0029"],
"0x8029" ["0x239A", "0x002A"],
], ["0x239A", "0x802A"]
[
"0x239A",
"0x0029"
],
[
"0x239A",
"0x002A"
],
[
"0x239A",
"0x802A"
]
], ],
"usb_product": "Feather nRF52832 Express", "usb_product": "Feather nRF52832 Express",
"mcu": "nrf52832", "mcu": "nrf52832",
@@ -41,17 +29,12 @@
"variant": "nrf52_adafruit_feather" "variant": "nrf52_adafruit_feather"
} }
}, },
"connectivity": [ "connectivity": ["bluetooth"],
"bluetooth"
],
"debug": { "debug": {
"jlink_device": "nRF52832_xxAA", "jlink_device": "nRF52832_xxAA",
"svd_path": "nrf52.svd" "svd_path": "nrf52.svd"
}, },
"frameworks": [ "frameworks": ["arduino", "zephyr"],
"arduino",
"zephyr"
],
"name": "Adafruit Bluefruit nRF52832 Feather", "name": "Adafruit Bluefruit nRF52832 Feather",
"upload": { "upload": {
"maximum_ram_size": 65536, "maximum_ram_size": 65536,
@@ -59,13 +42,8 @@
"require_upload_port": true, "require_upload_port": true,
"speed": 115200, "speed": 115200,
"protocol": "nrfutil", "protocol": "nrfutil",
"protocols": [ "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"]
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
]
}, },
"url": "https://www.adafruit.com/product/3406", "url": "https://www.adafruit.com/product/3406",
"vendor": "Adafruit" "vendor": "Adafruit"
} }
+46 -67
View File
@@ -1,72 +1,51 @@
{ {
"build": { "build": {
"arduino": { "arduino": {
"ldscript": "nrf52840_s140_v6.ld" "ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[
"0x239A",
"0x8029"
],
[
"0x239A",
"0x0029"
],
[
"0x239A",
"0x002A"
],
[
"0x239A",
"0x802A"
]
],
"usb_product": "WisCore RAK4631 Board",
"mcu": "nrf52840",
"variant": "WisCore_RAK4631_Board",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
}, },
"connectivity": [ "core": "nRF5",
"bluetooth" "cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
], ],
"debug": { "usb_product": "WisCore RAK4631 Board",
"jlink_device": "nRF52840_xxAA", "mcu": "nrf52840",
"svd_path": "nrf52840.svd" "variant": "WisCore_RAK4631_Board",
"bsp": {
"name": "adafruit"
}, },
"frameworks": [ "softdevice": {
"arduino" "sd_flags": "-DS140",
], "sd_name": "s140",
"name": "WisCore RAK4631 Board", "sd_version": "6.1.1",
"upload": { "sd_fwid": "0x00B6"
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
}, },
"url": "https://www.rakwireless.com", "bootloader": {
"vendor": "RAKwireless" "settings_addr": "0xFF000"
} }
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "WisCore RAK4631 Board",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.rakwireless.com",
"vendor": "RAKwireless"
}
+2 -2
View File
@@ -8,6 +8,6 @@ services:
replicas: 4 replicas: 4
networks: networks:
- mesh - mesh
networks: networks:
mesh: mesh:
+2 -2
View File
@@ -39,7 +39,7 @@ extra_scripts = bin/platformio-custom.py
; The Radiolib stuff will speed up building considerably. Exclud all the stuff we dont need. ; The Radiolib stuff will speed up building considerably. Exclud all the stuff we dont need.
build_flags = -Wno-missing-field-initializers build_flags = -Wno-missing-field-initializers
-Wno-format -Wno-format
-Isrc -Isrc/mesh -Isrc/gps -Isrc/buzz -Wl,-Map,.pio/build/output.map -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,.pio/build/output.map
-DUSE_THREAD_NAMES -DUSE_THREAD_NAMES
-DTINYGPS_OPTION_NO_CUSTOM_FIELDS -DTINYGPS_OPTION_NO_CUSTOM_FIELDS
-DPB_ENABLE_MALLOC=1 -DPB_ENABLE_MALLOC=1
@@ -58,7 +58,7 @@ build_flags = -Wno-missing-field-initializers
monitor_speed = 115200 monitor_speed = 115200
lib_deps = lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git#53580644255b48ebb7a737343c6b4e71c7e11cf2 ; ESP8266_SSD1306 https://github.com/meshtastic/esp8266-oled-ssd1306.git#da1ede4dfcd91074283b029080759fd744120909 ; ESP8266_SSD1306
mathertel/OneButton@^2.0.3 ; OneButton library for non-blocking button debounce mathertel/OneButton@^2.0.3 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#127ad674ef85f0201cb68a065879653ed94792c4 https://github.com/meshtastic/TinyGPSPlus.git#127ad674ef85f0201cb68a065879653ed94792c4
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "BluetoothCommon.h" #include "BluetoothCommon.h"
#include "configuration.h"
// NRF52 wants these constants as byte arrays // NRF52 wants these constants as byte arrays
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER // Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
+17 -21
View File
@@ -115,8 +115,7 @@ class ButtonThread : public concurrency::OSThread
{ {
// LOG_DEBUG("press!\n"); // LOG_DEBUG("press!\n");
#ifdef BUTTON_PIN #ifdef BUTTON_PIN
if ((BUTTON_PIN != moduleConfig.canned_message.inputbroker_pin_press) || if ((BUTTON_PIN != moduleConfig.canned_message.inputbroker_pin_press) || !moduleConfig.canned_message.enabled) {
!moduleConfig.canned_message.enabled) {
powerFSM.trigger(EVENT_PRESS); powerFSM.trigger(EVENT_PRESS);
} }
#endif #endif
@@ -144,10 +143,10 @@ class ButtonThread : public concurrency::OSThread
#ifdef PIN_LED1 #ifdef PIN_LED1
ledOff(PIN_LED1); ledOff(PIN_LED1);
#endif #endif
#ifdef PIN_LED2 #ifdef PIN_LED2
ledOff(PIN_LED2); ledOff(PIN_LED2);
#endif #endif
#ifdef PIN_LED3 #ifdef PIN_LED3
ledOff(PIN_LED3); ledOff(PIN_LED3);
#endif #endif
shutdown_on_long_stop = true; shutdown_on_long_stop = true;
@@ -160,28 +159,25 @@ class ButtonThread : public concurrency::OSThread
static void userButtonDoublePressed() static void userButtonDoublePressed()
{ {
#if defined(USE_EINK) && defined(PIN_EINK_EN) #if defined(USE_EINK) && defined(PIN_EINK_EN)
digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
#endif #endif
#if defined(GPS_POWER_TOGGLE) screen->print("Sent ad-hoc ping\n");
if(config.position.gps_enabled) service.refreshMyNodeInfo();
{ service.sendNetworkPing(NODENUM_BROADCAST, true);
LOG_DEBUG("Flag set to false for gps power\n");
}
else
{
LOG_DEBUG("Flag set to true to restore power\n");
}
config.position.gps_enabled = !(config.position.gps_enabled);
doGPSpowersave(config.position.gps_enabled);
#endif
} }
static void userButtonMultiPressed() static void userButtonMultiPressed()
{ {
screen->print("Sent ad-hoc ping\n"); #if defined(GPS_POWER_TOGGLE)
service.refreshMyNodeInfo(); if (config.position.gps_enabled) {
service.sendNetworkPing(NODENUM_BROADCAST, true); LOG_DEBUG("Flag set to false for gps power\n");
} else {
LOG_DEBUG("Flag set to true to restore power\n");
}
config.position.gps_enabled = !(config.position.gps_enabled);
doGPSpowersave(config.position.gps_enabled);
#endif
} }
static void userButtonPressedLongStart() static void userButtonPressedLongStart()
+35 -42
View File
@@ -1,26 +1,24 @@
#include "configuration.h"
#include "FSCommon.h" #include "FSCommon.h"
#include "configuration.h"
#ifdef HAS_SDCARD #ifdef HAS_SDCARD
#include <SPI.h>
#include <SD.h> #include <SD.h>
#include <SPI.h>
#ifdef SDCARD_USE_SPI1
#ifdef SDCARD_USE_SPI1
SPIClass SPI1(HSPI); SPIClass SPI1(HSPI);
#define SDHandler SPI1 #define SDHandler SPI1
#endif #endif
#endif // HAS_SDCARD
#endif //HAS_SDCARD bool copyFile(const char *from, const char *to)
bool copyFile(const char* from, const char* to)
{ {
#ifdef FSCom #ifdef FSCom
unsigned char cbuffer[16]; unsigned char cbuffer[16];
File f1 = FSCom.open(from, FILE_O_READ); File f1 = FSCom.open(from, FILE_O_READ);
if (!f1){ if (!f1) {
LOG_ERROR("Failed to open source file %s\n", from); LOG_ERROR("Failed to open source file %s\n", from);
return false; return false;
} }
@@ -30,55 +28,55 @@ bool copyFile(const char* from, const char* to)
LOG_ERROR("Failed to open destination file %s\n", to); LOG_ERROR("Failed to open destination file %s\n", to);
return false; return false;
} }
while (f1.available() > 0) { while (f1.available() > 0) {
byte i = f1.read(cbuffer, 16); byte i = f1.read(cbuffer, 16);
f2.write(cbuffer, i); f2.write(cbuffer, i);
} }
f2.close(); f2.close();
f1.close(); f1.close();
return true; return true;
#endif #endif
} }
bool renameFile(const char* pathFrom, const char* pathTo) bool renameFile(const char *pathFrom, const char *pathTo)
{ {
#ifdef FSCom #ifdef FSCom
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
// rename was fixed for ESP32 IDF LittleFS in April // rename was fixed for ESP32 IDF LittleFS in April
return FSCom.rename(pathFrom, pathTo); return FSCom.rename(pathFrom, pathTo);
#else #else
if (copyFile(pathFrom, pathTo) && FSCom.remove(pathFrom) ) { if (copyFile(pathFrom, pathTo) && FSCom.remove(pathFrom)) {
return true; return true;
} else{ } else {
return false; return false;
} }
#endif #endif
#endif #endif
} }
void listDir(const char * dirname, uint8_t levels, boolean del = false) void listDir(const char *dirname, uint8_t levels, boolean del = false)
{ {
#ifdef FSCom #ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) #if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
char buffer[255]; char buffer[255];
#endif #endif
File root = FSCom.open(dirname, FILE_O_READ); File root = FSCom.open(dirname, FILE_O_READ);
if(!root){ if (!root) {
return; return;
} }
if(!root.isDirectory()){ if (!root.isDirectory()) {
return; return;
} }
File file = root.openNextFile(); File file = root.openNextFile();
while(file){ while (file) {
if(file.isDirectory() && !String(file.name()).endsWith(".")) { if (file.isDirectory() && !String(file.name()).endsWith(".")) {
if(levels){ if (levels) {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
listDir(file.path(), levels -1, del); listDir(file.path(), levels - 1, del);
if(del) { if (del) {
LOG_DEBUG("Removing %s\n", file.path()); LOG_DEBUG("Removing %s\n", file.path());
strncpy(buffer, file.path(), sizeof(buffer)); strncpy(buffer, file.path(), sizeof(buffer));
file.close(); file.close();
@@ -87,33 +85,33 @@ void listDir(const char * dirname, uint8_t levels, boolean del = false)
file.close(); file.close();
} }
#elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) #elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
listDir(file.name(), levels -1, del); listDir(file.name(), levels - 1, del);
if(del) { if (del) {
LOG_DEBUG("Removing %s\n", file.name()); LOG_DEBUG("Removing %s\n", file.name());
strncpy(buffer, file.name(), sizeof(buffer)); strncpy(buffer, file.name(), sizeof(buffer));
file.close(); file.close();
FSCom.rmdir(buffer); FSCom.rmdir(buffer);
} else { } else {
file.close(); file.close();
} }
#else #else
listDir(file.name(), levels -1, del); listDir(file.name(), levels - 1, del);
file.close(); file.close();
#endif #endif
} }
} else { } else {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
if(del) { if (del) {
LOG_DEBUG("Deleting %s\n", file.path()); LOG_DEBUG("Deleting %s\n", file.path());
strncpy(buffer, file.path(), sizeof(buffer)); strncpy(buffer, file.path(), sizeof(buffer));
file.close(); file.close();
FSCom.remove(buffer); FSCom.remove(buffer);
} else { } else {
LOG_DEBUG(" %s (%i Bytes)\n", file.path(), file.size()); LOG_DEBUG(" %s (%i Bytes)\n", file.path(), file.size());
file.close(); file.close();
} }
#elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) #elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
if(del) { if (del) {
LOG_DEBUG("Deleting %s\n", file.name()); LOG_DEBUG("Deleting %s\n", file.name());
strncpy(buffer, file.name(), sizeof(buffer)); strncpy(buffer, file.name(), sizeof(buffer));
file.close(); file.close();
@@ -125,12 +123,12 @@ void listDir(const char * dirname, uint8_t levels, boolean del = false)
#else #else
LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size()); LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size());
file.close(); file.close();
#endif #endif
} }
file = root.openNextFile(); file = root.openNextFile();
} }
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
if(del) { if (del) {
LOG_DEBUG("Removing %s\n", root.path()); LOG_DEBUG("Removing %s\n", root.path());
strncpy(buffer, root.path(), sizeof(buffer)); strncpy(buffer, root.path(), sizeof(buffer));
root.close(); root.close();
@@ -139,7 +137,7 @@ void listDir(const char * dirname, uint8_t levels, boolean del = false)
root.close(); root.close();
} }
#elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) #elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
if(del) { if (del) {
LOG_DEBUG("Removing %s\n", root.name()); LOG_DEBUG("Removing %s\n", root.name());
strncpy(buffer, root.name(), sizeof(buffer)); strncpy(buffer, root.name(), sizeof(buffer));
root.close(); root.close();
@@ -153,7 +151,7 @@ void listDir(const char * dirname, uint8_t levels, boolean del = false)
#endif #endif
} }
void rmDir(const char * dirname) void rmDir(const char *dirname)
{ {
#ifdef FSCom #ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) #if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
@@ -168,8 +166,7 @@ void rmDir(const char * dirname)
void fsInit() void fsInit()
{ {
#ifdef FSCom #ifdef FSCom
if (!FSBegin()) if (!FSBegin()) {
{
LOG_ERROR("Filesystem mount Failed.\n"); LOG_ERROR("Filesystem mount Failed.\n");
// assert(0); This auto-formats the partition, so no need to fail here. // assert(0); This auto-formats the partition, so no need to fail here.
} }
@@ -182,7 +179,6 @@ void fsInit()
#endif #endif
} }
void setupSDCard() void setupSDCard()
{ {
#ifdef HAS_SDCARD #ifdef HAS_SDCARD
@@ -190,12 +186,12 @@ void setupSDCard()
if (!SD.begin(SDCARD_CS, SDHandler)) { if (!SD.begin(SDCARD_CS, SDHandler)) {
LOG_DEBUG("No SD_MMC card detected\n"); LOG_DEBUG("No SD_MMC card detected\n");
return ; return;
} }
uint8_t cardType = SD.cardType(); uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) { if (cardType == CARD_NONE) {
LOG_DEBUG("No SD_MMC card attached\n"); LOG_DEBUG("No SD_MMC card attached\n");
return ; return;
} }
LOG_DEBUG("SD_MMC Card Type: "); LOG_DEBUG("SD_MMC Card Type: ");
if (cardType == CARD_MMC) { if (cardType == CARD_MMC) {
@@ -214,6 +210,3 @@ void setupSDCard()
LOG_DEBUG("Used space: %llu MB\n", SD.usedBytes() / (1024 * 1024)); LOG_DEBUG("Used space: %llu MB\n", SD.usedBytes() / (1024 * 1024));
#endif #endif
} }
+4 -4
View File
@@ -40,8 +40,8 @@ using namespace Adafruit_LittleFS_Namespace;
#endif #endif
void fsInit(); void fsInit();
bool copyFile(const char* from, const char* to); bool copyFile(const char *from, const char *to);
bool renameFile(const char* pathFrom, const char* pathTo); bool renameFile(const char *pathFrom, const char *pathTo);
void listDir(const char * dirname, uint8_t levels, boolean del); void listDir(const char *dirname, uint8_t levels, boolean del);
void rmDir(const char * dirname); void rmDir(const char *dirname);
void setupSDCard(); void setupSDCard();
+24 -15
View File
@@ -20,15 +20,15 @@ class GPSStatus : public Status
bool hasLock = false; // default to false, until we complete our first read bool hasLock = false; // default to false, until we complete our first read
bool isConnected = false; // Do we have a GPS we are talking to bool isConnected = false; // Do we have a GPS we are talking to
bool isPowerSaving = false; //Are we in power saving state bool isPowerSaving = false; // Are we in power saving state
Position p = Position_init_default; meshtastic_Position p = meshtastic_Position_init_default;
public: public:
GPSStatus() { statusType = STATUS_TYPE_GPS; } GPSStatus() { statusType = STATUS_TYPE_GPS; }
// preferred method // preferred method
GPSStatus(bool hasLock, bool isConnected, bool isPowerSaving, const Position &pos) : Status() GPSStatus(bool hasLock, bool isConnected, bool isPowerSaving, const meshtastic_Position &pos) : Status()
{ {
this->hasLock = hasLock; this->hasLock = hasLock;
this->isConnected = isConnected; this->isConnected = isConnected;
@@ -47,7 +47,7 @@ class GPSStatus : public Status
bool getIsConnected() const { return isConnected; } bool getIsConnected() const { return isConnected; }
bool getIsPowerSaving() const { return isPowerSaving;} bool getIsPowerSaving() const { return isPowerSaving; }
int32_t getLatitude() const int32_t getLatitude() const
{ {
@@ -55,7 +55,7 @@ class GPSStatus : public Status
#ifdef GPS_EXTRAVERBOSE #ifdef GPS_EXTRAVERBOSE
LOG_WARN("Using fixed latitude\n"); LOG_WARN("Using fixed latitude\n");
#endif #endif
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
return node->position.latitude_i; return node->position.latitude_i;
} else { } else {
return p.latitude_i; return p.latitude_i;
@@ -68,7 +68,7 @@ class GPSStatus : public Status
#ifdef GPS_EXTRAVERBOSE #ifdef GPS_EXTRAVERBOSE
LOG_WARN("Using fixed longitude\n"); LOG_WARN("Using fixed longitude\n");
#endif #endif
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
return node->position.longitude_i; return node->position.longitude_i;
} else { } else {
return p.longitude_i; return p.longitude_i;
@@ -81,29 +81,38 @@ class GPSStatus : public Status
#ifdef GPS_EXTRAVERBOSE #ifdef GPS_EXTRAVERBOSE
LOG_WARN("Using fixed altitude\n"); LOG_WARN("Using fixed altitude\n");
#endif #endif
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
return node->position.altitude; return node->position.altitude;
} else { } else {
return p.altitude; return p.altitude;
} }
} }
uint32_t getDOP() const { return p.PDOP; } uint32_t getDOP() const
{
return p.PDOP;
}
uint32_t getHeading() const { return p.ground_track; } uint32_t getHeading() const
{
return p.ground_track;
}
uint32_t getNumSatellites() const { return p.sats_in_view; } uint32_t getNumSatellites() const
{
return p.sats_in_view;
}
bool matches(const GPSStatus *newStatus) const bool matches(const GPSStatus *newStatus) const
{ {
#ifdef GPS_EXTRAVERBOSE #ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x\n", newStatus->p.pos_timestamp, p.pos_timestamp); LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x\n", newStatus->p.pos_timestamp, p.pos_timestamp);
#endif #endif
return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->isPowerSaving !=isPowerSaving || return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected ||
newStatus->p.latitude_i != p.latitude_i || newStatus->p.longitude_i != p.longitude_i || newStatus->isPowerSaving != isPowerSaving || newStatus->p.latitude_i != p.latitude_i ||
newStatus->p.altitude != p.altitude || newStatus->p.altitude_hae != p.altitude_hae || newStatus->p.longitude_i != p.longitude_i || newStatus->p.altitude != p.altitude ||
newStatus->p.PDOP != p.PDOP || newStatus->p.ground_track != p.ground_track || newStatus->p.altitude_hae != p.altitude_hae || newStatus->p.PDOP != p.PDOP ||
newStatus->p.ground_speed != p.ground_speed || newStatus->p.ground_track != p.ground_track || newStatus->p.ground_speed != p.ground_speed ||
newStatus->p.sats_in_view != p.sats_in_view); newStatus->p.sats_in_view != p.sats_in_view);
} }
+51 -66
View File
@@ -1,83 +1,68 @@
#pragma once #pragma once
#include <Arduino.h>
#include "Status.h" #include "Status.h"
#include "configuration.h" #include "configuration.h"
#include <Arduino.h>
namespace meshtastic { namespace meshtastic
{
/// Describes the state of the NodeDB system. /// Describes the state of the NodeDB system.
class NodeStatus : public Status class NodeStatus : public Status
{
private:
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver =
CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
uint8_t numOnline = 0;
uint8_t numTotal = 0;
uint8_t lastNumTotal = 0;
public:
bool forceUpdate = false;
NodeStatus() { statusType = STATUS_TYPE_NODE; }
NodeStatus(uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false) : Status()
{ {
this->forceUpdate = forceUpdate;
this->numOnline = numOnline;
this->numTotal = numTotal;
}
NodeStatus(const NodeStatus &);
NodeStatus &operator=(const NodeStatus &);
private: void observe(Observable<const NodeStatus *> *source) { statusObserver.observe(source); }
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver = CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
uint8_t numOnline = 0; uint8_t getNumOnline() const { return numOnline; }
uint8_t numTotal = 0;
uint8_t lastNumTotal = 0; uint8_t getNumTotal() const { return numTotal; }
public: uint8_t getLastNumTotal() const { return lastNumTotal; }
bool forceUpdate = false;
NodeStatus() { bool matches(const NodeStatus *newStatus) const
statusType = STATUS_TYPE_NODE; {
} return (newStatus->getNumOnline() != numOnline || newStatus->getNumTotal() != numTotal);
NodeStatus( uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false ) : Status() }
int updateStatus(const NodeStatus *newStatus)
{
// Only update the status if values have actually changed
lastNumTotal = numTotal;
bool isDirty;
{ {
this->forceUpdate = forceUpdate; isDirty = matches(newStatus);
this->numOnline = numOnline; initialized = true;
this->numTotal = numTotal; numOnline = newStatus->getNumOnline();
numTotal = newStatus->getNumTotal();
} }
NodeStatus(const NodeStatus &); if (isDirty || newStatus->forceUpdate) {
NodeStatus &operator=(const NodeStatus &); LOG_DEBUG("Node status update: %d online, %d total\n", numOnline, numTotal);
onNewStatus.notifyObservers(this);
void observe(Observable<const NodeStatus *> *source)
{
statusObserver.observe(source);
} }
return 0;
}
};
uint8_t getNumOnline() const } // namespace meshtastic
{
return numOnline;
}
uint8_t getNumTotal() const
{
return numTotal;
}
uint8_t getLastNumTotal() const
{
return lastNumTotal;
}
bool matches(const NodeStatus *newStatus) const
{
return (
newStatus->getNumOnline() != numOnline ||
newStatus->getNumTotal() != numTotal
);
}
int updateStatus(const NodeStatus *newStatus) {
// Only update the status if values have actually changed
lastNumTotal = numTotal;
bool isDirty;
{
isDirty = matches(newStatus);
initialized = true;
numOnline = newStatus->getNumOnline();
numTotal = newStatus->getNumTotal();
}
if(isDirty || newStatus->forceUpdate) {
LOG_DEBUG("Node status update: %d online, %d total\n", numOnline, numTotal);
onNewStatus.notifyObservers(this);
}
return 0;
}
};
}
extern meshtastic::NodeStatus *nodeStatus; extern meshtastic::NodeStatus *nodeStatus;
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "OSTimer.h" #include "OSTimer.h"
#include "configuration.h"
/** /**
* Schedule a callback to run. The callback must _not_ block, though it is called from regular thread level (not ISR) * Schedule a callback to run. The callback must _not_ block, though it is called from regular thread level (not ISR)
+1 -2
View File
@@ -1,3 +1,2 @@
#include "configuration.h"
#include "Observer.h" #include "Observer.h"
#include "configuration.h"
+2 -2
View File
@@ -10,7 +10,7 @@ template <class T> class Observable;
*/ */
template <class T> class Observer template <class T> class Observer
{ {
std::list<Observable<T> *> observed; std::list<Observable<T> *> observed;
public: public:
virtual ~Observer(); virtual ~Observer();
@@ -87,7 +87,7 @@ template <class T> class Observable
template <class T> Observer<T>::~Observer() template <class T> Observer<T>::~Observer()
{ {
for (typename std::list<Observable<T> *>::const_iterator iterator = observed.begin(); iterator != observed.end(); for (typename std::list<Observable<T> *>::const_iterator iterator = observed.begin(); iterator != observed.end();
++iterator) { ++iterator) {
(*iterator)->removeObserver(this); (*iterator)->removeObserver(this);
} }
observed.clear(); observed.clear();
+111 -94
View File
@@ -1,16 +1,16 @@
#include "power.h" #include "power.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "buzz/buzz.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include "sleep.h" #include "sleep.h"
#include "utils.h" #include "utils.h"
#include "buzz/buzz.h"
#ifdef HAS_PMU #ifdef HAS_PMU
#include "XPowersLibInterface.hpp"
#include "XPowersAXP2101.tpp"
#include "XPowersAXP192.tpp" #include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
#include "XPowersLibInterface.hpp"
XPowersLibInterface *PMU = NULL; XPowersLibInterface *PMU = NULL;
#else #else
// Copy of the base class defined in axp20x.h. // Copy of the base class defined in axp20x.h.
@@ -108,20 +108,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
#ifdef BATTERY_PIN #ifdef BATTERY_PIN
// Override variant or default ADC_MULTIPLIER if we have the override pref // Override variant or default ADC_MULTIPLIER if we have the override pref
float operativeAdcMultiplier = config.power.adc_multiplier_override > 0 float operativeAdcMultiplier =
? config.power.adc_multiplier_override config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;
: ADC_MULTIPLIER;
// Do not call analogRead() often. // Do not call analogRead() often.
const uint32_t min_read_interval = 5000; const uint32_t min_read_interval = 5000;
if (millis() - last_read_time_ms > min_read_interval) { if (millis() - last_read_time_ms > min_read_interval) {
last_read_time_ms = millis(); last_read_time_ms = millis();
//Set the number of samples, it has an effect of increasing sensitivity, especially in complex electromagnetic environment. // Set the number of samples, it has an effect of increasing sensitivity, especially in complex electromagnetic
// environment.
uint32_t raw = 0; uint32_t raw = 0;
for(uint32_t i=0; i<BATTERY_SENSE_SAMPLES; i++){ for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
raw += analogRead(BATTERY_PIN); raw += analogRead(BATTERY_PIN);
} }
raw = raw/BATTERY_SENSE_SAMPLES; raw = raw / BATTERY_SENSE_SAMPLES;
float scaled; float scaled;
#ifndef VBAT_RAW_TO_SCALED #ifndef VBAT_RAW_TO_SCALED
@@ -143,15 +143,24 @@ class AnalogBatteryLevel : public HasBatteryLevel
/** /**
* return true if there is a battery installed in this unit * return true if there is a battery installed in this unit
*/ */
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; } virtual bool isBatteryConnect() override
{
return getBatteryPercent() != -1;
}
/// If we see a battery voltage higher than physics allows - assume charger is pumping /// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power /// in power
virtual bool isVbusIn() override { return getBattVoltage() > chargingVolt; } virtual bool isVbusIn() override
{
return getBattVoltage() > chargingVolt;
}
/// Assume charging if we have a battery and external power is connected. /// Assume charging if we have a battery and external power is connected.
/// we can't be smart enough to say 'full'? /// we can't be smart enough to say 'full'?
virtual bool isCharging() override { return isBatteryConnect() && isVbusIn(); } virtual bool isCharging() override
{
return isBatteryConnect() && isVbusIn();
}
private: private:
/// If we see a battery voltage higher than physics allows - assume charger is pumping /// If we see a battery voltage higher than physics allows - assume charger is pumping
@@ -159,16 +168,16 @@ class AnalogBatteryLevel : public HasBatteryLevel
#ifndef BAT_FULLVOLT #ifndef BAT_FULLVOLT
#define BAT_FULLVOLT 4200 #define BAT_FULLVOLT 4200
#endif #endif
#ifndef BAT_EMPTYVOLT #ifndef BAT_EMPTYVOLT
#define BAT_EMPTYVOLT 3270 #define BAT_EMPTYVOLT 3270
#endif #endif
#ifndef BAT_CHARGINGVOLT #ifndef BAT_CHARGINGVOLT
#define BAT_CHARGINGVOLT 4210 #define BAT_CHARGINGVOLT 4210
#endif #endif
#ifndef BAT_NOBATVOLT #ifndef BAT_NOBATVOLT
#define BAT_NOBATVOLT 2230 #define BAT_NOBATVOLT 2230
#endif #endif
/// For heltecs with no battery connected, the measured voltage is 2204, so raising to 2230 from 2100 /// For heltecs with no battery connected, the measured voltage is 2204, so raising to 2230 from 2100
const float fullVolt = BAT_FULLVOLT, emptyVolt = BAT_EMPTYVOLT, chargingVolt = BAT_CHARGINGVOLT, noBatVolt = BAT_NOBATVOLT; const float fullVolt = BAT_FULLVOLT, emptyVolt = BAT_EMPTYVOLT, chargingVolt = BAT_CHARGINGVOLT, noBatVolt = BAT_NOBATVOLT;
@@ -238,12 +247,12 @@ void Power::shutdown()
{ {
screen->setOn(false); screen->setOn(false);
#if defined(USE_EINK) && defined(PIN_EINK_EN) #if defined(USE_EINK) && defined(PIN_EINK_EN)
digitalWrite(PIN_EINK_EN, LOW); //power off backlight first digitalWrite(PIN_EINK_EN, LOW); // power off backlight first
#endif #endif
#ifdef HAS_PMU #ifdef HAS_PMU
LOG_INFO("Shutting down\n"); LOG_INFO("Shutting down\n");
if(PMU) { if (PMU) {
PMU->setChargingLedMode(XPOWERS_CHG_LED_OFF); PMU->setChargingLedMode(XPOWERS_CHG_LED_OFF);
PMU->shutdown(); PMU->shutdown();
} }
@@ -290,15 +299,16 @@ void Power::readPowerStatus()
if (lastheap != ESP.getFreeHeap()) { if (lastheap != ESP.getFreeHeap()) {
LOG_DEBUG("Threads running:"); LOG_DEBUG("Threads running:");
int running = 0; int running = 0;
for(int i = 0; i < MAX_THREADS; i++){ for (int i = 0; i < MAX_THREADS; i++) {
auto thread = concurrency::mainController.get(i); auto thread = concurrency::mainController.get(i);
if((thread != nullptr) && (thread->enabled)) { if ((thread != nullptr) && (thread->enabled)) {
LOG_DEBUG(" %s", thread->ThreadName.c_str()); LOG_DEBUG(" %s", thread->ThreadName.c_str());
running++; running++;
} }
} }
LOG_DEBUG("\n"); LOG_DEBUG("\n");
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(),
ESP.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
lastheap = ESP.getFreeHeap(); lastheap = ESP.getFreeHeap();
} }
#endif #endif
@@ -312,7 +322,7 @@ void Power::readPowerStatus()
LOG_DEBUG("Warning RAK4631 Low voltage counter: %d/10\n", low_voltage_counter); LOG_DEBUG("Warning RAK4631 Low voltage counter: %d/10\n", low_voltage_counter);
if (low_voltage_counter > 10) { if (low_voltage_counter > 10) {
// We can't trigger deep sleep on NRF52, it's freezing the board // We can't trigger deep sleep on NRF52, it's freezing the board
//powerFSM.trigger(EVENT_LOW_BATTERY); // powerFSM.trigger(EVENT_LOW_BATTERY);
LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n"); LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n");
} }
} else { } else {
@@ -338,11 +348,11 @@ int32_t Power::runOnce()
#ifdef HAS_PMU #ifdef HAS_PMU
// WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll // WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll
// the IRQ status by reading the registers over I2C // the IRQ status by reading the registers over I2C
if(PMU) { if (PMU) {
PMU->getIrqStatus(); PMU->getIrqStatus();
if(PMU->isVbusRemoveIrq()){ if (PMU->isVbusRemoveIrq()) {
LOG_INFO("USB unplugged\n"); LOG_INFO("USB unplugged\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED); powerFSM.trigger(EVENT_POWER_DISCONNECTED);
} }
@@ -388,24 +398,24 @@ int32_t Power::runOnce()
share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1 share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
* *
*/ */
bool Power::axpChipInit() bool Power::axpChipInit()
{ {
#ifdef HAS_PMU #ifdef HAS_PMU
TwoWire * w = NULL; TwoWire *w = NULL;
// Use macro to distinguish which wire is used by PMU // Use macro to distinguish which wire is used by PMU
#ifdef PMU_USE_WIRE1 #ifdef PMU_USE_WIRE1
w = &Wire1; w = &Wire1;
#else #else
w = &Wire; w = &Wire;
#endif #endif
/** /**
* It is not necessary to specify the wire pin, * It is not necessary to specify the wire pin,
* just input the wire, because the wire has been initialized in main.cpp * just input the wire, because the wire has been initialized in main.cpp
*/ */
if (!PMU) { if (!PMU) {
@@ -431,11 +441,11 @@ bool Power::axpChipInit()
} }
if (!PMU) { if (!PMU) {
/* /*
* In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will be called at the same time. * In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will be called at the same time.
* In order not to affect other devices, if the initialization of the PMU fails, Wire needs to be re-initialized once, * In order not to affect other devices, if the initialization of the PMU fails, Wire needs to be re-initialized once,
* if there are multiple devices sharing the bus. * if there are multiple devices sharing the bus.
* * */ * * */
#ifndef PMU_USE_WIRE1 #ifndef PMU_USE_WIRE1
w->begin(I2C_SDA, I2C_SCL); w->begin(I2C_SDA, I2C_SCL);
#endif #endif
@@ -445,48 +455,45 @@ bool Power::axpChipInit()
batteryLevel = PMU; batteryLevel = PMU;
if (PMU->getChipModel() == XPOWERS_AXP192) { if (PMU->getChipModel() == XPOWERS_AXP192) {
// lora radio power channel // lora radio power channel
PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300); PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300);
PMU->enablePowerOutput(XPOWERS_LDO2); PMU->enablePowerOutput(XPOWERS_LDO2);
// oled module power channel, // oled module power channel,
// disable it will cause abnormal communication between boot and AXP power supply, // disable it will cause abnormal communication between boot and AXP power supply,
// do not turn it off // do not turn it off
PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300); PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
// enable oled power // enable oled power
PMU->enablePowerOutput(XPOWERS_DCDC1); PMU->enablePowerOutput(XPOWERS_DCDC1);
// gnss module power channel - now turned on in setGpsPower // gnss module power channel - now turned on in setGpsPower
PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300); PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300);
// PMU->enablePowerOutput(XPOWERS_LDO3); // PMU->enablePowerOutput(XPOWERS_LDO3);
// protected oled power source
//protected oled power source
PMU->setProtectedChannel(XPOWERS_DCDC1); PMU->setProtectedChannel(XPOWERS_DCDC1);
//protected esp32 power source // protected esp32 power source
PMU->setProtectedChannel(XPOWERS_DCDC3); PMU->setProtectedChannel(XPOWERS_DCDC3);
//disable not use channel // disable not use channel
PMU->disablePowerOutput(XPOWERS_DCDC2); PMU->disablePowerOutput(XPOWERS_DCDC2);
//disable all axp chip interrupt // disable all axp chip interrupt
PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ); PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ);
// Set constant current charging current // Set constant current charging current
PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_450MA); PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_450MA);
//Set up the charging voltage // Set up the charging voltage
PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2); PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2);
} else if (PMU->getChipModel() == XPOWERS_AXP2101) { } else if (PMU->getChipModel() == XPOWERS_AXP2101) {
// t-beam s3 core // t-beam s3 core
/** /**
* gnss module power channel * gnss module power channel
* The default ALDO4 is off, you need to turn on the GNSS power first, otherwise it will be invalid during initialization * The default ALDO4 is off, you need to turn on the GNSS power first, otherwise it will be invalid during initialization
*/ */
PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300); PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
@@ -496,51 +503,50 @@ bool Power::axpChipInit()
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
PMU->enablePowerOutput(XPOWERS_ALDO3); PMU->enablePowerOutput(XPOWERS_ALDO3);
// m.2 interface // m.2 interface
PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300); PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300);
PMU->enablePowerOutput(XPOWERS_DCDC3); PMU->enablePowerOutput(XPOWERS_DCDC3);
/** /**
* ALDO2 cannot be turned off. * ALDO2 cannot be turned off.
* It is a necessary condition for sensor communication. * It is a necessary condition for sensor communication.
* It must be turned on to properly access the sensor and screen * It must be turned on to properly access the sensor and screen
* It is also responsible for the power supply of PCF8563 * It is also responsible for the power supply of PCF8563
*/ */
PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300); PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
PMU->enablePowerOutput(XPOWERS_ALDO2); PMU->enablePowerOutput(XPOWERS_ALDO2);
// 6-axis , magnetometer ,bme280 , oled screen power channel // 6-axis , magnetometer ,bme280 , oled screen power channel
PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300); PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
PMU->enablePowerOutput(XPOWERS_ALDO1); PMU->enablePowerOutput(XPOWERS_ALDO1);
// sdcard power channle // sdcard power channle
PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300); PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
PMU->enablePowerOutput(XPOWERS_BLDO1); PMU->enablePowerOutput(XPOWERS_BLDO1);
// PMU->setPowerChannelVoltage(XPOWERS_DCDC4, 3300); // PMU->setPowerChannelVoltage(XPOWERS_DCDC4, 3300);
// PMU->enablePowerOutput(XPOWERS_DCDC4); // PMU->enablePowerOutput(XPOWERS_DCDC4);
//not use channel // not use channel
PMU->disablePowerOutput(XPOWERS_DCDC2); //not elicited PMU->disablePowerOutput(XPOWERS_DCDC2); // not elicited
PMU->disablePowerOutput(XPOWERS_DCDC5); //not elicited PMU->disablePowerOutput(XPOWERS_DCDC5); // not elicited
PMU->disablePowerOutput(XPOWERS_DLDO1); //Invalid power channel, it does not exist PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist
PMU->disablePowerOutput(XPOWERS_DLDO2); //Invalid power channel, it does not exist PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist
PMU->disablePowerOutput(XPOWERS_VBACKUP); PMU->disablePowerOutput(XPOWERS_VBACKUP);
//disable all axp chip interrupt // disable all axp chip interrupt
PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ); PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
//Set the constant current charging current of AXP2101, temporarily use 500mA by default // Set the constant current charging current of AXP2101, temporarily use 500mA by default
PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
//Set up the charging voltage // Set up the charging voltage
PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2); PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2);
} }
PMU->clearIrqStatus(); PMU->clearIrqStatus();
// TBeam1.1 /T-Beam S3-Core has no external TS detection, // TBeam1.1 /T-Beam S3-Core has no external TS detection,
// it needs to be disabled, otherwise it will cause abnormal charging // it needs to be disabled, otherwise it will cause abnormal charging
PMU->disableTSPinMeasure(); PMU->disableTSPinMeasure();
@@ -550,40 +556,52 @@ bool Power::axpChipInit()
LOG_DEBUG("=======================================================================\n"); LOG_DEBUG("=======================================================================\n");
if (PMU->isChannelAvailable(XPOWERS_DCDC1)) { if (PMU->isChannelAvailable(XPOWERS_DCDC1)) {
LOG_DEBUG("DC1 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC1)); LOG_DEBUG("DC1 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_DCDC1));
} }
if (PMU->isChannelAvailable(XPOWERS_DCDC2)) { if (PMU->isChannelAvailable(XPOWERS_DCDC2)) {
LOG_DEBUG("DC2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC2)); LOG_DEBUG("DC2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_DCDC2));
} }
if (PMU->isChannelAvailable(XPOWERS_DCDC3)) { if (PMU->isChannelAvailable(XPOWERS_DCDC3)) {
LOG_DEBUG("DC3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC3)); LOG_DEBUG("DC3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_DCDC3));
} }
if (PMU->isChannelAvailable(XPOWERS_DCDC4)) { if (PMU->isChannelAvailable(XPOWERS_DCDC4)) {
LOG_DEBUG("DC4 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC4)); LOG_DEBUG("DC4 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_DCDC4));
} }
if (PMU->isChannelAvailable(XPOWERS_LDO2)) { if (PMU->isChannelAvailable(XPOWERS_LDO2)) {
LOG_DEBUG("LDO2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO2)); LOG_DEBUG("LDO2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_LDO2));
} }
if (PMU->isChannelAvailable(XPOWERS_LDO3)) { if (PMU->isChannelAvailable(XPOWERS_LDO3)) {
LOG_DEBUG("LDO3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO3)); LOG_DEBUG("LDO3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_LDO3));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO1)) { if (PMU->isChannelAvailable(XPOWERS_ALDO1)) {
LOG_DEBUG("ALDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO1)); LOG_DEBUG("ALDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_ALDO1));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO2)) { if (PMU->isChannelAvailable(XPOWERS_ALDO2)) {
LOG_DEBUG("ALDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO2)); LOG_DEBUG("ALDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_ALDO2));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO3)) { if (PMU->isChannelAvailable(XPOWERS_ALDO3)) {
LOG_DEBUG("ALDO3: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO3)); LOG_DEBUG("ALDO3: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_ALDO3));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO4)) { if (PMU->isChannelAvailable(XPOWERS_ALDO4)) {
LOG_DEBUG("ALDO4: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO4)); LOG_DEBUG("ALDO4: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_ALDO4));
} }
if (PMU->isChannelAvailable(XPOWERS_BLDO1)) { if (PMU->isChannelAvailable(XPOWERS_BLDO1)) {
LOG_DEBUG("BLDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO1)); LOG_DEBUG("BLDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_BLDO1));
} }
if (PMU->isChannelAvailable(XPOWERS_BLDO2)) { if (PMU->isChannelAvailable(XPOWERS_BLDO2)) {
LOG_DEBUG("BLDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO2)); LOG_DEBUG("BLDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
} }
LOG_DEBUG("=======================================================================\n"); LOG_DEBUG("=======================================================================\n");
@@ -597,30 +615,29 @@ bool Power::axpChipInit()
PMU->setSysPowerDownVoltage(2600); PMU->setSysPowerDownVoltage(2600);
#endif #endif
#ifdef PMU_IRQ #ifdef PMU_IRQ
uint64_t pmuIrqMask = 0; uint64_t pmuIrqMask = 0;
if (PMU->getChipModel() == XPOWERS_AXP192) { if (PMU->getChipModel() == XPOWERS_AXP192) {
pmuIrqMask = XPOWERS_AXP192_VBUS_INSERT_IRQ | XPOWERS_AXP192_BAT_INSERT_IRQ | XPOWERS_AXP192_PKEY_SHORT_IRQ; pmuIrqMask = XPOWERS_AXP192_VBUS_INSERT_IRQ | XPOWERS_AXP192_BAT_INSERT_IRQ | XPOWERS_AXP192_PKEY_SHORT_IRQ;
} else if (PMU->getChipModel() == XPOWERS_AXP2101) { } else if (PMU->getChipModel() == XPOWERS_AXP2101) {
pmuIrqMask = XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_PKEY_SHORT_IRQ; pmuIrqMask = XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_PKEY_SHORT_IRQ;
} }
pinMode(PMU_IRQ, INPUT); pinMode(PMU_IRQ, INPUT);
attachInterrupt( attachInterrupt(
PMU_IRQ, [] { pmu_irq = true; }, FALLING); PMU_IRQ, [] { pmu_irq = true; }, FALLING);
// we do not look for AXPXXX_CHARGING_FINISHED_IRQ & AXPXXX_CHARGING_IRQ because it occurs repeatedly while there is // we do not look for AXPXXX_CHARGING_FINISHED_IRQ & AXPXXX_CHARGING_IRQ because it occurs repeatedly while there is
// no battery also it could cause inadvertent waking from light sleep just because the battery filled // no battery also it could cause inadvertent waking from light sleep just because the battery filled
// we don't look for AXPXXX_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed // we don't look for AXPXXX_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed
// we don't look at AXPXXX_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus // we don't look at AXPXXX_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus
PMU->enableIRQ(pmuIrqMask); PMU->enableIRQ(pmuIrqMask);
PMU->clearIrqStatus(); PMU->clearIrqStatus();
#endif /*PMU_IRQ*/ #endif /*PMU_IRQ*/
readPowerStatus(); readPowerStatus();
pmu_found = true; pmu_found = true;
+23 -14
View File
@@ -11,12 +11,12 @@
/// Should we behave as if we have AC power now? /// Should we behave as if we have AC power now?
static bool isPowered() static bool isPowered()
{ {
// Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC // Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC
#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) #if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101)
return true; return true;
#endif #endif
bool isRouter = (config.device.role == Config_DeviceConfig_Role_ROUTER ? 1 : 0); bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON // If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
// We assume routers might be powered all the time, but from a low current (solar) source // We assume routers might be powered all the time, but from a low current (solar) source
@@ -199,7 +199,8 @@ static void onEnter()
uint32_t now = millis(); uint32_t now = millis();
if ((now - lastPingMs) > 30 * 1000) { // if more than a minute since our last press, ask node we are looking at to update their state if ((now - lastPingMs) >
30 * 1000) { // if more than a minute since our last press, ask node we are looking at to update their state
if (displayedNodeNum) if (displayedNodeNum)
service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node
lastPingMs = now; lastPingMs = now;
@@ -237,7 +238,7 @@ Fsm powerFSM(&stateBOOT);
void PowerFSM_setup() void PowerFSM_setup()
{ {
bool isRouter = (config.device.role == Config_DeviceConfig_Role_ROUTER ? 1 : 0); bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
bool hasPower = isPowered(); bool hasPower = isPowered();
LOG_INFO("PowerFSM init, USB power=%d\n", hasPower ? 1 : 0); LOG_INFO("PowerFSM init, USB power=%d\n", hasPower ? 1 : 0);
@@ -249,7 +250,8 @@ void PowerFSM_setup()
// We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from // We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from
// light sleep we _always_ transition to NB or dark and // light sleep we _always_ transition to NB or dark and
powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, exiting light sleep"); powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_PACKET_FOR_PHONE, NULL,
"Received packet, exiting light sleep");
powerFSM.add_transition(&stateNB, &stateNB, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, resetting win wake"); powerFSM.add_transition(&stateNB, &stateNB, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, resetting win wake");
// Handle press events - note: we ignore button presses when in API mode // Handle press events - note: we ignore button presses when in API mode
@@ -258,7 +260,8 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press"); powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press");
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers
powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress, "Press"); // Allow button to work while in serial API powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress,
"Press"); // Allow button to work while in serial API
// Handle critically low power battery by forcing deep sleep // Handle critically low power battery by forcing deep sleep
powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
@@ -324,7 +327,9 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
powerFSM.add_timed_transition(&stateON, &stateDARK, getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, "Screen-on timeout"); powerFSM.add_timed_transition(&stateON, &stateDARK,
getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
"Screen-on timeout");
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
State *lowPowerState = &stateLS; State *lowPowerState = &stateLS;
@@ -332,14 +337,18 @@ void PowerFSM_setup()
// See: https://github.com/meshtastic/firmware/issues/1071 // See: https://github.com/meshtastic/firmware/issues/1071
if (isRouter || config.power.is_power_saving) { if (isRouter || config.power.is_power_saving) {
powerFSM.add_timed_transition(&stateNB, &stateLS, getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL, "Min wake timeout"); powerFSM.add_timed_transition(&stateNB, &stateLS,
powerFSM.add_timed_transition(&stateDARK, &stateLS, getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs), NULL, "Bluetooth timeout"); getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL,
"Min wake timeout");
powerFSM.add_timed_transition(&stateDARK, &stateLS,
getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs),
NULL, "Bluetooth timeout");
} }
if (config.power.sds_secs != UINT32_MAX) if (config.power.sds_secs != UINT32_MAX)
powerFSM.add_timed_transition(lowPowerState, &stateSDS, getConfiguredOrDefaultMs(config.power.sds_secs), NULL, "mesh timeout"); powerFSM.add_timed_transition(lowPowerState, &stateSDS, getConfiguredOrDefaultMs(config.power.sds_secs), NULL,
"mesh timeout");
#endif #endif
powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state
} }
+2 -2
View File
@@ -19,8 +19,8 @@
#define EVENT_POWER_CONNECTED 13 #define EVENT_POWER_CONNECTED 13
#define EVENT_POWER_DISCONNECTED 14 #define EVENT_POWER_DISCONNECTED 14
#define EVENT_FIRMWARE_UPDATE 15 // We just received a new firmware update packet from the phone #define EVENT_FIRMWARE_UPDATE 15 // We just received a new firmware update packet from the phone
#define EVENT_SHUTDOWN 16 //force a full shutdown now (not just sleep) #define EVENT_SHUTDOWN 16 // force a full shutdown now (not just sleep)
#define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen #define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen
extern Fsm powerFSM; extern Fsm powerFSM;
extern State statePOWER, stateSERIAL; extern State statePOWER, stateSERIAL;
+4 -3
View File
@@ -26,9 +26,10 @@ class PowerFSMThread : public OSThread
if (powerStatus->getHasUSB()) { if (powerStatus->getHasUSB()) {
timeLastPowered = millis(); timeLastPowered = millis();
} else if (config.power.on_battery_shutdown_after_secs > 0 && } else if (config.power.on_battery_shutdown_after_secs > 0 && config.power.on_battery_shutdown_after_secs != UINT32_MAX &&
config.power.on_battery_shutdown_after_secs != UINT32_MAX && millis() > (timeLastPowered +
millis() > (timeLastPowered + getConfiguredOrDefaultMs(config.power.on_battery_shutdown_after_secs))) { // shutdown after 30 minutes unpowered getConfiguredOrDefaultMs(
config.power.on_battery_shutdown_after_secs))) { // shutdown after 30 minutes unpowered
powerFSM.trigger(EVENT_SHUTDOWN); powerFSM.trigger(EVENT_SHUTDOWN);
} }
+34 -29
View File
@@ -1,12 +1,12 @@
#include "configuration.h"
#include "RedirectablePrint.h" #include "RedirectablePrint.h"
#include "RTC.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "configuration.h"
#include <assert.h> #include <assert.h>
#include <cstring>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <cstring>
/** /**
* A printer that doesn't go anywhere * A printer that doesn't go anywhere
@@ -42,7 +42,8 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy); size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy);
va_end(copy); va_end(copy);
// If the resulting string is longer than sizeof(printBuf)-1 characters, the remaining characters are still counted for the return value // If the resulting string is longer than sizeof(printBuf)-1 characters, the remaining characters are still counted for the
// return value
if (len > sizeof(printBuf) - 1) { if (len > sizeof(printBuf) - 1) {
len = sizeof(printBuf) - 1; len = sizeof(printBuf) - 1;
@@ -104,30 +105,34 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
return r; return r;
} }
void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len) { void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len)
const char alphabet[17] = "0123456789abcdef"; {
log(logLevel, " +------------------------------------------------+ +----------------+\n"); const char alphabet[17] = "0123456789abcdef";
log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |\n"); log(logLevel, " +------------------------------------------------+ +----------------+\n");
for (uint16_t i = 0; i < len; i += 16) { log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |\n");
if (i % 128 == 0) for (uint16_t i = 0; i < len; i += 16) {
log(logLevel, " +------------------------------------------------+ +----------------+\n"); if (i % 128 == 0)
char s[] = "| | | |\n"; log(logLevel, " +------------------------------------------------+ +----------------+\n");
uint8_t ix = 1, iy = 52; char s[] = "| | | |\n";
for (uint8_t j = 0; j < 16; j++) { uint8_t ix = 1, iy = 52;
if (i + j < len) { for (uint8_t j = 0; j < 16; j++) {
uint8_t c = buf[i + j]; if (i + j < len) {
s[ix++] = alphabet[(c >> 4) & 0x0F]; uint8_t c = buf[i + j];
s[ix++] = alphabet[c & 0x0F]; s[ix++] = alphabet[(c >> 4) & 0x0F];
ix++; s[ix++] = alphabet[c & 0x0F];
if (c > 31 && c < 128) s[iy++] = c; ix++;
else s[iy++] = '.'; if (c > 31 && c < 128)
} s[iy++] = c;
else
s[iy++] = '.';
}
}
uint8_t index = i / 16;
if (i < 256)
log(logLevel, " ");
log(logLevel, "%02x", index);
log(logLevel, ".");
log(logLevel, s);
} }
uint8_t index = i / 16; log(logLevel, " +------------------------------------------------+ +----------------+\n");
if (i < 256) log(logLevel, " ");
log(logLevel, "%02x",index);
log(logLevel, ".");
log(logLevel, s);
}
log(logLevel, " +------------------------------------------------+ +----------------+\n");
} }
+2 -2
View File
@@ -29,12 +29,12 @@ class RedirectablePrint : public Print
/** /**
* Debug logging print message * Debug logging print message
* *
* If the provide format string ends with a newline we assume it is the final print of a single * If the provide format string ends with a newline we assume it is the final print of a single
* log message. Otherwise we assume more prints will come before the log message ends. This * log message. Otherwise we assume more prints will come before the log message ends. This
* allows you to call logDebug a few times to build up a single log message line if you wish. * allows you to call logDebug a few times to build up a single log message line if you wish.
*/ */
size_t log(const char *logLevel, const char *format, ...) __attribute__ ((format (printf, 3, 4))); size_t log(const char *logLevel, const char *format, ...) __attribute__((format(printf, 3, 4)));
/** like printf but va_list based */ /** like printf but va_list based */
size_t vprintf(const char *format, va_list arg); size_t vprintf(const char *format, va_list arg);
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "SPILock.h" #include "SPILock.h"
#include "configuration.h"
#include <Arduino.h> #include <Arduino.h>
#include <assert.h> #include <assert.h>
+3 -2
View File
@@ -49,7 +49,8 @@ int32_t SerialConsole::runOnce()
return runOncePart(); return runOncePart();
} }
void SerialConsole::flush() { void SerialConsole::flush()
{
Port.flush(); Port.flush();
} }
@@ -74,7 +75,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
canWrite = true; canWrite = true;
return StreamAPI::handleToRadio(buf, len); return StreamAPI::handleToRadio(buf, len);
}else{ } else {
return false; return false;
} }
} }
-1
View File
@@ -29,7 +29,6 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
void flush(); void flush();
protected: protected:
/// Check the current underlying physical link to see if the client is currently connected /// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override; virtual bool checkIsConnected() override;
}; };
+34 -50
View File
@@ -8,65 +8,49 @@
#define STATUS_TYPE_GPS 2 #define STATUS_TYPE_GPS 2
#define STATUS_TYPE_NODE 3 #define STATUS_TYPE_NODE 3
namespace meshtastic namespace meshtastic
{ {
// A base class for observable status // A base class for observable status
class Status class Status
{
protected:
// Allows us to observe an Observable
CallbackObserver<Status, const Status *> statusObserver =
CallbackObserver<Status, const Status *>(this, &Status::updateStatus);
bool initialized = false;
// Workaround for no typeid support
int statusType = 0;
public:
// Allows us to generate observable events
Observable<const Status *> onNewStatus;
// Enable polymorphism ?
virtual ~Status() = default;
Status()
{ {
protected: if (!statusType) {
// Allows us to observe an Observable statusType = STATUS_TYPE_BASE;
CallbackObserver<Status, const Status *> statusObserver = CallbackObserver<Status, const Status *>(this, &Status::updateStatus);
bool initialized = false;
// Workaround for no typeid support
int statusType = 0;
public:
// Allows us to generate observable events
Observable<const Status *> onNewStatus;
// Enable polymorphism ?
virtual ~Status() = default;
Status() {
if (!statusType)
{
statusType = STATUS_TYPE_BASE;
}
} }
}
// Prevent object copy/move // Prevent object copy/move
Status(const Status &) = delete; Status(const Status &) = delete;
Status &operator=(const Status &) = delete; Status &operator=(const Status &) = delete;
// Start observing a source of data // Start observing a source of data
void observe(Observable<const Status *> *source) void observe(Observable<const Status *> *source) { statusObserver.observe(source); }
{
statusObserver.observe(source);
}
// Determines whether or not existing data matches the data in another Status instance // Determines whether or not existing data matches the data in another Status instance
bool matches(const Status *otherStatus) const bool matches(const Status *otherStatus) const { return true; }
{
return true;
}
bool isInitialized() const bool isInitialized() const { return initialized; }
{
return initialized;
}
int getStatusType() const int getStatusType() const { return statusType; }
{
return statusType;
}
// Called when the Observable we're observing generates a new notification // Called when the Observable we're observing generates a new notification
int updateStatus(const Status *newStatus) int updateStatus(const Status *newStatus) { return 0; }
{
return 0;
}
};
}; };
}; // namespace meshtastic
+29 -29
View File
@@ -34,11 +34,13 @@ uint8_t AirTime::currentPeriodIndex()
return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG); return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG);
} }
uint8_t AirTime::getPeriodUtilMinute() { uint8_t AirTime::getPeriodUtilMinute()
{
return (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS; return (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS;
} }
uint8_t AirTime::getPeriodUtilHour() { uint8_t AirTime::getPeriodUtilHour()
{
return (getSecondsSinceBoot() / 60) % MINUTES_IN_HOUR; return (getSecondsSinceBoot() / 60) % MINUTES_IN_HOUR;
} }
@@ -119,23 +121,23 @@ float AirTime::utilizationTXPercent()
bool AirTime::isTxAllowedChannelUtil(bool polite) bool AirTime::isTxAllowedChannelUtil(bool polite)
{ {
uint8_t percentage = (polite ? polite_channel_util_percent : max_channel_util_percent); uint8_t percentage = (polite ? polite_channel_util_percent : max_channel_util_percent);
if (channelUtilizationPercent() < percentage) { if (channelUtilizationPercent() < percentage) {
return true; return true;
} else { } else {
LOG_WARN("Channel utilization is >%d percent. Skipping this opportunity to send.\n", percentage); LOG_WARN("Channel utilization is >%d percent. Skipping this opportunity to send.\n", percentage);
return false; return false;
} }
} }
bool AirTime::isTxAllowedAirUtil()
bool AirTime::isTxAllowedAirUtil()
{ {
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) { if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) { if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) {
return true; return true;
} else { } else {
LOG_WARN("Tx air utilization is >%d percent. Skipping this opportunity to send.\n", myRegion->dutyCycle * polite_duty_cycle_percent / 100); LOG_WARN("Tx air utilization is >%f percent. Skipping this opportunity to send.\n",
myRegion->dutyCycle * polite_duty_cycle_percent / 100);
return false; return false;
} }
} }
@@ -143,21 +145,19 @@ bool AirTime::isTxAllowedAirUtil()
} }
// Get the amount of minutes we have to be silent before we can send again // Get the amount of minutes we have to be silent before we can send again
uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle) uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle)
{ {
float newTxPercent = txPercent; float newTxPercent = txPercent;
for (int8_t i = MINUTES_IN_HOUR-1; i >= 0; --i) { for (int8_t i = MINUTES_IN_HOUR - 1; i >= 0; --i) {
newTxPercent -= ((float)this->utilizationTX[i] / (MS_IN_MINUTE * MINUTES_IN_HOUR / 100)); newTxPercent -= ((float)this->utilizationTX[i] / (MS_IN_MINUTE * MINUTES_IN_HOUR / 100));
if (newTxPercent < dutyCycle) if (newTxPercent < dutyCycle)
return MINUTES_IN_HOUR-1-i; return MINUTES_IN_HOUR - 1 - i;
} }
return MINUTES_IN_HOUR; return MINUTES_IN_HOUR;
} }
AirTime::AirTime() : concurrency::OSThread("AirTime"), airtimes({}) {}
AirTime::AirTime() : concurrency::OSThread("AirTime"),airtimes({}) {
}
int32_t AirTime::runOnce() int32_t AirTime::runOnce()
{ {
@@ -213,14 +213,14 @@ int32_t AirTime::runOnce()
// Update channel_utilization every second. // Update channel_utilization every second.
myNodeInfo.air_util_tx = airTime->utilizationTXPercent(); myNodeInfo.air_util_tx = airTime->utilizationTXPercent();
} }
/* /*
LOG_DEBUG("utilPeriodTX %d TX Airtime %3.2f%\n", utilPeriodTX, airTime->utilizationTXPercent()); LOG_DEBUG("utilPeriodTX %d TX Airtime %3.2f%\n", utilPeriodTX, airTime->utilizationTXPercent());
for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) { for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) {
LOG_DEBUG( LOG_DEBUG(
"%d,", this->utilizationTX[i] "%d,", this->utilizationTX[i]
); );
} }
LOG_DEBUG("\n"); LOG_DEBUG("\n");
*/ */
return (1000 * 1); return (1000 * 1);
} }
+3 -4
View File
@@ -1,10 +1,10 @@
#pragma once #pragma once
#include "MeshRadio.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "configuration.h" #include "configuration.h"
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include <functional>
#include "MeshRadio.h"
/* /*
TX_LOG - Time on air this device has transmitted TX_LOG - Time on air this device has transmitted
@@ -33,7 +33,6 @@
#define MS_IN_MINUTE (SECONDS_IN_MINUTE * 1000) #define MS_IN_MINUTE (SECONDS_IN_MINUTE * 1000)
#define MS_IN_HOUR (MINUTES_IN_HOUR * SECONDS_IN_MINUTE * 1000) #define MS_IN_HOUR (MINUTES_IN_HOUR * SECONDS_IN_MINUTE * 1000)
enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG }; enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
void logAirtime(reportTypes reportType, uint32_t airtime_ms); void logAirtime(reportTypes reportType, uint32_t airtime_ms);
@@ -60,7 +59,7 @@ class AirTime : private concurrency::OSThread
uint32_t getSecondsSinceBoot(); uint32_t getSecondsSinceBoot();
uint32_t *airtimeReport(reportTypes reportType); uint32_t *airtimeReport(reportTypes reportType);
uint8_t getSilentMinutes(float txPercent, float dutyCycle); uint8_t getSilentMinutes(float txPercent, float dutyCycle);
bool isTxAllowedChannelUtil(bool polite=false); bool isTxAllowedChannelUtil(bool polite = false);
bool isTxAllowedAirUtil(); bool isTxAllowedAirUtil();
private: private:
@@ -70,7 +69,7 @@ class AirTime : private concurrency::OSThread
uint32_t secSinceBoot = 0; uint32_t secSinceBoot = 0;
uint8_t max_channel_util_percent = 40; uint8_t max_channel_util_percent = 40;
uint8_t polite_channel_util_percent = 25; uint8_t polite_channel_util_percent = 25;
uint8_t polite_duty_cycle_percent = 50; // half of Duty Cycle allowance is ok for metadata uint8_t polite_duty_cycle_percent = 50; // half of Duty Cycle allowance is ok for metadata
struct airtimeStruct { struct airtimeStruct {
uint32_t periodTX[PERIODS_TO_LOG]; // AirTime transmitted uint32_t periodTX[PERIODS_TO_LOG]; // AirTime transmitted
+28 -29
View File
@@ -1,6 +1,6 @@
#include "buzz.h" #include "buzz.h"
#include "configuration.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "configuration.h"
#if !defined(ARCH_ESP32) && !defined(ARCH_RP2040) && !defined(ARCH_PORTDUINO) #if !defined(ARCH_ESP32) && !defined(ARCH_RP2040) && !defined(ARCH_PORTDUINO)
#include "Tone.h" #include "Tone.h"
@@ -11,8 +11,8 @@ extern "C" void delay(uint32_t dwMs);
#endif #endif
struct ToneDuration { struct ToneDuration {
int frequency_khz; int frequency_khz;
int duration_ms; int duration_ms;
}; };
// Some common frequencies. // Some common frequencies.
@@ -30,40 +30,39 @@ struct ToneDuration {
#define NOTE_B3 247 #define NOTE_B3 247
#define NOTE_CS4 277 #define NOTE_CS4 277
const int DURATION_1_8 = 125; // 1/8 note const int DURATION_1_8 = 125; // 1/8 note
const int DURATION_1_4 = 250; // 1/4 note const int DURATION_1_4 = 250; // 1/4 note
void playTones(const ToneDuration *tone_durations, int size) { void playTones(const ToneDuration *tone_durations, int size)
{
#ifdef PIN_BUZZER #ifdef PIN_BUZZER
if (!config.device.buzzer_gpio) if (!config.device.buzzer_gpio)
config.device.buzzer_gpio = PIN_BUZZER; config.device.buzzer_gpio = PIN_BUZZER;
#endif #endif
if (config.device.buzzer_gpio) { if (config.device.buzzer_gpio) {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
const auto &tone_duration = tone_durations[i]; const auto &tone_duration = tone_durations[i];
tone(config.device.buzzer_gpio, tone_duration.frequency_khz, tone_duration.duration_ms); tone(config.device.buzzer_gpio, tone_duration.frequency_khz, tone_duration.duration_ms);
// to distinguish the notes, set a minimum time between them. // to distinguish the notes, set a minimum time between them.
delay(1.3 * tone_duration.duration_ms); delay(1.3 * tone_duration.duration_ms);
}
} }
}
} }
void playBeep()
void playBeep() { {
ToneDuration melody[] = {{NOTE_B3, DURATION_1_4}}; ToneDuration melody[] = {{NOTE_B3, DURATION_1_4}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playStartMelody() { void playStartMelody()
ToneDuration melody[] = {{NOTE_FS3, DURATION_1_8}, {
{NOTE_AS3, DURATION_1_8}, ToneDuration melody[] = {{NOTE_FS3, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_CS4, DURATION_1_4}};
{NOTE_CS4, DURATION_1_4}}; playTones(melody, sizeof(melody) / sizeof(ToneDuration));
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playShutdownMelody() { void playShutdownMelody()
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {
{NOTE_AS3, DURATION_1_8}, ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}};
{NOTE_FS3, DURATION_1_4}}; playTones(melody, sizeof(melody) / sizeof(ToneDuration));
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
+11 -11
View File
@@ -4,15 +4,15 @@
*/ */
enum class Cmd { enum class Cmd {
INVALID, INVALID,
SET_ON, SET_ON,
SET_OFF, SET_OFF,
ON_PRESS, ON_PRESS,
START_BLUETOOTH_PIN_SCREEN, START_BLUETOOTH_PIN_SCREEN,
START_FIRMWARE_UPDATE_SCREEN, START_FIRMWARE_UPDATE_SCREEN,
STOP_BLUETOOTH_PIN_SCREEN, STOP_BLUETOOTH_PIN_SCREEN,
STOP_BOOT_SCREEN, STOP_BOOT_SCREEN,
PRINT, PRINT,
START_SHUTDOWN_SCREEN, START_SHUTDOWN_SCREEN,
START_REBOOT_SCREEN, START_REBOOT_SCREEN,
}; };
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "concurrency/BinarySemaphoreFreeRTOS.h" #include "concurrency/BinarySemaphoreFreeRTOS.h"
#include "configuration.h"
#include <assert.h> #include <assert.h>
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
+5 -13
View File
@@ -1,18 +1,14 @@
#include "configuration.h"
#include "concurrency/BinarySemaphorePosix.h" #include "concurrency/BinarySemaphorePosix.h"
#include "configuration.h"
#ifndef HAS_FREE_RTOS #ifndef HAS_FREE_RTOS
namespace concurrency namespace concurrency
{ {
BinarySemaphorePosix::BinarySemaphorePosix() BinarySemaphorePosix::BinarySemaphorePosix() {}
{
}
BinarySemaphorePosix::~BinarySemaphorePosix() BinarySemaphorePosix::~BinarySemaphorePosix() {}
{
}
/** /**
* Returns false if we timed out * Returns false if we timed out
@@ -23,13 +19,9 @@ bool BinarySemaphorePosix::take(uint32_t msec)
return false; return false;
} }
void BinarySemaphorePosix::give() void BinarySemaphorePosix::give() {}
{
}
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {}
{
}
} // namespace concurrency } // namespace concurrency
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "concurrency/InterruptableDelay.h" #include "concurrency/InterruptableDelay.h"
#include "configuration.h"
namespace concurrency namespace concurrency
{ {
-1
View File
@@ -2,7 +2,6 @@
#include "../freertosinc.h" #include "../freertosinc.h"
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
#include "concurrency/BinarySemaphoreFreeRTOS.h" #include "concurrency/BinarySemaphoreFreeRTOS.h"
#define BinarySemaphore BinarySemaphoreFreeRTOS #define BinarySemaphore BinarySemaphoreFreeRTOS
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "Lock.h" #include "Lock.h"
#include "configuration.h"
#include <cassert> #include <cassert>
namespace concurrency namespace concurrency
+3 -2
View File
@@ -1,7 +1,8 @@
#include "configuration.h"
#include "LockGuard.h" #include "LockGuard.h"
#include "configuration.h"
namespace concurrency { namespace concurrency
{
LockGuard::LockGuard(Lock *lock) : lock(lock) LockGuard::LockGuard(Lock *lock) : lock(lock)
{ {
+2 -1
View File
@@ -2,7 +2,8 @@
#include "Lock.h" #include "Lock.h"
namespace concurrency { namespace concurrency
{
/** /**
* @brief RAII lock guard * @brief RAII lock guard
+2 -4
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "NotifiedWorkerThread.h" #include "NotifiedWorkerThread.h"
#include "configuration.h"
#include "main.h" #include "main.h"
namespace concurrency namespace concurrency
@@ -80,11 +80,9 @@ void NotifiedWorkerThread::checkNotification()
} }
} }
int32_t NotifiedWorkerThread::runOnce() int32_t NotifiedWorkerThread::runOnce()
{ {
enabled = false; // Only run once per notification enabled = false; // Only run once per notification
checkNotification(); checkNotification();
return RUN_SAME; return RUN_SAME;
+3 -3
View File
@@ -41,9 +41,9 @@ class NotifiedWorkerThread : public OSThread
/// just calls checkNotification() /// just calls checkNotification()
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
/// Sometimes we might want to check notifications independently of when our thread was getting woken up (i.e. if we are about to change /// Sometimes we might want to check notifications independently of when our thread was getting woken up (i.e. if we are about
/// radio transmit/receive modes we want to handle any pending interrupts first). You can call this method and if any notifications are currently /// to change radio transmit/receive modes we want to handle any pending interrupts first). You can call this method and if
/// pending they will be handled immediately. /// any notifications are currently pending they will be handled immediately.
void checkNotification(); void checkNotification();
private: private:
+4 -4
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "OSThread.h" #include "OSThread.h"
#include "configuration.h"
#include <assert.h> #include <assert.h>
namespace concurrency namespace concurrency
@@ -76,7 +76,7 @@ void OSThread::run()
{ {
#ifdef DEBUG_HEAP #ifdef DEBUG_HEAP
auto heap = ESP.getFreeHeap(); auto heap = ESP.getFreeHeap();
#endif #endif
currentThread = this; currentThread = this;
auto newDelay = runOnce(); auto newDelay = runOnce();
#ifdef DEBUG_HEAP #ifdef DEBUG_HEAP
@@ -95,11 +95,11 @@ void OSThread::run()
currentThread = NULL; currentThread = NULL;
} }
int32_t OSThread::disable() int32_t OSThread::disable()
{ {
enabled = false; enabled = false;
setInterval(INT32_MAX); setInterval(INT32_MAX);
return INT32_MAX; return INT32_MAX;
} }
+22 -21
View File
@@ -27,10 +27,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <Arduino.h> #include <Arduino.h>
#ifdef RV3028_RTC #ifdef RV3028_RTC
#include "Melopero_RV3028.h" #include "Melopero_RV3028.h"
#endif #endif
#ifdef PCF8563_RTC #ifdef PCF8563_RTC
#include "pcf8563.h" #include "pcf8563.h"
#endif #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -42,7 +42,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#error APP_VERSION must be set by the build environment #error APP_VERSION must be set by the build environment
#endif #endif
// FIXME: This is still needed by the Bluetooth Stack and needs to be replaced by something better. Remnant of the old versioning system. // FIXME: This is still needed by the Bluetooth Stack and needs to be replaced by something better. Remnant of the old versioning
// system.
#ifndef HW_VERSION #ifndef HW_VERSION
#define HW_VERSION "1.0" #define HW_VERSION "1.0"
#endif #endif
@@ -64,13 +65,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Nop definition for these attributes that are specific to ESP32 // Nop definition for these attributes that are specific to ESP32
#ifndef EXT_RAM_ATTR #ifndef EXT_RAM_ATTR
#define EXT_RAM_ATTR #define EXT_RAM_ATTR
#endif #endif
#ifndef IRAM_ATTR #ifndef IRAM_ATTR
#define IRAM_ATTR #define IRAM_ATTR
#endif #endif
#ifndef RTC_DATA_ATTR #ifndef RTC_DATA_ATTR
#define RTC_DATA_ATTR #define RTC_DATA_ATTR
#endif #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -80,7 +81,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Disable use of the NTP library and related features // Disable use of the NTP library and related features
// #define DISABLE_NTP // #define DISABLE_NTP
// Disable the welcome screen and allow // Disable the welcome screen and allow
//#define DISABLE_WELCOME_UNSET //#define DISABLE_WELCOME_UNSET
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -135,49 +136,49 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* Step #1: offer chance for variant-specific defines */ /* Step #1: offer chance for variant-specific defines */
#include "variant.h" #include "variant.h"
/* Step #2: follow with defines common to the architecture; /* Step #2: follow with defines common to the architecture;
also enable HAS_ option not specifically disabled by variant.h */ also enable HAS_ option not specifically disabled by variant.h */
#include "architecture.h" #include "architecture.h"
/* Step #3: mop up with disabled values for HAS_ options not handled by the above two */ /* Step #3: mop up with disabled values for HAS_ options not handled by the above two */
#ifndef HAS_WIFI #ifndef HAS_WIFI
#define HAS_WIFI 0 #define HAS_WIFI 0
#endif #endif
#ifndef HAS_ETHERNET #ifndef HAS_ETHERNET
#define HAS_ETHERNET 0 #define HAS_ETHERNET 0
#endif #endif
#ifndef HAS_SCREEN #ifndef HAS_SCREEN
#define HAS_SCREEN 0 #define HAS_SCREEN 0
#endif #endif
#ifndef HAS_WIRE #ifndef HAS_WIRE
#define HAS_WIRE 0 #define HAS_WIRE 0
#endif #endif
#ifndef HAS_GPS #ifndef HAS_GPS
#define HAS_GPS 0 #define HAS_GPS 0
#endif #endif
#ifndef HAS_BUTTON #ifndef HAS_BUTTON
#define HAS_BUTTON 0 #define HAS_BUTTON 0
#endif #endif
#ifndef HAS_TELEMETRY #ifndef HAS_TELEMETRY
#define HAS_TELEMETRY 0 #define HAS_TELEMETRY 0
#endif #endif
#ifndef HAS_RADIO #ifndef HAS_RADIO
#define HAS_RADIO 0 #define HAS_RADIO 0
#endif #endif
#ifndef HAS_RTC #ifndef HAS_RTC
#define HAS_RTC 0 #define HAS_RTC 0
#endif #endif
#ifndef HAS_CPU_SHUTDOWN #ifndef HAS_CPU_SHUTDOWN
#define HAS_CPU_SHUTDOWN 0 #define HAS_CPU_SHUTDOWN 0
#endif #endif
#ifndef HAS_BLUETOOTH #ifndef HAS_BLUETOOTH
#define HAS_BLUETOOTH 0 #define HAS_BLUETOOTH 0
#endif #endif
#include "RF95Configuration.h"
#include "DebugConfiguration.h" #include "DebugConfiguration.h"
#include "RF95Configuration.h"
#ifndef HW_VENDOR #ifndef HW_VENDOR
#error HW_VENDOR must be defined #error HW_VENDOR must be defined
#endif #endif
+38 -31
View File
@@ -6,42 +6,49 @@
void d_writeCommand(uint8_t c) void d_writeCommand(uint8_t c)
{ {
SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
if (PIN_EINK_DC >= 0) digitalWrite(PIN_EINK_DC, LOW); if (PIN_EINK_DC >= 0)
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, LOW); digitalWrite(PIN_EINK_DC, LOW);
SPI1.transfer(c); if (PIN_EINK_CS >= 0)
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, HIGH); digitalWrite(PIN_EINK_CS, LOW);
if (PIN_EINK_DC >= 0) digitalWrite(PIN_EINK_DC, HIGH); SPI1.transfer(c);
SPI1.endTransaction(); if (PIN_EINK_CS >= 0)
digitalWrite(PIN_EINK_CS, HIGH);
if (PIN_EINK_DC >= 0)
digitalWrite(PIN_EINK_DC, HIGH);
SPI1.endTransaction();
} }
void d_writeData(uint8_t d) void d_writeData(uint8_t d)
{ {
SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, LOW); if (PIN_EINK_CS >= 0)
SPI1.transfer(d); digitalWrite(PIN_EINK_CS, LOW);
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, HIGH); SPI1.transfer(d);
SPI1.endTransaction(); if (PIN_EINK_CS >= 0)
digitalWrite(PIN_EINK_CS, HIGH);
SPI1.endTransaction();
} }
unsigned long d_waitWhileBusy(uint16_t busy_time) unsigned long d_waitWhileBusy(uint16_t busy_time)
{ {
if (PIN_EINK_BUSY >= 0) if (PIN_EINK_BUSY >= 0) {
{ delay(1); // add some margin to become active
delay(1); // add some margin to become active unsigned long start = micros();
unsigned long start = micros(); while (1) {
while (1) if (digitalRead(PIN_EINK_BUSY) != HIGH)
{ break;
if (digitalRead(PIN_EINK_BUSY) != HIGH) break; delay(1);
delay(1); if (digitalRead(PIN_EINK_BUSY) != HIGH)
if (digitalRead(PIN_EINK_BUSY) != HIGH) break; break;
if (micros() - start > 10000000) break; if (micros() - start > 10000000)
} break;
unsigned long elapsed = micros() - start; }
(void) start; unsigned long elapsed = micros() - start;
return elapsed; (void)start;
} return elapsed;
else return busy_time; } else
return busy_time;
} }
void scanEInkDevice(void) void scanEInkDevice(void)
@@ -51,10 +58,10 @@ void scanEInkDevice(void)
d_writeData(0x83); d_writeData(0x83);
d_writeCommand(0x20); d_writeCommand(0x20);
eink_found = (d_waitWhileBusy(150) > 0) ? true : false; eink_found = (d_waitWhileBusy(150) > 0) ? true : false;
if(eink_found) if (eink_found)
LOG_DEBUG("EInk display found\n"); LOG_DEBUG("EInk display found\n");
else else
LOG_DEBUG("EInk display not found\n"); LOG_DEBUG("EInk display not found\n");
SPI1.end(); SPI1.end();
} }
#endif #endif
+12 -12
View File
@@ -168,13 +168,13 @@ void scanI2Cdevice()
registerValue = getRegisterValue(addr, 0xD0, 1); // GET_ID registerValue = getRegisterValue(addr, 0xD0, 1); // GET_ID
if (registerValue == 0x61) { if (registerValue == 0x61) {
LOG_INFO("BME-680 sensor found at address 0x%x\n", (uint8_t)addr); LOG_INFO("BME-680 sensor found at address 0x%x\n", (uint8_t)addr);
nodeTelemetrySensorsMap[TelemetrySensorType_BME680] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_BME680] = addr;
} else if (registerValue == 0x60) { } else if (registerValue == 0x60) {
LOG_INFO("BME-280 sensor found at address 0x%x\n", (uint8_t)addr); LOG_INFO("BME-280 sensor found at address 0x%x\n", (uint8_t)addr);
nodeTelemetrySensorsMap[TelemetrySensorType_BME280] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_BME280] = addr;
} else { } else {
LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr); LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr);
nodeTelemetrySensorsMap[TelemetrySensorType_BMP280] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_BMP280] = addr;
} }
} }
if (addr == INA_ADDR || addr == INA_ADDR_ALTERNATE) { if (addr == INA_ADDR || addr == INA_ADDR_ALTERNATE) {
@@ -182,41 +182,41 @@ void scanI2Cdevice()
LOG_DEBUG("Register MFG_UID: 0x%x\n", registerValue); LOG_DEBUG("Register MFG_UID: 0x%x\n", registerValue);
if (registerValue == 0x5449) { if (registerValue == 0x5449) {
LOG_INFO("INA260 sensor found at address 0x%x\n", (uint8_t)addr); LOG_INFO("INA260 sensor found at address 0x%x\n", (uint8_t)addr);
nodeTelemetrySensorsMap[TelemetrySensorType_INA260] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260] = addr;
} else { // Assume INA219 if INA260 ID is not found } else { // Assume INA219 if INA260 ID is not found
LOG_INFO("INA219 sensor found at address 0x%x\n", (uint8_t)addr); LOG_INFO("INA219 sensor found at address 0x%x\n", (uint8_t)addr);
nodeTelemetrySensorsMap[TelemetrySensorType_INA219] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219] = addr;
} }
} }
if (addr == MCP9808_ADDR) { if (addr == MCP9808_ADDR) {
nodeTelemetrySensorsMap[TelemetrySensorType_MCP9808] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MCP9808] = addr;
LOG_INFO("MCP9808 sensor found\n"); LOG_INFO("MCP9808 sensor found\n");
} }
if (addr == SHT31_ADDR) { if (addr == SHT31_ADDR) {
LOG_INFO("SHT31 sensor found\n"); LOG_INFO("SHT31 sensor found\n");
nodeTelemetrySensorsMap[TelemetrySensorType_SHT31] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_SHT31] = addr;
} }
if (addr == SHTC3_ADDR) { if (addr == SHTC3_ADDR) {
LOG_INFO("SHTC3 sensor found\n"); LOG_INFO("SHTC3 sensor found\n");
nodeTelemetrySensorsMap[TelemetrySensorType_SHTC3] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_SHTC3] = addr;
} }
if (addr == LPS22HB_ADDR || addr == LPS22HB_ADDR_ALT) { if (addr == LPS22HB_ADDR || addr == LPS22HB_ADDR_ALT) {
LOG_INFO("LPS22HB sensor found\n"); LOG_INFO("LPS22HB sensor found\n");
nodeTelemetrySensorsMap[TelemetrySensorType_LPS22] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_LPS22] = addr;
} }
// High rate sensors, will be processed internally // High rate sensors, will be processed internally
if (addr == QMC6310_ADDR) { if (addr == QMC6310_ADDR) {
LOG_INFO("QMC6310 Highrate 3-Axis magnetic sensor found\n"); LOG_INFO("QMC6310 Highrate 3-Axis magnetic sensor found\n");
nodeTelemetrySensorsMap[TelemetrySensorType_QMC6310] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_QMC6310] = addr;
} }
if (addr == QMI8658_ADDR) { if (addr == QMI8658_ADDR) {
LOG_INFO("QMI8658 Highrate 6-Axis inertial measurement sensor found\n"); LOG_INFO("QMI8658 Highrate 6-Axis inertial measurement sensor found\n");
nodeTelemetrySensorsMap[TelemetrySensorType_QMI8658] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_QMI8658] = addr;
} }
if (addr == QMC5883L_ADDR) { if (addr == QMC5883L_ADDR) {
LOG_INFO("QMC5883L Highrate 3-Axis magnetic sensor found\n"); LOG_INFO("QMC5883L Highrate 3-Axis magnetic sensor found\n");
nodeTelemetrySensorsMap[TelemetrySensorType_QMC5883L] = addr; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_QMC5883L] = addr;
} }
} else if (err == 4) { } else if (err == 4) {
LOG_ERROR("Unknow error at address 0x%x\n", addr); LOG_ERROR("Unknow error at address 0x%x\n", addr);
+1 -1
View File
@@ -8,5 +8,5 @@
#define RECORD_CRITICALERROR(code) recordCriticalError(code, __LINE__, __FILE__) #define RECORD_CRITICALERROR(code) recordCriticalError(code, __LINE__, __FILE__)
/// Record an error that should be reported via analytics /// Record an error that should be reported via analytics
void recordCriticalError(CriticalErrorCode code = CriticalErrorCode_UNSPECIFIED, uint32_t address = 0, void recordCriticalError(meshtastic_CriticalErrorCode code = meshtastic_CriticalErrorCode_UNSPECIFIED, uint32_t address = 0,
const char *filename = NULL); const char *filename = NULL);
+103 -99
View File
@@ -21,45 +21,45 @@ GPS *gps;
/// only init that port once. /// only init that port once.
static bool didSerialInit; static bool didSerialInit;
bool GPS::getACK(uint8_t c, uint8_t i) { bool GPS::getACK(uint8_t c, uint8_t i)
uint8_t b; {
uint8_t ack = 0; uint8_t b;
const uint8_t ackP[2] = {c, i}; uint8_t ack = 0;
uint8_t buf[10] = {0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t ackP[2] = {c, i};
unsigned long startTime = millis(); uint8_t buf[10] = {0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned long startTime = millis();
for (int j = 2; j < 6; j++) { for (int j = 2; j < 6; j++) {
buf[8] += buf[j]; buf[8] += buf[j];
buf[9] += buf[8]; buf[9] += buf[8];
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
buf[6 + j] = ackP[j]; buf[6 + j] = ackP[j];
buf[8] += buf[6 + j]; buf[8] += buf[6 + j];
buf[9] += buf[8]; buf[9] += buf[8];
} }
while (1) { while (1) {
if (ack > 9) { if (ack > 9) {
return true; return true;
}
if (millis() - startTime > 1000) {
return false;
}
if (_serial_gps->available()) {
b = _serial_gps->read();
if (b == buf[ack]) {
ack++;
} else {
ack = 0;
}
}
} }
if (millis() - startTime > 1000) {
return false;
}
if (_serial_gps->available()) {
b = _serial_gps->read();
if (b == buf[ack]) {
ack++;
}
else {
ack = 0;
}
}
}
} }
/** /**
* @brief * @brief
* @note New method, this method can wait for the specified class and message ID, and return the payload * @note New method, this method can wait for the specified class and message ID, and return the payload
* @param *buffer: The message buffer, if there is a response payload message, it will be returned through the buffer parameter * @param *buffer: The message buffer, if there is a response payload message, it will be returned through the buffer parameter
* @param size: size of buffer * @param size: size of buffer
@@ -69,22 +69,22 @@ bool GPS::getACK(uint8_t c, uint8_t i) {
*/ */
int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID) int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID)
{ {
uint16_t ubxFrameCounter = 0; uint16_t ubxFrameCounter = 0;
uint32_t startTime = millis(); uint32_t startTime = millis();
uint16_t needRead; uint16_t needRead;
while (millis() - startTime < 800) { while (millis() - startTime < 800) {
while (_serial_gps->available()) { while (_serial_gps->available()) {
int c = _serial_gps->read(); int c = _serial_gps->read();
switch (ubxFrameCounter) { switch (ubxFrameCounter) {
case 0: case 0:
//ubxFrame 'μ' // ubxFrame 'μ'
if (c == 0xB5) { if (c == 0xB5) {
ubxFrameCounter++; ubxFrameCounter++;
} }
break; break;
case 1: case 1:
//ubxFrame 'b' // ubxFrame 'b'
if (c == 0x62) { if (c == 0x62) {
ubxFrameCounter++; ubxFrameCounter++;
} else { } else {
@@ -92,7 +92,7 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
} }
break; break;
case 2: case 2:
//Class // Class
if (c == requestedClass) { if (c == requestedClass) {
ubxFrameCounter++; ubxFrameCounter++;
} else { } else {
@@ -100,7 +100,7 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
} }
break; break;
case 3: case 3:
//Message ID // Message ID
if (c == requestedID) { if (c == requestedID) {
ubxFrameCounter++; ubxFrameCounter++;
} else { } else {
@@ -108,13 +108,13 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
} }
break; break;
case 4: case 4:
//Payload lenght lsb // Payload lenght lsb
needRead = c; needRead = c;
ubxFrameCounter++; ubxFrameCounter++;
break; break;
case 5: case 5:
//Payload lenght msb // Payload lenght msb
needRead |= (c << 8); needRead |= (c << 8);
ubxFrameCounter++; ubxFrameCounter++;
break; break;
case 6: case 6:
@@ -145,41 +145,41 @@ bool GPS::setupGPS()
didSerialInit = true; didSerialInit = true;
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
// In esp32 framework, setRxBufferSize needs to be initialized before Serial // In esp32 framework, setRxBufferSize needs to be initialized before Serial
_serial_gps->setRxBufferSize(2048); // the default is 256 _serial_gps->setRxBufferSize(2048); // the default is 256
#endif #endif
// if the overrides are not dialled in, set them from the board definitions, if they exist // if the overrides are not dialled in, set them from the board definitions, if they exist
#if defined(GPS_RX_PIN) #if defined(GPS_RX_PIN)
if (!config.position.rx_gpio) if (!config.position.rx_gpio)
config.position.rx_gpio = GPS_RX_PIN; config.position.rx_gpio = GPS_RX_PIN;
#endif #endif
#if defined(GPS_TX_PIN) #if defined(GPS_TX_PIN)
if (!config.position.tx_gpio) if (!config.position.tx_gpio)
config.position.tx_gpio = GPS_TX_PIN; config.position.tx_gpio = GPS_TX_PIN;
#endif #endif
// ESP32 has a special set of parameters vs other arduino ports // ESP32 has a special set of parameters vs other arduino ports
#if defined(ARCH_ESP32) #if defined(ARCH_ESP32)
if(config.position.rx_gpio) if (config.position.rx_gpio)
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, config.position.rx_gpio, config.position.tx_gpio); _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, config.position.rx_gpio, config.position.tx_gpio);
#else #else
_serial_gps->begin(GPS_BAUDRATE); _serial_gps->begin(GPS_BAUDRATE);
#endif #endif
/* /*
* T-Beam-S3-Core will be preset to use gps Probe here, and other boards will not be changed first * T-Beam-S3-Core will be preset to use gps Probe here, and other boards will not be changed first
*/ */
gnssModel = probe(); gnssModel = probe();
if(gnssModel == GNSS_MODEL_MTK){ if (gnssModel == GNSS_MODEL_MTK) {
/* /*
* t-beam-s3-core uses the same L76K GNSS module as t-echo. * t-beam-s3-core uses the same L76K GNSS module as t-echo.
* Unlike t-echo, L76K uses 9600 baud rate for communication by default. * Unlike t-echo, L76K uses 9600 baud rate for communication by default.
* */ * */
// _serial_gps->begin(9600); //The baud rate of 9600 has been initialized at the beginning of setupGPS, this line is the redundant part // _serial_gps->begin(9600); //The baud rate of 9600 has been initialized at the beginning of setupGPS, this line
// delay(250); // is the redundant part delay(250);
// Initialize the L76K Chip, use GPS + GLONASS // Initialize the L76K Chip, use GPS + GLONASS
_serial_gps->write("$PCAS04,5*1C\r\n"); _serial_gps->write("$PCAS04,5*1C\r\n");
@@ -191,10 +191,10 @@ if (!config.position.tx_gpio)
_serial_gps->write("$PCAS11,3*1E\r\n"); _serial_gps->write("$PCAS11,3*1E\r\n");
delay(250); delay(250);
}else if(gnssModel == GNSS_MODEL_UBLOX){ } else if (gnssModel == GNSS_MODEL_UBLOX) {
/* /*
tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after
setting will not output command messages in UART1, resulting in unrecognized module information setting will not output command messages in UART1, resulting in unrecognized module information
// Set the UART port to output NMEA only // Set the UART port to output NMEA only
@@ -204,13 +204,14 @@ if (!config.position.tx_gpio)
if (!getACK(0x06, 0x00)) { if (!getACK(0x06, 0x00)) {
LOG_WARN("Unable to enable NMEA Mode.\n"); LOG_WARN("Unable to enable NMEA Mode.\n");
return true; return true;
} }
*/ */
// ublox-M10S can be compatible with UBLOX traditional protocol, so the following sentence settings are also valid // ublox-M10S can be compatible with UBLOX traditional protocol, so the following sentence settings are also valid
// disable GGL // disable GGL
byte _message_GGL[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x05, 0x3A}; byte _message_GGL[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x05, 0x3A};
_serial_gps->write(_message_GGL, sizeof(_message_GGL)); _serial_gps->write(_message_GGL, sizeof(_message_GGL));
if (!getACK(0x06, 0x01)) { if (!getACK(0x06, 0x01)) {
LOG_WARN("Unable to disable NMEA GGL.\n"); LOG_WARN("Unable to disable NMEA GGL.\n");
@@ -218,7 +219,8 @@ if (!config.position.tx_gpio)
} }
// disable GSA // disable GSA
byte _message_GSA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 0x41}; byte _message_GSA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 0x41};
_serial_gps->write(_message_GSA, sizeof(_message_GSA)); _serial_gps->write(_message_GSA, sizeof(_message_GSA));
if (!getACK(0x06, 0x01)) { if (!getACK(0x06, 0x01)) {
LOG_WARN("Unable to disable NMEA GSA.\n"); LOG_WARN("Unable to disable NMEA GSA.\n");
@@ -226,7 +228,8 @@ if (!config.position.tx_gpio)
} }
// disable GSV // disable GSV
byte _message_GSV[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x07, 0x48}; byte _message_GSV[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x07, 0x48};
_serial_gps->write(_message_GSV, sizeof(_message_GSV)); _serial_gps->write(_message_GSV, sizeof(_message_GSV));
if (!getACK(0x06, 0x01)) { if (!getACK(0x06, 0x01)) {
LOG_WARN("Unable to disable NMEA GSV.\n"); LOG_WARN("Unable to disable NMEA GSV.\n");
@@ -234,7 +237,8 @@ if (!config.position.tx_gpio)
} }
// disable VTG // disable VTG
byte _message_VTG[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x09, 0x56}; byte _message_VTG[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x09, 0x56};
_serial_gps->write(_message_VTG, sizeof(_message_VTG)); _serial_gps->write(_message_VTG, sizeof(_message_VTG));
if (!getACK(0x06, 0x01)) { if (!getACK(0x06, 0x01)) {
LOG_WARN("Unable to disable NMEA VTG.\n"); LOG_WARN("Unable to disable NMEA VTG.\n");
@@ -242,7 +246,8 @@ if (!config.position.tx_gpio)
} }
// enable RMC // enable RMC
byte _message_RMC[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x09, 0x54}; byte _message_RMC[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x09, 0x54};
_serial_gps->write(_message_RMC, sizeof(_message_RMC)); _serial_gps->write(_message_RMC, sizeof(_message_RMC));
if (!getACK(0x06, 0x01)) { if (!getACK(0x06, 0x01)) {
LOG_WARN("Unable to enable NMEA RMC.\n"); LOG_WARN("Unable to enable NMEA RMC.\n");
@@ -250,7 +255,8 @@ if (!config.position.tx_gpio)
} }
// enable GGA // enable GGA
byte _message_GGA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x38}; byte _message_GGA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x38};
_serial_gps->write(_message_GGA, sizeof(_message_GGA)); _serial_gps->write(_message_GGA, sizeof(_message_GGA));
if (!getACK(0x06, 0x01)) { if (!getACK(0x06, 0x01)) {
LOG_WARN("Unable to enable NMEA GGA.\n"); LOG_WARN("Unable to enable NMEA GGA.\n");
@@ -270,9 +276,9 @@ bool GPS::setup()
#endif #endif
#ifdef HAS_PMU #ifdef HAS_PMU
if(config.position.gps_enabled){ if (config.position.gps_enabled) {
setGPSPower(true); setGPSPower(true);
} }
#endif #endif
#ifdef PIN_GPS_RESET #ifdef PIN_GPS_RESET
@@ -289,7 +295,7 @@ if(config.position.gps_enabled){
notifyDeepSleepObserver.observe(&notifyDeepSleep); notifyDeepSleepObserver.observe(&notifyDeepSleep);
notifyGPSSleepObserver.observe(&notifyGPSSleep); notifyGPSSleepObserver.observe(&notifyGPSSleep);
} }
if (config.position.gps_enabled==false) { if (config.position.gps_enabled == false) {
setAwake(false); setAwake(false);
doGPSpowersave(false); doGPSpowersave(false);
} }
@@ -401,7 +407,7 @@ uint32_t GPS::getSleepTime() const
if (t == UINT32_MAX) if (t == UINT32_MAX)
return t; // already maxint return t; // already maxint
return t * 1000; return t * 1000;
} }
@@ -425,9 +431,9 @@ int32_t GPS::runOnce()
// if we have received valid NMEA claim we are connected // if we have received valid NMEA claim we are connected
setConnected(); setConnected();
} else { } else {
if((config.position.gps_enabled == 1) && (gnssModel == GNSS_MODEL_UBLOX)){ if ((config.position.gps_enabled == 1) && (gnssModel == GNSS_MODEL_UBLOX)) {
// reset the GPS on next bootup // reset the GPS on next bootup
if(devicestate.did_gps_reset && (millis() > 60000) && !hasFlow()) { if (devicestate.did_gps_reset && (millis() > 60000) && !hasFlow()) {
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n"); LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
devicestate.did_gps_reset = false; devicestate.did_gps_reset = false;
nodeDB.saveDeviceStateToDisk(); nodeDB.saveDeviceStateToDisk();
@@ -480,7 +486,7 @@ int32_t GPS::runOnce()
if (hasValidLocation) { if (hasValidLocation) {
LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc); LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc);
} }
p = Position_init_default; p = meshtastic_Position_init_default;
hasValidLocation = false; hasValidLocation = false;
} }
@@ -544,10 +550,10 @@ GnssModel_t GPS::probe()
// we use autodetect, only T-BEAM S3 for now... // we use autodetect, only T-BEAM S3 for now...
uint8_t buffer[256]; uint8_t buffer[256];
/* /*
* The GNSS module information variable is temporarily placed inside the function body, * The GNSS module information variable is temporarily placed inside the function body,
* if it needs to be used elsewhere, it can be moved to the outside * if it needs to be used elsewhere, it can be moved to the outside
* */ * */
struct uBloxGnssModelInfo info ; struct uBloxGnssModelInfo info;
memset(&info, 0, sizeof(struct uBloxGnssModelInfo)); memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
@@ -561,10 +567,10 @@ GnssModel_t GPS::probe()
while (millis() < startTimeout) { while (millis() < startTimeout) {
if (_serial_gps->available()) { if (_serial_gps->available()) {
String ver = _serial_gps->readStringUntil('\r'); String ver = _serial_gps->readStringUntil('\r');
// Get module info , If the correct header is returned, // Get module info , If the correct header is returned,
// it can be determined that it is the MTK chip // it can be determined that it is the MTK chip
int index = ver.indexOf("$"); int index = ver.indexOf("$");
if(index != -1){ if (index != -1) {
ver = ver.substring(index); ver = ver.substring(index);
if (ver.startsWith("$GPTXT,01,01,02")) { if (ver.startsWith("$GPTXT,01,01,02")) {
LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n"); LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
@@ -573,7 +579,6 @@ GnssModel_t GPS::probe()
} }
} }
} }
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30}; uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30};
_serial_gps->write(cfg_rate, sizeof(cfg_rate)); _serial_gps->write(cfg_rate, sizeof(cfg_rate));
@@ -581,10 +586,10 @@ GnssModel_t GPS::probe()
if (!getAck(buffer, 256, 0x06, 0x08)) { if (!getAck(buffer, 256, 0x06, 0x08)) {
LOG_WARN("Failed to find UBlox & MTK GNSS Module\n"); LOG_WARN("Failed to find UBlox & MTK GNSS Module\n");
return GNSS_MODEL_UNKONW; return GNSS_MODEL_UNKONW;
} }
// Get Ublox gnss module hardware and software info // Get Ublox gnss module hardware and software info
uint8_t cfg_get_hw[] = {0xB5, 0x62, 0x0A, 0x04, 0x00, 0x00, 0x0E, 0x34}; uint8_t cfg_get_hw[] = {0xB5, 0x62, 0x0A, 0x04, 0x00, 0x00, 0x0E, 0x34};
_serial_gps->write(cfg_get_hw, sizeof(cfg_get_hw)); _serial_gps->write(cfg_get_hw, sizeof(cfg_get_hw));
uint16_t len = getAck(buffer, 256, 0x0A, 0x04); uint16_t len = getAck(buffer, 256, 0x0A, 0x04);
@@ -611,27 +616,27 @@ GnssModel_t GPS::probe()
} }
LOG_DEBUG("Module Info : \n"); LOG_DEBUG("Module Info : \n");
LOG_DEBUG("Soft version: %s\n",info.swVersion); LOG_DEBUG("Soft version: %s\n", info.swVersion);
LOG_DEBUG("Hard version: %s\n",info.hwVersion); LOG_DEBUG("Hard version: %s\n", info.hwVersion);
LOG_DEBUG("Extensions:%d\n",info.extensionNo); LOG_DEBUG("Extensions:%d\n", info.extensionNo);
for (int i = 0; i < info.extensionNo; i++) { for (int i = 0; i < info.extensionNo; i++) {
LOG_DEBUG(" %s\n",info.extension[i]); LOG_DEBUG(" %s\n", info.extension[i]);
} }
memset(buffer,0,sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
//tips: extensionNo field is 0 on some 6M GNSS modules // tips: extensionNo field is 0 on some 6M GNSS modules
for (int i = 0; i < info.extensionNo; ++i) { for (int i = 0; i < info.extensionNo; ++i) {
if (!strncmp(info.extension[i], "OD=", 3)) { if (!strncmp(info.extension[i], "OD=", 3)) {
strncpy((char *)buffer, &(info.extension[i][3]), sizeof(buffer)); strncpy((char *)buffer, &(info.extension[i][3]), sizeof(buffer));
LOG_DEBUG("GetModel:%s\n",(char *)buffer); LOG_DEBUG("GetModel:%s\n", (char *)buffer);
} }
} }
} }
if (strlen((char*)buffer)) { if (strlen((char *)buffer)) {
LOG_INFO("UBlox GNSS init succeeded, using UBlox %s GNSS Module\n" , buffer); LOG_INFO("UBlox GNSS init succeeded, using UBlox %s GNSS Module\n", buffer);
}else{ } else {
LOG_INFO("UBlox GNSS init succeeded, using UBlox GNSS Module\n"); LOG_INFO("UBlox GNSS init succeeded, using UBlox GNSS Module\n");
} }
@@ -662,8 +667,7 @@ GPS *createGps()
new_gps->setup(); new_gps->setup();
return new_gps; return new_gps;
} }
} } else {
else{
GPS *new_gps = new NMEAGPS(); GPS *new_gps = new NMEAGPS();
new_gps->setup(); new_gps->setup();
return new_gps; return new_gps;
+15 -16
View File
@@ -4,19 +4,18 @@
#include "Observer.h" #include "Observer.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
struct uBloxGnssModelInfo {
struct uBloxGnssModelInfo { char swVersion[30];
char swVersion[30]; char hwVersion[10];
char hwVersion[10];
uint8_t extensionNo; uint8_t extensionNo;
char extension[10][30]; char extension[10][30];
} ; };
typedef enum{ typedef enum {
GNSS_MODEL_MTK, GNSS_MODEL_MTK,
GNSS_MODEL_UBLOX, GNSS_MODEL_UBLOX,
GNSS_MODEL_UNKONW, GNSS_MODEL_UNKONW,
}GnssModel_t; } GnssModel_t;
// Generate a string representation of DOP // Generate a string representation of DOP
const char *getDOPString(uint32_t dop); const char *getDOPString(uint32_t dop);
@@ -55,7 +54,7 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */ /** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps; static HardwareSerial *_serial_gps;
Position p = Position_init_default; meshtastic_Position p = meshtastic_Position_init_default;
GPS() : concurrency::OSThread("GPS") {} GPS() : concurrency::OSThread("GPS") {}
@@ -78,7 +77,7 @@ class GPS : private concurrency::OSThread
/// Return true if we are connected to a GPS /// Return true if we are connected to a GPS
bool isConnected() const { return hasGPS; } bool isConnected() const { return hasGPS; }
bool isPowerSaving() const { return !config.position.gps_enabled;} bool isPowerSaving() const { return !config.position.gps_enabled; }
/** /**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP * Restart our lock attempt - try to get and broadcast a GPS reading ASAP
@@ -164,7 +163,7 @@ class GPS : private concurrency::OSThread
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
// Get GNSS model // Get GNSS model
GnssModel_t probe(); GnssModel_t probe();
int getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID); int getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID);
@@ -173,8 +172,8 @@ class GPS : private concurrency::OSThread
GnssModel_t gnssModel = GNSS_MODEL_UNKONW; GnssModel_t gnssModel = GNSS_MODEL_UNKONW;
}; };
// Creates an instance of the GPS class. // Creates an instance of the GPS class.
// Returns the new instance or null if the GPS is not present. // Returns the new instance or null if the GPS is not present.
GPS* createGps(); GPS *createGps();
extern GPS *gps; extern GPS *gps;
+160 -125
View File
@@ -1,21 +1,25 @@
#include "GeoCoord.h" #include "GeoCoord.h"
GeoCoord::GeoCoord() { GeoCoord::GeoCoord()
{
_dirty = true; _dirty = true;
} }
GeoCoord::GeoCoord (int32_t lat, int32_t lon, int32_t alt) : _latitude(lat), _longitude(lon), _altitude(alt) { GeoCoord::GeoCoord(int32_t lat, int32_t lon, int32_t alt) : _latitude(lat), _longitude(lon), _altitude(alt)
{
GeoCoord::setCoords(); GeoCoord::setCoords();
} }
GeoCoord::GeoCoord (float lat, float lon, int32_t alt) : _altitude(alt) { GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt)
{
// Change decimial reprsentation to int32_t. I.e., 12.345 becomes 123450000 // Change decimial reprsentation to int32_t. I.e., 12.345 becomes 123450000
_latitude = int32_t(lat * 1e+7); _latitude = int32_t(lat * 1e+7);
_longitude = int32_t(lon * 1e+7); _longitude = int32_t(lon * 1e+7);
GeoCoord::setCoords(); GeoCoord::setCoords();
} }
GeoCoord::GeoCoord(double lat, double lon, int32_t alt): _altitude(alt) { GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt)
{
// Change decimial reprsentation to int32_t. I.e., 12.345 becomes 123450000 // Change decimial reprsentation to int32_t. I.e., 12.345 becomes 123450000
_latitude = int32_t(lat * 1e+7); _latitude = int32_t(lat * 1e+7);
_longitude = int32_t(lon * 1e+7); _longitude = int32_t(lon * 1e+7);
@@ -23,7 +27,8 @@ GeoCoord::GeoCoord(double lat, double lon, int32_t alt): _altitude(alt) {
} }
// Initialize all the coordinate systems // Initialize all the coordinate systems
void GeoCoord::setCoords() { void GeoCoord::setCoords()
{
double lat = _latitude * 1e-7; double lat = _latitude * 1e-7;
double lon = _longitude * 1e-7; double lon = _longitude * 1e-7;
GeoCoord::latLongToDMS(lat, lon, _dms); GeoCoord::latLongToDMS(lat, lon, _dms);
@@ -34,9 +39,10 @@ void GeoCoord::setCoords() {
_dirty = false; _dirty = false;
} }
void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt) { void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt)
{
// If marked dirty or new coordiantes // If marked dirty or new coordiantes
if(_dirty || _latitude != lat || _longitude != lon || _altitude != alt) { if (_dirty || _latitude != lat || _longitude != lon || _altitude != alt) {
_dirty = true; _dirty = true;
_latitude = lat; _latitude = lat;
_longitude = lon; _longitude = lon;
@@ -45,25 +51,26 @@ void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt) {
} }
} }
void GeoCoord::updateCoords(const double lat, const double lon, const int32_t alt) { void GeoCoord::updateCoords(const double lat, const double lon, const int32_t alt)
{
int32_t iLat = lat * 1e+7; int32_t iLat = lat * 1e+7;
int32_t iLon = lon * 1e+7; int32_t iLon = lon * 1e+7;
// If marked dirty or new coordiantes // If marked dirty or new coordiantes
if(_dirty || _latitude != iLat || _longitude != iLon || _altitude != alt) { if (_dirty || _latitude != iLat || _longitude != iLon || _altitude != alt) {
_dirty = true; _dirty = true;
_latitude = iLat; _latitude = iLat;
_longitude = iLon; _longitude = iLon;
_altitude = alt; _altitude = alt;
setCoords(); setCoords();
} }
} }
void GeoCoord::updateCoords(const float lat, const float lon, const int32_t alt) { void GeoCoord::updateCoords(const float lat, const float lon, const int32_t alt)
{
int32_t iLat = lat * 1e+7; int32_t iLat = lat * 1e+7;
int32_t iLon = lon * 1e+7; int32_t iLon = lon * 1e+7;
// If marked dirty or new coordiantes // If marked dirty or new coordiantes
if(_dirty || _latitude != iLat || _longitude != iLon || _altitude != alt) { if (_dirty || _latitude != iLat || _longitude != iLon || _altitude != alt) {
_dirty = true; _dirty = true;
_latitude = iLat; _latitude = iLat;
_longitude = iLon; _longitude = iLon;
@@ -73,12 +80,15 @@ void GeoCoord::updateCoords(const float lat, const float lon, const int32_t alt)
} }
/** /**
* Converts lat long coordinates from decimal degrees to degrees minutes seconds format. * Converts lat long coordinates from decimal degrees to degrees minutes seconds format.
* DD°MM'SS"C DDD°MM'SS"C * DD°MM'SS"C DDD°MM'SS"C
*/ */
void GeoCoord::latLongToDMS(const double lat, const double lon, DMS &dms) { void GeoCoord::latLongToDMS(const double lat, const double lon, DMS &dms)
if (lat < 0) dms.latCP = 'S'; {
else dms.latCP = 'N'; if (lat < 0)
dms.latCP = 'S';
else
dms.latCP = 'N';
double latDeg = lat; double latDeg = lat;
@@ -90,8 +100,10 @@ void GeoCoord::latLongToDMS(const double lat, const double lon, DMS &dms) {
dms.latMin = floor(latMin); dms.latMin = floor(latMin);
dms.latSec = (latMin - dms.latMin) * 60; dms.latSec = (latMin - dms.latMin) * 60;
if (lon < 0) dms.lonCP = 'W'; if (lon < 0)
else dms.lonCP = 'E'; dms.lonCP = 'W';
else
dms.lonCP = 'E';
double lonDeg = lon; double lonDeg = lon;
@@ -108,52 +120,64 @@ void GeoCoord::latLongToDMS(const double lat, const double lon, DMS &dms) {
* Converts lat long coordinates to UTM. * Converts lat long coordinates to UTM.
* based on this: https://github.com/walvok/LatLonToUTM/blob/master/latlon_utm.ino * based on this: https://github.com/walvok/LatLonToUTM/blob/master/latlon_utm.ino
*/ */
void GeoCoord::latLongToUTM(const double lat, const double lon, UTM &utm) { void GeoCoord::latLongToUTM(const double lat, const double lon, UTM &utm)
{
const std::string latBands = "CDEFGHJKLMNPQRSTUVWXX"; const std::string latBands = "CDEFGHJKLMNPQRSTUVWXX";
utm.zone = int((lon + 180)/6 + 1); utm.zone = int((lon + 180) / 6 + 1);
utm.band = latBands[int(lat/8 + 10)]; utm.band = latBands[int(lat / 8 + 10)];
double a = 6378137; // WGS84 - equatorial radius double a = 6378137; // WGS84 - equatorial radius
double k0 = 0.9996; // UTM point scale on the central meridian double k0 = 0.9996; // UTM point scale on the central meridian
double eccSquared = 0.00669438; // eccentricity squared double eccSquared = 0.00669438; // eccentricity squared
double lonTemp = (lon + 180) - int((lon + 180)/360) * 360 - 180; //Make sure the longitude is between -180.00 .. 179.9 double lonTemp = (lon + 180) - int((lon + 180) / 360) * 360 - 180; // Make sure the longitude is between -180.00 .. 179.9
double latRad = toRadians(lat); double latRad = toRadians(lat);
double lonRad = toRadians(lonTemp); double lonRad = toRadians(lonTemp);
// Special Zones for Norway and Svalbard // Special Zones for Norway and Svalbard
if( lat >= 56.0 && lat < 64.0 && lonTemp >= 3.0 && lonTemp < 12.0 ) // Norway if (lat >= 56.0 && lat < 64.0 && lonTemp >= 3.0 && lonTemp < 12.0) // Norway
utm.zone = 32; utm.zone = 32;
if( lat >= 72.0 && lat < 84.0 ) { // Svalbard if (lat >= 72.0 && lat < 84.0) { // Svalbard
if ( lonTemp >= 0.0 && lonTemp < 9.0 ) utm.zone = 31; if (lonTemp >= 0.0 && lonTemp < 9.0)
else if( lonTemp >= 9.0 && lonTemp < 21.0 ) utm.zone = 33; utm.zone = 31;
else if( lonTemp >= 21.0 && lonTemp < 33.0 ) utm.zone = 35; else if (lonTemp >= 9.0 && lonTemp < 21.0)
else if( lonTemp >= 33.0 && lonTemp < 42.0 ) utm.zone = 37; utm.zone = 33;
else if (lonTemp >= 21.0 && lonTemp < 33.0)
utm.zone = 35;
else if (lonTemp >= 33.0 && lonTemp < 42.0)
utm.zone = 37;
} }
double lonOrigin = (utm.zone - 1)*6 - 180 + 3; // puts origin in middle of zone double lonOrigin = (utm.zone - 1) * 6 - 180 + 3; // puts origin in middle of zone
double lonOriginRad = toRadians(lonOrigin); double lonOriginRad = toRadians(lonOrigin);
double eccPrimeSquared = (eccSquared)/(1 - eccSquared); double eccPrimeSquared = (eccSquared) / (1 - eccSquared);
double N = a/sqrt(1 - eccSquared*sin(latRad)*sin(latRad)); double N = a / sqrt(1 - eccSquared * sin(latRad) * sin(latRad));
double T = tan(latRad)*tan(latRad); double T = tan(latRad) * tan(latRad);
double C = eccPrimeSquared*cos(latRad)*cos(latRad); double C = eccPrimeSquared * cos(latRad) * cos(latRad);
double A = cos(latRad)*(lonRad - lonOriginRad); double A = cos(latRad) * (lonRad - lonOriginRad);
double M = a*((1 - eccSquared/4 - 3*eccSquared*eccSquared/64 - 5*eccSquared*eccSquared*eccSquared/256)*latRad double M =
- (3*eccSquared/8 + 3*eccSquared*eccSquared/32 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*latRad) a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * latRad -
+ (15*eccSquared*eccSquared/256 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*latRad) (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) *
- (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*latRad)); sin(2 * latRad) +
utm.easting = (double)(k0*N*(A+(1-T+C)*pow(A, 3)/6 + (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120) (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(4 * latRad) -
+ 500000.0); (35 * eccSquared * eccSquared * eccSquared / 3072) * sin(6 * latRad));
utm.northing = (double)(k0*(M+N*tan(latRad)*(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24 utm.easting = (double)(k0 * N *
+ (61-58*T+T*T+600*C-330*eccPrimeSquared)*A*A*A*A*A*A/720))); (A + (1 - T + C) * pow(A, 3) / 6 +
(5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120) +
if(lat < 0) 500000.0);
utm.northing += 10000000.0; //10000000 meter offset for southern hemisphere utm.northing =
(double)(k0 * (M + N * tan(latRad) *
(A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
(61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720)));
if (lat < 0)
utm.northing += 10000000.0; // 10000000 meter offset for southern hemisphere
} }
// Converts lat long coordinates to an MGRS. // Converts lat long coordinates to an MGRS.
void GeoCoord::latLongToMGRS(const double lat, const double lon, MGRS &mgrs) { void GeoCoord::latLongToMGRS(const double lat, const double lon, MGRS &mgrs)
const std::string e100kLetters[3] = { "ABCDEFGH", "JKLMNPQR", "STUVWXYZ" }; {
const std::string n100kLetters[2] = { "ABCDEFGHJKLMNPQRSTUV", "FGHJKLMNPQRSTUVABCDE" }; const std::string e100kLetters[3] = {"ABCDEFGH", "JKLMNPQR", "STUVWXYZ"};
const std::string n100kLetters[2] = {"ABCDEFGHJKLMNPQRSTUV", "FGHJKLMNPQRSTUVABCDE"};
UTM utm; UTM utm;
latLongToUTM(lat, lon, utm); latLongToUTM(lat, lon, utm);
mgrs.zone = utm.zone; mgrs.zone = utm.zone;
@@ -161,7 +185,7 @@ void GeoCoord::latLongToMGRS(const double lat, const double lon, MGRS &mgrs) {
double col = floor(utm.easting / 100000); double col = floor(utm.easting / 100000);
mgrs.east100k = e100kLetters[(mgrs.zone - 1) % 3][col - 1]; mgrs.east100k = e100kLetters[(mgrs.zone - 1) % 3][col - 1];
double row = (int32_t)floor(utm.northing / 100000.0) % 20; double row = (int32_t)floor(utm.northing / 100000.0) % 20;
mgrs.north100k = n100kLetters[(mgrs.zone-1)%2][row]; mgrs.north100k = n100kLetters[(mgrs.zone - 1) % 2][row];
mgrs.easting = (int32_t)utm.easting % 100000; mgrs.easting = (int32_t)utm.easting % 100000;
mgrs.northing = (int32_t)utm.northing % 100000; mgrs.northing = (int32_t)utm.northing % 100000;
} }
@@ -170,52 +194,54 @@ void GeoCoord::latLongToMGRS(const double lat, const double lon, MGRS &mgrs) {
* Converts lat long coordinates to Ordnance Survey Grid Reference (UK National Grid Ref). * Converts lat long coordinates to Ordnance Survey Grid Reference (UK National Grid Ref).
* Based on: https://www.movable-type.co.uk/scripts/latlong-os-gridref.html * Based on: https://www.movable-type.co.uk/scripts/latlong-os-gridref.html
*/ */
void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr) { void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr)
{
const char letter[] = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; // No 'I' in OSGR const char letter[] = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; // No 'I' in OSGR
double a = 6377563.396; // Airy 1830 semi-major axis double a = 6377563.396; // Airy 1830 semi-major axis
double b = 6356256.909; // Airy 1830 semi-minor axis double b = 6356256.909; // Airy 1830 semi-minor axis
double f0 = 0.9996012717; // National Grid point scale factor on the central meridian double f0 = 0.9996012717; // National Grid point scale factor on the central meridian
double phi0 = toRadians(49); double phi0 = toRadians(49);
double lambda0 = toRadians(-2); double lambda0 = toRadians(-2);
double n0 = -100000; double n0 = -100000;
double e0 = 400000; double e0 = 400000;
double e2 = 1 - (b*b)/(a*a); // eccentricity squared double e2 = 1 - (b * b) / (a * a); // eccentricity squared
double n = (a - b)/(a + b); double n = (a - b) / (a + b);
double osgb_Latitude; double osgb_Latitude;
double osgb_Longitude; double osgb_Longitude;
convertWGS84ToOSGB36(lat, lon, osgb_Latitude, osgb_Longitude); convertWGS84ToOSGB36(lat, lon, osgb_Latitude, osgb_Longitude);
double phi = osgb_Latitude; // already in radians double phi = osgb_Latitude; // already in radians
double lambda = osgb_Longitude; // already in radians double lambda = osgb_Longitude; // already in radians
double v = a * f0 / sqrt(1 - e2 * sin(phi) * sin(phi)); double v = a * f0 / sqrt(1 - e2 * sin(phi) * sin(phi));
double rho = a * f0 * (1 - e2) / pow(1 - e2 * sin(phi) * sin(phi), 1.5); double rho = a * f0 * (1 - e2) / pow(1 - e2 * sin(phi) * sin(phi), 1.5);
double eta2 = v / rho - 1; double eta2 = v / rho - 1;
double mA = (1 + n + (5/4)*n*n + (5/4)*n*n*n) * (phi - phi0); double mA = (1 + n + (5 / 4) * n * n + (5 / 4) * n * n * n) * (phi - phi0);
double mB = (3*n + 3*n*n + (21/8)*n*n*n) * sin(phi - phi0) * cos(phi + phi0); double mB = (3 * n + 3 * n * n + (21 / 8) * n * n * n) * sin(phi - phi0) * cos(phi + phi0);
// loss of precision in mC & mD due to floating point rounding can cause innaccuracy of northing by a few meters // loss of precision in mC & mD due to floating point rounding can cause innaccuracy of northing by a few meters
double mC = (15/8*n*n + 15/8*n*n*n) * sin(2*(phi - phi0)) * cos(2*(phi + phi0)); double mC = (15 / 8 * n * n + 15 / 8 * n * n * n) * sin(2 * (phi - phi0)) * cos(2 * (phi + phi0));
double mD = (35/24)*n*n*n * sin(3*(phi - phi0)) * cos(3*(phi + phi0)); double mD = (35 / 24) * n * n * n * sin(3 * (phi - phi0)) * cos(3 * (phi + phi0));
double m = b*f0*(mA - mB + mC - mD); double m = b * f0 * (mA - mB + mC - mD);
double cos3Phi = cos(phi)*cos(phi)*cos(phi); double cos3Phi = cos(phi) * cos(phi) * cos(phi);
double cos5Phi = cos3Phi*cos(phi)*cos(phi); double cos5Phi = cos3Phi * cos(phi) * cos(phi);
double tan2Phi = tan(phi)*tan(phi); double tan2Phi = tan(phi) * tan(phi);
double tan4Phi = tan2Phi*tan2Phi; double tan4Phi = tan2Phi * tan2Phi;
double I = m + n0; double I = m + n0;
double II = (v/2)*sin(phi)*cos(phi); double II = (v / 2) * sin(phi) * cos(phi);
double III = (v/24)*sin(phi)*cos3Phi*(5 - tan2Phi + 9*eta2); double III = (v / 24) * sin(phi) * cos3Phi * (5 - tan2Phi + 9 * eta2);
double IIIA = (v/720)*sin(phi)*cos5Phi*(61 - 58*tan2Phi + tan4Phi); double IIIA = (v / 720) * sin(phi) * cos5Phi * (61 - 58 * tan2Phi + tan4Phi);
double IV = v*cos(phi); double IV = v * cos(phi);
double V = (v/6)*cos3Phi*(v/rho - tan2Phi); double V = (v / 6) * cos3Phi * (v / rho - tan2Phi);
double VI = (v/120)*cos5Phi*(5 - 18*tan2Phi + tan4Phi + 14*eta2 - 58*tan2Phi*eta2); double VI = (v / 120) * cos5Phi * (5 - 18 * tan2Phi + tan4Phi + 14 * eta2 - 58 * tan2Phi * eta2);
double deltaLambda = lambda - lambda0; double deltaLambda = lambda - lambda0;
double deltaLambda2 = deltaLambda*deltaLambda; double deltaLambda2 = deltaLambda * deltaLambda;
double northing = I + II*deltaLambda2 + III*deltaLambda2*deltaLambda2 + IIIA*deltaLambda2*deltaLambda2*deltaLambda2; double northing =
double easting = e0 + IV*deltaLambda + V*deltaLambda2*deltaLambda + VI*deltaLambda2*deltaLambda2*deltaLambda; I + II * deltaLambda2 + III * deltaLambda2 * deltaLambda2 + IIIA * deltaLambda2 * deltaLambda2 * deltaLambda2;
double easting = e0 + IV * deltaLambda + V * deltaLambda2 * deltaLambda + VI * deltaLambda2 * deltaLambda2 * deltaLambda;
if (easting < 0 || easting > 700000 || northing < 0 || northing > 1300000) // Check if out of boundaries if (easting < 0 || easting > 700000 || northing < 0 || northing > 1300000) // Check if out of boundaries
osgr = { 'I', 'I', 0, 0 }; osgr = {'I', 'I', 0, 0};
else { else {
uint32_t e100k = floor(easting / 100000); uint32_t e100k = floor(easting / 100000);
uint32_t n100k = floor(northing / 100000); uint32_t n100k = floor(northing / 100000);
@@ -232,7 +258,8 @@ void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr) {
* Converts lat long coordinates to Open Location Code. * Converts lat long coordinates to Open Location Code.
* Based on: https://github.com/google/open-location-code/blob/main/c/src/olc.c * Based on: https://github.com/google/open-location-code/blob/main/c/src/olc.c
*/ */
void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc) { void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc)
{
char tempCode[] = "1234567890abc"; char tempCode[] = "1234567890abc";
const char kAlphabet[] = "23456789CFGHJMPQRVWX"; const char kAlphabet[] = "23456789CFGHJMPQRVWX";
double latitude; double latitude;
@@ -258,7 +285,7 @@ void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc) {
lat_val += latitude * 2.5e7; lat_val += latitude * 2.5e7;
lng_val += longitude * 8.192e6; lng_val += longitude * 8.192e6;
size_t pos = OLC_CODE_LEN; size_t pos = OLC_CODE_LEN;
if (OLC_CODE_LEN > 10) { // Compute grid part of code if needed if (OLC_CODE_LEN > 10) { // Compute grid part of code if needed
for (size_t i = 0; i < 5; i++) { for (size_t i = 0; i < 5; i++) {
int lat_digit = lat_val % 5; int lat_digit = lat_val % 5;
@@ -272,9 +299,9 @@ void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc) {
lat_val /= pow(5, 5); lat_val /= pow(5, 5);
lng_val /= pow(4, 5); lng_val /= pow(4, 5);
} }
pos = 10; pos = 10;
for (size_t i = 0; i < 5; i++) { // Compute pair section of code for (size_t i = 0; i < 5; i++) { // Compute pair section of code
int lat_ndx = lat_val % 20; int lat_ndx = lat_val % 20;
int lng_ndx = lng_val % 20; int lng_ndx = lng_val % 20;
@@ -286,7 +313,7 @@ void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc) {
if (i == 0) if (i == 0)
tempCode[pos--] = '+'; tempCode[pos--] = '+';
} }
if (OLC_CODE_LEN < 9) { // Add padding if needed if (OLC_CODE_LEN < 9) { // Add padding if needed
for (size_t i = OLC_CODE_LEN; i < 9; i++) for (size_t i = OLC_CODE_LEN; i < 9; i++)
tempCode[i] = '0'; tempCode[i] = '0';
@@ -300,50 +327,52 @@ void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc) {
for (size_t i = 0; i < char_count; i++) { for (size_t i = 0; i < char_count; i++) {
olc.code[i] = tempCode[i]; olc.code[i] = tempCode[i];
} }
olc.code[char_count] = '\0'; olc.code[char_count] = '\0';
} }
// Converts the coordinate in WGS84 datum to the OSGB36 datum. // Converts the coordinate in WGS84 datum to the OSGB36 datum.
void GeoCoord::convertWGS84ToOSGB36(const double lat, const double lon, double &osgb_Latitude, double &osgb_Longitude) { void GeoCoord::convertWGS84ToOSGB36(const double lat, const double lon, double &osgb_Latitude, double &osgb_Longitude)
{
// Convert lat long to cartesian // Convert lat long to cartesian
double phi = toRadians(lat); double phi = toRadians(lat);
double lambda = toRadians(lon); double lambda = toRadians(lon);
double h = 0.0; // No OSTN height data used, some loss of accuracy (up to 5m) double h = 0.0; // No OSTN height data used, some loss of accuracy (up to 5m)
double wgsA = 6378137; // WGS84 datum semi major axis double wgsA = 6378137; // WGS84 datum semi major axis
double wgsF = 1 / 298.257223563; // WGS84 datum flattening double wgsF = 1 / 298.257223563; // WGS84 datum flattening
double ecc = 2*wgsF - wgsF*wgsF; double ecc = 2 * wgsF - wgsF * wgsF;
double vee = wgsA / sqrt(1 - ecc * pow(sin(phi), 2)); double vee = wgsA / sqrt(1 - ecc * pow(sin(phi), 2));
double wgsX = (vee + h) * cos(phi) * cos(lambda); double wgsX = (vee + h) * cos(phi) * cos(lambda);
double wgsY = (vee + h) * cos(phi) * sin(lambda); double wgsY = (vee + h) * cos(phi) * sin(lambda);
double wgsZ = ((1 - ecc) * vee + h) * sin(phi); double wgsZ = ((1 - ecc) * vee + h) * sin(phi);
// 7-parameter Helmert transform // 7-parameter Helmert transform
double tx = -446.448; // x shift in meters double tx = -446.448; // x shift in meters
double ty = 125.157; // y shift in meters double ty = 125.157; // y shift in meters
double tz = -542.060; // z shift in meters double tz = -542.060; // z shift in meters
double s = 20.4894/1e6 + 1; // scale normalized parts per million to (s + 1) double s = 20.4894 / 1e6 + 1; // scale normalized parts per million to (s + 1)
double rx = toRadians(-0.1502/3600); // x rotation normalize arcseconds to radians double rx = toRadians(-0.1502 / 3600); // x rotation normalize arcseconds to radians
double ry = toRadians(-0.2470/3600); // y rotation normalize arcseconds to radians double ry = toRadians(-0.2470 / 3600); // y rotation normalize arcseconds to radians
double rz = toRadians(-0.8421/3600); // z rotation normalize arcseconds to radians double rz = toRadians(-0.8421 / 3600); // z rotation normalize arcseconds to radians
double osgbX = tx + wgsX*s - wgsY*rz + wgsZ*ry; double osgbX = tx + wgsX * s - wgsY * rz + wgsZ * ry;
double osgbY = ty + wgsX*rz + wgsY*s - wgsZ*rx; double osgbY = ty + wgsX * rz + wgsY * s - wgsZ * rx;
double osgbZ = tz - wgsX*ry + wgsY*rx + wgsZ*s; double osgbZ = tz - wgsX * ry + wgsY * rx + wgsZ * s;
// Convert cartesian to lat long // Convert cartesian to lat long
double airyA = 6377563.396; // Airy1830 datum semi major axis double airyA = 6377563.396; // Airy1830 datum semi major axis
double airyB = 6356256.909; // Airy1830 datum semi minor axis double airyB = 6356256.909; // Airy1830 datum semi minor axis
double airyF = 1/ 299.3249646; // Airy1830 datum flattening double airyF = 1 / 299.3249646; // Airy1830 datum flattening
double airyEcc = 2*airyF - airyF*airyF; double airyEcc = 2 * airyF - airyF * airyF;
double airyEcc2 = airyEcc / (1 - airyEcc); double airyEcc2 = airyEcc / (1 - airyEcc);
double p = sqrt(osgbX*osgbX + osgbY*osgbY); double p = sqrt(osgbX * osgbX + osgbY * osgbY);
double R = sqrt(p*p + osgbZ*osgbZ); double R = sqrt(p * p + osgbZ * osgbZ);
double tanBeta = (airyB*osgbZ) / (airyA*p) * (1 + airyEcc2*airyB/R); double tanBeta = (airyB * osgbZ) / (airyA * p) * (1 + airyEcc2 * airyB / R);
double sinBeta = tanBeta / sqrt(1 + tanBeta*tanBeta); double sinBeta = tanBeta / sqrt(1 + tanBeta * tanBeta);
double cosBeta = sinBeta / tanBeta; double cosBeta = sinBeta / tanBeta;
osgb_Latitude = atan2(osgbZ + airyEcc2*airyB*sinBeta*sinBeta*sinBeta, p - airyEcc*airyA*cosBeta*cosBeta*cosBeta); // leave in radians osgb_Latitude = atan2(osgbZ + airyEcc2 * airyB * sinBeta * sinBeta * sinBeta,
osgb_Longitude = atan2(osgbY, osgbX); // leave in radians p - airyEcc * airyA * cosBeta * cosBeta * cosBeta); // leave in radians
//osgb height = p*cos(osgb.latitude) + osgbZ*sin(osgb.latitude) - osgb_Longitude = atan2(osgbY, osgbX); // leave in radians
//(airyA*airyA/(airyA / sqrt(1 - airyEcc*sin(osgb.latitude)*sin(osgb.latitude)))); // Not used, no OSTN data // osgb height = p*cos(osgb.latitude) + osgbZ*sin(osgb.latitude) -
//(airyA*airyA/(airyA / sqrt(1 - airyEcc*sin(osgb.latitude)*sin(osgb.latitude)))); // Not used, no OSTN data
} }
/// 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
@@ -397,12 +426,13 @@ float GeoCoord::bearing(double lat1, double lon1, double lat2, double lon2)
* @brief Convert from meters to range in radians on a great circle * @brief Convert from meters to range in radians on a great circle
* @param range_meters * @param range_meters
* The range in meters * The range in meters
* @return range in radians on a great circle * @return range in radians on a great circle
*/ */
float GeoCoord::rangeMetersToRadians(double range_meters) { float GeoCoord::rangeMetersToRadians(double range_meters)
{
// 1 nm is 1852 meters // 1 nm is 1852 meters
double distance_nm = range_meters * 1852; double distance_nm = range_meters * 1852;
return (PI / (180 * 60)) *distance_nm; return (PI / (180 * 60)) * distance_nm;
} }
/** /**
@@ -410,22 +440,27 @@ float GeoCoord::rangeMetersToRadians(double range_meters) {
* @brief Convert from radians to range in meters on a great circle * @brief Convert from radians to range in meters on a great circle
* @param range_radians * @param range_radians
* The range in radians * The range in radians
* @return Range in meters on a great circle * @return Range in meters on a great circle
*/ */
float GeoCoord::rangeRadiansToMeters(double range_radians) { float GeoCoord::rangeRadiansToMeters(double range_radians)
{
double distance_nm = ((180 * 60) / PI) * range_radians; double distance_nm = ((180 * 60) / PI) * range_radians;
// 1 meter is 0.000539957 nm // 1 meter is 0.000539957 nm
return distance_nm * 0.000539957; return distance_nm * 0.000539957;
} }
// Find distance from point to passed in point // Find distance from point to passed in point
int32_t GeoCoord::distanceTo(const GeoCoord& pointB) { int32_t GeoCoord::distanceTo(const GeoCoord &pointB)
return latLongToMeter(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7, pointB.getLongitude() * 1e-7); {
return latLongToMeter(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7,
pointB.getLongitude() * 1e-7);
} }
// Find bearing from point to passed in point // Find bearing from point to passed in point
int32_t GeoCoord::bearingTo(const GeoCoord& pointB) { int32_t GeoCoord::bearingTo(const GeoCoord &pointB)
return bearing(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7, pointB.getLongitude() * 1e-7); {
return bearing(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7,
pointB.getLongitude() * 1e-7);
} }
/** /**
@@ -436,8 +471,9 @@ int32_t GeoCoord::bearingTo(const GeoCoord& pointB) {
* @param range_meters * @param range_meters
* range in meters * range in meters
* @return GeoCoord object of point at bearing and range from initial point * @return GeoCoord object of point at bearing and range from initial point
*/ */
std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range_meters) { std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range_meters)
{
double range_radians = rangeMetersToRadians(range_meters); double range_radians = rangeMetersToRadians(range_meters);
double lat1 = this->getLatitude() * 1e-7; double lat1 = this->getLatitude() * 1e-7;
double lon1 = this->getLongitude() * 1e-7; double lon1 = this->getLongitude() * 1e-7;
@@ -446,5 +482,4 @@ std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range
double lon = fmod(lon1 - dlon + PI, 2 * PI) - PI; double lon = fmod(lon1 - dlon + PI, 2 * PI) - PI;
return std::make_shared<GeoCoord>(double(lat), double(lon), this->getAltitude()); return std::make_shared<GeoCoord>(double(lat), double(lon), this->getAltitude());
} }
+83 -85
View File
@@ -1,26 +1,27 @@
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <string>
#include <cstring>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <math.h> #include <math.h>
#include <stdint.h>
#include <stdexcept>
#include <memory> #include <memory>
#include <stdexcept>
#include <stdint.h>
#include <string>
#define PI 3.1415926535897932384626433832795 #define PI 3.1415926535897932384626433832795
#define OLC_CODE_LEN 11 #define OLC_CODE_LEN 11
// Helper functions // Helper functions
// Raises a number to an exponent, handling negative exponents. // Raises a number to an exponent, handling negative exponents.
static inline double pow_neg(double base, double exponent) { static inline double pow_neg(double base, double exponent)
if (exponent == 0) { {
return 1; if (exponent == 0) {
} else if (exponent > 0) { return 1;
return pow(base, exponent); } else if (exponent > 0) {
} return pow(base, exponent);
return 1 / pow(base, -exponent); }
return 1 / pow(base, -exponent);
} }
static inline double toRadians(double deg) static inline double toRadians(double deg)
@@ -35,8 +36,7 @@ static inline double toDegrees(double r)
// GeoCoord structs/classes // GeoCoord structs/classes
// A struct to hold the data for a DMS coordinate. // A struct to hold the data for a DMS coordinate.
struct DMS struct DMS {
{
uint8_t latDeg; uint8_t latDeg;
uint8_t latMin; uint8_t latMin;
uint32_t latSec; uint32_t latSec;
@@ -48,8 +48,7 @@ struct DMS
}; };
// A struct to hold the data for a UTM coordinate, this is also used when creating an MGRS coordinate. // A struct to hold the data for a UTM coordinate, this is also used when creating an MGRS coordinate.
struct UTM struct UTM {
{
uint8_t zone; uint8_t zone;
char band; char band;
uint32_t easting; uint32_t easting;
@@ -57,8 +56,7 @@ struct UTM
}; };
// A struct to hold the data for a MGRS coordinate. // A struct to hold the data for a MGRS coordinate.
struct MGRS struct MGRS {
{
uint8_t zone; uint8_t zone;
char band; char band;
char east100k; char east100k;
@@ -80,85 +78,85 @@ struct OLC {
char code[OLC_CODE_LEN + 1]; // +1 for null termination char code[OLC_CODE_LEN + 1]; // +1 for null termination
}; };
class GeoCoord { class GeoCoord
private: {
int32_t _latitude = 0; private:
int32_t _longitude = 0; int32_t _latitude = 0;
int32_t _altitude = 0; int32_t _longitude = 0;
int32_t _altitude = 0;
DMS _dms = {}; DMS _dms = {};
UTM _utm = {}; UTM _utm = {};
MGRS _mgrs = {}; MGRS _mgrs = {};
OSGR _osgr = {}; OSGR _osgr = {};
OLC _olc = {}; OLC _olc = {};
bool _dirty = true; bool _dirty = true;
void setCoords(); void setCoords();
public: public:
GeoCoord(); GeoCoord();
GeoCoord(int32_t lat, int32_t lon, int32_t alt); GeoCoord(int32_t lat, int32_t lon, int32_t alt);
GeoCoord(double lat, double lon, int32_t alt); GeoCoord(double lat, double lon, int32_t alt);
GeoCoord(float lat, float lon, int32_t alt); GeoCoord(float lat, float lon, int32_t alt);
void updateCoords(const int32_t lat, const int32_t lon, const int32_t alt); void updateCoords(const int32_t lat, const int32_t lon, const int32_t alt);
void updateCoords(const double lat, const double lon, const int32_t alt); void updateCoords(const double lat, const double lon, const int32_t alt);
void updateCoords(const float lat, const float lon, const int32_t alt); void updateCoords(const float lat, const float lon, const int32_t alt);
// Conversions // Conversions
static void latLongToDMS(const double lat, const double lon, DMS &dms); static void latLongToDMS(const double lat, const double lon, DMS &dms);
static void latLongToUTM(const double lat, const double lon, UTM &utm); static void latLongToUTM(const double lat, const double lon, UTM &utm);
static void latLongToMGRS(const double lat, const double lon, MGRS &mgrs); static void latLongToMGRS(const double lat, const double lon, MGRS &mgrs);
static void latLongToOSGR(const double lat, const double lon, OSGR &osgr); static void latLongToOSGR(const double lat, const double lon, OSGR &osgr);
static void latLongToOLC(const double lat, const double lon, OLC &olc); static void latLongToOLC(const double lat, const double lon, OLC &olc);
static void convertWGS84ToOSGB36(const double lat, const double lon, double &osgb_Latitude, double &osgb_Longitude); static void convertWGS84ToOSGB36(const double lat, const double lon, double &osgb_Latitude, double &osgb_Longitude);
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);
static float bearing(double lat1, double lon1, double lat2, double lon2); static float bearing(double lat1, double lon1, double lat2, double lon2);
static float rangeRadiansToMeters(double range_radians); static float rangeRadiansToMeters(double range_radians);
static float rangeMetersToRadians(double range_meters); static float rangeMetersToRadians(double range_meters);
// Point to point conversions // Point to point conversions
int32_t distanceTo(const GeoCoord& pointB); int32_t distanceTo(const GeoCoord &pointB);
int32_t bearingTo(const GeoCoord& pointB); int32_t bearingTo(const GeoCoord &pointB);
std::shared_ptr<GeoCoord> pointAtDistance(double bearing, double range); std::shared_ptr<GeoCoord> pointAtDistance(double bearing, double range);
// Lat lon alt getters // Lat lon alt getters
int32_t getLatitude() const { return _latitude; } int32_t getLatitude() const { return _latitude; }
int32_t getLongitude() const { return _longitude; } int32_t getLongitude() const { return _longitude; }
int32_t getAltitude() const { return _altitude; } int32_t getAltitude() const { return _altitude; }
// DMS getters // DMS getters
uint8_t getDMSLatDeg() const { return _dms.latDeg; } uint8_t getDMSLatDeg() const { return _dms.latDeg; }
uint8_t getDMSLatMin() const { return _dms.latMin; } uint8_t getDMSLatMin() const { return _dms.latMin; }
uint32_t getDMSLatSec() const { return _dms.latSec; } uint32_t getDMSLatSec() const { return _dms.latSec; }
char getDMSLatCP() const { return _dms.latCP; } char getDMSLatCP() const { return _dms.latCP; }
uint8_t getDMSLonDeg() const { return _dms.lonDeg; } uint8_t getDMSLonDeg() const { return _dms.lonDeg; }
uint8_t getDMSLonMin() const { return _dms.lonMin; } uint8_t getDMSLonMin() const { return _dms.lonMin; }
uint32_t getDMSLonSec() const { return _dms.lonSec; } uint32_t getDMSLonSec() const { return _dms.lonSec; }
char getDMSLonCP() const { return _dms.lonCP; } char getDMSLonCP() const { return _dms.lonCP; }
// UTM getters // UTM getters
uint8_t getUTMZone() const { return _utm.zone; } uint8_t getUTMZone() const { return _utm.zone; }
char getUTMBand() const { return _utm.band; } char getUTMBand() const { return _utm.band; }
uint32_t getUTMEasting() const { return _utm.easting; } uint32_t getUTMEasting() const { return _utm.easting; }
uint32_t getUTMNorthing() const { return _utm.northing; } uint32_t getUTMNorthing() const { return _utm.northing; }
// MGRS getters // MGRS getters
uint8_t getMGRSZone() const { return _mgrs.zone; } uint8_t getMGRSZone() const { return _mgrs.zone; }
char getMGRSBand() const { return _mgrs.band; } char getMGRSBand() const { return _mgrs.band; }
char getMGRSEast100k() const { return _mgrs.east100k; } char getMGRSEast100k() const { return _mgrs.east100k; }
char getMGRSNorth100k() const { return _mgrs.north100k; } char getMGRSNorth100k() const { return _mgrs.north100k; }
uint32_t getMGRSEasting() const { return _mgrs.easting; } uint32_t getMGRSEasting() const { return _mgrs.easting; }
uint32_t getMGRSNorthing() const { return _mgrs.northing; } uint32_t getMGRSNorthing() const { return _mgrs.northing; }
// OSGR getters // OSGR getters
char getOSGRE100k() const { return _osgr.e100k; } char getOSGRE100k() const { return _osgr.e100k; }
char getOSGRN100k() const { return _osgr.n100k; } char getOSGRN100k() const { return _osgr.n100k; }
uint32_t getOSGREasting() const { return _osgr.easting; } uint32_t getOSGREasting() const { return _osgr.easting; }
uint32_t getOSGRNorthing() const { return _osgr.northing; } uint32_t getOSGRNorthing() const { return _osgr.northing; }
// OLC getter // OLC getter
void getOLCCode(char* code) { strncpy(code, _olc.code, OLC_CODE_LEN + 1); } // +1 for null termination void getOLCCode(char *code) { strncpy(code, _olc.code, OLC_CODE_LEN + 1); } // +1 for null termination
}; };
+35 -39
View File
@@ -1,12 +1,12 @@
#include "configuration.h"
#include "NMEAGPS.h" #include "NMEAGPS.h"
#include "RTC.h" #include "RTC.h"
#include "configuration.h"
#include <TinyGPS++.h> #include <TinyGPS++.h>
// GPS solutions older than this will be rejected - see TinyGPSDatum::age() // GPS solutions older than this will be rejected - see TinyGPSDatum::age()
#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway #define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
static int32_t toDegInt(RawDegrees d) static int32_t toDegInt(RawDegrees d)
{ {
@@ -20,19 +20,18 @@ static int32_t toDegInt(RawDegrees d)
bool NMEAGPS::factoryReset() bool NMEAGPS::factoryReset()
{ {
#ifdef PIN_GPS_REINIT #ifdef PIN_GPS_REINIT
//The L76K GNSS on the T-Echo requires the RESET pin to be pulled LOW // The L76K GNSS on the T-Echo requires the RESET pin to be pulled LOW
digitalWrite(PIN_GPS_REINIT, 0); digitalWrite(PIN_GPS_REINIT, 0);
pinMode(PIN_GPS_REINIT, OUTPUT); pinMode(PIN_GPS_REINIT, OUTPUT);
delay(150); //The L76K datasheet calls for at least 100MS delay delay(150); // The L76K datasheet calls for at least 100MS delay
digitalWrite(PIN_GPS_REINIT, 1); digitalWrite(PIN_GPS_REINIT, 1);
#endif #endif
// send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX. // send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX.
// Factory Reset // Factory Reset
byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00,
0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E};
0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E}; _serial_gps->write(_message_reset, sizeof(_message_reset));
_serial_gps->write(_message_reset,sizeof(_message_reset));
delay(1000); delay(1000);
return true; return true;
} }
@@ -40,7 +39,7 @@ bool NMEAGPS::factoryReset()
bool NMEAGPS::setupGPS() bool NMEAGPS::setupGPS()
{ {
GPS::setupGPS(); GPS::setupGPS();
#ifdef PIN_GPS_PPS #ifdef PIN_GPS_PPS
// pulse per second // pulse per second
// FIXME - move into shared GPS code // FIXME - move into shared GPS code
@@ -84,8 +83,9 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
t.tm_mon = d.month() - 1; t.tm_mon = d.month() - 1;
t.tm_year = d.year() - 1900; t.tm_year = d.year() - 1900;
t.tm_isdst = false; t.tm_isdst = false;
if (t.tm_mon > -1){ if (t.tm_mon > -1) {
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
t.tm_sec);
perhapsSetRTC(RTCQualityGPS, t); perhapsSetRTC(RTCQualityGPS, t);
return true; return true;
} else } else
@@ -102,47 +102,44 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
*/ */
bool NMEAGPS::lookForLocation() bool NMEAGPS::lookForLocation()
{ {
// By default, TinyGPS++ does not parse GPGSA lines, which give us // By default, TinyGPS++ does not parse GPGSA lines, which give us
// the 2D/3D fixType (see NMEAGPS.h) // the 2D/3D fixType (see NMEAGPS.h)
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?) // At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
fixQual = reader.fixQuality(); fixQual = reader.fixQuality();
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
fixType = atoi(gsafixtype.value()); // will set to zero if no data fixType = atoi(gsafixtype.value()); // will set to zero if no data
// LOG_DEBUG("FIX QUAL=%d, TYPE=%d\n", fixQual, fixType); // LOG_DEBUG("FIX QUAL=%d, TYPE=%d\n", fixQual, fixType);
#endif #endif
// check if GPS has an acceptable lock // check if GPS has an acceptable lock
if (! hasLock()) if (!hasLock())
return false; return false;
#ifdef GPS_EXTRAVERBOSE #ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", reader.location.age(),
reader.location.age(),
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
gsafixtype.age(), gsafixtype.age(),
#else #else
0, 0,
#endif #endif
reader.date.age(), reader.time.age()); reader.date.age(), reader.time.age());
#endif // GPS_EXTRAVERBOSE #endif // GPS_EXTRAVERBOSE
// check if a complete GPS solution set is available for reading // check if a complete GPS solution set is available for reading
// tinyGPSDatum::age() also includes isValid() test // tinyGPSDatum::age() also includes isValid() test
// FIXME // FIXME
if (! ((reader.location.age() < GPS_SOL_EXPIRY_MS) && if (!((reader.location.age() < GPS_SOL_EXPIRY_MS) &&
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
(gsafixtype.age() < GPS_SOL_EXPIRY_MS) && (gsafixtype.age() < GPS_SOL_EXPIRY_MS) &&
#endif #endif
(reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.date.age() < GPS_SOL_EXPIRY_MS))) {
(reader.date.age() < GPS_SOL_EXPIRY_MS)))
{
LOG_WARN("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age()); LOG_WARN("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age());
return false; return false;
} }
// Is this a new point or are we re-reading the previous one? // Is this a new point or are we re-reading the previous one?
if (! reader.location.isUpdated()) if (!reader.location.isUpdated())
return false; return false;
// We know the solution is fresh and valid, so just read the data // We know the solution is fresh and valid, so just read the data
@@ -150,19 +147,19 @@ bool NMEAGPS::lookForLocation()
// Bail out EARLY to avoid overwriting previous good data (like #857) // Bail out EARLY to avoid overwriting previous good data (like #857)
if (toDegInt(loc.lat) > 900000000) { if (toDegInt(loc.lat) > 900000000) {
#ifdef GPS_EXTRAVERBOSE #ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("Bail out EARLY on LAT %i\n",toDegInt(loc.lat)); LOG_DEBUG("Bail out EARLY on LAT %i\n", toDegInt(loc.lat));
#endif #endif
return false; return false;
} }
if (toDegInt(loc.lng) > 1800000000) { if (toDegInt(loc.lng) > 1800000000) {
#ifdef GPS_EXTRAVERBOSE #ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("Bail out EARLY on LNG %i\n",toDegInt(loc.lng)); LOG_DEBUG("Bail out EARLY on LNG %i\n", toDegInt(loc.lng));
#endif #endif
return false; return false;
} }
p.location_source = Position_LocSource_LOC_INTERNAL; p.location_source = meshtastic_Position_LocSource_LOC_INTERNAL;
// Dilution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it // Dilution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
@@ -209,11 +206,11 @@ bool NMEAGPS::lookForLocation()
} }
if (reader.course.isUpdated() && reader.course.isValid()) { if (reader.course.isUpdated() && reader.course.isValid()) {
if (reader.course.value() < 36000) { // sanity check if (reader.course.value() < 36000) { // sanity check
p.ground_track = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5 p.ground_track =
reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
} else { } else {
LOG_WARN("BOGUS course.value() REJECTED: %d\n", LOG_WARN("BOGUS course.value() REJECTED: %d\n", reader.course.value());
reader.course.value());
} }
} }
@@ -224,14 +221,13 @@ bool NMEAGPS::lookForLocation()
return true; return true;
} }
bool NMEAGPS::hasLock() bool NMEAGPS::hasLock()
{ {
// Using GPGGA fix quality indicator // Using GPGGA fix quality indicator
if (fixQual >= 1 && fixQual <= 5) { if (fixQual >= 1 && fixQual <= 5) {
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
// Use GPGSA fix type 2D/3D (better) if available // Use GPGSA fix type 2D/3D (better) if available
if (fixType == 3 || fixType == 0) // zero means "no data received" if (fixType == 3 || fixType == 0) // zero means "no data received"
#endif #endif
return true; return true;
} }
+6 -6
View File
@@ -12,14 +12,14 @@
class NMEAGPS : public GPS class NMEAGPS : public GPS
{ {
TinyGPSPlus reader; TinyGPSPlus reader;
uint8_t fixQual = 0; // fix quality from GPGGA uint8_t fixQual = 0; // fix quality from GPGGA
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field // (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
// via optional feature "custom fields", currently disabled (bug #525) // via optional feature "custom fields", currently disabled (bug #525)
TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA
TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA
uint8_t fixType = 0; // fix type from GPGSA uint8_t fixType = 0; // fix type from GPGSA
#endif #endif
public: public:
@@ -29,9 +29,9 @@ class NMEAGPS : public GPS
protected: protected:
/** Subclasses should look for serial rx characters here and feed it to their GPS parser /** Subclasses should look for serial rx characters here and feed it to their GPS parser
* *
* Return true if we received a valid message from the GPS * Return true if we received a valid message from the GPS
*/ */
virtual bool whileIdle() override; virtual bool whileIdle() override;
/** /**
+17 -35
View File
@@ -16,17 +16,13 @@
* ------------------------------------------- * -------------------------------------------
*/ */
uint32_t printWPL(char *buf, size_t bufsz, const Position &pos, const char *name) uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name)
{ {
GeoCoord geoCoord(pos.latitude_i,pos.longitude_i,pos.altitude); GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
uint32_t len = snprintf(buf, bufsz, "$GNWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", uint32_t len = snprintf(buf, bufsz, "$GNWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", geoCoord.getDMSLatDeg(),
geoCoord.getDMSLatDeg(), (abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(),
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonDeg(), (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6,
geoCoord.getDMSLatCP(), geoCoord.getDMSLonCP(), name);
geoCoord.getDMSLonDeg(),
(abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6,
geoCoord.getDMSLonCP(),
name);
uint32_t chk = 0; uint32_t chk = 0;
for (uint32_t i = 1; i < len; i++) { for (uint32_t i = 1; i < len; i++) {
chk ^= buf[i]; chk ^= buf[i];
@@ -51,35 +47,21 @@ uint32_t printWPL(char *buf, size_t bufsz, const Position &pos, const char *name
* 8 Horizontal Dilution of precision (meters) * 8 Horizontal Dilution of precision (meters)
* 9 Antenna Altitude above/below mean-sea-level (geoid) (in meters) * 9 Antenna Altitude above/below mean-sea-level (geoid) (in meters)
* 10 Units of antenna altitude, meters * 10 Units of antenna altitude, meters
* 11 Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level below ellipsoid * 11 Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level
* 12 Units of geoidal separation, meters * below ellipsoid 12 Units of geoidal separation, meters 13 Age of differential GPS data, time in seconds since last SC104 type 1
* 13 Age of differential GPS data, time in seconds since last SC104 type 1 or 9 update, null field when DGPS is not used * or 9 update, null field when DGPS is not used 14 Differential reference station ID, 0000-1023 15 Checksum
* 14 Differential reference station ID, 0000-1023
* 15 Checksum
* ------------------------------------------- * -------------------------------------------
*/ */
uint32_t printGGA(char *buf, size_t bufsz, const Position &pos) uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos)
{ {
GeoCoord geoCoord(pos.latitude_i,pos.longitude_i,pos.altitude); GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
uint32_t len = snprintf(buf, bufsz, "$GNGGA,%06u.%03u,%02d%07.4f,%c,%03d%07.4f,%c,%u,%02u,%04u,%04d,%c,%04d,%c,%d,%04d", uint32_t len =
pos.time / 1000, snprintf(buf, bufsz, "$GNGGA,%06u.%03u,%02d%07.4f,%c,%03d%07.4f,%c,%u,%02u,%04u,%04d,%c,%04d,%c,%d,%04d", pos.time / 1000,
pos.time % 1000, pos.time % 1000, geoCoord.getDMSLatDeg(), (abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6,
geoCoord.getDMSLatDeg(), geoCoord.getDMSLatCP(), geoCoord.getDMSLonDeg(),
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonCP(), pos.fix_type,
geoCoord.getDMSLatCP(), pos.sats_in_view, pos.HDOP, geoCoord.getAltitude(), 'M', pos.altitude_geoidal_separation, 'M', 0, 0);
geoCoord.getDMSLonDeg(),
(abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6,
geoCoord.getDMSLonCP(),
pos.fix_type,
pos.sats_in_view,
pos.HDOP,
geoCoord.getAltitude(),
'M',
pos.altitude_geoidal_separation,
'M',
0,
0);
uint32_t chk = 0; uint32_t chk = 0;
for (uint32_t i = 1; i < len; i++) { for (uint32_t i = 1; i < len; i++) {
+3 -3
View File
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Arduino.h>
#include "main.h" #include "main.h"
#include <Arduino.h>
uint32_t printWPL(char *buf, size_t bufsz, const Position &pos, const char *name); uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name);
uint32_t printGGA(char *buf, size_t bufsz, const Position &pos); uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos);
+14 -13
View File
@@ -20,7 +20,7 @@ void readFromRTC()
{ {
struct timeval tv; /* btw settimeofday() is helpfull here too*/ struct timeval tv; /* btw settimeofday() is helpfull here too*/
#ifdef RV3028_RTC #ifdef RV3028_RTC
if(rtc_found == RV3028_RTC) { if (rtc_found == RV3028_RTC) {
uint32_t now = millis(); uint32_t now = millis();
Melopero_RV3028 rtc; Melopero_RV3028 rtc;
rtc.initI2C(); rtc.initI2C();
@@ -41,7 +41,7 @@ void readFromRTC()
} }
} }
#elif defined(PCF8563_RTC) #elif defined(PCF8563_RTC)
if(rtc_found == PCF8563_RTC) { if (rtc_found == PCF8563_RTC) {
uint32_t now = millis(); uint32_t now = millis();
PCF8563_Class rtc; PCF8563_Class rtc;
#ifdef RTC_USE_WIRE1 #ifdef RTC_USE_WIRE1
@@ -66,7 +66,7 @@ void readFromRTC()
currentQuality = RTCQualityDevice; currentQuality = RTCQualityDevice;
} }
} }
#else #else
if (!gettimeofday(&tv, NULL)) { if (!gettimeofday(&tv, NULL)) {
uint32_t now = millis(); uint32_t now = millis();
LOG_DEBUG("Read RTC time as %ld\n", tv.tv_sec); LOG_DEBUG("Read RTC time as %ld\n", tv.tv_sec);
@@ -87,12 +87,11 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
currentQuality = q; currentQuality = q;
shouldSet = true; shouldSet = true;
LOG_DEBUG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q); LOG_DEBUG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q);
} else if(q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) { } else if (q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
// Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift // Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift
shouldSet = true; shouldSet = true;
LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec); LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec);
} } else
else
shouldSet = false; shouldSet = false;
if (shouldSet) { if (shouldSet) {
@@ -104,24 +103,26 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
// If this platform has a setable RTC, set it // If this platform has a setable RTC, set it
#ifdef RV3028_RTC #ifdef RV3028_RTC
if(rtc_found == RV3028_RTC) { if (rtc_found == RV3028_RTC) {
Melopero_RV3028 rtc; Melopero_RV3028 rtc;
rtc.initI2C(); rtc.initI2C();
tm *t = localtime(&tv->tv_sec); tm *t = localtime(&tv->tv_sec);
rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec); LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec);
} }
#elif defined(PCF8563_RTC) #elif defined(PCF8563_RTC)
if(rtc_found == PCF8563_RTC) { if (rtc_found == PCF8563_RTC) {
PCF8563_Class rtc; PCF8563_Class rtc;
#ifdef RTC_USE_WIRE1 #ifdef RTC_USE_WIRE1
rtc.begin(Wire1); rtc.begin(Wire1);
#else #else
rtc.begin(); rtc.begin();
#endif #endif
tm *t = localtime(&tv->tv_sec); tm *t = localtime(&tv->tv_sec);
rtc.setDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); rtc.setDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec); LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec);
} }
#elif defined(ARCH_ESP32) #elif defined(ARCH_ESP32)
settimeofday(tv, NULL); settimeofday(tv, NULL);
@@ -160,7 +161,7 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t)
uint32_t getTime() uint32_t getTime()
{ {
return (((uint32_t) millis() - timeStartMsec) / 1000) + zeroOffsetSecs; return (((uint32_t)millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
} }
uint32_t getValidTime(RTCQuality minQuality) uint32_t getValidTime(RTCQuality minQuality)
+83 -84
View File
@@ -1,11 +1,11 @@
#include "configuration.h" #include "configuration.h"
#ifdef USE_EINK #ifdef USE_EINK
#include "main.h"
#include "EInkDisplay2.h" #include "EInkDisplay2.h"
#include "SPILock.h"
#include <SPI.h>
#include "GxEPD2_BW.h" #include "GxEPD2_BW.h"
#include "SPILock.h"
#include "main.h"
#include <SPI.h>
#define COLORED GxEPD_BLACK #define COLORED GxEPD_BLACK
#define UNCOLORED GxEPD_WHITE #define UNCOLORED GxEPD_WHITE
@@ -14,62 +14,63 @@
#define TECHO_DISPLAY_MODEL GxEPD2_154_D67 #define TECHO_DISPLAY_MODEL GxEPD2_154_D67
#elif defined(RAK4630) #elif defined(RAK4630)
//GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122 - changed from GxEPD2_213_B74 - which was not going to give partial update support // GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122 - changed from GxEPD2_213_B74 - which was not going to give partial update
// support
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN #define TECHO_DISPLAY_MODEL GxEPD2_213_BN
//4.2 inch 300x400 - GxEPD2_420_M01 // 4.2 inch 300x400 - GxEPD2_420_M01
//#define TECHO_DISPLAY_MODEL GxEPD2_420_M01 //#define TECHO_DISPLAY_MODEL GxEPD2_420_M01
//2.9 inch 296x128 - GxEPD2_290_T5D // 2.9 inch 296x128 - GxEPD2_290_T5D
//#define TECHO_DISPLAY_MODEL GxEPD2_290_T5D //#define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
//1.54 inch 200x200 - GxEPD2_154_M09 // 1.54 inch 200x200 - GxEPD2_154_M09
//#define TECHO_DISPLAY_MODEL GxEPD2_154_M09 //#define TECHO_DISPLAY_MODEL GxEPD2_154_M09
#elif defined(PCA10059) #elif defined(PCA10059)
//4.2 inch 300x400 - GxEPD2_420_M01 // 4.2 inch 300x400 - GxEPD2_420_M01
#define TECHO_DISPLAY_MODEL GxEPD2_420_M01 #define TECHO_DISPLAY_MODEL GxEPD2_420_M01
#elif defined(M5_COREINK) #elif defined(M5_COREINK)
//M5Stack CoreInk // M5Stack CoreInk
//1.54 inch 200x200 - GxEPD2_154_M09 // 1.54 inch 200x200 - GxEPD2_154_M09
#define TECHO_DISPLAY_MODEL GxEPD2_154_M09 #define TECHO_DISPLAY_MODEL GxEPD2_154_M09
#endif #endif
GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT> *adafruitDisplay; GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT> *adafruitDisplay;
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl) EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, uint8_t screen_model)
{ {
#if defined(TTGO_T_ECHO) #if defined(TTGO_T_ECHO)
setGeometry(GEOMETRY_RAWMODE, TECHO_DISPLAY_MODEL::WIDTH, TECHO_DISPLAY_MODEL::HEIGHT); setGeometry(GEOMETRY_RAWMODE, TECHO_DISPLAY_MODEL::WIDTH, TECHO_DISPLAY_MODEL::HEIGHT);
#elif defined(RAK4630) #elif defined(RAK4630)
//GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122 // GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122
setGeometry(GEOMETRY_RAWMODE, 250, 122); setGeometry(GEOMETRY_RAWMODE, 250, 122);
//GxEPD2_420_M01 // GxEPD2_420_M01
//setGeometry(GEOMETRY_RAWMODE, 300, 400); // setGeometry(GEOMETRY_RAWMODE, 300, 400);
//GxEPD2_290_T5D // GxEPD2_290_T5D
//setGeometry(GEOMETRY_RAWMODE, 296, 128); // setGeometry(GEOMETRY_RAWMODE, 296, 128);
//GxEPD2_154_M09 // GxEPD2_154_M09
//setGeometry(GEOMETRY_RAWMODE, 200, 200); // setGeometry(GEOMETRY_RAWMODE, 200, 200);
#elif defined(PCA10059)
//GxEPD2_420_M01 #elif defined(PCA10059)
// GxEPD2_420_M01
setGeometry(GEOMETRY_RAWMODE, 300, 400); setGeometry(GEOMETRY_RAWMODE, 300, 400);
#elif defined(M5_COREINK) #elif defined(M5_COREINK)
//M5Stack_CoreInk 200x200 // M5Stack_CoreInk 200x200
//1.54 inch 200x200 - GxEPD2_154_M09 // 1.54 inch 200x200 - GxEPD2_154_M09
setGeometry(GEOMETRY_RAWMODE, EPD_HEIGHT, EPD_WIDTH); setGeometry(GEOMETRY_RAWMODE, EPD_HEIGHT, EPD_WIDTH);
#endif #endif
// setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution // setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution
// setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does // setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does
} }
@@ -104,28 +105,28 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
} }
LOG_DEBUG("Updating E-Paper... "); LOG_DEBUG("Updating E-Paper... ");
#if defined(TTGO_T_ECHO) #if defined(TTGO_T_ECHO)
// ePaper.Reset(); // wake the screen from sleep // ePaper.Reset(); // wake the screen from sleep
adafruitDisplay->display(false); // FIXME, use partial update mode adafruitDisplay->display(false); // FIXME, use partial update mode
#elif defined(RAK4630) #elif defined(RAK4630)
//RAK14000 2.13 inch b/w 250x122 actually now does support partial updates // RAK14000 2.13 inch b/w 250x122 actually now does support partial updates
//Full update mode (slow) // Full update mode (slow)
//adafruitDisplay->display(false); // FIXME, use partial update mode // adafruitDisplay->display(false); // FIXME, use partial update mode
//Only enable for e-Paper with support for partial updates and comment out above adafruitDisplay->display(false); // Only enable for e-Paper with support for partial updates and comment out above adafruitDisplay->display(false);
// 1.54 inch 200x200 - GxEPD2_154_M09 // 1.54 inch 200x200 - GxEPD2_154_M09
// 2.13 inch 250x122 - GxEPD2_213_BN // 2.13 inch 250x122 - GxEPD2_213_BN
// 2.9 inch 296x128 - GxEPD2_290_T5D // 2.9 inch 296x128 - GxEPD2_290_T5D
// 4.2 inch 300x400 - GxEPD2_420_M01 // 4.2 inch 300x400 - GxEPD2_420_M01
adafruitDisplay->nextPage(); adafruitDisplay->nextPage();
#elif defined(PCA10059) || defined(M5_COREINK) #elif defined(PCA10059) || defined(M5_COREINK)
adafruitDisplay->nextPage(); adafruitDisplay->nextPage();
#endif #endif
// Put screen to sleep to save power (possibly not necessary because we already did poweroff inside of display) // Put screen to sleep to save power (possibly not necessary because we already did poweroff inside of display)
adafruitDisplay->hibernate(); adafruitDisplay->hibernate();
LOG_DEBUG("done\n"); LOG_DEBUG("done\n");
@@ -175,41 +176,40 @@ bool EInkDisplay::connect()
pinMode(PIN_EINK_EN, OUTPUT); pinMode(PIN_EINK_EN, OUTPUT);
#endif #endif
#if defined(TTGO_T_ECHO) #if defined(TTGO_T_ECHO)
{ {
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1); auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
}
#elif defined(RAK4630)
{
if (eink_found) {
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel); adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init();
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
//RAK14000 2.13 inch b/w 250x122 does actually now support partial updates
adafruitDisplay->setRotation(3); adafruitDisplay->setRotation(3);
//Partial update support for 1.54, 2.13 RAK14000 b/w , 2.9 and 4.2 }
//adafruitDisplay->setRotation(1); #elif defined(RAK4630)
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); {
} else { if (eink_found) {
(void)adafruitDisplay; auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
}
} adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
// RAK14000 2.13 inch b/w 250x122 does actually now support partial updates
adafruitDisplay->setRotation(3);
// Partial update support for 1.54, 2.13 RAK14000 b/w , 2.9 and 4.2
// adafruitDisplay->setRotation(1);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
} else {
(void)adafruitDisplay;
}
}
#elif defined(PCA10059) #elif defined(PCA10059)
{ {
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel); adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
adafruitDisplay->setRotation(3); adafruitDisplay->setRotation(3);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
} }
#elif defined(M5_COREINK) #elif defined(M5_COREINK)
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel); adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
@@ -217,12 +217,11 @@ bool EInkDisplay::connect()
adafruitDisplay->setRotation(0); adafruitDisplay->setRotation(0);
adafruitDisplay->setPartialWindow(0, 0, EPD_WIDTH, EPD_HEIGHT); adafruitDisplay->setPartialWindow(0, 0, EPD_WIDTH, EPD_HEIGHT);
#endif #endif
// adafruitDisplay->setFullWindow();
//adafruitDisplay->setFullWindow(); // adafruitDisplay->fillScreen(UNCOLORED);
//adafruitDisplay->fillScreen(UNCOLORED); // adafruitDisplay->drawCircle(100, 100, 20, COLORED);
//adafruitDisplay->drawCircle(100, 100, 20, COLORED); // adafruitDisplay->display(false);
//adafruitDisplay->display(false);
return true; return true;
} }
+4 -6
View File
@@ -17,26 +17,26 @@ class EInkDisplay : public OLEDDisplay
/// How often should we update the display /// How often should we update the display
/// thereafter we do once per 5 minutes /// thereafter we do once per 5 minutes
uint32_t slowUpdateMsec = 5 * 60 * 1000; uint32_t slowUpdateMsec = 5 * 60 * 1000;
public: public:
/* constructor /* constructor
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
*/ */
EInkDisplay(uint8_t address, int sda, int scl); EInkDisplay(uint8_t address, int sda, int scl, uint8_t screen_model);
// Write the buffer to the display memory (for eink we only do this occasionally) // Write the buffer to the display memory (for eink we only do this occasionally)
virtual void display(void) override; virtual void display(void) override;
/** /**
* Force a display update if we haven't drawn within the specified msecLimit * Force a display update if we haven't drawn within the specified msecLimit
* *
* @return true if we did draw the screen * @return true if we did draw the screen
*/ */
bool forceDisplay(uint32_t msecLimit = 1000); bool forceDisplay(uint32_t msecLimit = 1000);
/** /**
* shim to make the abstraction happy * shim to make the abstraction happy
* *
*/ */
void setDetected(uint8_t detected); void setDetected(uint8_t detected);
@@ -50,5 +50,3 @@ class EInkDisplay : public OLEDDisplay
// Connect to the display // Connect to the display
virtual bool connect() override; virtual bool connect() override;
}; };
+47 -40
View File
@@ -352,7 +352,7 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
} }
// Ignore messages orginating from phone (from the current node 0x0) unless range test or store and forward module are enabled // Ignore messages orginating from phone (from the current node 0x0) unless range test or store and forward module are enabled
static bool shouldDrawMessage(const MeshPacket *packet) static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
{ {
return packet->from != 0 && !moduleConfig.range_test.enabled && !moduleConfig.store_forward.enabled; return packet->from != 0 && !moduleConfig.range_test.enabled && !moduleConfig.store_forward.enabled;
} }
@@ -365,8 +365,8 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// the max length of this buffer is much longer than we can possibly print // the max length of this buffer is much longer than we can possibly print
static char tempBuf[237]; static char tempBuf[237];
MeshPacket &mp = devicestate.rx_text_message; meshtastic_MeshPacket &mp = devicestate.rx_text_message;
NodeInfo *node = nodeDB.getNode(getFrom(&mp)); meshtastic_NodeInfo *node = nodeDB.getNode(getFrom(&mp));
// LOG_DEBUG("drawing text message from 0x%x: %s\n", mp.from, // LOG_DEBUG("drawing text message from 0x%x: %s\n", mp.from,
// mp.decoded.variant.data.decoded.bytes); // mp.decoded.variant.data.decoded.bytes);
@@ -375,7 +375,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// be wrapped. Currently only spaces and "-" are allowed for wrapping // be wrapped. Currently only spaces and "-" are allowed for wrapping
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_INVERTED) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK); display->setColor(BLACK);
} }
@@ -556,7 +556,7 @@ static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GP
} else { } else {
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude())); geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
displayLine = "Altitude: " + String(geoCoord.getAltitude()) + "m"; displayLine = "Altitude: " + String(geoCoord.getAltitude()) + "m";
if (config.display.units == Config_DisplayConfig_DisplayUnits_IMPERIAL) if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
displayLine = "Altitude: " + String(geoCoord.getAltitude() * METERS_TO_FEET) + "ft"; displayLine = "Altitude: " + String(geoCoord.getAltitude() * METERS_TO_FEET) + "ft";
display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine); display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} }
@@ -578,21 +578,21 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const
geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude())); geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
if (gpsFormat != Config_DisplayConfig_GpsCoordinateFormat_DMS) { if (gpsFormat != meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) {
char coordinateLine[22]; char coordinateLine[22];
if (gpsFormat == Config_DisplayConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
snprintf(coordinateLine, sizeof(coordinateLine), "%f %f", geoCoord.getLatitude() * 1e-7, snprintf(coordinateLine, sizeof(coordinateLine), "%f %f", geoCoord.getLatitude() * 1e-7,
geoCoord.getLongitude() * 1e-7); geoCoord.getLongitude() * 1e-7);
} else if (gpsFormat == Config_DisplayConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %06u %07u", geoCoord.getUTMZone(), geoCoord.getUTMBand(), snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %06u %07u", geoCoord.getUTMZone(), geoCoord.getUTMBand(),
geoCoord.getUTMEasting(), geoCoord.getUTMNorthing()); geoCoord.getUTMEasting(), geoCoord.getUTMNorthing());
} else if (gpsFormat == Config_DisplayConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %1c%1c %05u %05u", geoCoord.getMGRSZone(), snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %1c%1c %05u %05u", geoCoord.getMGRSZone(),
geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k(), geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k(),
geoCoord.getMGRSEasting(), geoCoord.getMGRSNorthing()); geoCoord.getMGRSEasting(), geoCoord.getMGRSNorthing());
} else if (gpsFormat == Config_DisplayConfig_GpsCoordinateFormat_OLC) { // Open Location Code } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OLC) { // Open Location Code
geoCoord.getOLCCode(coordinateLine); geoCoord.getOLCCode(coordinateLine);
} else if (gpsFormat == Config_DisplayConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') // OSGR is only valid around the UK region if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') // OSGR is only valid around the UK region
snprintf(coordinateLine, sizeof(coordinateLine), "%s", "Out of Boundary"); snprintf(coordinateLine, sizeof(coordinateLine), "%s", "Out of Boundary");
else else
@@ -699,7 +699,7 @@ static float estimatedHeading(double lat, double lon)
/// Sometimes we will have Position objects that only have a time, so check for /// Sometimes we will have Position objects that only have a time, so check for
/// valid lat/lon /// valid lat/lon
static bool hasPosition(NodeInfo *n) static bool hasPosition(meshtastic_NodeInfo *n)
{ {
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
} }
@@ -709,7 +709,7 @@ static uint16_t getCompassDiam(OLEDDisplay *display)
uint16_t diam = 0; uint16_t diam = 0;
uint16_t offset = 0; uint16_t offset = 0;
if (config.display.displaymode != Config_DisplayConfig_DisplayMode_DEFAULT) if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
offset = FONT_HEIGHT_SMALL; offset = FONT_HEIGHT_SMALL;
// get the smaller of the 2 dimensions and subtract 20 // get the smaller of the 2 dimensions and subtract 20
@@ -786,7 +786,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
prevFrame = state->currentFrame; prevFrame = state->currentFrame;
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes(); nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
NodeInfo *n = nodeDB.getNodeByIndex(nodeIndex); meshtastic_NodeInfo *n = nodeDB.getNodeByIndex(nodeIndex);
if (n->num == nodeDB.getNodeNum()) { if (n->num == nodeDB.getNodeNum()) {
// Don't show our node, just skip to next // Don't show our node, just skip to next
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes(); nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
@@ -795,14 +795,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
displayedNodeNum = n->num; displayedNodeNum = n->num;
} }
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex); meshtastic_NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
// 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);
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_INVERTED) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
} }
@@ -832,12 +832,12 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
static char distStr[20]; static char distStr[20];
strncpy(distStr, "? km", sizeof(distStr)); // might not have location data strncpy(distStr, "? km", sizeof(distStr)); // might not have location data
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum()); meshtastic_NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
const char *fields[] = {username, distStr, signalStr, lastStr, NULL}; const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
int16_t compassX = 0, compassY = 0; int16_t compassX = 0, compassY = 0;
// coordinates for the center of the compass/circle // coordinates for the center of the compass/circle
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_DEFAULT) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5; compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
compassY = y + SCREEN_HEIGHT / 2; compassY = y + SCREEN_HEIGHT / 2;
} else { } else {
@@ -847,18 +847,18 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
bool hasNodeHeading = false; bool hasNodeHeading = false;
if (ourNode && hasPosition(ourNode)) { if (ourNode && hasPosition(ourNode)) {
Position &op = ourNode->position; meshtastic_Position &op = ourNode->position;
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
drawCompassNorth(display, compassX, compassY, myHeading); drawCompassNorth(display, compassX, compassY, myHeading);
if (hasPosition(node)) { if (hasPosition(node)) {
// display direction toward node // display direction toward node
hasNodeHeading = true; hasNodeHeading = true;
Position &p = node->position; meshtastic_Position &p = node->position;
float d = float d =
GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
if (config.display.units == Config_DisplayConfig_DisplayUnits_IMPERIAL) { if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
if (d < (2 * MILES_TO_FEET)) if (d < (2 * MILES_TO_FEET))
snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET); snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET);
else else
@@ -887,7 +887,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
} }
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2); display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_INVERTED) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->setColor(BLACK); display->setColor(BLACK);
} }
// Must be after distStr is populated // Must be after distStr is populated
@@ -908,7 +908,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
// } // }
// } // }
// #else // #else
Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev) Screen::Screen(uint8_t address, int sda, int scl)
: OSThread("Screen"), cmdQueue(32),
dispdev(address, sda, scl,
screen_model == meshtastic_Config_DisplayConfig_OledType_OLED_SH1107 ? GEOMETRY_128_128 : GEOMETRY_128_64),
ui(&dispdev)
{ {
address_found = address; address_found = address;
cmdQueue.setReader(this); cmdQueue.setReader(this);
@@ -958,6 +962,8 @@ void Screen::setup()
useDisplay = true; useDisplay = true;
#ifdef AutoOLEDWire_h #ifdef AutoOLEDWire_h
if (screen_model == meshtastic_Config_DisplayConfig_OledType_OLED_SH1107)
screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1106;
dispdev.setDetected(screen_model); dispdev.setDetected(screen_model);
#endif #endif
@@ -1079,7 +1085,7 @@ int32_t Screen::runOnce()
} }
#ifndef DISABLE_WELCOME_UNSET #ifndef DISABLE_WELCOME_UNSET
if (showingNormalScreen && config.lora.region == Config_LoRaConfig_RegionCode_UNSET) { if (showingNormalScreen && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
setWelcomeFrames(); setWelcomeFrames();
} }
#endif #endif
@@ -1399,7 +1405,7 @@ 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);
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_INVERTED) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK); display->setColor(BLACK);
} }
@@ -1413,20 +1419,20 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Display power status // Display power status
if (powerStatus->getHasBattery()) { if (powerStatus->getHasBattery()) {
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_DEFAULT) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
drawBattery(display, x, y + 2, imgBattery, powerStatus); drawBattery(display, x, y + 2, imgBattery, powerStatus);
} else { } else {
drawBattery(display, x + 1, y + 3, imgBattery, powerStatus); drawBattery(display, x + 1, y + 3, imgBattery, powerStatus);
} }
} else if (powerStatus->knowsUSB()) { } else if (powerStatus->knowsUSB()) {
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_DEFAULT) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower); display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
} else { } else {
display->drawFastImage(x + 1, y + 3, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower); display->drawFastImage(x + 1, y + 3, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
} }
} }
// Display nodes status // Display nodes status
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_DEFAULT) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus); drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
} else { } else {
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus); drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus);
@@ -1439,7 +1445,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
#endif #endif
drawGPSpowerstat(display, x, yPos, gpsStatus); drawGPSpowerstat(display, x, yPos, gpsStatus);
} else { } else {
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_DEFAULT) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
} else { } else {
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus); drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus);
@@ -1512,7 +1518,7 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
// 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);
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_INVERTED) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK); display->setColor(BLACK);
} }
@@ -1644,7 +1650,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// 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);
if (config.display.displaymode == Config_DisplayConfig_DisplayMode_INVERTED) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK); display->setColor(BLACK);
} }
@@ -1671,25 +1677,25 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
auto mode = ""; auto mode = "";
switch (config.lora.modem_preset) { switch (config.lora.modem_preset) {
case Config_LoRaConfig_ModemPreset_SHORT_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
mode = "ShortS"; mode = "ShortS";
break; break;
case Config_LoRaConfig_ModemPreset_SHORT_FAST: case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
mode = "ShortF"; mode = "ShortF";
break; break;
case Config_LoRaConfig_ModemPreset_MEDIUM_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
mode = "MedS"; mode = "MedS";
break; break;
case Config_LoRaConfig_ModemPreset_MEDIUM_FAST: case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
mode = "MedF"; mode = "MedF";
break; break;
case Config_LoRaConfig_ModemPreset_LONG_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
mode = "LongS"; mode = "LongS";
break; break;
case Config_LoRaConfig_ModemPreset_LONG_FAST: case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
mode = "LongF"; mode = "LongF";
break; break;
case Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
mode = "VeryL"; mode = "VeryL";
break; break;
default: default:
@@ -1751,7 +1757,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil);
if (config.position.gps_enabled) { if (config.position.gps_enabled) {
// Line 3 // Line 3
if (config.display.gps_format != Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude if (config.display.gps_format !=
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
drawGPSAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus); drawGPSAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
// Line 4 // Line 4
@@ -1799,7 +1806,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
return 0; return 0;
} }
int Screen::handleTextMessage(const MeshPacket *packet) int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
{ {
if (showingNormalScreen) { if (showingNormalScreen) {
setFrames(); // Regen the list of screens (will show new text message) setFrames(); // Regen the list of screens (will show new text message)
+41 -39
View File
@@ -7,15 +7,15 @@
namespace graphics namespace graphics
{ {
// Noop class for boards without screen. // Noop class for boards without screen.
class Screen class Screen
{ {
public: public:
explicit Screen(char){} explicit Screen(char) {}
void onPress() {} void onPress() {}
void setup() {} void setup() {}
void setOn(bool) {} void setOn(bool) {}
void print(const char*){} void print(const char *) {}
void adjustBrightness(){} void adjustBrightness() {}
void doDeepSleep() {} void doDeepSleep() {}
void forceDisplay() {} void forceDisplay() {}
void startBluetoothPinScreen(uint32_t pin) {} void startBluetoothPinScreen(uint32_t pin) {}
@@ -23,7 +23,7 @@ class Screen
void startRebootScreen() {} void startRebootScreen() {}
void startFirmwareUpdateScreen() {} void startFirmwareUpdateScreen() {}
}; };
} } // namespace graphics
#else #else
#include <cstring> #include <cstring>
@@ -34,7 +34,7 @@ class Screen
#ifdef USE_ST7567 #ifdef USE_ST7567
#include <ST7567Wire.h> #include <ST7567Wire.h>
#elif defined(USE_SH1106) #elif defined(USE_SH1106) || defined(USE_SH1107)
#include <SH1106Wire.h> #include <SH1106Wire.h>
#elif defined(USE_SSD1306) #elif defined(USE_SSD1306)
#include <SSD1306Wire.h> #include <SSD1306Wire.h>
@@ -49,9 +49,9 @@ class Screen
#include "commands.h" #include "commands.h"
#include "concurrency/LockGuard.h" #include "concurrency/LockGuard.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "mesh/MeshModule.h"
#include "power.h" #include "power.h"
#include <string> #include <string>
#include "mesh/MeshModule.h"
// 0 to 255, though particular variants might define different defaults // 0 to 255, though particular variants might define different defaults
#ifndef BRIGHTNESS_DEFAULT #ifndef BRIGHTNESS_DEFAULT
@@ -110,8 +110,8 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate); CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate); CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const MeshPacket *> textMessageObserver = CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const MeshPacket *>(this, &Screen::handleTextMessage); CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver = CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
@@ -132,13 +132,14 @@ class Screen : public concurrency::OSThread
void setOn(bool on) void setOn(bool on)
{ {
if (!on) if (!on)
handleSetOn(false); // We handle off commands immediately, because they might be called because the CPU is shutting down handleSetOn(
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
else else
enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF}); enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
} }
/** /**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just * Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code * poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
*/ */
void doDeepSleep(); void doDeepSleep();
@@ -223,29 +224,33 @@ class Screen : public concurrency::OSThread
LASTCHAR = ch; LASTCHAR = ch;
switch (last) { // conversion depending on first UTF8-character switch (last) { // conversion depending on first UTF8-character
case 0xC2: { case 0xC2: {
SKIPREST = false; SKIPREST = false;
return (uint8_t)ch; return (uint8_t)ch;
} }
case 0xC3: { case 0xC3: {
SKIPREST = false; SKIPREST = false;
return (uint8_t)(ch | 0xC0); return (uint8_t)(ch | 0xC0);
} }
// map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes // map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes
// note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by 'ThingPulse/esp8266-oled-ssd1306' library // note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by 'ThingPulse/esp8266-oled-ssd1306'
// have empty chars for non-latin ASCII symbols // library have empty chars for non-latin ASCII symbols
case 0xD0: { case 0xD0: {
SKIPREST = false; SKIPREST = false;
if (ch == 129) return (uint8_t)(168); // Ё if (ch == 129)
if (ch > 143 && ch < 192) return (uint8_t)(ch + 48); return (uint8_t)(168); // Ё
break; if (ch > 143 && ch < 192)
} return (uint8_t)(ch + 48);
case 0xD1: { break;
SKIPREST = false; }
if (ch == 145) return (uint8_t)(184); // ё case 0xD1: {
if (ch > 127 && ch < 144) return (uint8_t)(ch + 112); SKIPREST = false;
break; if (ch == 145)
} return (uint8_t)(184); // ё
if (ch > 127 && ch < 144)
return (uint8_t)(ch + 112);
break;
}
} }
// We want to strip out prefix chars for two-byte char formats // We want to strip out prefix chars for two-byte char formats
@@ -268,7 +273,7 @@ class Screen : public concurrency::OSThread
DebugInfo *debug_info() { return &debugInfo; } DebugInfo *debug_info() { return &debugInfo; }
int handleStatusUpdate(const meshtastic::Status *arg); int handleStatusUpdate(const meshtastic::Status *arg);
int handleTextMessage(const MeshPacket *arg); int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg); int handleUIFrameEvent(const UIFrameEvent *arg);
/// Used to force (super slow) eink displays to draw critical frames /// Used to force (super slow) eink displays to draw critical frames
@@ -342,10 +347,7 @@ class Screen : public concurrency::OSThread
/// Display device /// Display device
// #ifdef RAK4630 #if defined(USE_SH1106) || defined(USE_SH1107)
// EInkDisplay dispdev;
// AutoOLEDWire dispdev_oled;
#ifdef USE_SH1106
SH1106Wire dispdev; SH1106Wire dispdev;
#elif defined(USE_SSD1306) #elif defined(USE_SSD1306)
SSD1306Wire dispdev; SSD1306Wire dispdev;
+2 -2
View File
@@ -8,7 +8,7 @@
static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl) TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, uint8_t screen_model)
{ {
#ifdef SCREEN_ROTATE #ifdef SCREEN_ROTATE
setGeometry(GEOMETRY_RAWMODE, TFT_HEIGHT, TFT_WIDTH); setGeometry(GEOMETRY_RAWMODE, TFT_HEIGHT, TFT_WIDTH);
@@ -22,7 +22,7 @@ void TFTDisplay::display(void)
{ {
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
uint16_t x,y; uint16_t x, y;
for (y = 0; y < displayHeight; y++) { for (y = 0; y < displayHeight; y++) {
for (x = 0; x < displayWidth; x++) { for (x = 0; x < displayWidth; x++) {
+3 -3
View File
@@ -18,14 +18,14 @@ class TFTDisplay : public OLEDDisplay
/* constructor /* constructor
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
*/ */
TFTDisplay(uint8_t address, int sda, int scl); TFTDisplay(uint8_t address, int sda, int scl, uint8_t screen_model);
// Write the buffer to the display memory // Write the buffer to the display memory
virtual void display(void) override; virtual void display(void) override;
/** /**
* shim to make the abstraction happy * shim to make the abstraction happy
* *
*/ */
void setDetected(uint8_t detected); void setDetected(uint8_t detected);
+421 -421
View File
@@ -2,425 +2,425 @@
// Font generated or edited with the glyphEditor // Font generated or edited with the glyphEditor
const uint8_t ArialMT_Plain_10_RU[] PROGMEM = { const uint8_t ArialMT_Plain_10_RU[] PROGMEM = {
0x0A, // Width: 10 0x0A, // Width: 10
0x0D, // Height: 13 0x0D, // Height: 13
0x20, // First char: 32 0x20, // First char: 32
0xE0, // Number of chars: 224 0xE0, // Number of chars: 224
// Jump Table: // Jump Table:
0xFF, 0xFF, 0x00, 0x0A, // 32 0xFF, 0xFF, 0x00, 0x0A, // 32
0x00, 0x00, 0x04, 0x03, // 33 0x00, 0x00, 0x04, 0x03, // 33
0x00, 0x04, 0x05, 0x04, // 34 0x00, 0x04, 0x05, 0x04, // 34
0x00, 0x09, 0x09, 0x06, // 35 0x00, 0x09, 0x09, 0x06, // 35
0x00, 0x12, 0x0A, 0x06, // 36 0x00, 0x12, 0x0A, 0x06, // 36
0x00, 0x1C, 0x10, 0x09, // 37 0x00, 0x1C, 0x10, 0x09, // 37
0x00, 0x2C, 0x0E, 0x08, // 38 0x00, 0x2C, 0x0E, 0x08, // 38
0x00, 0x3A, 0x01, 0x02, // 39 0x00, 0x3A, 0x01, 0x02, // 39
0x00, 0x3B, 0x06, 0x04, // 40 0x00, 0x3B, 0x06, 0x04, // 40
0x00, 0x41, 0x06, 0x04, // 41 0x00, 0x41, 0x06, 0x04, // 41
0x00, 0x47, 0x05, 0x04, // 42 0x00, 0x47, 0x05, 0x04, // 42
0x00, 0x4C, 0x09, 0x06, // 43 0x00, 0x4C, 0x09, 0x06, // 43
0x00, 0x55, 0x04, 0x03, // 44 0x00, 0x55, 0x04, 0x03, // 44
0x00, 0x59, 0x03, 0x03, // 45 0x00, 0x59, 0x03, 0x03, // 45
0x00, 0x5C, 0x04, 0x03, // 46 0x00, 0x5C, 0x04, 0x03, // 46
0x00, 0x60, 0x05, 0x04, // 47 0x00, 0x60, 0x05, 0x04, // 47
0x00, 0x65, 0x0A, 0x06, // 48 0x00, 0x65, 0x0A, 0x06, // 48
0x00, 0x6F, 0x08, 0x05, // 49 0x00, 0x6F, 0x08, 0x05, // 49
0x00, 0x77, 0x0A, 0x06, // 50 0x00, 0x77, 0x0A, 0x06, // 50
0x00, 0x81, 0x0A, 0x06, // 51 0x00, 0x81, 0x0A, 0x06, // 51
0x00, 0x8B, 0x0B, 0x07, // 52 0x00, 0x8B, 0x0B, 0x07, // 52
0x00, 0x96, 0x0A, 0x06, // 53 0x00, 0x96, 0x0A, 0x06, // 53
0x00, 0xA0, 0x0A, 0x06, // 54 0x00, 0xA0, 0x0A, 0x06, // 54
0x00, 0xAA, 0x09, 0x06, // 55 0x00, 0xAA, 0x09, 0x06, // 55
0x00, 0xB3, 0x0A, 0x06, // 56 0x00, 0xB3, 0x0A, 0x06, // 56
0x00, 0xBD, 0x0A, 0x06, // 57 0x00, 0xBD, 0x0A, 0x06, // 57
0x00, 0xC7, 0x04, 0x03, // 58 0x00, 0xC7, 0x04, 0x03, // 58
0x00, 0xCB, 0x04, 0x03, // 59 0x00, 0xCB, 0x04, 0x03, // 59
0x00, 0xCF, 0x0A, 0x06, // 60 0x00, 0xCF, 0x0A, 0x06, // 60
0x00, 0xD9, 0x09, 0x06, // 61 0x00, 0xD9, 0x09, 0x06, // 61
0x00, 0xE2, 0x09, 0x06, // 62 0x00, 0xE2, 0x09, 0x06, // 62
0x00, 0xEB, 0x0B, 0x07, // 63 0x00, 0xEB, 0x0B, 0x07, // 63
0x00, 0xF6, 0x14, 0x0B, // 64 0x00, 0xF6, 0x14, 0x0B, // 64
0x01, 0x0A, 0x0E, 0x08, // 65 0x01, 0x0A, 0x0E, 0x08, // 65
0x01, 0x18, 0x0C, 0x07, // 66 0x01, 0x18, 0x0C, 0x07, // 66
0x01, 0x24, 0x0C, 0x07, // 67 0x01, 0x24, 0x0C, 0x07, // 67
0x01, 0x30, 0x0B, 0x07, // 68 0x01, 0x30, 0x0B, 0x07, // 68
0x01, 0x3B, 0x0C, 0x07, // 69 0x01, 0x3B, 0x0C, 0x07, // 69
0x01, 0x47, 0x09, 0x06, // 70 0x01, 0x47, 0x09, 0x06, // 70
0x01, 0x50, 0x0D, 0x08, // 71 0x01, 0x50, 0x0D, 0x08, // 71
0x01, 0x5D, 0x0C, 0x07, // 72 0x01, 0x5D, 0x0C, 0x07, // 72
0x01, 0x69, 0x04, 0x03, // 73 0x01, 0x69, 0x04, 0x03, // 73
0x01, 0x6D, 0x08, 0x05, // 74 0x01, 0x6D, 0x08, 0x05, // 74
0x01, 0x75, 0x0E, 0x08, // 75 0x01, 0x75, 0x0E, 0x08, // 75
0x01, 0x83, 0x0C, 0x07, // 76 0x01, 0x83, 0x0C, 0x07, // 76
0x01, 0x8F, 0x10, 0x09, // 77 0x01, 0x8F, 0x10, 0x09, // 77
0x01, 0x9F, 0x0C, 0x07, // 78 0x01, 0x9F, 0x0C, 0x07, // 78
0x01, 0xAB, 0x0E, 0x08, // 79 0x01, 0xAB, 0x0E, 0x08, // 79
0x01, 0xB9, 0x0B, 0x07, // 80 0x01, 0xB9, 0x0B, 0x07, // 80
0x01, 0xC4, 0x0E, 0x08, // 81 0x01, 0xC4, 0x0E, 0x08, // 81
0x01, 0xD2, 0x0C, 0x07, // 82 0x01, 0xD2, 0x0C, 0x07, // 82
0x01, 0xDE, 0x0C, 0x07, // 83 0x01, 0xDE, 0x0C, 0x07, // 83
0x01, 0xEA, 0x0B, 0x07, // 84 0x01, 0xEA, 0x0B, 0x07, // 84
0x01, 0xF5, 0x0C, 0x07, // 85 0x01, 0xF5, 0x0C, 0x07, // 85
0x02, 0x01, 0x0D, 0x08, // 86 0x02, 0x01, 0x0D, 0x08, // 86
0x02, 0x0E, 0x11, 0x0A, // 87 0x02, 0x0E, 0x11, 0x0A, // 87
0x02, 0x1F, 0x0E, 0x08, // 88 0x02, 0x1F, 0x0E, 0x08, // 88
0x02, 0x2D, 0x0D, 0x08, // 89 0x02, 0x2D, 0x0D, 0x08, // 89
0x02, 0x3A, 0x0C, 0x07, // 90 0x02, 0x3A, 0x0C, 0x07, // 90
0x02, 0x46, 0x06, 0x04, // 91 0x02, 0x46, 0x06, 0x04, // 91
0x02, 0x4C, 0x06, 0x04, // 92 0x02, 0x4C, 0x06, 0x04, // 92
0x02, 0x52, 0x04, 0x03, // 93 0x02, 0x52, 0x04, 0x03, // 93
0x02, 0x56, 0x09, 0x06, // 94 0x02, 0x56, 0x09, 0x06, // 94
0x02, 0x5F, 0x0C, 0x07, // 95 0x02, 0x5F, 0x0C, 0x07, // 95
0x02, 0x6B, 0x03, 0x03, // 96 0x02, 0x6B, 0x03, 0x03, // 96
0x02, 0x6E, 0x0A, 0x06, // 97 0x02, 0x6E, 0x0A, 0x06, // 97
0x02, 0x78, 0x0A, 0x06, // 98 0x02, 0x78, 0x0A, 0x06, // 98
0x02, 0x82, 0x0A, 0x06, // 99 0x02, 0x82, 0x0A, 0x06, // 99
0x02, 0x8C, 0x0A, 0x06, // 100 0x02, 0x8C, 0x0A, 0x06, // 100
0x02, 0x96, 0x0A, 0x06, // 101 0x02, 0x96, 0x0A, 0x06, // 101
0x02, 0xA0, 0x05, 0x04, // 102 0x02, 0xA0, 0x05, 0x04, // 102
0x02, 0xA5, 0x0A, 0x06, // 103 0x02, 0xA5, 0x0A, 0x06, // 103
0x02, 0xAF, 0x0A, 0x06, // 104 0x02, 0xAF, 0x0A, 0x06, // 104
0x02, 0xB9, 0x04, 0x03, // 105 0x02, 0xB9, 0x04, 0x03, // 105
0x02, 0xBD, 0x04, 0x03, // 106 0x02, 0xBD, 0x04, 0x03, // 106
0x02, 0xC1, 0x08, 0x05, // 107 0x02, 0xC1, 0x08, 0x05, // 107
0x02, 0xC9, 0x04, 0x03, // 108 0x02, 0xC9, 0x04, 0x03, // 108
0x02, 0xCD, 0x10, 0x09, // 109 0x02, 0xCD, 0x10, 0x09, // 109
0x02, 0xDD, 0x0A, 0x06, // 110 0x02, 0xDD, 0x0A, 0x06, // 110
0x02, 0xE7, 0x0A, 0x06, // 111 0x02, 0xE7, 0x0A, 0x06, // 111
0x02, 0xF1, 0x0A, 0x06, // 112 0x02, 0xF1, 0x0A, 0x06, // 112
0x02, 0xFB, 0x0A, 0x06, // 113 0x02, 0xFB, 0x0A, 0x06, // 113
0x03, 0x05, 0x05, 0x04, // 114 0x03, 0x05, 0x05, 0x04, // 114
0x03, 0x0A, 0x08, 0x05, // 115 0x03, 0x0A, 0x08, 0x05, // 115
0x03, 0x12, 0x06, 0x04, // 116 0x03, 0x12, 0x06, 0x04, // 116
0x03, 0x18, 0x0A, 0x06, // 117 0x03, 0x18, 0x0A, 0x06, // 117
0x03, 0x22, 0x09, 0x06, // 118 0x03, 0x22, 0x09, 0x06, // 118
0x03, 0x2B, 0x0E, 0x08, // 119 0x03, 0x2B, 0x0E, 0x08, // 119
0x03, 0x39, 0x0A, 0x06, // 120 0x03, 0x39, 0x0A, 0x06, // 120
0x03, 0x43, 0x09, 0x06, // 121 0x03, 0x43, 0x09, 0x06, // 121
0x03, 0x4C, 0x0A, 0x06, // 122 0x03, 0x4C, 0x0A, 0x06, // 122
0x03, 0x56, 0x06, 0x04, // 123 0x03, 0x56, 0x06, 0x04, // 123
0x03, 0x5C, 0x04, 0x03, // 124 0x03, 0x5C, 0x04, 0x03, // 124
0x03, 0x60, 0x05, 0x04, // 125 0x03, 0x60, 0x05, 0x04, // 125
0x03, 0x65, 0x09, 0x06, // 126 0x03, 0x65, 0x09, 0x06, // 126
0xFF, 0xFF, 0x00, 0x0A, // 127 0xFF, 0xFF, 0x00, 0x0A, // 127
0xFF, 0xFF, 0x00, 0x0A, // 128 0xFF, 0xFF, 0x00, 0x0A, // 128
0xFF, 0xFF, 0x00, 0x0A, // 129 0xFF, 0xFF, 0x00, 0x0A, // 129
0xFF, 0xFF, 0x00, 0x0A, // 130 0xFF, 0xFF, 0x00, 0x0A, // 130
0xFF, 0xFF, 0x00, 0x0A, // 131 0xFF, 0xFF, 0x00, 0x0A, // 131
0xFF, 0xFF, 0x00, 0x0A, // 132 0xFF, 0xFF, 0x00, 0x0A, // 132
0xFF, 0xFF, 0x00, 0x0A, // 133 0xFF, 0xFF, 0x00, 0x0A, // 133
0xFF, 0xFF, 0x00, 0x0A, // 134 0xFF, 0xFF, 0x00, 0x0A, // 134
0xFF, 0xFF, 0x00, 0x0A, // 135 0xFF, 0xFF, 0x00, 0x0A, // 135
0xFF, 0xFF, 0x00, 0x0A, // 136 0xFF, 0xFF, 0x00, 0x0A, // 136
0xFF, 0xFF, 0x00, 0x0A, // 137 0xFF, 0xFF, 0x00, 0x0A, // 137
0xFF, 0xFF, 0x00, 0x0A, // 138 0xFF, 0xFF, 0x00, 0x0A, // 138
0xFF, 0xFF, 0x00, 0x0A, // 139 0xFF, 0xFF, 0x00, 0x0A, // 139
0xFF, 0xFF, 0x00, 0x0A, // 140 0xFF, 0xFF, 0x00, 0x0A, // 140
0xFF, 0xFF, 0x00, 0x0A, // 141 0xFF, 0xFF, 0x00, 0x0A, // 141
0xFF, 0xFF, 0x00, 0x0A, // 142 0xFF, 0xFF, 0x00, 0x0A, // 142
0xFF, 0xFF, 0x00, 0x0A, // 143 0xFF, 0xFF, 0x00, 0x0A, // 143
0xFF, 0xFF, 0x00, 0x0A, // 144 0xFF, 0xFF, 0x00, 0x0A, // 144
0xFF, 0xFF, 0x00, 0x0A, // 145 0xFF, 0xFF, 0x00, 0x0A, // 145
0xFF, 0xFF, 0x00, 0x0A, // 146 0xFF, 0xFF, 0x00, 0x0A, // 146
0xFF, 0xFF, 0x00, 0x0A, // 147 0xFF, 0xFF, 0x00, 0x0A, // 147
0xFF, 0xFF, 0x00, 0x0A, // 148 0xFF, 0xFF, 0x00, 0x0A, // 148
0xFF, 0xFF, 0x00, 0x0A, // 149 0xFF, 0xFF, 0x00, 0x0A, // 149
0xFF, 0xFF, 0x00, 0x0A, // 150 0xFF, 0xFF, 0x00, 0x0A, // 150
0xFF, 0xFF, 0x00, 0x0A, // 151 0xFF, 0xFF, 0x00, 0x0A, // 151
0xFF, 0xFF, 0x00, 0x0A, // 152 0xFF, 0xFF, 0x00, 0x0A, // 152
0xFF, 0xFF, 0x00, 0x0A, // 153 0xFF, 0xFF, 0x00, 0x0A, // 153
0xFF, 0xFF, 0x00, 0x0A, // 154 0xFF, 0xFF, 0x00, 0x0A, // 154
0xFF, 0xFF, 0x00, 0x0A, // 155 0xFF, 0xFF, 0x00, 0x0A, // 155
0xFF, 0xFF, 0x00, 0x0A, // 156 0xFF, 0xFF, 0x00, 0x0A, // 156
0xFF, 0xFF, 0x00, 0x0A, // 157 0xFF, 0xFF, 0x00, 0x0A, // 157
0xFF, 0xFF, 0x00, 0x0A, // 158 0xFF, 0xFF, 0x00, 0x0A, // 158
0xFF, 0xFF, 0x00, 0x0A, // 159 0xFF, 0xFF, 0x00, 0x0A, // 159
0xFF, 0xFF, 0x00, 0x0A, // 160 0xFF, 0xFF, 0x00, 0x0A, // 160
0x03, 0x6E, 0x04, 0x03, // 161 0x03, 0x6E, 0x04, 0x03, // 161
0x03, 0x72, 0x0A, 0x06, // 162 0x03, 0x72, 0x0A, 0x06, // 162
0x03, 0x7C, 0x0C, 0x07, // 163 0x03, 0x7C, 0x0C, 0x07, // 163
0x03, 0x88, 0x0A, 0x06, // 164 0x03, 0x88, 0x0A, 0x06, // 164
0x03, 0x92, 0x0A, 0x06, // 165 0x03, 0x92, 0x0A, 0x06, // 165
0x03, 0x9C, 0x04, 0x03, // 166 0x03, 0x9C, 0x04, 0x03, // 166
0x03, 0xA0, 0x0A, 0x06, // 167 0x03, 0xA0, 0x0A, 0x06, // 167
0x03, 0xAA, 0x0C, 0x07, // 168 0x03, 0xAA, 0x0C, 0x07, // 168
0x03, 0xB6, 0x0D, 0x08, // 169 0x03, 0xB6, 0x0D, 0x08, // 169
0x03, 0xC3, 0x07, 0x05, // 170 0x03, 0xC3, 0x07, 0x05, // 170
0x03, 0xCA, 0x0A, 0x06, // 171 0x03, 0xCA, 0x0A, 0x06, // 171
0x03, 0xD4, 0x09, 0x06, // 172 0x03, 0xD4, 0x09, 0x06, // 172
0x03, 0xDD, 0x03, 0x03, // 173 0x03, 0xDD, 0x03, 0x03, // 173
0x03, 0xE0, 0x0D, 0x08, // 174 0x03, 0xE0, 0x0D, 0x08, // 174
0x03, 0xED, 0x0B, 0x07, // 175 0x03, 0xED, 0x0B, 0x07, // 175
0x03, 0xF8, 0x07, 0x05, // 176 0x03, 0xF8, 0x07, 0x05, // 176
0x03, 0xFF, 0x0A, 0x06, // 177 0x03, 0xFF, 0x0A, 0x06, // 177
0x04, 0x09, 0x05, 0x04, // 178 0x04, 0x09, 0x05, 0x04, // 178
0x04, 0x0E, 0x05, 0x04, // 179 0x04, 0x0E, 0x05, 0x04, // 179
0x04, 0x13, 0x05, 0x04, // 180 0x04, 0x13, 0x05, 0x04, // 180
0x04, 0x18, 0x0A, 0x06, // 181 0x04, 0x18, 0x0A, 0x06, // 181
0x04, 0x22, 0x09, 0x06, // 182 0x04, 0x22, 0x09, 0x06, // 182
0x04, 0x2B, 0x03, 0x03, // 183 0x04, 0x2B, 0x03, 0x03, // 183
0x04, 0x2E, 0x0B, 0x07, // 184 0x04, 0x2E, 0x0B, 0x07, // 184
0x04, 0x39, 0x0B, 0x07, // 185 0x04, 0x39, 0x0B, 0x07, // 185
0x04, 0x44, 0x07, 0x05, // 186 0x04, 0x44, 0x07, 0x05, // 186
0x04, 0x4B, 0x0A, 0x06, // 187 0x04, 0x4B, 0x0A, 0x06, // 187
0x04, 0x55, 0x10, 0x09, // 188 0x04, 0x55, 0x10, 0x09, // 188
0x04, 0x65, 0x10, 0x09, // 189 0x04, 0x65, 0x10, 0x09, // 189
0x04, 0x75, 0x10, 0x09, // 190 0x04, 0x75, 0x10, 0x09, // 190
0x04, 0x85, 0x0A, 0x06, // 191 0x04, 0x85, 0x0A, 0x06, // 191
0x04, 0x8F, 0x0C, 0x07, // 192 0x04, 0x8F, 0x0C, 0x07, // 192
0x04, 0x9B, 0x0C, 0x07, // 193 0x04, 0x9B, 0x0C, 0x07, // 193
0x04, 0xA7, 0x0C, 0x07, // 194 0x04, 0xA7, 0x0C, 0x07, // 194
0x04, 0xB3, 0x0B, 0x07, // 195 0x04, 0xB3, 0x0B, 0x07, // 195
0x04, 0xBE, 0x0C, 0x07, // 196 0x04, 0xBE, 0x0C, 0x07, // 196
0x04, 0xCA, 0x0C, 0x07, // 197 0x04, 0xCA, 0x0C, 0x07, // 197
0x04, 0xD6, 0x0C, 0x07, // 198 0x04, 0xD6, 0x0C, 0x07, // 198
0x04, 0xE2, 0x0C, 0x07, // 199 0x04, 0xE2, 0x0C, 0x07, // 199
0x04, 0xEE, 0x0C, 0x07, // 200 0x04, 0xEE, 0x0C, 0x07, // 200
0x04, 0xFA, 0x0C, 0x07, // 201 0x04, 0xFA, 0x0C, 0x07, // 201
0x05, 0x06, 0x0C, 0x07, // 202 0x05, 0x06, 0x0C, 0x07, // 202
0x05, 0x12, 0x0C, 0x07, // 203 0x05, 0x12, 0x0C, 0x07, // 203
0x05, 0x1E, 0x0C, 0x07, // 204 0x05, 0x1E, 0x0C, 0x07, // 204
0x05, 0x2A, 0x0C, 0x07, // 205 0x05, 0x2A, 0x0C, 0x07, // 205
0x05, 0x36, 0x0C, 0x07, // 206 0x05, 0x36, 0x0C, 0x07, // 206
0x05, 0x42, 0x0C, 0x07, // 207 0x05, 0x42, 0x0C, 0x07, // 207
0x05, 0x4E, 0x0B, 0x07, // 208 0x05, 0x4E, 0x0B, 0x07, // 208
0x05, 0x59, 0x0C, 0x07, // 209 0x05, 0x59, 0x0C, 0x07, // 209
0x05, 0x65, 0x0B, 0x07, // 210 0x05, 0x65, 0x0B, 0x07, // 210
0x05, 0x70, 0x0C, 0x07, // 211 0x05, 0x70, 0x0C, 0x07, // 211
0x05, 0x7C, 0x0B, 0x07, // 212 0x05, 0x7C, 0x0B, 0x07, // 212
0x05, 0x87, 0x0C, 0x07, // 213 0x05, 0x87, 0x0C, 0x07, // 213
0x05, 0x93, 0x0C, 0x07, // 214 0x05, 0x93, 0x0C, 0x07, // 214
0x05, 0x9F, 0x0C, 0x07, // 215 0x05, 0x9F, 0x0C, 0x07, // 215
0x05, 0xAB, 0x0C, 0x07, // 216 0x05, 0xAB, 0x0C, 0x07, // 216
0x05, 0xB7, 0x0E, 0x08, // 217 0x05, 0xB7, 0x0E, 0x08, // 217
0x05, 0xC5, 0x0C, 0x07, // 218 0x05, 0xC5, 0x0C, 0x07, // 218
0x05, 0xD1, 0x0C, 0x07, // 219 0x05, 0xD1, 0x0C, 0x07, // 219
0x05, 0xDD, 0x0C, 0x07, // 220 0x05, 0xDD, 0x0C, 0x07, // 220
0x05, 0xE9, 0x0C, 0x07, // 221 0x05, 0xE9, 0x0C, 0x07, // 221
0x05, 0xF5, 0x0C, 0x07, // 222 0x05, 0xF5, 0x0C, 0x07, // 222
0x06, 0x01, 0x0C, 0x07, // 223 0x06, 0x01, 0x0C, 0x07, // 223
0x06, 0x0D, 0x0C, 0x07, // 224 0x06, 0x0D, 0x0C, 0x07, // 224
0x06, 0x19, 0x0C, 0x07, // 225 0x06, 0x19, 0x0C, 0x07, // 225
0x06, 0x25, 0x0C, 0x07, // 226 0x06, 0x25, 0x0C, 0x07, // 226
0x06, 0x31, 0x0B, 0x07, // 227 0x06, 0x31, 0x0B, 0x07, // 227
0x06, 0x3C, 0x0C, 0x07, // 228 0x06, 0x3C, 0x0C, 0x07, // 228
0x06, 0x48, 0x0B, 0x07, // 229 0x06, 0x48, 0x0B, 0x07, // 229
0x06, 0x53, 0x0C, 0x07, // 230 0x06, 0x53, 0x0C, 0x07, // 230
0x06, 0x5F, 0x0C, 0x07, // 231 0x06, 0x5F, 0x0C, 0x07, // 231
0x06, 0x6B, 0x0C, 0x07, // 232 0x06, 0x6B, 0x0C, 0x07, // 232
0x06, 0x77, 0x0C, 0x07, // 233 0x06, 0x77, 0x0C, 0x07, // 233
0x06, 0x83, 0x0C, 0x07, // 234 0x06, 0x83, 0x0C, 0x07, // 234
0x06, 0x8F, 0x0C, 0x07, // 235 0x06, 0x8F, 0x0C, 0x07, // 235
0x06, 0x9B, 0x0C, 0x07, // 236 0x06, 0x9B, 0x0C, 0x07, // 236
0x06, 0xA7, 0x0C, 0x07, // 237 0x06, 0xA7, 0x0C, 0x07, // 237
0x06, 0xB3, 0x0C, 0x07, // 238 0x06, 0xB3, 0x0C, 0x07, // 238
0x06, 0xBF, 0x0C, 0x07, // 239 0x06, 0xBF, 0x0C, 0x07, // 239
0x06, 0xCB, 0x0B, 0x07, // 240 0x06, 0xCB, 0x0B, 0x07, // 240
0x06, 0xD6, 0x0C, 0x07, // 241 0x06, 0xD6, 0x0C, 0x07, // 241
0x06, 0xE2, 0x0B, 0x07, // 242 0x06, 0xE2, 0x0B, 0x07, // 242
0x06, 0xED, 0x0C, 0x07, // 243 0x06, 0xED, 0x0C, 0x07, // 243
0x06, 0xF9, 0x0B, 0x07, // 244 0x06, 0xF9, 0x0B, 0x07, // 244
0x07, 0x04, 0x0C, 0x07, // 245 0x07, 0x04, 0x0C, 0x07, // 245
0x07, 0x10, 0x0C, 0x07, // 246 0x07, 0x10, 0x0C, 0x07, // 246
0x07, 0x1C, 0x0C, 0x07, // 247 0x07, 0x1C, 0x0C, 0x07, // 247
0x07, 0x28, 0x0C, 0x07, // 248 0x07, 0x28, 0x0C, 0x07, // 248
0x07, 0x34, 0x0E, 0x08, // 249 0x07, 0x34, 0x0E, 0x08, // 249
0x07, 0x42, 0x0C, 0x07, // 250 0x07, 0x42, 0x0C, 0x07, // 250
0x07, 0x4E, 0x0C, 0x07, // 251 0x07, 0x4E, 0x0C, 0x07, // 251
0x07, 0x5A, 0x0C, 0x07, // 252 0x07, 0x5A, 0x0C, 0x07, // 252
0x07, 0x66, 0x0C, 0x07, // 253 0x07, 0x66, 0x0C, 0x07, // 253
0x07, 0x72, 0x0C, 0x07, // 254 0x07, 0x72, 0x0C, 0x07, // 254
0x07, 0x7E, 0x0C, 0x07, // 255 0x07, 0x7E, 0x0C, 0x07, // 255
// Font Data: // Font Data:
0x00, 0x00, 0xF8, 0x02, // 33 0x00, 0x00, 0xF8, 0x02, // 33
0x38, 0x00, 0x00, 0x00, 0x38, // 34 0x38, 0x00, 0x00, 0x00, 0x38, // 34
0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35 0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35
0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36 0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36
0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37 0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37
0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38 0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38
0x38, // 39 0x38, // 39
0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40 0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40
0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41 0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41
0x28, 0x00, 0x18, 0x00, 0x28, // 42 0x28, 0x00, 0x18, 0x00, 0x28, // 42
0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43 0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43
0x00, 0x00, 0x00, 0x06, // 44 0x00, 0x00, 0x00, 0x06, // 44
0x80, 0x00, 0x80, // 45 0x80, 0x00, 0x80, // 45
0x00, 0x00, 0x00, 0x02, // 46 0x00, 0x00, 0x00, 0x02, // 46
0x00, 0x03, 0xE0, 0x00, 0x18, // 47 0x00, 0x03, 0xE0, 0x00, 0x18, // 47
0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48
0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49
0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50 0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50
0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51
0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52 0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52
0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53 0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53
0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54 0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54
0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55 0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55
0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56 0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56
0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57 0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57
0x00, 0x00, 0x20, 0x02, // 58 0x00, 0x00, 0x20, 0x02, // 58
0x00, 0x00, 0x20, 0x06, // 59 0x00, 0x00, 0x20, 0x06, // 59
0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60 0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60
0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61
0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62 0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62
0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63
0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64 0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64
0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67
0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68 0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69
0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70
0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71 0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71
0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72
0x00, 0x00, 0xF8, 0x03, // 73 0x00, 0x00, 0xF8, 0x03, // 73
0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74
0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75 0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75
0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76
0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77
0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79
0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81
0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82
0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83 0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83
0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84
0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85 0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85
0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86 0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86
0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87 0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87
0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88 0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88
0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89
0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90 0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90
0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91 0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91
0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92 0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92
0x08, 0x08, 0xF8, 0x0F, // 93 0x08, 0x08, 0xF8, 0x0F, // 93
0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94 0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94
0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95
0x08, 0x00, 0x10, // 96 0x08, 0x00, 0x10, // 96
0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97 0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97
0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98 0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100
0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101
0x20, 0x00, 0xF0, 0x03, 0x28, // 102 0x20, 0x00, 0xF0, 0x03, 0x28, // 102
0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103 0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103
0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104 0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104
0x00, 0x00, 0xE8, 0x03, // 105 0x00, 0x00, 0xE8, 0x03, // 105
0x00, 0x08, 0xE8, 0x07, // 106 0x00, 0x08, 0xE8, 0x07, // 106
0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107 0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107
0x00, 0x00, 0xF8, 0x03, // 108 0x00, 0x00, 0xF8, 0x03, // 108
0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109
0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111
0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112 0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113
0x00, 0x00, 0xE0, 0x03, 0x20, // 114 0x00, 0x00, 0xE0, 0x03, 0x20, // 114
0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115 0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115
0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116 0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116
0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117 0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117
0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118
0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119 0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119
0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120
0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121 0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121
0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122 0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122
0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123 0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123
0x00, 0x00, 0xF8, 0x0F, // 124 0x00, 0x00, 0xF8, 0x0F, // 124
0x08, 0x08, 0x78, 0x0F, 0x80, // 125 0x08, 0x08, 0x78, 0x0F, 0x80, // 125
0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126 0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126
0x00, 0x00, 0xA0, 0x0F, // 161 0x00, 0x00, 0xA0, 0x0F, // 161
0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162
0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163 0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163
0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164 0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164
0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165 0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165
0x00, 0x00, 0x38, 0x0F, // 166 0x00, 0x00, 0x38, 0x0F, // 166
0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167 0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167
0x00, 0x00, 0xE0, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x20, 0x02, // 168 0x00, 0x00, 0xE0, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x20, 0x02, // 168
0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169 0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169
0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170
0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171 0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172
0x80, 0x00, 0x80, // 173 0x80, 0x00, 0x80, // 173
0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174 0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175
0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176 0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176
0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177 0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177
0x48, 0x00, 0x68, 0x00, 0x58, // 178 0x48, 0x00, 0x68, 0x00, 0x58, // 178
0x48, 0x00, 0x58, 0x00, 0x68, // 179 0x48, 0x00, 0x58, 0x00, 0x68, // 179
0x00, 0x00, 0x10, 0x00, 0x08, // 180 0x00, 0x00, 0x10, 0x00, 0x08, // 180
0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181
0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182 0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182
0x00, 0x00, 0x40, // 183 0x00, 0x00, 0x40, // 183
0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0xC0, // 184 0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0xC0, // 184
0x00, 0x00, 0xF0, 0x03, 0x40, 0x00, 0x80, 0x00, 0xF8, 0x03, 0x08, // 185 0x00, 0x00, 0xF0, 0x03, 0x40, 0x00, 0x80, 0x00, 0xF8, 0x03, 0x08, // 185
0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186 0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186
0x00, 0x00, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, // 187 0x00, 0x00, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, // 187
0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0xC0, 0x00, 0x20, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 188 0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0xC0, 0x00, 0x20, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 188
0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189 0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189
0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190 0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190
0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191
0x00, 0x00, 0xF0, 0x03, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xF0, 0x03, // 192 0x00, 0x00, 0xF0, 0x03, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xF0, 0x03, // 192
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x88, 0x01, // 193 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x88, 0x01, // 193
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 194 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 194
0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x18, // 195 0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x18, // 195
0x00, 0x00, 0x00, 0x02, 0xFC, 0x03, 0x04, 0x02, 0xFC, 0x03, 0x00, 0x02, // 196 0x00, 0x00, 0x00, 0x02, 0xFC, 0x03, 0x04, 0x02, 0xFC, 0x03, 0x00, 0x02, // 196
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x08, 0x02, // 197 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x08, 0x02, // 197
0x00, 0x00, 0xB8, 0x03, 0x40, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xB8, 0x03, // 198 0x00, 0x00, 0xB8, 0x03, 0x40, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xB8, 0x03, // 198
0x00, 0x00, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 199 0x00, 0x00, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 199
0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0xF8, 0x03, // 200 0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0xF8, 0x03, // 200
0x00, 0x00, 0xE0, 0x03, 0x08, 0x01, 0x90, 0x00, 0x48, 0x00, 0xE0, 0x03, // 201 0x00, 0x00, 0xE0, 0x03, 0x08, 0x01, 0x90, 0x00, 0x48, 0x00, 0xE0, 0x03, // 201
0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xA0, 0x00, 0x10, 0x01, 0x08, 0x02, // 202 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xA0, 0x00, 0x10, 0x01, 0x08, 0x02, // 202
0x00, 0x00, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 203 0x00, 0x00, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 203
0x00, 0x00, 0xF8, 0x03, 0x10, 0x00, 0x60, 0x00, 0x10, 0x00, 0xF8, 0x03, // 204 0x00, 0x00, 0xF8, 0x03, 0x10, 0x00, 0x60, 0x00, 0x10, 0x00, 0xF8, 0x03, // 204
0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 205 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 205
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 206 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 206
0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 207 0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 207
0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 208 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 208
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 209 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 209
0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 210 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 210
0x00, 0x00, 0x38, 0x00, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0xF8, 0x01, // 211 0x00, 0x00, 0x38, 0x00, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0xF8, 0x01, // 211
0x00, 0x00, 0x70, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x88, 0x00, 0x70, // 212 0x00, 0x00, 0x70, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x88, 0x00, 0x70, // 212
0x00, 0x00, 0x18, 0x03, 0xA0, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x18, 0x03, // 213 0x00, 0x00, 0x18, 0x03, 0xA0, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x18, 0x03, // 213
0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, // 214 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, // 214
0x00, 0x00, 0x38, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 215 0x00, 0x00, 0x38, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 215
0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, // 216 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, // 216
0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x06, // 217 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x06, // 217
0x00, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 218 0x00, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 218
0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xF8, 0x03, // 219 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xF8, 0x03, // 219
0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 220 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 220
0x00, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 221 0x00, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 221
0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xF0, 0x01, 0x08, 0x02, 0xF0, 0x01, // 222 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xF0, 0x01, 0x08, 0x02, 0xF0, 0x01, // 222
0x00, 0x00, 0x30, 0x02, 0x48, 0x01, 0xC8, 0x00, 0x48, 0x00, 0xF8, 0x03, // 223 0x00, 0x00, 0x30, 0x02, 0x48, 0x01, 0xC8, 0x00, 0x48, 0x00, 0xF8, 0x03, // 223
0x00, 0x00, 0x00, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x03, // 224 0x00, 0x00, 0x00, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x03, // 224
0x00, 0x00, 0xE0, 0x01, 0x50, 0x02, 0x50, 0x02, 0x48, 0x02, 0x88, 0x01, // 225 0x00, 0x00, 0xE0, 0x01, 0x50, 0x02, 0x50, 0x02, 0x48, 0x02, 0x88, 0x01, // 225
0x00, 0x00, 0xE0, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 226 0x00, 0x00, 0xE0, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 226
0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, // 227 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, // 227
0x00, 0x00, 0x00, 0x02, 0xC0, 0x03, 0x20, 0x02, 0xE0, 0x03, 0x00, 0x02, // 228 0x00, 0x00, 0x00, 0x02, 0xC0, 0x03, 0x20, 0x02, 0xE0, 0x03, 0x00, 0x02, // 228
0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, // 229 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, // 229
0x00, 0x00, 0x60, 0x03, 0x80, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 230 0x00, 0x00, 0x60, 0x03, 0x80, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 230
0x00, 0x00, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 231 0x00, 0x00, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 231
0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 232 0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 232
0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x98, 0x00, 0x40, 0x00, 0xE0, 0x03, // 233 0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x98, 0x00, 0x40, 0x00, 0xE0, 0x03, // 233
0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 234 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 234
0x00, 0x00, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 235 0x00, 0x00, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 235
0x00, 0x00, 0xE0, 0x03, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 236 0x00, 0x00, 0xE0, 0x03, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 236
0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 237 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 237
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 238 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 238
0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 239 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 239
0x00, 0x00, 0xE0, 0x03, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 240 0x00, 0x00, 0xE0, 0x03, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 240
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x40, 0x02, // 241 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x40, 0x02, // 241
0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, // 242 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, // 242
0x00, 0x00, 0x60, 0x00, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0xE0, 0x01, // 243 0x00, 0x00, 0x60, 0x00, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0xE0, 0x01, // 243
0x00, 0x00, 0xC0, 0x00, 0x20, 0x01, 0xE0, 0x03, 0x20, 0x01, 0xC0, // 244 0x00, 0x00, 0xC0, 0x00, 0x20, 0x01, 0xE0, 0x03, 0x20, 0x01, 0xC0, // 244
0x00, 0x00, 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 245 0x00, 0x00, 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 245
0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, // 246 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, // 246
0x00, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 247 0x00, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 247
0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, // 248 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, // 248
0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 249 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 249
0x00, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 250 0x00, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 250
0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, 0xE0, 0x03, // 251 0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, 0xE0, 0x03, // 251
0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 252 0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 252
0x00, 0x00, 0x40, 0x01, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, // 253 0x00, 0x00, 0x40, 0x01, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, // 253
0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, 0xC0, 0x01, // 254 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, 0xC0, 0x01, // 254
0x00, 0x00, 0x40, 0x02, 0xA0, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0xE0, 0x03, // 255 0x00, 0x00, 0x40, 0x02, 0xA0, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0xE0, 0x03, // 255
}; };
+18 -15
View File
@@ -6,24 +6,27 @@ 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 uint8_t imgSatellite[] PROGMEM = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 }; const uint8_t imgSatellite[] PROGMEM = {0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70};
const uint8_t imgUSB[] PROGMEM = { 0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C }; 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 imgPower[] PROGMEM = {0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08,
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C }; 0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22};
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF }; const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C};
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF }; const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF};
const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF};
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) #if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
const uint8_t imgQuestionL1[] PROGMEM = { 0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff }; const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = { 0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f }; const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
const uint8_t imgInfoL1[] PROGMEM = { 0xff, 0x01, 0x01, 0x01, 0x1e, 0x7f, 0x1e, 0x01, 0x01, 0x01, 0x01, 0xff }; const uint8_t imgInfoL1[] PROGMEM = {0xff, 0x01, 0x01, 0x01, 0x1e, 0x7f, 0x1e, 0x01, 0x01, 0x01, 0x01, 0xff};
const uint8_t imgInfoL2[] PROGMEM = { 0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f }; const uint8_t imgInfoL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
const uint8_t imgSFL1[] PROGMEM = { 0xb6, 0x8f, 0x19, 0x11, 0x31, 0xe3, 0xc2, 0x01, 0x01, 0xf9, 0xf9, 0x89, 0x89, 0x89, 0x09, 0xeb}; const uint8_t imgSFL1[] PROGMEM = {0xb6, 0x8f, 0x19, 0x11, 0x31, 0xe3, 0xc2, 0x01,
const uint8_t imgSFL2[] PROGMEM = { 0x0e, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x00, 0x0f, 0x0f, 0x00, 0x08, 0x08, 0x08, 0x0f}; 0x01, 0xf9, 0xf9, 0x89, 0x89, 0x89, 0x09, 0xeb};
const uint8_t imgSFL2[] PROGMEM = {0x0e, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08,
0x00, 0x0f, 0x0f, 0x00, 0x08, 0x08, 0x08, 0x0f};
#else #else
const uint8_t imgInfo[] PROGMEM = { 0xff, 0x81, 0x00, 0xfb, 0xfb, 0x00, 0x81, 0xff }; const uint8_t imgInfo[] PROGMEM = {0xff, 0x81, 0x00, 0xfb, 0xfb, 0x00, 0x81, 0xff};
const uint8_t imgQuestion[] PROGMEM = { 0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1, 0xdf }; const uint8_t imgQuestion[] PROGMEM = {0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1, 0xdf};
const uint8_t imgSF[] PROGMEM = { 0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5}; const uint8_t imgSF[] PROGMEM = {0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5};
#endif #endif
#include "img/icon.xbm" #include "img/icon.xbm"
+4 -6
View File
@@ -3,9 +3,7 @@
InputBroker *inputBroker; InputBroker *inputBroker;
InputBroker::InputBroker() InputBroker::InputBroker(){};
{
};
void InputBroker::registerSource(Observable<const InputEvent *> *source) void InputBroker::registerSource(Observable<const InputEvent *> *source)
{ {
@@ -14,7 +12,7 @@ void InputBroker::registerSource(Observable<const InputEvent *> *source)
int InputBroker::handleInputEvent(const InputEvent *event) int InputBroker::handleInputEvent(const InputEvent *event)
{ {
powerFSM.trigger(EVENT_INPUT); powerFSM.trigger(EVENT_INPUT);
this->notifyObservers(event); this->notifyObservers(event);
return 0; return 0;
} }
+2 -3
View File
@@ -5,12 +5,11 @@
#define MATRIXKEY 0xFE #define MATRIXKEY 0xFE
typedef struct _InputEvent { typedef struct _InputEvent {
const char* source; const char *source;
char inputEvent; char inputEvent;
char kbchar; char kbchar;
} InputEvent; } InputEvent;
class InputBroker : class InputBroker : public Observable<const InputEvent *>
public Observable<const InputEvent *>
{ {
CallbackObserver<InputBroker, const InputEvent *> inputEventObserver = CallbackObserver<InputBroker, const InputEvent *> inputEventObserver =
CallbackObserver<InputBroker, const InputEvent *>(this, &InputBroker::handleInputEvent); CallbackObserver<InputBroker, const InputEvent *>(this, &InputBroker::handleInputEvent);
+2 -2
View File
@@ -34,7 +34,7 @@ void RotaryEncoderInterruptBase::init(
int32_t RotaryEncoderInterruptBase::runOnce() int32_t RotaryEncoderInterruptBase::runOnce()
{ {
InputEvent e; InputEvent e;
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.source = this->_originName; e.source = this->_originName;
if (this->action == ROTARY_ACTION_PRESSED) { if (this->action == ROTARY_ACTION_PRESSED) {
@@ -48,7 +48,7 @@ int32_t RotaryEncoderInterruptBase::runOnce()
e.inputEvent = this->_eventCcw; e.inputEvent = this->_eventCcw;
} }
if (e.inputEvent != ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
this->notifyObservers(&e); this->notifyObservers(&e);
} }
+3 -3
View File
@@ -33,8 +33,8 @@ class RotaryEncoderInterruptBase : public Observable<const InputEvent *>, public
private: private:
uint8_t _pinA = 0; uint8_t _pinA = 0;
uint8_t _pinB = 0; uint8_t _pinB = 0;
char _eventCw = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; char _eventCw = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventCcw = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; char _eventCcw = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventPressed = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; char _eventPressed = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
const char *_originName; const char *_originName;
}; };
+1 -2
View File
@@ -8,8 +8,7 @@
* to your device as you wish, but you always need to have separate event * to your device as you wish, but you always need to have separate event
* handlers, thus you need to have a RotaryEncoderInterrupt implementation. * handlers, thus you need to have a RotaryEncoderInterrupt implementation.
*/ */
class RotaryEncoderInterruptImpl1 : class RotaryEncoderInterruptImpl1 : public RotaryEncoderInterruptBase
public RotaryEncoderInterruptBase
{ {
public: public:
RotaryEncoderInterruptImpl1(); RotaryEncoderInterruptImpl1();
+5 -9
View File
@@ -1,16 +1,13 @@
#include "configuration.h"
#include "UpDownInterruptBase.h" #include "UpDownInterruptBase.h"
#include "configuration.h"
UpDownInterruptBase::UpDownInterruptBase( UpDownInterruptBase::UpDownInterruptBase(const char *name)
const char *name)
{ {
this->_originName = name; this->_originName = name;
} }
void UpDownInterruptBase::init( void UpDownInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinPress, char eventDown, char eventUp, char eventPressed,
uint8_t pinDown, uint8_t pinUp, uint8_t pinPress, void (*onIntDown)(), void (*onIntUp)(), void (*onIntPress)())
char eventDown, char eventUp, char eventPressed,
void (*onIntDown)(), void (*onIntUp)(), void (*onIntPress)())
{ {
this->_pinDown = pinDown; this->_pinDown = pinDown;
this->_pinUp = pinUp; this->_pinUp = pinUp;
@@ -26,8 +23,7 @@ void UpDownInterruptBase::init(
attachInterrupt(this->_pinDown, onIntDown, RISING); attachInterrupt(this->_pinDown, onIntDown, RISING);
attachInterrupt(this->_pinUp, onIntUp, RISING); attachInterrupt(this->_pinUp, onIntUp, RISING);
LOG_DEBUG("GPIO initialized (%d, %d, %d)\n", LOG_DEBUG("GPIO initialized (%d, %d, %d)\n", this->_pinDown, this->_pinUp, pinPress);
this->_pinDown, this->_pinUp, pinPress);
} }
void UpDownInterruptBase::intPressHandler() void UpDownInterruptBase::intPressHandler()
+3 -3
View File
@@ -16,8 +16,8 @@ class UpDownInterruptBase : public Observable<const InputEvent *>
private: private:
uint8_t _pinDown = 0; uint8_t _pinDown = 0;
uint8_t _pinUp = 0; uint8_t _pinUp = 0;
char _eventDown = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; char _eventDown = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventUp = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; char _eventUp = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventPressed = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; char _eventPressed = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
const char *_originName; const char *_originName;
}; };
+3 -3
View File
@@ -17,9 +17,9 @@ void UpDownInterruptImpl1::init()
uint8_t pinDown = moduleConfig.canned_message.inputbroker_pin_b; uint8_t pinDown = moduleConfig.canned_message.inputbroker_pin_b;
uint8_t pinPress = moduleConfig.canned_message.inputbroker_pin_press; uint8_t pinPress = moduleConfig.canned_message.inputbroker_pin_press;
char eventDown = static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_DOWN); char eventDown = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN);
char eventUp = static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_UP); char eventUp = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP);
char eventPressed = static_cast<char>(ModuleConfig_CannedMessageConfig_InputEventChar_SELECT); char eventPressed = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT);
UpDownInterruptBase::init(pinDown, pinUp, pinPress, eventDown, eventUp, eventPressed, UpDownInterruptImpl1::handleIntDown, UpDownInterruptBase::init(pinDown, pinUp, pinPress, eventDown, eventUp, eventPressed, UpDownInterruptImpl1::handleIntDown,
UpDownInterruptImpl1::handleIntUp, UpDownInterruptImpl1::handleIntPressed); UpDownInterruptImpl1::handleIntUp, UpDownInterruptImpl1::handleIntPressed);
+1 -2
View File
@@ -1,8 +1,7 @@
#pragma once #pragma once
#include "UpDownInterruptBase.h" #include "UpDownInterruptBase.h"
class UpDownInterruptImpl1 : class UpDownInterruptImpl1 : public UpDownInterruptBase
public UpDownInterruptBase
{ {
public: public:
UpDownInterruptImpl1(); UpDownInterruptImpl1();
+2 -6
View File
@@ -3,15 +3,11 @@
CardKbI2cImpl *cardKbI2cImpl; CardKbI2cImpl *cardKbI2cImpl;
CardKbI2cImpl::CardKbI2cImpl() : CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}
KbI2cBase("cardKB")
{
}
void CardKbI2cImpl::init() void CardKbI2cImpl::init()
{ {
if (cardkb_found != CARDKB_ADDR) if (cardkb_found != CARDKB_ADDR) {
{
disable(); disable();
return; return;
} }
+1 -2
View File
@@ -9,8 +9,7 @@
* to your device as you wish, but you always need to have separate event * to your device as you wish, but you always need to have separate event
* handlers, thus you need to have a RotaryEncoderInterrupt implementation. * handlers, thus you need to have a RotaryEncoderInterrupt implementation.
*/ */
class CardKbI2cImpl : class CardKbI2cImpl : public KbI2cBase
public KbI2cBase
{ {
public: public:
CardKbI2cImpl(); CardKbI2cImpl();
+30 -30
View File
@@ -12,32 +12,32 @@ KbI2cBase::KbI2cBase(const char *name) : concurrency::OSThread(name)
uint8_t read_from_14004(uint8_t reg, uint8_t *data, uint8_t length) uint8_t read_from_14004(uint8_t reg, uint8_t *data, uint8_t length)
{ {
uint8_t readflag = 0; uint8_t readflag = 0;
Wire.beginTransmission(CARDKB_ADDR); Wire.beginTransmission(CARDKB_ADDR);
Wire.write(reg); Wire.write(reg);
Wire.endTransmission(); // stop transmitting Wire.endTransmission(); // stop transmitting
delay(20); delay(20);
Wire.requestFrom(CARDKB_ADDR, (int)length); Wire.requestFrom(CARDKB_ADDR, (int)length);
int i = 0; int i = 0;
while ( Wire.available() ) // slave may send less than requested while (Wire.available()) // slave may send less than requested
{ {
data[i++] = Wire.read(); // receive a byte as a proper uint8_t data[i++] = Wire.read(); // receive a byte as a proper uint8_t
readflag = 1; readflag = 1;
} }
return readflag; return readflag;
} }
void write_to_14004(uint8_t reg, uint8_t data) void write_to_14004(uint8_t reg, uint8_t data)
{ {
Wire.beginTransmission(CARDKB_ADDR); Wire.beginTransmission(CARDKB_ADDR);
Wire.write(reg); Wire.write(reg);
Wire.write(data); Wire.write(data);
Wire.endTransmission(); // stop transmitting Wire.endTransmission(); // stop transmitting
} }
int32_t KbI2cBase::runOnce() int32_t KbI2cBase::runOnce()
{ {
if (cardkb_found != CARDKB_ADDR){ if (cardkb_found != CARDKB_ADDR) {
// Input device is not detected. // Input device is not detected.
return INT32_MAX; return INT32_MAX;
} }
@@ -48,7 +48,7 @@ int32_t KbI2cBase::runOnce()
uint8_t PrintDataBuf = 0; uint8_t PrintDataBuf = 0;
if (read_from_14004(0x01, rDataBuf, 0x04) == 1) { if (read_from_14004(0x01, rDataBuf, 0x04) == 1) {
for (uint8_t aCount = 0; aCount < 0x04; aCount++) { for (uint8_t aCount = 0; aCount < 0x04; aCount++) {
for (uint8_t bCount = 0; bCount < 0x04; bCount++ ) { for (uint8_t bCount = 0; bCount < 0x04; bCount++) {
if (((rDataBuf[aCount] >> bCount) & 0x01) == 0x01) { if (((rDataBuf[aCount] >> bCount) & 0x01) == 0x01) {
PrintDataBuf = aCount * 0x04 + bCount + 1; PrintDataBuf = aCount * 0x04 + bCount + 1;
} }
@@ -70,35 +70,35 @@ int32_t KbI2cBase::runOnce()
while (Wire.available()) { while (Wire.available()) {
char c = Wire.read(); char c = Wire.read();
InputEvent e; InputEvent e;
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.source = this->_originName; e.source = this->_originName;
switch (c) { switch (c) {
case 0x1b: // ESC case 0x1b: // ESC
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
break; break;
case 0x08: // Back case 0x08: // Back
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_BACK; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
e.kbchar = c; e.kbchar = c;
break; break;
case 0xb5: // Up case 0xb5: // Up
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_UP; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
break; break;
case 0xb6: // Down case 0xb6: // Down
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_DOWN; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
break; break;
case 0xb4: // Left case 0xb4: // Left
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_LEFT; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
e.kbchar = c; e.kbchar = c;
break; break;
case 0xb7: // Right case 0xb7: // Right
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = c; e.kbchar = c;
break; break;
case 0x0d: // Enter case 0x0d: // Enter
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
break; break;
case 0x00: //nopress case 0x00: // nopress
e.inputEvent = ModuleConfig_CannedMessageConfig_InputEventChar_NONE; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
break; break;
default: // all other keys default: // all other keys
e.inputEvent = ANYKEY; e.inputEvent = ANYKEY;
@@ -106,7 +106,7 @@ int32_t KbI2cBase::runOnce()
break; break;
} }
if (e.inputEvent != ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
this->notifyObservers(&e); this->notifyObservers(&e);
} }
} }
+2 -4
View File
@@ -1,11 +1,9 @@
#pragma once #pragma once
#include "SinglePortModule.h" // TODO: what header file to include?
#include "InputBroker.h" #include "InputBroker.h"
#include "SinglePortModule.h" // TODO: what header file to include?
class KbI2cBase : class KbI2cBase : public Observable<const InputEvent *>, public concurrency::OSThread
public Observable<const InputEvent *>,
public concurrency::OSThread
{ {
public: public:
explicit KbI2cBase(const char *name); explicit KbI2cBase(const char *name);
+13 -7
View File
@@ -98,7 +98,7 @@ uint32_t serialSinceMsec;
bool pmu_found; bool pmu_found;
// Array map of sensor types (as array index) and i2c address as value we'll find in the i2c scan // Array map of sensor types (as array index) and i2c address as value we'll find in the i2c scan
uint8_t nodeTelemetrySensorsMap[_TelemetrySensorType_MAX + 1] = { uint8_t nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1] = {
0}; // one is enough, missing elements will be initialized to 0 anyway. 0}; // one is enough, missing elements will be initialized to 0 anyway.
Router *router = NULL; // Users of router don't care what sort of subclass implements that API Router *router = NULL; // Users of router don't care what sort of subclass implements that API
@@ -302,9 +302,13 @@ void setup()
playStartMelody(); playStartMelody();
// fixed screen override? // fixed screen override?
if (config.display.oled != Config_DisplayConfig_OledType_OLED_AUTO) if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO)
screen_model = config.display.oled; screen_model = config.display.oled;
#if defined(USE_SH1107)
screen_model = Config_DisplayConfig_OledType_OLED_SH1107; // set dimension of 128x128
#endif
// Init our SPI controller (must be before screen and lora) // Init our SPI controller (must be before screen and lora)
initSPI(); initSPI();
#ifndef ARCH_ESP32 #ifndef ARCH_ESP32
@@ -337,7 +341,7 @@ void setup()
// Do this after service.init (because that clears error_code) // Do this after service.init (because that clears error_code)
#ifdef HAS_PMU #ifdef HAS_PMU
if (!pmu_found) if (!pmu_found)
RECORD_CRITICALERROR(CriticalErrorCode_NO_AXP192); // Record a hardware fault for missing hardware RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_NO_AXP192); // Record a hardware fault for missing hardware
#endif #endif
// Don't call screen setup until after nodedb is setup (because we need // Don't call screen setup until after nodedb is setup (because we need
@@ -450,9 +454,9 @@ void setup()
// check if the radio chip matches the selected region // check if the radio chip matches the selected region
if ((config.lora.region == Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) { if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) {
LOG_WARN("Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n"); LOG_WARN("Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n");
config.lora.region = Config_LoRaConfig_RegionCode_UNSET; config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
nodeDB.saveToDisk(SEGMENT_CONFIG); nodeDB.saveToDisk(SEGMENT_CONFIG);
if (!rIf->reconfigure()) { if (!rIf->reconfigure()) {
LOG_WARN("Reconfigure failed, rebooting\n"); LOG_WARN("Reconfigure failed, rebooting\n");
@@ -486,13 +490,15 @@ void setup()
airTime = new AirTime(); airTime = new AirTime();
if (!rIf) if (!rIf)
RECORD_CRITICALERROR(CriticalErrorCode_NO_RADIO); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_NO_RADIO);
else { else {
router->addInterface(rIf); router->addInterface(rIf);
// Calculate and save the bit rate to myNodeInfo // Calculate and save the bit rate to myNodeInfo
// TODO: This needs to be added what ever method changes the channel from the phone. // TODO: This needs to be added what ever method changes the channel from the phone.
myNodeInfo.bitrate = (float(Constants_DATA_PAYLOAD_LEN) / (float(rIf->getPacketTime(Constants_DATA_PAYLOAD_LEN)))) * 1000; myNodeInfo.bitrate =
(float(meshtastic_Constants_DATA_PAYLOAD_LEN) / (float(rIf->getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN)))) *
1000;
LOG_DEBUG("myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate); LOG_DEBUG("myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
} }
+1 -1
View File
@@ -26,7 +26,7 @@ extern bool isUSBPowered;
extern ATECCX08A atecc; extern ATECCX08A atecc;
#endif #endif
extern uint8_t nodeTelemetrySensorsMap[_TelemetrySensorType_MAX + 1]; extern uint8_t nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
extern int TCPPort; // set by Portduino extern int TCPPort; // set by Portduino
+38 -39
View File
@@ -45,23 +45,23 @@ int16_t Channels::generateHash(ChannelIndex channelNum)
/** /**
* Validate a channel, fixing any errors as needed * Validate a channel, fixing any errors as needed
*/ */
Channel &Channels::fixupChannel(ChannelIndex chIndex) meshtastic_Channel &Channels::fixupChannel(ChannelIndex chIndex)
{ {
Channel &ch = getByIndex(chIndex); meshtastic_Channel &ch = getByIndex(chIndex);
ch.index = chIndex; // Preinit the index so it be ready to share with the phone (we'll never change it later) ch.index = chIndex; // Preinit the index so it be ready to share with the phone (we'll never change it later)
if (!ch.has_settings) { if (!ch.has_settings) {
// No settings! Must disable and skip // No settings! Must disable and skip
ch.role = Channel_Role_DISABLED; ch.role = meshtastic_Channel_Role_DISABLED;
memset(&ch.settings, 0, sizeof(ch.settings)); memset(&ch.settings, 0, sizeof(ch.settings));
ch.has_settings = true; ch.has_settings = true;
} else { } else {
ChannelSettings &channelSettings = ch.settings; meshtastic_ChannelSettings &meshtastic_channelSettings = ch.settings;
// Convert the old string "Default" to our new short representation // Convert the old string "Default" to our new short representation
if (strcmp(channelSettings.name, "Default") == 0) if (strcmp(meshtastic_channelSettings.name, "Default") == 0)
*channelSettings.name = '\0'; *meshtastic_channelSettings.name = '\0';
} }
hashes[chIndex] = generateHash(chIndex); hashes[chIndex] = generateHash(chIndex);
@@ -74,11 +74,11 @@ Channel &Channels::fixupChannel(ChannelIndex chIndex)
*/ */
void Channels::initDefaultChannel(ChannelIndex chIndex) void Channels::initDefaultChannel(ChannelIndex chIndex)
{ {
Channel &ch = getByIndex(chIndex); meshtastic_Channel &ch = getByIndex(chIndex);
ChannelSettings &channelSettings = ch.settings; meshtastic_ChannelSettings &channelSettings = ch.settings;
Config_LoRaConfig &loraConfig = config.lora; meshtastic_Config_LoRaConfig &loraConfig = config.lora;
loraConfig.modem_preset = Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast
loraConfig.use_preset = true; loraConfig.use_preset = true;
loraConfig.tx_power = 0; // default loraConfig.tx_power = 0; // default
uint8_t defaultpskIndex = 1; uint8_t defaultpskIndex = 1;
@@ -87,25 +87,25 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
strncpy(channelSettings.name, "", sizeof(channelSettings.name)); strncpy(channelSettings.name, "", sizeof(channelSettings.name));
ch.has_settings = true; ch.has_settings = true;
ch.role = Channel_Role_PRIMARY; ch.role = meshtastic_Channel_Role_PRIMARY;
} }
CryptoKey Channels::getKey(ChannelIndex chIndex) CryptoKey Channels::getKey(ChannelIndex chIndex)
{ {
Channel &ch = getByIndex(chIndex); meshtastic_Channel &ch = getByIndex(chIndex);
const ChannelSettings &channelSettings = ch.settings; const meshtastic_ChannelSettings &channelSettings = ch.settings;
assert(ch.has_settings); assert(ch.has_settings);
CryptoKey k; CryptoKey k;
memset(k.bytes, 0, sizeof(k.bytes)); // In case the user provided a short key, we want to pad the rest with zeros memset(k.bytes, 0, sizeof(k.bytes)); // In case the user provided a short key, we want to pad the rest with zeros
if (ch.role == Channel_Role_DISABLED) { if (ch.role == meshtastic_Channel_Role_DISABLED) {
k.length = -1; // invalid k.length = -1; // invalid
} else { } else {
memcpy(k.bytes, channelSettings.psk.bytes, channelSettings.psk.size); memcpy(k.bytes, channelSettings.psk.bytes, channelSettings.psk.size);
k.length = channelSettings.psk.size; k.length = channelSettings.psk.size;
if (k.length == 0) { if (k.length == 0) {
if (ch.role == Channel_Role_SECONDARY) { if (ch.role == meshtastic_Channel_Role_SECONDARY) {
LOG_DEBUG("Unset PSK for secondary channel %s. using primary key\n", ch.settings.name); LOG_DEBUG("Unset PSK for secondary channel %s. using primary key\n", ch.settings.name);
k = getKey(primaryIndex); k = getKey(primaryIndex);
} else } else
@@ -120,7 +120,7 @@ CryptoKey Channels::getKey(ChannelIndex chIndex)
else if (oemStore.oem_aes_key.size > 1) { else if (oemStore.oem_aes_key.size > 1) {
// Use the OEM key // Use the OEM key
LOG_DEBUG("Using OEM Key with %d bytes\n", oemStore.oem_aes_key.size); LOG_DEBUG("Using OEM Key with %d bytes\n", oemStore.oem_aes_key.size);
memcpy(k.bytes, oemStore.oem_aes_key.bytes , oemStore.oem_aes_key.size); memcpy(k.bytes, oemStore.oem_aes_key.bytes, oemStore.oem_aes_key.size);
k.length = oemStore.oem_aes_key.size; k.length = oemStore.oem_aes_key.size;
// Bump up the last byte of PSK as needed // Bump up the last byte of PSK as needed
uint8_t *last = k.bytes + oemStore.oem_aes_key.size - 1; uint8_t *last = k.bytes + oemStore.oem_aes_key.size - 1;
@@ -182,24 +182,24 @@ void Channels::onConfigChanged()
{ {
// Make sure the phone hasn't mucked anything up // Make sure the phone hasn't mucked anything up
for (int i = 0; i < channelFile.channels_count; i++) { for (int i = 0; i < channelFile.channels_count; i++) {
Channel &ch = fixupChannel(i); meshtastic_Channel &ch = fixupChannel(i);
if (ch.role == Channel_Role_PRIMARY) if (ch.role == meshtastic_Channel_Role_PRIMARY)
primaryIndex = i; primaryIndex = i;
} }
} }
Channel &Channels::getByIndex(ChannelIndex chIndex) meshtastic_Channel &Channels::getByIndex(ChannelIndex chIndex)
{ {
// remove this assert cause malformed packets can make our firmware reboot here. // remove this assert cause malformed packets can make our firmware reboot here.
if (chIndex < channelFile.channels_count) { // This should be equal to MAX_NUM_CHANNELS if (chIndex < channelFile.channels_count) { // This should be equal to MAX_NUM_CHANNELS
Channel *ch = channelFile.channels + chIndex; meshtastic_Channel *ch = channelFile.channels + chIndex;
return *ch; return *ch;
} else { } else {
LOG_ERROR("Invalid channel index %d > %d, malformed packet received?\n", chIndex , channelFile.channels_count); LOG_ERROR("Invalid channel index %d > %d, malformed packet received?\n", chIndex, channelFile.channels_count);
static Channel *ch = (Channel *)malloc(sizeof(Channel)); static meshtastic_Channel *ch = (meshtastic_Channel *)malloc(sizeof(meshtastic_Channel));
memset(ch, 0, sizeof(Channel)); memset(ch, 0, sizeof(meshtastic_Channel));
// ch.index -1 means we don't know the channel locally and need to look it up by settings.name // ch.index -1 means we don't know the channel locally and need to look it up by settings.name
// not sure this is handled right everywhere // not sure this is handled right everywhere
ch->index = -1; ch->index = -1;
@@ -207,7 +207,7 @@ Channel &Channels::getByIndex(ChannelIndex chIndex)
} }
} }
Channel &Channels::getByName(const char* chName) meshtastic_Channel &Channels::getByName(const char *chName)
{ {
for (ChannelIndex i = 0; i < getNumChannels(); i++) { for (ChannelIndex i = 0; i < getNumChannels(); i++) {
if (strcasecmp(getGlobalId(i), chName) == 0) { if (strcasecmp(getGlobalId(i), chName) == 0) {
@@ -218,15 +218,15 @@ Channel &Channels::getByName(const char* chName)
return getByIndex(getPrimaryIndex()); return getByIndex(getPrimaryIndex());
} }
void Channels::setChannel(const Channel &c) void Channels::setChannel(const meshtastic_Channel &c)
{ {
Channel &old = getByIndex(c.index); meshtastic_Channel &old = getByIndex(c.index);
// if this is the new primary, demote any existing roles // if this is the new primary, demote any existing roles
if (c.role == Channel_Role_PRIMARY) if (c.role == meshtastic_Channel_Role_PRIMARY)
for (int i = 0; i < getNumChannels(); i++) for (int i = 0; i < getNumChannels(); i++)
if (channelFile.channels[i].role == Channel_Role_PRIMARY) if (channelFile.channels[i].role == meshtastic_Channel_Role_PRIMARY)
channelFile.channels[i].role = Channel_Role_SECONDARY; channelFile.channels[i].role = meshtastic_Channel_Role_SECONDARY;
old = c; // slam in the new settings/role old = c; // slam in the new settings/role
} }
@@ -234,7 +234,7 @@ void Channels::setChannel(const Channel &c)
const char *Channels::getName(size_t chIndex) const char *Channels::getName(size_t chIndex)
{ {
// Convert the short "" representation for Default into a usable string // Convert the short "" representation for Default into a usable string
const ChannelSettings &channelSettings = getByIndex(chIndex).settings; const meshtastic_ChannelSettings &channelSettings = getByIndex(chIndex).settings;
const char *channelName = channelSettings.name; const char *channelName = channelSettings.name;
if (!*channelName) { // emptystring if (!*channelName) { // emptystring
// Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case // Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case
@@ -242,33 +242,32 @@ const char *Channels::getName(size_t chIndex)
if (config.lora.use_preset) { if (config.lora.use_preset) {
switch (config.lora.modem_preset) { switch (config.lora.modem_preset) {
case Config_LoRaConfig_ModemPreset_SHORT_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
channelName = "ShortSlow"; channelName = "ShortSlow";
break; break;
case Config_LoRaConfig_ModemPreset_SHORT_FAST: case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
channelName = "ShortFast"; channelName = "ShortFast";
break; break;
case Config_LoRaConfig_ModemPreset_MEDIUM_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
channelName = "MediumSlow"; channelName = "MediumSlow";
break; break;
case Config_LoRaConfig_ModemPreset_MEDIUM_FAST: case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
channelName = "MediumFast"; channelName = "MediumFast";
break; break;
case Config_LoRaConfig_ModemPreset_LONG_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
channelName = "LongSlow"; channelName = "LongSlow";
break; break;
case Config_LoRaConfig_ModemPreset_LONG_FAST: case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
channelName = "LongFast"; channelName = "LongFast";
break; break;
case Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW: case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
channelName = "VLongSlow"; channelName = "VLongSlow";
break; break;
default: default:
channelName = "Invalid"; channelName = "Invalid";
break; break;
} }
} } else {
else {
channelName = "Custom"; channelName = "Custom";
} }
} }
+6 -7
View File
@@ -29,24 +29,23 @@ class Channels
int16_t hashes[MAX_NUM_CHANNELS] = {}; int16_t hashes[MAX_NUM_CHANNELS] = {};
public: public:
Channels() {} Channels() {}
/// Well known channel names /// Well known channel names
static const char *adminChannel, *gpioChannel, *serialChannel; static const char *adminChannel, *gpioChannel, *serialChannel;
const ChannelSettings &getPrimary() { return getByIndex(getPrimaryIndex()).settings; } const meshtastic_ChannelSettings &getPrimary() { return getByIndex(getPrimaryIndex()).settings; }
/** Return the Channel for a specified index */ /** Return the Channel for a specified index */
Channel &getByIndex(ChannelIndex chIndex); meshtastic_Channel &getByIndex(ChannelIndex chIndex);
/** Return the Channel for a specified name, return primary if not found. */ /** Return the Channel for a specified name, return primary if not found. */
Channel &getByName(const char* chName); meshtastic_Channel &getByName(const char *chName);
/** Using the index inside the channel, update the specified channel's settings and role. If this channel is being promoted /** Using the index inside the channel, update the specified channel's settings and role. If this channel is being promoted
* to be primary, force all other channels to be secondary. * to be primary, force all other channels to be secondary.
*/ */
void setChannel(const Channel &c); void setChannel(const meshtastic_Channel &c);
/** Return a human friendly name for this channel (and expand any short strings as needed) /** Return a human friendly name for this channel (and expand any short strings as needed)
*/ */
@@ -125,7 +124,7 @@ class Channels
/** /**
* Validate a channel, fixing any errors as needed * Validate a channel, fixing any errors as needed
*/ */
Channel &fixupChannel(ChannelIndex chIndex); meshtastic_Channel &fixupChannel(ChannelIndex chIndex);
/** /**
* Write a default channel to the specified channel index * Write a default channel to the specified channel index
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "CryptoEngine.h" #include "CryptoEngine.h"
#include "configuration.h"
void CryptoEngine::setKey(const CryptoKey &k) void CryptoEngine::setKey(const CryptoKey &k)
{ {
+11 -10
View File
@@ -9,7 +9,7 @@ FloodingRouter::FloodingRouter() {}
* later free() the packet to pool. This routine is not allowed to stall. * later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error * If the txmit queue is full it might return an error
*/ */
ErrorCode FloodingRouter::send(MeshPacket *p) ErrorCode FloodingRouter::send(meshtastic_MeshPacket *p)
{ {
// Add any messages _we_ send to the seen message list (so we will ignore all retransmissions we see) // Add any messages _we_ send to the seen message list (so we will ignore all retransmissions we see)
wasSeenRecently(p); // FIXME, move this to a sniffSent method wasSeenRecently(p); // FIXME, move this to a sniffSent method
@@ -17,7 +17,7 @@ ErrorCode FloodingRouter::send(MeshPacket *p)
return Router::send(p); return Router::send(p);
} }
bool FloodingRouter::shouldFilterReceived(const MeshPacket *p) bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
{ {
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
printPacket("Ignoring incoming msg, because we've already seen it", p); printPacket("Ignoring incoming msg, because we've already seen it", p);
@@ -27,23 +27,24 @@ bool FloodingRouter::shouldFilterReceived(const MeshPacket *p)
return Router::shouldFilterReceived(p); return Router::shouldFilterReceived(p);
} }
void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c) void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c)
{ {
bool isAck = ((c && c->error_reason == Routing_Error_NONE)); // consider only ROUTING_APP message without error as ACK bool isAck =
((c && c->error_reason == meshtastic_Routing_Error_NONE)); // consider only ROUTING_APP message without error as ACK
if (isAck && p->to != getNodeNum()) { if (isAck && p->to != getNodeNum()) {
// do not flood direct message that is ACKed // do not flood direct message that is ACKed
LOG_DEBUG("Receiving an ACK not for me, but don't need to rebroadcast this direct message anymore.\n"); LOG_DEBUG("Receiving an ACK not for me, but don't need to rebroadcast this direct message anymore.\n");
Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM
} }
if ((p->to != getNodeNum()) && (p->hop_limit > 0) && (getFrom(p) != getNodeNum())) { if ((p->to != getNodeNum()) && (p->hop_limit > 0) && (getFrom(p) != getNodeNum())) {
if (p->id != 0) { if (p->id != 0) {
if (config.device.role != Config_DeviceConfig_Role_CLIENT_MUTE) { if (config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) {
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
tosend->hop_limit--; // bump down the hop count tosend->hop_limit--; // bump down the hop count
// If it is a traceRoute request, update the route that it went via me // If it is a traceRoute request, update the route that it went via me
if (p->which_payload_variant == MeshPacket_decoded_tag && traceRouteModule->wantPacket(p)) { if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && traceRouteModule->wantPacket(p)) {
traceRouteModule->updateRoute(tosend); traceRouteModule->updateRoute(tosend);
} }
+3 -3
View File
@@ -42,7 +42,7 @@ class FloodingRouter : public Router, protected PacketHistory
* later free() the packet to pool. This routine is not allowed to stall. * later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error * If the txmit queue is full it might return an error
*/ */
virtual ErrorCode send(MeshPacket *p) override; virtual ErrorCode send(meshtastic_MeshPacket *p) override;
protected: protected:
/** /**
@@ -51,10 +51,10 @@ class FloodingRouter : public Router, protected PacketHistory
* Called immedately on receiption, before any further processing. * Called immedately on receiption, before any further processing.
* @return true to abandon the packet * @return true to abandon the packet
*/ */
virtual bool shouldFilterReceived(const MeshPacket *p) override; virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) override;
/** /**
* Look for broadcasts we need to rebroadcast * Look for broadcasts we need to rebroadcast
*/ */
virtual void sniffReceived(const MeshPacket *p, const Routing *c) override; virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
}; };
+3 -3
View File
@@ -1,9 +1,9 @@
#include "SX126xInterface.h"
#include "SX126xInterface.cpp" #include "SX126xInterface.cpp"
#include "SX128xInterface.h" #include "SX126xInterface.h"
#include "SX128xInterface.cpp" #include "SX128xInterface.cpp"
#include "api/ServerAPI.h" #include "SX128xInterface.h"
#include "api/ServerAPI.cpp" #include "api/ServerAPI.cpp"
#include "api/ServerAPI.h"
// We need this declaration for proper linking in derived classes // We need this declaration for proper linking in derived classes
template class SX126xInterface<SX1262>; template class SX126xInterface<SX1262>;
+1 -1
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#include "LLCC68Interface.h" #include "LLCC68Interface.h"
#include "configuration.h"
#include "error.h" #include "error.h"
LLCC68Interface::LLCC68Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, LLCC68Interface::LLCC68Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
+36 -42
View File
@@ -1,20 +1,20 @@
#include "configuration.h"
#include "MeshModule.h" #include "MeshModule.h"
#include "Channels.h" #include "Channels.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "configuration.h"
#include "modules/RoutingModule.h" #include "modules/RoutingModule.h"
#include <assert.h> #include <assert.h>
std::vector<MeshModule *> *MeshModule::modules; std::vector<MeshModule *> *MeshModule::modules;
const MeshPacket *MeshModule::currentRequest; const meshtastic_MeshPacket *MeshModule::currentRequest;
/** /**
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow * If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow
* the RoutingPlugin to avoid sending redundant acks * the RoutingPlugin to avoid sending redundant acks
*/ */
MeshPacket *MeshModule::currentReply; meshtastic_MeshPacket *MeshModule::currentReply;
MeshModule::MeshModule(const char *_name) : name(_name) MeshModule::MeshModule(const char *_name) : name(_name)
{ {
@@ -32,21 +32,22 @@ MeshModule::~MeshModule()
assert(0); // FIXME - remove from list of modules once someone needs this feature assert(0); // FIXME - remove from list of modules once someone needs this feature
} }
MeshPacket *MeshModule::allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
{ {
Routing c = Routing_init_default; meshtastic_Routing c = meshtastic_Routing_init_default;
c.error_reason = err; c.error_reason = err;
c.which_variant = Routing_error_reason_tag; c.which_variant = meshtastic_Routing_error_reason_tag;
// Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin // Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin
// So we manually call pb_encode_to_bytes and specify routing port number // So we manually call pb_encode_to_bytes and specify routing port number
// auto p = allocDataProtobuf(c); // auto p = allocDataProtobuf(c);
MeshPacket *p = router->allocForSending(); meshtastic_MeshPacket *p = router->allocForSending();
p->decoded.portnum = PortNum_ROUTING_APP; p->decoded.portnum = meshtastic_PortNum_ROUTING_APP;
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &Routing_msg, &c); p->decoded.payload.size =
pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Routing_msg, &c);
p->priority = MeshPacket_Priority_ACK; p->priority = meshtastic_MeshPacket_Priority_ACK;
p->hop_limit = config.lora.hop_limit; // Flood ACK back to original sender p->hop_limit = config.lora.hop_limit; // Flood ACK back to original sender
p->to = to; p->to = to;
@@ -57,7 +58,7 @@ MeshPacket *MeshModule::allocAckNak(Routing_Error err, NodeNum to, PacketId idFr
return p; return p;
} }
MeshPacket *MeshModule::allocErrorResponse(Routing_Error err, const MeshPacket *p) meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p)
{ {
auto r = allocAckNak(err, getFrom(p), p->id, p->channel); auto r = allocAckNak(err, getFrom(p), p->id, p->channel);
@@ -66,13 +67,13 @@ MeshPacket *MeshModule::allocErrorResponse(Routing_Error err, const MeshPacket *
return r; return r;
} }
void MeshModule::callPlugins(const MeshPacket &mp, RxSource src) void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src)
{ {
// LOG_DEBUG("In call modules\n"); // LOG_DEBUG("In call modules\n");
bool moduleFound = false; bool moduleFound = false;
// We now allow **encrypted** packets to pass through the modules // We now allow **encrypted** packets to pass through the modules
bool isDecoded = mp.which_payload_variant == MeshPacket_decoded_tag; bool isDecoded = mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag;
currentReply = NULL; // No reply yet currentReply = NULL; // No reply yet
@@ -101,13 +102,13 @@ void MeshModule::callPlugins(const MeshPacket &mp, RxSource src)
moduleFound = true; moduleFound = true;
/// received channel (or NULL if not decoded) /// received channel (or NULL if not decoded)
Channel *ch = isDecoded ? &channels.getByIndex(mp.channel) : NULL; meshtastic_Channel *ch = isDecoded ? &channels.getByIndex(mp.channel) : NULL;
/// Is the channel this packet arrived on acceptable? (security check) /// Is the channel this packet arrived on acceptable? (security check)
/// Note: we can't know channel names for encrypted packets, so those are NEVER sent to boundChannel modules /// Note: we can't know channel names for encrypted packets, so those are NEVER sent to boundChannel modules
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and it needs to /// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and
/// to be able to fetch the initial admin packets without yet knowing any channels. /// it needs to to be able to fetch the initial admin packets without yet knowing any channels.
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcasecmp(ch->settings.name, pi.boundChannel) == 0); bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcasecmp(ch->settings.name, pi.boundChannel) == 0);
@@ -117,7 +118,7 @@ void MeshModule::callPlugins(const MeshPacket &mp, RxSource src)
if (mp.decoded.want_response) { if (mp.decoded.want_response) {
printPacket("packet on wrong channel, returning error", &mp); printPacket("packet on wrong channel, returning error", &mp);
currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp); currentReply = pi.allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp);
} else } else
printPacket("packet on wrong channel, but can't respond", &mp); printPacket("packet on wrong channel, but can't respond", &mp);
} else { } else {
@@ -161,7 +162,7 @@ void MeshModule::callPlugins(const MeshPacket &mp, RxSource src)
printPacket("Sending response", currentReply); printPacket("Sending response", currentReply);
service.sendToMesh(currentReply); service.sendToMesh(currentReply);
currentReply = NULL; currentReply = NULL;
} else if(mp.from != ourNodeNum) { } else if (mp.from != ourNodeNum) {
// Note: if the message started with the local node we don't want to send a no response reply // Note: if the message started with the local node we don't want to send a no response reply
// No one wanted to reply to this requst, tell the requster that happened // No one wanted to reply to this requst, tell the requster that happened
@@ -170,17 +171,16 @@ void MeshModule::callPlugins(const MeshPacket &mp, RxSource src)
// SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded) // SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded)
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs // but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
// bad. // bad.
routingModule->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel); routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
} }
} }
if (!moduleFound) if (!moduleFound)
LOG_DEBUG("No modules interested in portnum=%d, src=%s\n", LOG_DEBUG("No modules interested in portnum=%d, src=%s\n", mp.decoded.portnum,
mp.decoded.portnum, (src == RX_SRC_LOCAL) ? "LOCAL" : "REMOTE");
(src == RX_SRC_LOCAL) ? "LOCAL":"REMOTE");
} }
MeshPacket *MeshModule::allocReply() meshtastic_MeshPacket *MeshModule::allocReply()
{ {
auto r = myReply; auto r = myReply;
myReply = NULL; // Only use each reply once myReply = NULL; // Only use each reply once
@@ -191,7 +191,7 @@ MeshPacket *MeshModule::allocReply()
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method * so that subclasses can (optionally) send a response back to the original sender. Implementing this method
* is optional * is optional
*/ */
void MeshModule::sendResponse(const MeshPacket &req) void MeshModule::sendResponse(const meshtastic_MeshPacket &req)
{ {
auto r = allocReply(); auto r = allocReply();
if (r) { if (r) {
@@ -206,16 +206,16 @@ void MeshModule::sendResponse(const MeshPacket &req)
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet /** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
* This ensures that if the request packet was sent reliably, the reply is sent that way as well. * This ensures that if the request packet was sent reliably, the reply is sent that way as well.
*/ */
void setReplyTo(MeshPacket *p, const MeshPacket &to) void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to)
{ {
assert(p->which_payload_variant == MeshPacket_decoded_tag); // Should already be set by now assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0 p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
p->channel = to.channel; // Use the same channel that the request came in on p->channel = to.channel; // Use the same channel that the request came in on
// No need for an ack if we are just delivering locally (it just generates an ignored ack) // No need for an ack if we are just delivering locally (it just generates an ignored ack)
p->want_ack = (to.from != 0) ? to.want_ack : false; p->want_ack = (to.from != 0) ? to.want_ack : false;
if (p->priority == MeshPacket_Priority_UNSET) if (p->priority == meshtastic_MeshPacket_Priority_UNSET)
p->priority = MeshPacket_Priority_RELIABLE; p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
p->decoded.request_id = to.id; p->decoded.request_id = to.id;
} }
@@ -235,14 +235,12 @@ std::vector<MeshModule *> MeshModule::GetMeshModulesWithUIFrames()
return modulesWithUIFrames; return modulesWithUIFrames;
} }
void MeshModule::observeUIEvents( void MeshModule::observeUIEvents(Observer<const UIFrameEvent *> *observer)
Observer<const UIFrameEvent *> *observer)
{ {
if (modules) { if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) { for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i; auto &pi = **i;
Observable<const UIFrameEvent *> *observable = Observable<const UIFrameEvent *> *observable = pi.getUIFrameObservable();
pi.getUIFrameObservable();
if (observable != NULL) { if (observable != NULL) {
LOG_DEBUG("Module wants a UI Frame\n"); LOG_DEBUG("Module wants a UI Frame\n");
observer->observe(observable); observer->observe(observable);
@@ -251,24 +249,20 @@ void MeshModule::observeUIEvents(
} }
} }
AdminMessageHandleResult MeshModule::handleAdminMessageForAllPlugins(const MeshPacket &mp, AdminMessage *request, AdminMessage *response) AdminMessageHandleResult MeshModule::handleAdminMessageForAllPlugins(const meshtastic_MeshPacket &mp,
meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response)
{ {
AdminMessageHandleResult handled = AdminMessageHandleResult::NOT_HANDLED; AdminMessageHandleResult handled = AdminMessageHandleResult::NOT_HANDLED;
if (modules) { if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) { for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i; auto &pi = **i;
AdminMessageHandleResult h = pi.handleAdminMessageForModule(mp, request, response); AdminMessageHandleResult h = pi.handleAdminMessageForModule(mp, request, response);
if (h == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) if (h == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) {
{
// In case we have a response it always has priority. // In case we have a response it always has priority.
LOG_DEBUG("Reply prepared by module '%s' of variant: %d\n", LOG_DEBUG("Reply prepared by module '%s' of variant: %d\n", pi.name, response->which_payload_variant);
pi.name,
response->which_payload_variant);
handled = h; handled = h;
} } else if ((handled != AdminMessageHandleResult::HANDLED_WITH_RESPONSE) && (h == AdminMessageHandleResult::HANDLED)) {
else if ((handled != AdminMessageHandleResult::HANDLED_WITH_RESPONSE) &&
(h == AdminMessageHandleResult::HANDLED))
{
// In case the message is handled it should be populated, but will not overwrite // In case the message is handled it should be populated, but will not overwrite
// a result with response. // a result with response.
handled = h; handled = h;
+53 -37
View File
@@ -10,15 +10,14 @@
#endif #endif
/** handleReceived return enumeration /** handleReceived return enumeration
* *
* Use ProcessMessage::CONTINUE to allows other modules to process a message. * Use ProcessMessage::CONTINUE to allows other modules to process a message.
* *
* Use ProcessMessage::STOP to stop further message processing. * Use ProcessMessage::STOP to stop further message processing.
*/ */
enum class ProcessMessage enum class ProcessMessage {
{ CONTINUE = 0,
CONTINUE = 0, STOP = 1,
STOP = 1,
}; };
/** /**
@@ -27,8 +26,7 @@ enum class ProcessMessage
* If response is also prepared for the request, then HANDLED_WITH_RESPONSE * If response is also prepared for the request, then HANDLED_WITH_RESPONSE
* should be returned. * should be returned.
*/ */
enum class AdminMessageHandleResult enum class AdminMessageHandleResult {
{
NOT_HANDLED = 0, NOT_HANDLED = 0,
HANDLED = 1, HANDLED = 1,
HANDLED_WITH_RESPONSE = 2, HANDLED_WITH_RESPONSE = 2,
@@ -66,14 +64,18 @@ class MeshModule
/** For use only by MeshService /** For use only by MeshService
*/ */
static void callPlugins(const MeshPacket &mp, RxSource src = RX_SRC_RADIO); static void callPlugins(const meshtastic_MeshPacket &mp, RxSource src = RX_SRC_RADIO);
static std::vector<MeshModule *> GetMeshModulesWithUIFrames(); static std::vector<MeshModule *> GetMeshModulesWithUIFrames();
static void observeUIEvents(Observer<const UIFrameEvent *> *observer); static void observeUIEvents(Observer<const UIFrameEvent *> *observer);
static AdminMessageHandleResult handleAdminMessageForAllPlugins( static AdminMessageHandleResult handleAdminMessageForAllPlugins(const meshtastic_MeshPacket &mp,
const MeshPacket &mp, AdminMessage *request, AdminMessage *response); meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response);
#if HAS_SCREEN #if HAS_SCREEN
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
return;
}
#endif #endif
protected: protected:
const char *name; const char *name;
@@ -107,12 +109,12 @@ class MeshModule
* Note: this can be static because we are guaranteed to be processing only one * Note: this can be static because we are guaranteed to be processing only one
* plumodulegin at a time. * plumodulegin at a time.
*/ */
static const MeshPacket *currentRequest; static const meshtastic_MeshPacket *currentRequest;
/** /**
* If your handler wants to send a response, simply set currentReply and it will be sent at the end of response handling. * If your handler wants to send a response, simply set currentReply and it will be sent at the end of response handling.
*/ */
MeshPacket *myReply = NULL; meshtastic_MeshPacket *myReply = NULL;
/** /**
* Initialize your module. This setup function is called once after all hardware and mesh protocol layers have * Initialize your module. This setup function is called once after all hardware and mesh protocol layers have
@@ -123,13 +125,17 @@ class MeshModule
/** /**
* @return true if you want to receive the specified portnum * @return true if you want to receive the specified portnum
*/ */
virtual bool wantPacket(const MeshPacket *p) = 0; virtual bool wantPacket(const meshtastic_MeshPacket *p) = 0;
/** Called to handle a particular incoming message /** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
it
*/ */
virtual ProcessMessage handleReceived(const MeshPacket &mp) { return ProcessMessage::CONTINUE; } virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp)
{
return ProcessMessage::CONTINUE;
}
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked /** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. * so that subclasses can (optionally) send a response back to the original sender.
@@ -137,38 +143,48 @@ class MeshModule
* Note: most implementers don't need to override this, instead: If while handling a request you have a reply, just set * Note: most implementers don't need to override this, instead: If while handling a request you have a reply, just set
* the protected reply field in this instance. * the protected reply field in this instance.
* */ * */
virtual MeshPacket *allocReply(); virtual meshtastic_MeshPacket *allocReply();
/*** /***
* @return true if you want to be alloced a UI screen frame * @return true if you want to be alloced a UI screen frame
*/ */
virtual bool wantUIFrame() { return false; } virtual bool wantUIFrame()
virtual Observable<const UIFrameEvent *>* getUIFrameObservable() { return NULL; } {
return false;
}
virtual Observable<const UIFrameEvent *> *getUIFrameObservable()
{
return NULL;
}
MeshPacket *allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
/// Send an error response for the specified packet. /// Send an error response for the specified packet.
MeshPacket *allocErrorResponse(Routing_Error err, const MeshPacket *p); meshtastic_MeshPacket *allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p);
/** /**
* @brief An admin message arrived to AdminModule. Module was asked whether it want to handle the request. * @brief An admin message arrived to AdminModule. Module was asked whether it want to handle the request.
* *
* @param mp The mesh packet arrived. * @param mp The mesh packet arrived.
* @param request The AdminMessage request extracted from the packet. * @param request The AdminMessage request extracted from the packet.
* @param response The prepared response * @param response The prepared response
* @return AdminMessageHandleResult * @return AdminMessageHandleResult
* HANDLED if message was handled * HANDLED if message was handled
* HANDLED_WITH_RESPONSE if a response is also prepared and to be sent. * HANDLED_WITH_RESPONSE if a response is also prepared and to be sent.
*/ */
virtual AdminMessageHandleResult handleAdminMessageForModule( virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
const MeshPacket &mp, AdminMessage *request, AdminMessage *response) { return AdminMessageHandleResult::NOT_HANDLED; }; meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response)
{
return AdminMessageHandleResult::NOT_HANDLED;
};
private: private:
/** /**
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow * If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow
* the RoutingModule to avoid sending redundant acks * the RoutingModule to avoid sending redundant acks
*/ */
static MeshPacket *currentReply; static meshtastic_MeshPacket *currentReply;
friend class ReliableRouter; friend class ReliableRouter;
@@ -176,10 +192,10 @@ class MeshModule
* so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply() * so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply()
* to generate the reply message, and if !NULL that message will be delivered to whoever sent req * to generate the reply message, and if !NULL that message will be delivered to whoever sent req
*/ */
void sendResponse(const MeshPacket &req); void sendResponse(const meshtastic_MeshPacket &req);
}; };
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet /** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
* This ensures that if the request packet was sent reliably, the reply is sent that way as well. * This ensures that if the request packet was sent reliably, the reply is sent that way as well.
*/ */
void setReplyTo(MeshPacket *p, const MeshPacket &to); void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to);
+17 -14
View File
@@ -1,18 +1,18 @@
#include "configuration.h"
#include "MeshPacketQueue.h" #include "MeshPacketQueue.h"
#include "configuration.h"
#include <assert.h> #include <assert.h>
#include <algorithm> #include <algorithm>
/// @return the priority of the specified packet /// @return the priority of the specified packet
inline uint32_t getPriority(const MeshPacket *p) inline uint32_t getPriority(const meshtastic_MeshPacket *p)
{ {
auto pri = p->priority; auto pri = p->priority;
return pri; return pri;
} }
/// @return "true" if "p1" is ordered before "p2" /// @return "true" if "p1" is ordered before "p2"
bool CompareMeshPacketFunc(const MeshPacket *p1, const MeshPacket *p2) bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_MeshPacket *p2)
{ {
assert(p1 && p2); assert(p1 && p2);
auto p1p = getPriority(p1), p2p = getPriority(p2); auto p1p = getPriority(p1), p2p = getPriority(p2);
@@ -26,27 +26,29 @@ bool CompareMeshPacketFunc(const MeshPacket *p1, const MeshPacket *p2)
MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {} MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {}
bool MeshPacketQueue::empty() { bool MeshPacketQueue::empty()
{
return queue.empty(); return queue.empty();
} }
/** /**
* Some clients might not properly set priority, therefore we fix it here. * Some clients might not properly set priority, therefore we fix it here.
*/ */
void fixPriority(MeshPacket *p) void fixPriority(meshtastic_MeshPacket *p)
{ {
// We might receive acks from other nodes (and since generated remotely, they won't have priority assigned. Check for that // We might receive acks from other nodes (and since generated remotely, they won't have priority assigned. Check for that
// and fix it // and fix it
if (p->priority == MeshPacket_Priority_UNSET) { if (p->priority == meshtastic_MeshPacket_Priority_UNSET) {
// if acks give high priority // if acks give high priority
// if a reliable message give a bit higher default priority // if a reliable message give a bit higher default priority
p->priority = (p->decoded.portnum == PortNum_ROUTING_APP) ? MeshPacket_Priority_ACK : p->priority = (p->decoded.portnum == meshtastic_PortNum_ROUTING_APP)
(p->want_ack ? MeshPacket_Priority_RELIABLE : MeshPacket_Priority_DEFAULT); ? meshtastic_MeshPacket_Priority_ACK
: (p->want_ack ? meshtastic_MeshPacket_Priority_RELIABLE : meshtastic_MeshPacket_Priority_DEFAULT);
} }
} }
/** enqueue a packet, return false if full */ /** enqueue a packet, return false if full */
bool MeshPacketQueue::enqueue(MeshPacket *p) bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p)
{ {
fixPriority(p); fixPriority(p);
@@ -60,7 +62,7 @@ bool MeshPacketQueue::enqueue(MeshPacket *p)
return true; return true;
} }
MeshPacket *MeshPacketQueue::dequeue() meshtastic_MeshPacket *MeshPacketQueue::dequeue()
{ {
if (empty()) { if (empty()) {
return NULL; return NULL;
@@ -73,7 +75,7 @@ MeshPacket *MeshPacketQueue::dequeue()
return p; return p;
} }
MeshPacket *MeshPacketQueue::getFront() meshtastic_MeshPacket *MeshPacketQueue::getFront()
{ {
if (empty()) { if (empty()) {
return NULL; return NULL;
@@ -84,7 +86,7 @@ MeshPacket *MeshPacketQueue::getFront()
} }
/** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */ /** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */
MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id) meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
{ {
for (auto it = queue.begin(); it != queue.end(); it++) { for (auto it = queue.begin(); it != queue.end(); it++) {
auto p = (*it); auto p = (*it);
@@ -99,7 +101,8 @@ MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
} }
/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */ /** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
bool MeshPacketQueue::replaceLowerPriorityPacket(MeshPacket *p) { bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
{
std::sort_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); // sort ascending based on priority (0 -> 127) std::sort_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); // sort ascending based on priority (0 -> 127)
// find first packet which does not compare less (in priority) than parameter packet // find first packet which does not compare less (in priority) than parameter packet
@@ -119,7 +122,7 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(MeshPacket *p) {
if (getPriority(p) > getPriority(*low)) { if (getPriority(p) > getPriority(*low)) {
packetPool.release(*low); // deallocate and drop the packet we're replacing packetPool.release(*low); // deallocate and drop the packet we're replacing
*low = p; // replace low-pri packet at this position with incoming packet with higher priority *low = p; // replace low-pri packet at this position with incoming packet with higher priority
} }
std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc);
+8 -8
View File
@@ -4,23 +4,23 @@
#include <queue> #include <queue>
/** /**
* A priority queue of packets * A priority queue of packets
*/ */
class MeshPacketQueue class MeshPacketQueue
{ {
size_t maxLen; size_t maxLen;
std::vector<MeshPacket *> queue; std::vector<meshtastic_MeshPacket *> queue;
/** Replace a lower priority package in the queue with 'mp' (provided there are lower pri packages). Return true if replaced. */ /** Replace a lower priority package in the queue with 'mp' (provided there are lower pri packages). Return true if replaced.
bool replaceLowerPriorityPacket(MeshPacket *mp); */
bool replaceLowerPriorityPacket(meshtastic_MeshPacket *mp);
public: public:
explicit MeshPacketQueue(size_t _maxLen); explicit MeshPacketQueue(size_t _maxLen);
/** enqueue a packet, return false if full */ /** enqueue a packet, return false if full */
bool enqueue(MeshPacket *p); bool enqueue(meshtastic_MeshPacket *p);
/** return true if the queue is empty */ /** return true if the queue is empty */
bool empty(); bool empty();
@@ -31,10 +31,10 @@ class MeshPacketQueue
/** return total size of the Queue */ /** return total size of the Queue */
size_t getMaxLen() { return maxLen; } size_t getMaxLen() { return maxLen; }
MeshPacket *dequeue(); meshtastic_MeshPacket *dequeue();
MeshPacket *getFront(); meshtastic_MeshPacket *getFront();
/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */ /** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
MeshPacket *remove(NodeNum from, PacketId id); meshtastic_MeshPacket *remove(NodeNum from, PacketId id);
}; };
+1 -1
View File
@@ -7,7 +7,7 @@
// Map from old region names to new region enums // Map from old region names to new region enums
struct RegionInfo { struct RegionInfo {
Config_LoRaConfig_RegionCode code; meshtastic_Config_LoRaConfig_RegionCode code;
float freqStart; float freqStart;
float freqEnd; float freqEnd;
float dutyCycle; float dutyCycle;
+34 -33
View File
@@ -2,9 +2,9 @@
#include <assert.h> #include <assert.h>
#include <string> #include <string>
#include "GPS.h"
#include "../concurrency/Periodic.h" #include "../concurrency/Periodic.h"
#include "BluetoothCommon.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here #include "BluetoothCommon.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
#include "GPS.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
@@ -51,15 +51,15 @@ FIXME in the initial proof of concept we just skip the entire want/deny flow and
MeshService service; MeshService service;
static MemoryDynamic<QueueStatus> staticQueueStatusPool; static MemoryDynamic<meshtastic_QueueStatus> staticQueueStatusPool;
Allocator<QueueStatus> &queueStatusPool = staticQueueStatusPool; Allocator<meshtastic_QueueStatus> &queueStatusPool = staticQueueStatusPool;
#include "Router.h" #include "Router.h"
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE) MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE)
{ {
lastQueueStatus = { 0, 0, 16, 0 }; lastQueueStatus = {0, 0, 16, 0};
} }
void MeshService::init() void MeshService::init()
@@ -71,14 +71,14 @@ void MeshService::init()
gpsObserver.observe(&gps->newStatus); gpsObserver.observe(&gps->newStatus);
} }
int MeshService::handleFromRadio(const MeshPacket *mp) int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
{ {
powerFSM.trigger(EVENT_PACKET_FOR_PHONE); // Possibly keep the node from sleeping powerFSM.trigger(EVENT_PACKET_FOR_PHONE); // Possibly keep the node from sleeping
printPacket("Forwarding to phone", mp); printPacket("Forwarding to phone", mp);
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
sendToPhone((MeshPacket *)mp); sendToPhone((meshtastic_MeshPacket *)mp);
return 0; return 0;
} }
@@ -87,7 +87,7 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
void MeshService::loop() void MeshService::loop()
{ {
if (lastQueueStatus.free == 0) { // check if there is now free space in TX queue if (lastQueueStatus.free == 0) { // check if there is now free space in TX queue
QueueStatus qs = router->getQueueStatus(); meshtastic_QueueStatus qs = router->getQueueStatus();
if (qs.free != lastQueueStatus.free) if (qs.free != lastQueueStatus.free)
(void)sendQueueStatusToPhone(qs, 0, 0); (void)sendQueueStatusToPhone(qs, 0, 0);
} }
@@ -130,31 +130,32 @@ void MeshService::reloadOwner(bool shouldSave)
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a
* reference * reference
*/ */
void MeshService::handleToRadio(MeshPacket &p) void MeshService::handleToRadio(meshtastic_MeshPacket &p)
{ {
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
// Simulates device is receiving a packet via the LoRa chip // Simulates device is receiving a packet via the LoRa chip
if (p.decoded.portnum == PortNum_SIMULATOR_APP) { if (p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) {
// Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first
Compressed scratch; meshtastic_Compressed scratch;
Compressed *decoded = NULL; meshtastic_Compressed *decoded = NULL;
if (p.which_payload_variant == MeshPacket_decoded_tag) { if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
p.decoded.payload.size = pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &Compressed_msg, &scratch); p.decoded.payload.size =
pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Compressed_msg, &scratch);
if (p.decoded.payload.size) { if (p.decoded.payload.size) {
decoded = &scratch; decoded = &scratch;
// Extract the original payload and replace // Extract the original payload and replace
memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data)); memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data));
// Switch the port from PortNum_SIMULATOR_APP back to the original PortNum // Switch the port from PortNum_SIMULATOR_APP back to the original PortNum
p.decoded.portnum = decoded->portnum; p.decoded.portnum = decoded->portnum;
} else } else
LOG_ERROR("Error decoding protobuf for simulator message!\n"); LOG_ERROR("Error decoding protobuf for simulator message!\n");
} }
// Let SimRadio receive as if it did via its LoRa chip // Let SimRadio receive as if it did via its LoRa chip
SimRadio::instance->startReceive(&p); SimRadio::instance->startReceive(&p);
return; return;
} }
#endif #endif
if (p.from != 0) { // We don't let phones assign nodenums to their sent messages if (p.from != 0) { // We don't let phones assign nodenums to their sent messages
LOG_WARN("phone tried to pick a nodenum, we don't allow that.\n"); LOG_WARN("phone tried to pick a nodenum, we don't allow that.\n");
p.from = 0; p.from = 0;
@@ -187,16 +188,16 @@ bool MeshService::cancelSending(PacketId id)
return router->cancelSending(nodeDB.getNodeNum(), id); return router->cancelSending(nodeDB.getNodeNum(), id);
} }
ErrorCode MeshService::sendQueueStatusToPhone(const QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id) ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id)
{ {
QueueStatus *copied = queueStatusPool.allocCopy(qs); meshtastic_QueueStatus *copied = queueStatusPool.allocCopy(qs);
copied->res = res; copied->res = res;
copied->mesh_packet_id = mesh_packet_id; copied->mesh_packet_id = mesh_packet_id;
if (toPhoneQueueStatusQueue.numFree() == 0) { if (toPhoneQueueStatusQueue.numFree() == 0) {
LOG_DEBUG("NOTE: tophone queue status queue is full, discarding oldest\n"); LOG_DEBUG("NOTE: tophone queue status queue is full, discarding oldest\n");
QueueStatus *d = toPhoneQueueStatusQueue.dequeuePtr(0); meshtastic_QueueStatus *d = toPhoneQueueStatusQueue.dequeuePtr(0);
if (d) if (d)
releaseQueueStatusToPool(d); releaseQueueStatusToPool(d);
} }
@@ -209,7 +210,7 @@ ErrorCode MeshService::sendQueueStatusToPhone(const QueueStatus &qs, ErrorCode r
return res ? ERRNO_OK : ERRNO_UNKNOWN; return res ? ERRNO_OK : ERRNO_UNKNOWN;
} }
void MeshService::sendToMesh(MeshPacket *p, RxSource src, bool ccToPhone) void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPhone)
{ {
uint32_t mesh_packet_id = p->id; uint32_t mesh_packet_id = p->id;
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...) nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
@@ -219,7 +220,7 @@ void MeshService::sendToMesh(MeshPacket *p, RxSource src, bool ccToPhone)
/* NOTE(pboldin): Prepare and send QueueStatus message to the phone as a /* NOTE(pboldin): Prepare and send QueueStatus message to the phone as a
* high-priority message. */ * high-priority message. */
QueueStatus qs = router->getQueueStatus(); meshtastic_QueueStatus qs = router->getQueueStatus();
ErrorCode r = sendQueueStatusToPhone(qs, res, mesh_packet_id); ErrorCode r = sendQueueStatusToPhone(qs, res, mesh_packet_id);
if (r != ERRNO_OK) { if (r != ERRNO_OK) {
LOG_DEBUG("Can't send status to phone"); LOG_DEBUG("Can't send status to phone");
@@ -232,7 +233,7 @@ void MeshService::sendToMesh(MeshPacket *p, RxSource src, bool ccToPhone)
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
{ {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node); assert(node);
if (node->has_position && (node->position.latitude_i != 0 || node->position.longitude_i != 0)) { if (node->has_position && (node->position.latitude_i != 0 || node->position.longitude_i != 0)) {
@@ -248,24 +249,24 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
} }
} }
void MeshService::sendToPhone(MeshPacket *p) void MeshService::sendToPhone(meshtastic_MeshPacket *p)
{ {
if (toPhoneQueue.numFree() == 0) { if (toPhoneQueue.numFree() == 0) {
LOG_WARN("ToPhone queue is full, discarding oldest\n"); LOG_WARN("ToPhone queue is full, discarding oldest\n");
MeshPacket *d = toPhoneQueue.dequeuePtr(0); meshtastic_MeshPacket *d = toPhoneQueue.dequeuePtr(0);
if (d) if (d)
releaseToPool(d); releaseToPool(d);
} }
MeshPacket *copied = packetPool.allocCopy(*p); meshtastic_MeshPacket *copied = packetPool.allocCopy(*p);
perhapsDecode(copied); perhapsDecode(copied);
assert(toPhoneQueue.enqueue(copied, 0)); assert(toPhoneQueue.enqueue(copied, 0));
fromNum++; fromNum++;
} }
NodeInfo *MeshService::refreshMyNodeInfo() meshtastic_NodeInfo *MeshService::refreshMyNodeInfo()
{ {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node); assert(node);
// We might not have a position yet for our local node, in that case, at least try to send the time // We might not have a position yet for our local node, in that case, at least try to send the time
@@ -274,7 +275,7 @@ NodeInfo *MeshService::refreshMyNodeInfo()
node->has_position = true; node->has_position = true;
} }
Position &position = node->position; meshtastic_Position &position = node->position;
// Update our local node info with our time (even if we don't decide to update anyone else) // Update our local node info with our time (even if we don't decide to update anyone else)
node->last_heard = node->last_heard =
@@ -290,8 +291,8 @@ NodeInfo *MeshService::refreshMyNodeInfo()
int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus) int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
{ {
// Update our local node info with our position (even if we don't decide to update anyone else) // Update our local node info with our position (even if we don't decide to update anyone else)
NodeInfo *node = refreshMyNodeInfo(); meshtastic_NodeInfo *node = refreshMyNodeInfo();
Position pos = Position_init_default; meshtastic_Position pos = meshtastic_Position_init_default;
if (newStatus->getHasLock()) { if (newStatus->getHasLock()) {
// load data from GPS object, will add timestamp + battery further down // load data from GPS object, will add timestamp + battery further down
@@ -323,7 +324,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
return 0; return 0;
} }
bool MeshService::isToPhoneQueueEmpty() bool MeshService::isToPhoneQueueEmpty()
{ {
return toPhoneQueue.isEmpty(); return toPhoneQueue.isEmpty();
} }

Some files were not shown because too many files have changed in this diff Show More