Porównaj commity

...

36 Commity

Autor SHA1 Wiadomość Data
Max-Plastix 415a77331e
Merge pull request #8 from mcauser/au915_dualplan_rollback
Rollback AU915 to FSB2 add DualPlan and AS923
2024-03-24 13:02:58 -07:00
Max-Plastix 947a7f630c
Merge pull request #9 from designer2k2/patch-1
Update unified_decoder.js to work also with ChirpStack V4
2024-03-24 13:01:13 -07:00
Stephan Martin 88e52f18b5
Update unified_decoder.js to work also with ChirpStack V4
ChirpStack V4 needs decodeUplink, this wraps the Decoder with it
2024-03-11 20:26:40 +01:00
Mike Causer 149f5b034d Rollback AU915 to FSB2 add DualPlan and AS923 2023-04-18 14:36:04 +10:00
Max-Plastix 00cec9c130
Merge pull request #7 from mcauser/au915_dualplan
Add support for new AU915 dualplan regional settings
2022-11-17 22:36:49 -08:00
Mike Causer bd95ec5e3b Prepare v1.7.8 2022-11-18 17:23:21 +11:00
Mike Causer 27372a617e Fix some typos 2022-11-18 17:22:54 +11:00
Mike Causer a3c662a52a Add support for AU915 dual plan 2022-11-18 17:22:25 +11:00
Max-Plastix bc87deb3f8
Update configuration.h 2022-05-08 12:28:19 -07:00
Max-Plastix 2b6ad6dab0 Merge branch 'main' of https://github.com/Max-Plastix/tbeam-helium-mapper into main 2022-05-07 21:12:59 -07:00
Max-Plastix ef64432f3b Experimenting with GPS voltage for power consumption. 2022-05-07 21:10:50 -07:00
Max-Plastix b1ca01581e Added battery info to configuration header 2022-05-07 21:09:48 -07:00
Max-Plastix 60a9462ebf
Pin espressif32 to v3.5.0 until LMIC supports 4.1.0
The new espressif32 v4.1.0 is out, which is good, but several libraries are no longer compatible with this code.
MCCI LMIC might also need updates to build.

This update pins the version of espressif32 to 3.5.0 so PlatformIO won't upgrade to the newest one.
Also pinning the library versions, just in case.
2022-04-21 22:00:55 -07:00
Max-Plastix 8dcd9d4f5e
Update README.md 2022-04-15 22:22:43 -07:00
Max-Plastix bc3226fd23
Added Rob Cryft's excellent MacOS guide
Added info for MacOS.
2022-04-15 21:59:56 -07:00
Max-Plastix 57dc008214
Added battery consumption information 2022-04-02 13:04:52 -07:00
Max-Plastix 5a5d23f45b Preparinv v1.7.6 2022-04-01 21:21:18 -07:00
Max-Plastix 3b6692b011
Update README.md
Added GPS debug information and OLED info.
2022-03-25 12:21:15 -07:00
Max-Plastix 32a1c84855 Remove .ino references from README 2022-03-09 14:29:38 -08:00
Max-Plastix 32f51fd068 Reset FCount at 50,000 uplinks. 2022-03-09 12:27:52 -08:00
Max-Plastix b64e90e9fb Changed to DejaVu font. 2022-02-18 18:13:26 -08:00
Max-Plastix 3ac1d7f5dc Updated Font. Disabled low battery irq 2022-02-18 10:53:01 -08:00
Max-Plastix a89c71d526 Only do GPS configuraiton once 2022-02-08 11:14:06 -08:00
Max-Plastix 4d64271b07 Added GPS Reset menu item 2022-02-07 23:27:02 -08:00
Max-Plastix 549a51951a Ready for 1.7.5 release. 2022-02-07 16:28:29 -08:00
Max-Plastix a68c2305a0 Added empty include directory 2022-02-07 16:24:23 -08:00
Max-Plastix 88e5ced7cf Remove outdated decoder function from README 2022-02-05 19:31:05 -08:00
Max-Plastix 981e6497ef Removed custom font. Made status optional. 2022-02-05 18:30:37 -08:00
Max-Plastix 6c18350127 Added missing AXP192 screen message 2022-02-01 19:06:38 -08:00
Max-Plastix c1155c13e4 Explained screen detect code in comments. A bit. 2022-01-31 17:34:01 -08:00
Max-Plastix 2749d67de6 Autodetect SSD1306 vs SH1106 OLED type 2022-01-29 23:46:48 -08:00
Max-Plastix b90680746b fix late enum definition bug per @zl3ag 2022-01-28 23:55:02 -08:00
Max-Plastix fa284c02a8 Cleaned up display to show HDOP 2022-01-28 12:00:11 -08:00
Max-Plastix c972309bf6 Preparing v1.7.2 release 2022-01-28 11:35:00 -08:00
Max-Plastix aebc32ccc9 Pay more attention to HDOP 2022-01-28 11:18:57 -08:00
Max-Plastix 21925d0f70 Pay more attention to HDOP 2022-01-28 11:18:37 -08:00
18 zmienionych plików z 1198 dodań i 757 usunięć

1
.gitignore vendored
Wyświetl plik

@ -3,3 +3,4 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vscode/extensions.json

Wyświetl plik

@ -3,5 +3,8 @@
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

218
README.md
Wyświetl plik

@ -3,55 +3,144 @@
by [Max-Plastix](https://github.com/Max-Plastix/tbeam-helium-mapper/)
### TL;DR
Read through the comments in the three source files listed below. Edit to taste, compile, and Upload to your TTGO T-Beam device. Drive around and map the Helium network!
This code loads onto LilyGo TTGO T-Beam v1.1 board to make a Helium Network Mapper. To build one: download this build, configure some files, and upload it to your device. Go travel the world to contribute to the Helium Network Coverage Maps!
## Purpose
The goal of this version is to have a **TTGO T-Beam** Mapper that's ideally suited to walking or driving, taking cues from the USB Power source and movement for activity level.
The goal of this software is to have a **TTGO T-Beam** Mapper that's ideally suited to walking or driving, taking cues from the USB Power source and movement for activity level.
This device uploads GPS coordinates from the TTGO T-Beam to the Helium network, be used for tracking and determining signal coverage of LoRaWAN gateways and hotspots.
With this software and a T-Beam device, one can contribute to the [Helium Network](https://www.helium.com) Mapper or Cargo projects.
Details for the Mapper project can be found [here](https://mappers.helium.com/) and details for Cargo can be found [here](https://cargo.helium.com/)
Details for the Mapper project can be found [here](https://mappers.helium.com/) and details for Cargo can be found [here](https://cargo.helium.com/). It also works with [Coverage Map](https://coveragemap.net/heatmap/), a new Heatmap view of Helium hotspots and coverage.
The Mapper is intended to be highly active while the vehicle is in motion, and quieter when the vehicle is stationary. By default, it is not miserly with Data Credits. If you want to conserve Data Credits or battery power, tune the default configuration to send packets less frequently.
The Mapper is intended to be highly active while the vehicle is in motion, and quieter when the vehicle is stationary. By default, it is not miserly with Data Credits. If you want to conserve Data Credits or battery power, tune the configuration to send packets less frequently.
### But do I get PAID for Mapping?!
No, you do not. I put this here because it seems to be the #1 FAQ. You do not earn HNT or Data Credits by mapping. Mapping costs you very little -- One Penny (USD $0.01) for every thousand mapping packets. It helps the Helium network by providing a coverage map, and it helps you by providing clarity on your own local Helium environment.
No, you do not. I put this here because it seems to be the #1 FAQ. You do not earn HNT or Data Credits by mapping. Mapping costs you very little -- One Penny (USD $0.01) for every thousand mapping packets. It helps the Helium network by providing a coverage map, and it helps you by providing clarity on your own local Helium environment. It's all volunteer.
### But do I get to flag and delist spoofing gamer Hotspots?!
No, you do not. It's the #2 FAQ. The Mapper data and coverage maps are not involved in any POC challenges or used for gaming denylists.
## Supported Hardware
I tested this software on (several) LilyGo TTGO T-Beam v1.1 devices, all on **US915**. These are commonly avaialable as "Meshtastic" devices from AliExpress, Amazon, Banggood, eBay, etc, usually as a kit with an unsoldered OLED screen and SMA antenna.
I tested this software on (many) LilyGo [TTGO T-Beam v1.1](http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1317&FId=t3:50060:3) devices, all on **US915**. Others have enjoyed success on **EU688** and other worldwide bands, with the matching device. These are commonly available as "Meshtastic" devices from AliExpress, Amazon, Banggood, eBay, etc, usually as a kit with an unsoldered OLED screen and SMA antenna for around USD $30.00.
If you have an older v0.7 board or different region, adjust the configuration to match. If you have a unique variant (Neo 8M?) and find something not working, open an Issue and provide what information you can.
If you have an older v0.7 board or different region, adjust the configuration to match. If you have a unique variant and find something not working, open an Issue and provide what information you can.
### Semtech LoRa Radio
This build uses the [MCCI Catena LMIC Library](https://github.com/mcci-catena/arduino-lmic) for LoRaWAN on the Semtech SX1276 or SX1272 radio modules.
Note that the T-Beam device with a [U.FL / I-PEX](https://www.hirose.com/product/series/U.FL#) LoRa antenna connector and uBlox NEO-8M GPS module comes with a Semtech SX1262 radio, which is NOT supported by the LMIC library.
**Do Not Buy** [this incorrect device](https://www.amazon.com/T-Beam-NEO-M8N-Wireless-Bluetooth-Display/dp/B07X2SNNGQ) for use on Helium! It will not work.
### OLED Display
Most T-Beam units arrive with no OLED Display attached. You **can** operate the mapper without an OLED display, if you would like. Of course, the status and menu will be invisible, but there may be applications where a display is not required.
The most common OLED Display used is a 0.96" screen with SSD1306 controller. You may also use 1.3" displays, or displays with an SH1106 controller. The software should auto-detect the correct controller protocol and display i2c address, but if you have an unusual display, file an issue to see if it can be supported. When an OLED display is installed, you should see the detected type and address in the startup messages on UART.
#### Soldering the 4-pin OLED connection
Since the display is not pre-installed, the buyer must solder the 4-pin connection between the T-Beam and OLED. Be very careful to match the pinout and position of the display! Some OLED displays have VCC and GND reversed from the expected pinout, and require some creative wiring to adapt. If your OLED came with the T-Beam, it's probably correct and ready-to-solder.
If you incorrectly power the OLED, short connections, or damage the Pin 21/22 connections (i2c), it is very likely that both the OLED and the AXP Power management unit are unreachable, and the board may fail in unexpected ways. It's a good idea to program the device and check the UART Monitor output before installing the OLED, for some confidence that the board works before soldering. (Nobody actually does this, but it's a good idea.)
# Mandatory Configuration
Before Buliding and Uploading, you will probably want to inspect or change some items in these three files:
- `platformio.ini`
- `main/configuration.h`
- `main/credentials.cpp`
The comments and text below will guide you on what values to look out for.
The comments and text below will guide you on what values to look out for. By default, this build is for the **US915** region. Change `platformio.ini` for a different locale.
You might have to adjust the COM Port in `platformio.ini` for Uploading and Monitoring if PlatformIO doesn't auto-detect your port correctly.
### Geographic Region, and Frequency
By default, this build is for the **US915** region. Change the declaration in `platformio.ini` for a different locale, to select the correct operating rules and frequency for your country.
You should choose your own AppKey value in `credentials.cpp`. Either take the random one suggested by the Console Device entry, or make up one of your own. Read the notes in `credentials.cpp` for details. By default, the DevEUI is generated automatically to be unique to each unit, but you may want to hardcode it in `credentials.cpp` instead.
### PlatformIO Communication port
You might have to adjust the COM Port in `platformio.ini` for Uploading and Monitoring if PlatformIO doesn't auto-detect your port correctly. If you have a single T-Beam on a Windows computer, PlatformIO will usually auto-detect it correctly. If you have a bunch of Serial USB devices, unusual USB topography, or weird device drivers, you might have to set this to a fixed port. Note that Upload and Monitor are set separately, and have different auto-detection logic.
On MacOS, it can be significantly more complicated to connect PlatformIO to your device. Ask in the Discord for help.
### Helium Device IDs
Each LoRaWAN device on Helium is identified by the three OTAA values used in Joining the network: `DevEUI`, `AppEUI`, and `AppKey`.
You should choose your own private `AppKey` value in `credentials.cpp`. Either take the random value generated by the new Console Device entry, or make up one of your own. Read the notes in `credentials.cpp` for details. The value in the build must match the value in Console, regardless of how you achieve that.
By default, the `DevEUI` is generated automatically to be unique to each unit, but you may want to hardcode it in `credentials.cpp` instead. There is an explanation there of why you might want to go either way.
### Mapper uplink period and behavior
Read through the comments in `configuration.h` to see if the default Mapper behavior suits your needs, especially in the area of default time/distance between Uplink packets.
## How it Works
# Building and Programming
This code started off as an Arduino IDE project, with `.ino` filenames, but the complexity of managing installed libraries in Arduino IDE made it difficult to sustain. Now, the code must be built with PlatformIO and Visual Studio Code. These is an excellent free/open-source IDE for ESP32 platforms, and allows each project to pull in the required libraries to complete the build, as well as program it into the device.
There are excellent guides to installing and using PlatformIO, but for this project, your goal is to open the project, edit the configuration files, and then select `Build` and `Upload` to program the T-Beam device.
## MacOS Guide
Building and programming with PlatformIO on MacOS is mostly the same, but has some unique challenges. `@Rob Cryft` wrote this excellent guide on ["Getting Started with Helium Mapping"](https://levelup.gitconnected.com/getting-started-with-helium-mapping-2833914c4d3) that walks through the whole process on Mac.
# Battery life and power consumption
The T-Beam is not the lowest-power Mapper device, because it has a powerful dual-core ESP32 CPU, Power Management IC (PMIC or PMU), and other design decisions that lead to a pretty high operating current of 110mA or so.
There is an excellent Lithium-Ion 18650 battery management and charger built-in, so you can install a battery cell, and operate it from USB power while it charges and maintains the battery. The USB port can draw up to 1 Amp, with 750mA or so going to battery charging.
## Battery Selection
If you plan to run the Mapper primarily on battery with infrequent charging, you will want a battery with the highest mAh (milliamp-hour) rating, typically 3000 mAh or more. Some battery cells like Sony's excellent VTC6, are optimized for very high current discharge (heaters, drones, scooters). These are expensive, and may make other compromises in capacity. You don't need high current at all, so look for a good balance of cost and capacity.
If you expect to have USB power available most of the time, then the cheapest 18650 cells will do just fine -- don't feel bad about using an old or low-capacity one.
### Battery Fitment
The included battery holder in the TTGO T-Beam is **small** and **tight**. Your battery cell must have **NO Protruding bump** or nipple on the Positive end. Look for a flat or low-profile positive end. (If you must, you may be able to crush the spring-terminals at the ends of the battery holder, but this should be done carefully to avoid damaging the attached PCB or cracking the holder.)
Always insert the cell carefully and deliberately, noting the `+` and `-` orientation but also supporting the PCB to avoid bending the whole assembly with force.
Some battery cells have a built-in Protection Circuit, advertised as "protected" or safe cells. These batteries are longer in size, and won't easily fit in the battery holder.
## Runtime and Battery Life
While the T-Beam Mapper is typically USB powered from a vehicle, it can run for weeks from the built-in 18650 cell responding to occasional movement.
The GPS receiver is the single largest power consumer on the Mapper, so settings that allow for more GPS-off time will save battery.
## Runtime
It depends on configuration, of course, but here's a ballpark figure: Without any sleep, the fully-operating Mapper should track for about **24 hours of nonstop movement**. (3000mAh cell, 105mA operating current, reasonable battery margins and uplink rate)
When mostly stationary, the mapper can wait for movement or events for about a **month**! (3000mA cell, mostly 2.23mA sleep, some wake and activity.)
## Operating power
In the full-operating state, with the GPS tracking, Mapper uplinks, and OLED display on, the T-Beam draws about 100 to **120mA** from the 18650 cell.
(Searching for GPS fix is the highest power, 120mA, and when it has a solid fix, closer to 110mA.)
There is a little extra power consumed for a short while as it charges the GPS coin cell automatically.
### OLED Screen OFF
Turning off the OLED Display is done more to save the screen from pixel burn-in than to save power. It draws just under 10mA when lit with text display, so the Mapper drops to about **101.6mA** when the screen is off.
### LoRa power?
Uplink transmissions use more power (200mA), but they are usually short and infrequent. Back-to-back SF10 transmissions could start to weigh on battery life, of course.
### Rest or Sleep
With the GPS and OLED off, and the ESP32 waiting for USB power, button press, or movement checks, it is in the lowest-power operating state.
This draws **2.23mA** from the battery.
### Power Off
Powered off, the circuit still draws **3.22 μA (micro-amps)**. Not significant, but not zero. Remove the battery cell at about half to 80% charge for long-term storage longer than a month or two.
# Operation: How it Works
When your car is started, and USB Power appears, the Mapper will power on, acquire GPS, and continue mapping.
It re-uses the last network Join state for faster connection and fewer packets.
The Mapper is always looking to see if it's been a long time, or you moved some distance from the last report. Whichever one happens first causes a transmission to be sent, plotting a point on the Helium map.
The Mapper is always looking to see if it's been a long time, or you moved some distance from the last report. Whichever one happens first causes a transmission to be sent, plotting a point on the Helium map. **Time or Distance** are the two main factors in determining when to send Uplinks.
When moving, the Mapper will send out a packet every time GPS indicates it has moved `MIN_DIST` meters. This is the primary knob to turn for more/fewer packets. A Helium hex cell is about 340meters across, so the default 68-meter packet distance will send quite a few redundant packets for each mapped cell. DC is incredibly cheap, but adjust the distance if you want to send fewer packets.
This is the normal operation of the Mapper in motion.. every `MIN_DIST` meters, one ping reporting position, while the battery charges from USB. If the speed of motion is fast, it may even result in back-to-back packet sends (at greater distance) limited by the bandwidth of your chosen Spreading Factor (Data Rate) and country restrictions. (In the United States US915, at SF10, this is about two seconds maximum speed. In Thailand, it can be 37 seconds or more.)
This is the normal operation of the Mapper in motion: every `MIN_DIST` meters, one Uplink is sent reporting position while the battery charges from USB. If the speed of motion is fast, it may even result in back-to-back packet sends (at greater distance) limited by the bandwidth of your chosen Spreading Factor (Data Rate) and country restrictions. (In the United States US915, at SF10, this is about two seconds maximum speed. In Thailand, it can be 37 seconds or more.)
When the Mapper comes to a stop, staying within `MIN_DIST` meters, it sends a hearbeat ping every `STATIONARY_TX_INTERVAL` seconds (default: 60). This serves to keep it visible on the map and report battery voltage. Too often? Dial up the `STATIONARY_TX_INTERVAL` to a longer interval.
When the Mapper comes to a stop, staying within `MIN_DIST` meters, it sends a heartbeat ping every `STATIONARY_TX_INTERVAL` seconds (default: 60). This serves to keep it visible on the map and report battery voltage. (Too often for you? Dial up the `STATIONARY_TX_INTERVAL` to a longer interval.)
After being stationary a long time (parked) with a decreasing battery voltage, we change to a slower pace of updates. This happens after `REST_WAIT` seconds (default: 30 minutes). In the Rest state, the Mapper transmits every `REST_TX_INTERVAL` seconds (default: 5 minutes).
After being stationary a long time (parked) with a decreasing battery voltage, we change to a slower pace of "not moving" updates. This happens after `REST_WAIT` seconds (default: 30 minutes). In the Rest state, the Mapper transmits every `REST_TX_INTERVAL` seconds (default: 5 minutes).
After an even longer time (parked, not moving, no USB), the Mapper will power off the GPS to save significant power. It will go into the lowest power state, waiting for USB power to come back. Periodically, it will power up the GPS, get a location fix and see if it moved while sleeping. It may have missed significant movement during sleep time, and wake to full Mapping. Or it hasn't moved at all and goes back to sleep.
Eventually, the ~100mA power drain of the mapper (with OLED screen & GPS) runs the battery down below `BATTERY_LOW_VOLTAGE` volts, and the Mapper will save state and completely power off.
It will power on and resume only when USB power appears.
Regardless of battery or sleep state, the Mapper will power on and resume when USB power appears.
# Detailed Operation
@ -98,9 +187,9 @@ indicating that the system experienced a normal boot (instead of a timer wakeup
Next are some important debugging messages printed at startup, indicating whether the OLED screen, AXP Power Management IC, and GPS were found and initialized. If these are not found or connected quickly, then there could be a hardware failure, soldering error, or other board-level issue preventing the software from working correctly.
You should see `AXP192 PMU` on the T-Beam, and `SSD1306 OLED display` if a display is installed. If you see neither of these, the i2c port likely has problems.
If you expected an OLED display, and none was found, then it may be misconnected.
If you expected an OLED display, and none was found, then it may be disconnected.
After this are serveral data messages about voltages and settings, largely uninteresting, until you get to..
After this are several data messages about voltages and settings, largely uninteresting, until you get to..
### Settings and Credentials
On startup, the USB Serial port will print the DevEUI, AppID, and AppKey values, suitable for cut & paste entry into the Helium Console for your Device.
@ -121,12 +210,12 @@ The Mapper will flash the Blue LED at 4Hz and attempt to Join the Helium network
1. Out of Range: The nearest Helium hotspot can't hear the device, or the device can't hear the response.
2. Wrong RF configuration or Localization: The frequency band and protocol must match the local Helium Network, as configured in `platformio.ini`.
3. The Device keys are not correctly registered in the Helium Console. Check all three.
4. The Device was recently added to Console and is still "Pending" or waiting for XOR Filter update to propegate through the blockchain or network. This can take 20 minutes or more.
4. The Device was recently added to Console and is still "Pending" or waiting for XOR Filter update to propagate through the blockchain or network. This can take 20 minutes or more.
5. Helium network outage. Any failure in the helium network that prevents Join will hang here, as was seen often in November 2021.
6. RF or hardware issues. Disconnected antenna, mismatched antenna frequency, etc.
### Or, Re-Join
Once the Mapper joins the Helium network, it stores these Network Key credentials for future use. Ideally, the Mapper does not have to send a Join request at the next startup, but fetches them from the nonvolatile "Preferences" memory. To successfully continue with these same credentials, the Mapper needs to continue the Frame Count from prior transmissions, or the Helium network will reject Uplink packets as "Late" (for re-using old Frame Count values).
Once the Mapper joins the Helium network, it stores these Network Key credentials for future use. Ideally, the Mapper does not have to send a Join request at the next startup, but fetches them from the non-volatile "Preferences" memory. To successfully continue with these same credentials, the Mapper needs to continue the Frame Count from prior transmissions, or the Helium network will reject Uplink packets as "Late" (for re-using old Frame Count values).
When you see `(re-used join)` on the screen and serial log, this means no Join Request/Accept packets were sent, and the unit will attempt to use the same credentials.
@ -136,12 +225,40 @@ There are some instances where this is problematic or not correct:
* Very long time since last Uplink
* Helium Network resets that invalidate Join keys.
At any time, you can select `Flush Prefs` in the system menu to discard these keys, reset the device, and Join fresh.
At any time, you can select `Full Reset` in the system menu to discard these keys, reset the device, and Join fresh.
### GPS Connected
You should also see a message reporting `GPS connected`. The first time you run this software on hardware that came with Mestastic or other builds, it will automatically try all common baud rates to find the Neo GPS module. Eventually it will connect, then configure the GPS for the needed NMEA Messages at 115,200 bps, then save the configuration to flash so that subsequent boot is faster. In any case, you should always see `GPS connected` at startup.
## GPS Connection and Issues
The typical GPS operation is to power on, search the sky for satellites, and get a 3D fix in 10 seconds or less. 3 to 5 seconds is common.
This means the Mapper is receving NMEA messages at the expected bitrate, but it may not yet have a 3D position fix from the GPS.
You should see a **Red LED** blinking once per second on the far right side of the T-Beam (away from the OLED). If this Red LED is not blinking, the GPS does not have a fix and no Mapper packets will be sent.
The T-Beam must have an Active GPS antenna connected to operate. The included rectangular antenna is stuck with tape to the battery holder, but any Active GPS Antenna with 3v power and a U.FL connector could be used as well. Many mappers use a larger 25x25mm GPS patch antenna for improved reception, but the included antenna is just fine.
### First Power-On
If your device is new, unused for a long time, or shipped from elsewhere in the world, it may take significantly longer for that first GPS fix. I recommend installing a charged battery, powering on, and leaving the unit with a clear view of the sky (outdoors) for 15 min before attempting any diagnostics. In most cases, this will allow it to get a 3D Fix, download the current GPS Almanac data, charge the battery cell, and prepare for optimum startups.
#### GPS battery cell
The Neo-6M has a dedicated GPS backup battery cell that recharges any time the Mapper is powered on. This helps retain the GPS state for faster time to first fix. If your device is new or unused for a long time, this battery is likely dead and will charge with some use. There's nothing to do but use the Mapper, and you should see fast GPS connections in the future.
#### GPS Bitrate and configuration
On the Debug/Monitoring UART console, you should also see a message reporting `GPS connected`. The first time you run this software on hardware that came with Meshtastic or other builds, it will automatically try all common baud rates to find the Neo GPS module. Eventually it will connect, then configure the GPS for the needed NMEA Messages at 115,200 bps, then save the configuration to flash so that subsequent boot is faster. In any case, you should always see `GPS connected` at startup if you are watching the UART/Monitor serial data.
This means the Mapper is receiving NMEA messages at the expected bitrate, but it may not yet have a 3D position fix from the GPS.
Note that you never need to load or run special "GPS Reset" scripts to change the GPS settings. This build will find and configure the GPS from any known state, including Meshtastic builds.
### GPS Debug
First, it is uncommon for GPS modules to be defective on the T-Beam. Try these steps to debug it, and reach out in the `#mappers` channel for help.
If 15 minutes of clear sky view did not result in a GPS Fix (blinking Red LED), then something is not correct. Ensure you have a good view of the sky, outdoors, with at least some horizon in view. The **most common** cause of failure is trying to get a first fix indoors, or with a limited view of the sky. "Near a window" doesn't count. That first fix takes time and needs many good and constant signals to download the full data set. Get it out in the open, and be patient.
Check the antenna cable connection to the board. The U.FL connector is fragile, and the routing of the wire through the hole in the PCB can cause tension, or disconnect in shipping. Some boards have been found with a broken U.FL connector, so inspect the solder joints as well.
Ensure the unit has good power, using a charged LiIon cell for power. It needs to stay on for 15 minutes or more without interruption.
If you see a blinking Red LED from the GPS, but the Mapper software does not have a fix, reporting `*** NO GPS ***`, then there is a software issue between the ESP32 and GPS. You can debug this further by selecting `USB GPS` from the menu and inspecting the NMEA sentences from the GPS to the UART console. You may also run Ublox tools this way, such as U-Center to study the GPS module behavior.
If `USB GPS` does not relay any NMEA sentences from the unit, then something is wrong in unusual ways. Reset the board and ensure that "GPS Connected" is shown during boot. It is not common for GPS modules to be defective, so keep trying different things and ask `#mappers` for ideas.
## LED Indicators
@ -166,7 +283,7 @@ The top status line alternates between two displays every few seconds:
- `#ABC` is the last three hex digits of your DevEUI, so you can match it to the correct device in Console. Handy if you have several Mappers that look the same.
- `4.10v` is the battery voltage
- `48mA` is the charge (or discharge) current to the battery. The TTGO charges the battery cell at around 300mA from USB, when possible.
- Satellite Count is displayed on the right at all times.
- Satellite HDOP & Count is displayed on the right at all times. (Lower HDOP hints at better GSP accuracy.)
- The GPS Time of Day (UTC) alternates on the display line every 2 seconds.
- A `*** NO GPS ***` message will show when no GPS Fix is available.
@ -192,43 +309,16 @@ for Frame Count 1850, a time-triggered packet after 2 minutes and only 1 meter a
How often Acks are requested is configurable, defaulting to one-every-ten.
# Uplink Payload
The Payload Port and byte content have been selected to match the format used by CubeCell mappers as well:
Lat, Long, Altitude, Speed, Battery, and Sats. This common decoder function can now be used for both TTGO and CubeCell mappers:
The Payload Port and byte content have been selected to match the format used by CubeCell mappers as well.
```
// From https://github.com/hkicko/CubeCell-GPS-Helium-Mapper
function Decoder(bytes, port) {
var decoded = {};
var latitude = ((bytes[0]<<16)>>>0) + ((bytes[1]<<8)>>>0) + bytes[2];
latitude = (latitude / 16777215.0 * 180) - 90;
var longitude = ((bytes[3]<<16)>>>0) + ((bytes[4]<<8)>>>0) + bytes[5];
longitude = (longitude / 16777215.0 * 360) - 180;
switch (port)
{
case 2:
decoded.latitude = latitude;
decoded.longitude = longitude;
var altValue = ((bytes[6]<<8)>>>0) + bytes[7];
var sign = bytes[6] & (1 << 7);
if(sign) decoded.altitude = 0xFFFF0000 | altValue;
else decoded.altitude = altValue;
decoded.speed = parseFloat((((bytes[8]))/1.609).toFixed(2));
decoded.battery = parseFloat((bytes[9]/100 + 2).toFixed(2));
decoded.sats = bytes[10];
decoded.accuracy = 2.5; // Bogus Accuracy required by Cargo/Mapper integration
break;
}
return decoded;
}
```
A custom Decoder Function translates the payload bytes into a set of JSON values required by the Integrations for both Mapper and Cargo.
This turns the Base64 Payload into values for Lat, Long, Altitude, Speed, Battery, and Sats.
Note that HDOP is not sent in this data.
This [Decoder Function](https://github.com/Max-Plastix/tbeam-helium-mapper/blob/main/console-decoders/unified_decoder.js) can be pasted directly into the Console custom function. Do not use Decoder functions from other builds or instructions! The Uplink decoding is specific to the software that made the packet, so it has to match. (Note that HDOP is not sent in this data.)
## Grafana integration for custom maps
If you want to maintain your own device map, there is an excellent [Grafana guide](https://friendsoflittleyus.nl/grafana-helium-gps-tracker-on-raspberry-pi/) here and additional information and [template scripts](https://github.com/takeabyte/helium_mapper_grafana) by @takeabyte (`@friends just call me bob`) available.
# Downlink
This builds adds the option to reconfigure the Mapper remotely via Helium Downlink (network to device). You can change the maximum Time Interval, Distance, and Battery Cut-off voltage remotely.
@ -284,7 +374,7 @@ This build is a modification of work by many experts, with input from the [Heliu
The Fork history here in Github shows the lineage and prior work, including https://github.com/helium/longfi-arduino/tree/master/TTGO-TBeam-Tracker
This code was originally developed for use on The Things Network (TTN) it has been editied/repurposed for use with the Helium Network.
This code was originally developed for use on The Things Network (TTN) it has been edited/repurposed for use with the Helium Network.
This version is based on a forked repo from github user [kizniche] https://github.com/kizniche/ttgo-tbeam-ttn-tracker. Which in turn is based on the code from [xoseperez/ttgo-beam-tracker](https://github.com/xoseperez/ttgo-beam-tracker), with excerpts from [dermatthias/Lora-TTNMapper-T-Beam](https://github.com/dermatthias/Lora-TTNMapper-T-Beam) to fix an issue with incorrect GPS data being transmitted to the network. Support was also added for the 915 MHz frequency (North and South America). [lewisxhe/TTGO-T-Beam](https://github.com/lewisxhe/TTGO-T-Beam) was referenced for enabling use on the newer T-Beam board (Rev1).
@ -299,7 +389,7 @@ NOTE: There are now 2 versions of the TTGO T-BEAM, the first version (Rev0) and
2. Add the PlattformIO extension within VS Code (https://platformio.org/install/ide?install=vscode)
3. Check Device Manager if a serialdevice appear on conntecting the T-Beam. If nothing appears a driver for the USB to serial Adpater need to be installed.(https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers)
3. Check Device Manager if a serial device appear on connecting the T-Beam. If nothing appears a driver for the USB to serial Adapter need to be installed.(https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers)
3. Check and edit platformio.ini to use the right bandplan for your region.
@ -307,9 +397,9 @@ NOTE: There are now 2 versions of the TTGO T-BEAM, the first version (Rev0) and
- step by step details for setting up a Mapper integration can be found [here](https://docs.helium.com/use-the-network/coverage-mapping/mappers-quickstart/#mappers-quickstart).
- detail for setting up a Cargo integration can be found [here](https://docs.helium.com/use-the-network/console/integrations/cargo).
The specific details for adding a Mapper or Cargo integration use a different edge node device than the one detailed here. When prompted to add a function decoder, be sure to use the following decoder. Note: This decoder can also be found within this project in the console-decoders directory.
The specific details for adding a Mapper or Cargo integration use a different edge node device than the one detailed here. When prompted to add a function decoder, be sure to use the Decoder Function above.
5. Open this project file ```main/main.ino``` with the Arduino IDE Verify/Compile the project. If the compile is successful upload the application to your TTGO T-Beam.
Filenames with `.ino` are a holdover from Arduino IDE. **Do not use Arduino IDE to compile this build; use PlatformIO instead.**
6. Disconnect and turn on the device and once a GPS lock is acquired, the device should start sending data to the Helium network and Helium Mapper or Helium Cargo depending upon which you configured in step 6.

Wyświetl plik

@ -0,0 +1,82 @@
function doPost(e) {
var GS = SpreadsheetApp.openById('<your sheet ID goes here>')
// Create a sheet for today if it doesn't exist and add column headers
var SheetDate = new Date().toLocaleDateString();
if (!GS.getSheetByName(SheetDate))
GS.insertSheet(SheetDate).getRange('A1:N1').setValues([[
'Time', 'DateTime', 'Device EUI', 'Device Name', 'Battery',
'Latitude', 'Longitude', 'Sats', 'Speed',
'Hotspot', 'RSSI', 'SNR', 'Hotspot Dist', 'Hotspot Count'
]]);
// Get all contents
var json = JSON.parse(e.postData.contents);
if (json.port == 2)
var ThisSheet = GS.getSheetByName(SheetDate);
else if (json.port == 5)
var ThisSheet = GS.getSheetByName('Status');
else if (json.port == 6)
var ThisSheet = GS.getSheetByName('Lost GPS');
else
var ThisSheet = GS.getSheetByName('Unknown');
// Row place holder
var ThisRecord = [];
var i = 0;
ThisRecord[i++] = new Date().toLocaleTimeString(); // Timestamp
ThisRecord[i++] = new Date().toLocaleString(); // DateTime
ThisRecord[i++] = json.dev_eui; // EUI
ThisRecord[i++] = json.name; // Device Name
ThisRecord[i++] = json.decoded.payload.battery; // Battery
if (json.port == 2) {
ThisRecord[i++] = json.decoded.payload.latitude; // Latitude
ThisRecord[i++] = json.decoded.payload.longitude; // Longitude
ThisRecord[i++] = json.decoded.payload.sats; // Sats
ThisRecord[i++] = json.decoded.payload.speed; // Speed
//ThisRecord[i++] = json.decoded.payload.accuracy; // Accuracy stuck at 2.5
} else if (json.port == 5) {
ThisRecord[i++] = json.decoded.payload.last_latitude; // Latitude
ThisRecord[i++] = json.decoded.payload.last_longitude; // Longitude
ThisRecord[i++] = json.decoded.payload.status;
ThisRecord[i++] = json.decoded.payload.value;
} else if (json.port == 6) {
ThisRecord[i++] = json.decoded.payload.last_latitude; // Latitude
ThisRecord[i++] = json.decoded.payload.last_longitude; // Longitude
ThisRecord[i++] = json.decoded.payload.sats;
ThisRecord[i++] = json.decoded.payload.minutes;
} else {
ThisRecord[i++] = json.port;
ThisRecord[i++] = json.payload;
ThisRecord[i++] = json.payload_size;
}
ThisRecord[i++] = json.hotspots[0].name; //Hotspot Name
// ThisRecord[i++] = json.hotspots[0].lat; //Hotspot Latitude
// ThisRecord[i++] = json.hotspots[0].long; //Hotspot Longitude
ThisRecord[i++] = json.hotspots[0].rssi; //Hotspot RSSI
ThisRecord[i++] = json.hotspots[0].snr; //Hotspot SNR
// Distance to Hotspot
var lat1 = Number(json.decoded.payload.latitude);
var lon1 = Number(json.decoded.payload.longitude);
var lat2 = Number(json.hotspots[0].lat);
var lon2 = Number(json.hotspots[0].long);
var R = 6378.137; // Radius of earth in KM
var dLat = lat2 * Math.PI / 180 - lat1 * Math.PI / 180;
var dLon = lon2 * Math.PI / 180 - lon1 * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
ThisRecord[i++] = (d * 1000);
ThisRecord[i++] = json.hotspots.length; // How many hotspots heard this?
// Save in spreadsheet
ThisSheet.getRange(ThisSheet.getLastRow() + 1, 1, 1, ThisRecord.length).setValues([ThisRecord]);
}

Wyświetl plik

@ -1,15 +1,15 @@
// Decoder for MaxPlastix mappers, compatible with:
// https://github.com/hkicko/CubeCell-GPS-Helium-Mapper
// but without the Tracker payload.
// Decoder for MaxPlastix mappers
//
// 11 Byte payload:
// 3 Lat, 3 Long, 2 Altitude (m), 1 Speed (km/hr), 1 Battery, 1 Sats.
// Accuracy is a dummy value required by some Integrations.
// Battery is 1/100 of a volt, offset by 2v for a range of 2.00 to 4.56 volts.
//
function Decoder(bytes, port) {
var decoded = {};
// All formats carry a lat & lon reading:
var latitude = ((bytes[0] << 16) >>> 0) + ((bytes[1] << 8) >>> 0) + bytes[2];
latitude = (latitude / 16777215.0 * 180) - 90;
@ -23,8 +23,10 @@ function Decoder(bytes, port) {
var altValue = ((bytes[6] << 8) >>> 0) + bytes[7];
var sign = bytes[6] & (1 << 7);
if (sign) decoded.altitude = 0xFFFF0000 | altValue;
else decoded.altitude = altValue;
if (sign)
decoded.altitude = 0xFFFF0000 | altValue;
else
decoded.altitude = altValue;
decoded.speed = parseFloat((((bytes[8])) / 1.609).toFixed(2));
decoded.battery = parseFloat((bytes[9] / 100 + 2).toFixed(2));
@ -32,9 +34,22 @@ function Decoder(bytes, port) {
decoded.accuracy = 2.5; // Bogus Accuracy required by Cargo/Mapper integration
break;
case 5: // System status
decoded.last_latitude = latitude;
decoded.last_longitude = longitude;
decoded.battery = parseFloat((bytes[6] / 100 + 2).toFixed(2));
decoded.status = bytes[7];
decoded.value = bytes[8];
decoded.status = bytes[7];
switch (bytes[7]) {
case 1:
decoded.status = "BOOT";
break;
case 2:
decoded.status = "USB ON";
break
case 3:
decoded.status = "USB OFF";
break;
}
break;
case 6: // Lost GPS
decoded.last_latitude = latitude;
@ -46,4 +61,11 @@ function Decoder(bytes, port) {
}
return decoded;
}
}
// Wrapper for ChirpStack V4:
function decodeUplink(input) {
return {
data: Decoder(input.bytes, input.fPort)
};
}

Wyświetl plik

@ -0,0 +1 @@
Empty file. Ignore. This directory needs to exist to hush a platformio warning about ./include missing.

Wyświetl plik

@ -27,8 +27,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// -----------------------------------------------------------------------------
// Version
// -----------------------------------------------------------------------------
#define APP_NAME "MaxP Mapper"
#define APP_VERSION "1.7.1"
#define APP_NAME "MaxP Mapper"
#define APP_VERSION "1.7.8" // 2022-Nov-18
// -----------------------------------------------------------------------------
// CONFIGURATION
@ -39,52 +39,58 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Minimum Distance between Mapper reports. This is your MAIN knob to turn for more/fewer uplink packets.
// SMALLER distance: More packets, more dots on the map, more DC spent, more power consumed
// (If you set this to a very small value, it will still be rate-limited by how often your Region allows back-to-back Uplink packets.)
// LARGER distance: Fewer packets, might miss some hexes, conserves DC, battery might last longer
// Note that a hex is about 340m across. Ideally, you want at least two uplinks in each hex to map it.
#define MIN_DIST 70.0 // Minimum distance in meters from the last sent location before we send again.
// (If you set this to a very small value, it will still be rate-limited by how often your Region allows back-to-back
// Uplink packets.) LARGER distance: Fewer packets, might miss some hexes, conserves DC, battery might last longer Note
// that a hex is about 340m across. Ideally, you want at least two uplinks in each hex to map it.
#define MIN_DIST 70.0 // Minimum distance in meters from the last sent location before we send again.
// If we are not moving at least MIN_DIST meters away from the last uplink, when should we send a redundant
// Mapper Uplink from the same location? This Heartbeat or ping isn't all that important for mapping, but might be
// useful for time-at-location tracking or other monitoring. You can safely set this value very high.
#define STATIONARY_TX_INTERVAL ( 5 * 60) // Send one uplink at least once every N seconds
#define NEVER_REST 0 // Change to 1 if you want to always send at THIS rate, with no slowing or sleeping.
#define STATIONARY_TX_INTERVAL (5 * 60) // Send one uplink at least once every N seconds
#define NEVER_REST 0 // Change to 1 if you want to always send at THIS rate, with no slowing or sleeping.
// After being stationary for a long while, we move to a slower heartbeat interval:
#define REST_WAIT (20 * 60) // If we still haven't moved in this many seconds, start sending even slower..
#define REST_TX_INTERVAL (30 * 60) // Slow resting ping frequency in seconds
#define REST_WAIT (20 * 60) // If we still haven't moved in this many seconds, start sending even slower..
#define REST_TX_INTERVAL (30 * 60) // Slow resting ping frequency in seconds
// This last stage is a low-power sleep to conserve battery when the Mapper has not moved for a long time.
// This one is a difficult compromise: Waking up to boot & power on the GPS is not a fast operation,
// so we want to avoid it as much as possible. There is no other motion sensor, so if we make it too long,
// This one is a difficult compromise: Waking up to boot & power on the GPS is not a fast operation,
// so we want to avoid it as much as possible. There is no other motion sensor, so if we make it too long,
// we miss the first minutes of each motion while sleeping.
// Note that USB Power will prevent this low-power sleep, and also wake us up from it.
// A button press will also wake from sleep, but takes some time to initialise and re-aquire
#define SLEEP_WAIT ( 2 * 60 * 60) // If we STILL haven't moved in this long, turn off the GPS to save power
// A button press will also wake from sleep, but takes some time to initialise and re-acquire
#define SLEEP_WAIT (2 * 60 * 60) // If we STILL haven't moved in this long, turn off the GPS to save power
// For a vehicle application where USB Power appears BEFORE motion, this can be set very high without missing anything:
#define SLEEP_TX_INTERVAL ( 1 * 60 * 60) // Wake up and check position every now and then to see if movement happened
#define SLEEP_TX_INTERVAL (1 * 60 * 60) // Wake up and check position every now and then to see if movement happened
// When searching for a GPS Fix, we may never find one due to obstruction, noise, or reduced availability.
// Note that GPS Lost also counts as no-movement, so the Sleep tier above still applies
#define GPS_LOST_WAIT ( 5 * 60) // How long to wait for a GPS fix before declaring failure
#define GPS_LOST_PING (15 * 60) // Without GPS reception, how often to send a non-mapper status packet
#define GPS_LOST_WAIT (5 * 60) // How long to wait for a GPS fix before declaring failure
#define GPS_LOST_PING (15 * 60) // Without GPS reception, how often to send a non-mapper status packet
#define SCREEN_IDLE_OFF_S ( 2 * 60) // If there are no Uplinks or button presses sent for this long, turn the screen off.
#define MENU_TIMEOUT_S 5 // Seconds to wait before exiting the menu.
#define SCREEN_IDLE_OFF_S (2 * 60) // If there are no Uplinks or button presses sent for this long, turn the screen off.
#define MENU_TIMEOUT_S 5 // Seconds to wait before exiting the menu.
// Below this voltage, power off until USB power allows charging. The PMIC also has a (safety) turn-off much lower than this.
// We use a conservative 3.3v here since the battery will last longer.
#define BATTERY_LOW_VOLTAGE 3.3
// Below BATTERY_LOW_VOLTAGE, power off until USB power allows charging.
// The PMIC also has a (safety) turn-off at 2.6v, much lower than this setting.
// We use a conservative cutoff here since the battery will last longer if it's not repeatedly run to minimum.
// A new-condition 18650 cell gives around 9Wh (Molicell) when discharged to the absolute lowest 2.5v
// 8110mWh to 3.3v
// 8875mWh to 3.1v
// 9210mWh to 2.5v
#define BATTERY_LOW_VOLTAGE 3.1
// Confirmed packets (ACK request) conflict with the function of a Mapper and should not normally be enabled.
// In areas of reduced coverage, the Mapper will try to send each packet six or more times with different SF/DR.
// This causes irregular results and the location updates are infrequent, unpredictable, and out of date.
#define LORAWAN_CONFIRMED_EVERY 0 // Request Confirmation message every N Uplinks (0 means never, 1 means always, 2 every-other-one..)
// (0 means never, 1 means always, 2 every-other-one..)
#define LORAWAN_CONFIRMED_EVERY 0 // Request Confirmation message every N Uplinks
// Spreading Factor (Data Rate) determines how long each 11-byte Mapper Uplink is on-air, and how observable it is.
// SF10 is about two seconds of transmission per packet, and the highest range, while SF7 is a good compromise
// SF10 is about two seconds per packet, and the highest range, while SF7 is a good compromise
// for moving vehicles and reasonable mapping observations.
#define LORAWAN_SF DR_SF7 // Spreading factor (recommended DR_SF7 for network map purposes)
#define LORAWAN_SF DR_SF7 // Spreading factor (recommended DR_SF7 for network map purposes)
// Deadzone defines a circular area where no map packets will originate.
// This is useful to avoid sending many redundant packets in your own driveway or office, or just for local privacy.
@ -101,14 +107,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define DEADZONE_RADIUS_M 500 // meters
#endif
// There are some extra non-Mapper Uplink messages we can send, but there's no good way to avoid sending these
// to all Integrations from the Decoder. This causes (normal) Error messages on the Console because Mapper will throw
// them out for having no coordinates. It doesn't hurt anything, as they are correctly filtered by the Decoder, but if
// you don't like seeing Integration Errors, then set these to 0. Set these to 1 for extra non-mapper messages.
#ifndef SEND_GPSLOST_UPLINKS
#define SEND_GPSLOST_UPLINKS 0 // GPS Lost messages
#endif
#ifndef SEND_STATUS_UPLINKS
#define SEND_STATUS_UPLINKS 0 // USB Connect/disconnect messages
#endif
// -----------------------------------------------------------------------------
// Less common Configuration iteams
// Less common Configuration items
// -----------------------------------------------------------------------------
// Select which T-Beam board is being used. Only uncomment one.
//#define T_BEAM_V07 // AKA Rev0 (first board released) UNTESTED! Expect bugs.
#define T_BEAM_V10 // AKA Rev1 (second board released), this is the common "v1.1"
#define T_BEAM_V10 // AKA Rev1 (second board released), this is the common "v1.1"
#define LOGO_DELAY 2000 // Time to show logo on first boot (ms)
@ -117,8 +133,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Never enable ADR on Mappers because they are moving, so we don't want to adjust
// anything based on packet reception.
#define LORAWAN_ADR 0 // Do not enable ADR
#define LORAWAN_ADR 0 // Do not enable ADR
// If you are having difficulty sending messages to TTN after the first successful send,
// uncomment the next option and experiment with values (~ 1 - 5)
@ -169,11 +184,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
#define RED_LED 4 // GPIO4 on T-Beam v1.1
// -----------------------------------------------------------------------------
// OLED
// -----------------------------------------------------------------------------
#define SSD1306_ADDRESS 0x3C
// -----------------------------------------------------------------------------
// GPS
@ -189,7 +199,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#elif defined(T_BEAM_V10) // Or T-Beam v1.1
#define GPS_RX_PIN 34
#define GPS_TX_PIN 12
#define GPS_INT 37 // 30ns accurate timepulse from Neo-6M pin 3
#define GPS_INT 37 // 30ns accurate timepulse from Neo-6M pin 3
#endif
// -----------------------------------------------------------------------------

460
main/font.cpp 100644
Wyświetl plik

@ -0,0 +1,460 @@
#include <Arduino.h>
#include "font.h"
// Created by http://oleddisplay.squix.ch/
// DejaVu Sans, Plain, Size 9Roboto Medium, Plain, 11
const uint8_t Custom_Font[] PROGMEM = {
0x0A, // Width: 10
0x0A, // Height: 12 (hacked to 10)
0x20, // First Char: 32
0xE0, // Numbers of Chars: 224
// Jump Table:
0xFF, 0xFF, 0x00, 0x03, // 32:65535
0x00, 0x00, 0x04, 0x03, // 33:0
0x00, 0x04, 0x07, 0x04, // 34:4
0x00, 0x0B, 0x0D, 0x08, // 35:11
0x00, 0x18, 0x09, 0x06, // 36:24
0x00, 0x21, 0x10, 0x09, // 37:33
0x00, 0x31, 0x0E, 0x08, // 38:49
0x00, 0x3F, 0x03, 0x02, // 39:63
0x00, 0x42, 0x06, 0x04, // 40:66
0x00, 0x48, 0x05, 0x04, // 41:72
0x00, 0x4D, 0x09, 0x05, // 42:77
0x00, 0x56, 0x0B, 0x08, // 43:86
0x00, 0x61, 0x04, 0x03, // 44:97
0x00, 0x65, 0x05, 0x03, // 45:101
0x00, 0x6A, 0x04, 0x03, // 46:106
0x00, 0x6E, 0x05, 0x03, // 47:110
0x00, 0x73, 0x09, 0x06, // 48:115
0x00, 0x7C, 0x0A, 0x06, // 49:124
0x00, 0x86, 0x0A, 0x06, // 50:134
0x00, 0x90, 0x09, 0x06, // 51:144
0x00, 0x99, 0x0B, 0x06, // 52:153
0x00, 0xA4, 0x09, 0x06, // 53:164
0x00, 0xAD, 0x09, 0x06, // 54:173
0x00, 0xB6, 0x09, 0x06, // 55:182
0x00, 0xBF, 0x09, 0x06, // 56:191
0x00, 0xC8, 0x09, 0x06, // 57:200
0x00, 0xD1, 0x04, 0x03, // 58:209
0x00, 0xD5, 0x04, 0x03, // 59:213
0x00, 0xD9, 0x0E, 0x08, // 60:217
0x00, 0xE7, 0x0D, 0x08, // 61:231
0x00, 0xF4, 0x0D, 0x08, // 62:244
0x01, 0x01, 0x09, 0x05, // 63:257
0x01, 0x0A, 0x11, 0x0A, // 64:266
0x01, 0x1B, 0x0C, 0x06, // 65:283
0x01, 0x27, 0x0B, 0x07, // 66:295
0x01, 0x32, 0x0C, 0x07, // 67:306
0x01, 0x3E, 0x0B, 0x07, // 68:318
0x01, 0x49, 0x0A, 0x06, // 69:329
0x01, 0x53, 0x09, 0x06, // 70:339
0x01, 0x5C, 0x0B, 0x07, // 71:348
0x01, 0x67, 0x0C, 0x07, // 72:359
0x01, 0x73, 0x04, 0x03, // 73:371
0x01, 0x77, 0x04, 0x03, // 74:375
0x01, 0x7B, 0x0C, 0x06, // 75:379
0x01, 0x87, 0x0A, 0x05, // 76:391
0x01, 0x91, 0x0E, 0x08, // 77:401
0x01, 0x9F, 0x0C, 0x07, // 78:415
0x01, 0xAB, 0x0B, 0x07, // 79:427
0x01, 0xB6, 0x09, 0x06, // 80:438
0x01, 0xBF, 0x0B, 0x07, // 81:447
0x01, 0xCA, 0x0C, 0x06, // 82:458
0x01, 0xD6, 0x0B, 0x07, // 83:470
0x01, 0xE1, 0x09, 0x05, // 84:481
0x01, 0xEA, 0x0B, 0x07, // 85:490
0x01, 0xF5, 0x0B, 0x06, // 86:501
0x02, 0x00, 0x0D, 0x07, // 87:512
0x02, 0x0D, 0x0C, 0x06, // 88:525
0x02, 0x19, 0x09, 0x05, // 89:537
0x02, 0x22, 0x0A, 0x05, // 90:546
0x02, 0x2C, 0x06, 0x04, // 91:556
0x02, 0x32, 0x06, 0x03, // 92:562
0x02, 0x38, 0x06, 0x04, // 93:568
0x02, 0x3E, 0x0B, 0x08, // 94:574
0x02, 0x49, 0x0A, 0x05, // 95:585
0x02, 0x53, 0x05, 0x05, // 96:595
0x02, 0x58, 0x0A, 0x06, // 97:600
0x02, 0x62, 0x09, 0x06, // 98:610
0x02, 0x6B, 0x0A, 0x06, // 99:619
0x02, 0x75, 0x0A, 0x06, // 100:629
0x02, 0x7F, 0x0A, 0x06, // 101:639
0x02, 0x89, 0x05, 0x03, // 102:649
0x02, 0x8E, 0x0A, 0x06, // 103:654
0x02, 0x98, 0x0A, 0x06, // 104:664
0x02, 0xA2, 0x04, 0x03, // 105:674
0x02, 0xA6, 0x04, 0x03, // 106:678
0x02, 0xAA, 0x0A, 0x05, // 107:682
0x02, 0xB4, 0x04, 0x03, // 108:692
0x02, 0xB8, 0x10, 0x09, // 109:696
0x02, 0xC8, 0x0A, 0x06, // 110:712
0x02, 0xD2, 0x09, 0x06, // 111:722
0x02, 0xDB, 0x09, 0x06, // 112:731
0x02, 0xE4, 0x0A, 0x06, // 113:740
0x02, 0xEE, 0x07, 0x04, // 114:750
0x02, 0xF5, 0x08, 0x05, // 115:757
0x02, 0xFD, 0x08, 0x04, // 116:765
0x03, 0x05, 0x0A, 0x06, // 117:773
0x03, 0x0F, 0x09, 0x05, // 118:783
0x03, 0x18, 0x0D, 0x07, // 119:792
0x03, 0x25, 0x0A, 0x05, // 120:805
0x03, 0x2F, 0x09, 0x05, // 121:815
0x03, 0x38, 0x0A, 0x06, // 122:824
0x03, 0x42, 0x08, 0x05, // 123:834
0x03, 0x4A, 0x04, 0x03, // 124:842
0x03, 0x4E, 0x07, 0x05, // 125:846
0x03, 0x55, 0x0D, 0x08, // 126:853
0x03, 0x62, 0x0A, 0x05, // 127:866
0x03, 0x6C, 0x0A, 0x05, // 128:876
0x03, 0x76, 0x0A, 0x05, // 129:886
0x03, 0x80, 0x0A, 0x05, // 130:896
0x03, 0x8A, 0x0A, 0x05, // 131:906
0x03, 0x94, 0x0A, 0x05, // 132:916
0x03, 0x9E, 0x0A, 0x05, // 133:926
0x03, 0xA8, 0x0A, 0x05, // 134:936
0x03, 0xB2, 0x0A, 0x05, // 135:946
0x03, 0xBC, 0x0A, 0x05, // 136:956
0x03, 0xC6, 0x0A, 0x05, // 137:966
0x03, 0xD0, 0x0A, 0x05, // 138:976
0x03, 0xDA, 0x0A, 0x05, // 139:986
0x03, 0xE4, 0x0A, 0x05, // 140:996
0x03, 0xEE, 0x0A, 0x05, // 141:1006
0x03, 0xF8, 0x0A, 0x05, // 142:1016
0x04, 0x02, 0x0A, 0x05, // 143:1026
0x04, 0x0C, 0x0A, 0x05, // 144:1036
0x04, 0x16, 0x0A, 0x05, // 145:1046
0x04, 0x20, 0x0A, 0x05, // 146:1056
0x04, 0x2A, 0x0A, 0x05, // 147:1066
0x04, 0x34, 0x0A, 0x05, // 148:1076
0x04, 0x3E, 0x0A, 0x05, // 149:1086
0x04, 0x48, 0x0A, 0x05, // 150:1096
0x04, 0x52, 0x0A, 0x05, // 151:1106
0x04, 0x5C, 0x0A, 0x05, // 152:1116
0x04, 0x66, 0x0A, 0x05, // 153:1126
0x04, 0x70, 0x0A, 0x05, // 154:1136
0x04, 0x7A, 0x0A, 0x05, // 155:1146
0x04, 0x84, 0x0A, 0x05, // 156:1156
0x04, 0x8E, 0x0A, 0x05, // 157:1166
0x04, 0x98, 0x0A, 0x05, // 158:1176
0x04, 0xA2, 0x0A, 0x05, // 159:1186
0xFF, 0xFF, 0x00, 0x03, // 160:65535
0x04, 0xAC, 0x04, 0x03, // 161:1196
0x04, 0xB0, 0x0A, 0x06, // 162:1200
0x04, 0xBA, 0x0A, 0x06, // 163:1210
0x04, 0xC4, 0x0C, 0x06, // 164:1220
0x04, 0xD0, 0x09, 0x06, // 165:1232
0x04, 0xD9, 0x04, 0x03, // 166:1241
0x04, 0xDD, 0x0A, 0x05, // 167:1245
0x04, 0xE7, 0x07, 0x05, // 168:1255
0x04, 0xEE, 0x0F, 0x09, // 169:1262
0x04, 0xFD, 0x07, 0x04, // 170:1277
0x05, 0x04, 0x09, 0x06, // 171:1284
0x05, 0x0D, 0x0D, 0x08, // 172:1293
0x05, 0x1A, 0x05, 0x03, // 173:1306
0x05, 0x1F, 0x0F, 0x09, // 174:1311
0x05, 0x2E, 0x07, 0x05, // 175:1326
0x05, 0x35, 0x07, 0x05, // 176:1333
0x05, 0x3C, 0x0C, 0x08, // 177:1340
0x05, 0x48, 0x07, 0x04, // 178:1352
0x05, 0x4F, 0x07, 0x04, // 179:1359
0x05, 0x56, 0x07, 0x05, // 180:1366
0x05, 0x5D, 0x0C, 0x06, // 181:1373
0x05, 0x69, 0x0A, 0x06, // 182:1385
0x05, 0x73, 0x03, 0x03, // 183:1395
0x05, 0x76, 0x06, 0x05, // 184:1398
0x05, 0x7C, 0x07, 0x04, // 185:1404
0x05, 0x83, 0x07, 0x04, // 186:1411
0x05, 0x8A, 0x09, 0x06, // 187:1418
0x05, 0x93, 0x11, 0x09, // 188:1427
0x05, 0xA4, 0x12, 0x09, // 189:1444
0x05, 0xB6, 0x11, 0x09, // 190:1462
0x05, 0xC7, 0x0A, 0x05, // 191:1479
0x05, 0xD1, 0x0C, 0x06, // 192:1489
0x05, 0xDD, 0x0C, 0x06, // 193:1501
0x05, 0xE9, 0x0C, 0x06, // 194:1513
0x05, 0xF5, 0x0C, 0x06, // 195:1525
0x06, 0x01, 0x0C, 0x06, // 196:1537
0x06, 0x0D, 0x0C, 0x06, // 197:1549
0x06, 0x19, 0x10, 0x09, // 198:1561
0x06, 0x29, 0x0C, 0x07, // 199:1577
0x06, 0x35, 0x0A, 0x06, // 200:1589
0x06, 0x3F, 0x0A, 0x06, // 201:1599
0x06, 0x49, 0x0A, 0x06, // 202:1609
0x06, 0x53, 0x0A, 0x06, // 203:1619
0x06, 0x5D, 0x05, 0x03, // 204:1629
0x06, 0x62, 0x04, 0x03, // 205:1634
0x06, 0x66, 0x05, 0x03, // 206:1638
0x06, 0x6B, 0x05, 0x03, // 207:1643
0x06, 0x70, 0x0B, 0x07, // 208:1648
0x06, 0x7B, 0x0C, 0x07, // 209:1659
0x06, 0x87, 0x0B, 0x07, // 210:1671
0x06, 0x92, 0x0B, 0x07, // 211:1682
0x06, 0x9D, 0x0B, 0x07, // 212:1693
0x06, 0xA8, 0x0B, 0x07, // 213:1704
0x06, 0xB3, 0x0B, 0x07, // 214:1715
0x06, 0xBE, 0x0C, 0x08, // 215:1726
0x06, 0xCA, 0x0B, 0x07, // 216:1738
0x06, 0xD5, 0x0B, 0x07, // 217:1749
0x06, 0xE0, 0x0B, 0x07, // 218:1760
0x06, 0xEB, 0x0B, 0x07, // 219:1771
0x06, 0xF6, 0x0B, 0x07, // 220:1782
0x07, 0x01, 0x09, 0x05, // 221:1793
0x07, 0x0A, 0x09, 0x06, // 222:1802
0x07, 0x13, 0x0A, 0x06, // 223:1811
0x07, 0x1D, 0x0A, 0x06, // 224:1821
0x07, 0x27, 0x0A, 0x06, // 225:1831
0x07, 0x31, 0x0A, 0x06, // 226:1841
0x07, 0x3B, 0x0A, 0x06, // 227:1851
0x07, 0x45, 0x0A, 0x06, // 228:1861
0x07, 0x4F, 0x0A, 0x06, // 229:1871
0x07, 0x59, 0x10, 0x09, // 230:1881
0x07, 0x69, 0x0A, 0x06, // 231:1897
0x07, 0x73, 0x0A, 0x06, // 232:1907
0x07, 0x7D, 0x0A, 0x06, // 233:1917
0x07, 0x87, 0x0A, 0x06, // 234:1927
0x07, 0x91, 0x0A, 0x06, // 235:1937
0x07, 0x9B, 0x04, 0x03, // 236:1947
0x07, 0x9F, 0x05, 0x03, // 237:1951
0x07, 0xA4, 0x05, 0x03, // 238:1956
0x07, 0xA9, 0x05, 0x03, // 239:1961
0x07, 0xAE, 0x09, 0x06, // 240:1966
0x07, 0xB7, 0x0A, 0x06, // 241:1975
0x07, 0xC1, 0x09, 0x06, // 242:1985
0x07, 0xCA, 0x09, 0x06, // 243:1994
0x07, 0xD3, 0x09, 0x06, // 244:2003
0x07, 0xDC, 0x09, 0x06, // 245:2012
0x07, 0xE5, 0x09, 0x06, // 246:2021
0x07, 0xEE, 0x0B, 0x08, // 247:2030
0x07, 0xF9, 0x09, 0x06, // 248:2041
0x08, 0x02, 0x0A, 0x06, // 249:2050
0x08, 0x0C, 0x0A, 0x06, // 250:2060
0x08, 0x16, 0x0A, 0x06, // 251:2070
0x08, 0x20, 0x0A, 0x06, // 252:2080
0x08, 0x2A, 0x09, 0x05, // 253:2090
0x08, 0x33, 0x09, 0x06, // 254:2099
0x08, 0x3C, 0x09, 0x05, // 255:2108
// Font Data:
0x00,0x00,0x7C,0x01, // 33
0x00,0x00,0x0C,0x00,0x00,0x00,0x0C, // 34
0x00,0x00,0x50,0x00,0xD0,0x01,0x7C,0x00,0xD0,0x01,0x7C,0x00,0x50, // 35
0x30,0x01,0x28,0x01,0xFC,0x03,0x48,0x01,0xC8, // 36
0x3C,0x00,0x24,0x00,0xBC,0x01,0x60,0x00,0x30,0x00,0xEC,0x01,0x20,0x01,0xE0,0x01, // 37
0x00,0x00,0xC0,0x00,0x38,0x01,0x24,0x01,0xC4,0x00,0x88,0x01,0x60,0x01, // 38
0x00,0x00,0x0C, // 39
0x00,0x00,0xFC,0x00,0x02,0x01, // 40
0x00,0x00,0x86,0x01,0x78, // 41
0x24,0x00,0x18,0x00,0x3C,0x00,0x18,0x00,0x24, // 42
0x00,0x00,0x40,0x00,0x40,0x00,0xF0,0x01,0x40,0x00,0x40, // 43
0x00,0x00,0x00,0x03, // 44
0x00,0x00,0x40,0x00,0x40, // 45
0x00,0x00,0x00,0x01, // 46
0x80,0x01,0x70,0x00,0x0C, // 47
0x00,0x00,0xF8,0x00,0x04,0x01,0x04,0x01,0xF8, // 48
0x00,0x00,0x00,0x00,0x04,0x01,0xFC,0x01,0x00,0x01, // 49
0x00,0x00,0x88,0x01,0x44,0x01,0x24,0x01,0x18,0x01, // 50
0x00,0x00,0x08,0x01,0x24,0x01,0x24,0x01,0xD8, // 51
0x00,0x00,0xC0,0x00,0xB0,0x00,0x88,0x00,0xFC,0x01,0x80, // 52
0x00,0x00,0x3C,0x01,0x24,0x01,0x24,0x01,0xC4, // 53
0x00,0x00,0xF8,0x00,0x2C,0x01,0x24,0x01,0xC4, // 54
0x00,0x00,0x04,0x00,0x84,0x01,0x74,0x00,0x0C, // 55
0x00,0x00,0xD8,0x00,0x24,0x01,0x24,0x01,0xD8, // 56
0x00,0x00,0x38,0x01,0x24,0x01,0xA4,0x01,0xF8, // 57
0x00,0x00,0x10,0x01, // 58
0x00,0x00,0x10,0x03, // 59
0x00,0x00,0x40,0x00,0x40,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0x10,0x01, // 60
0x00,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0, // 61
0x00,0x00,0x10,0x01,0xA0,0x00,0xA0,0x00,0xA0,0x00,0x40,0x00,0x40, // 62
0x00,0x00,0x04,0x00,0x64,0x01,0x14,0x00,0x0C, // 63
0x00,0x00,0xF0,0x00,0x08,0x01,0x64,0x02,0x94,0x02,0x94,0x02,0xF4,0x01,0x88,0x00,0x70, // 64
0x80,0x01,0x70,0x00,0x4C,0x00,0x4C,0x00,0x70,0x00,0x80,0x01, // 65
0x00,0x00,0xFC,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0xD8, // 66
0x00,0x00,0xF8,0x00,0x8C,0x01,0x04,0x01,0x04,0x01,0x08,0x01, // 67
0x00,0x00,0xFC,0x01,0x04,0x01,0x04,0x01,0x8C,0x01,0xF8, // 68
0x00,0x00,0xFC,0x01,0x24,0x01,0x24,0x01,0x24,0x01, // 69
0x00,0x00,0xFC,0x01,0x24,0x00,0x24,0x00,0x24, // 70
0x00,0x00,0xF8,0x00,0x8C,0x01,0x04,0x01,0x24,0x01,0xE8, // 71
0x00,0x00,0xFC,0x01,0x20,0x00,0x20,0x00,0x20,0x00,0xFC,0x01, // 72
0x00,0x00,0xFC,0x01, // 73
0x00,0x04,0xFC,0x03, // 74
0x00,0x00,0xFC,0x01,0x20,0x00,0x50,0x00,0x88,0x00,0x04,0x01, // 75
0x00,0x00,0xFC,0x01,0x00,0x01,0x00,0x01,0x00,0x01, // 76
0x00,0x00,0xFC,0x01,0x18,0x00,0x60,0x00,0x60,0x00,0x18,0x00,0xFC,0x01, // 77
0x00,0x00,0xFC,0x01,0x18,0x00,0x20,0x00,0xC0,0x00,0xFC,0x01, // 78
0x00,0x00,0xF8,0x00,0x8C,0x01,0x04,0x01,0x8C,0x01,0xF8, // 79
0x00,0x00,0xFC,0x01,0x24,0x00,0x24,0x00,0x18, // 80
0x00,0x00,0xF8,0x00,0x8C,0x01,0x04,0x01,0x8C,0x03,0xF8, // 81
0x00,0x00,0xFC,0x01,0x24,0x00,0x64,0x00,0x9C,0x00,0x00,0x01, // 82
0x00,0x00,0x98,0x00,0x24,0x01,0x24,0x01,0x24,0x01,0xC8, // 83
0x04,0x00,0x04,0x00,0xFC,0x01,0x04,0x00,0x04, // 84
0x00,0x00,0xFC,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0xFC, // 85
0x0C,0x00,0x70,0x00,0x80,0x01,0x80,0x01,0x70,0x00,0x0C, // 86
0x0C,0x00,0x70,0x00,0xC0,0x01,0x3C,0x00,0xC0,0x01,0x70,0x00,0x0C, // 87
0x04,0x01,0x8C,0x00,0x70,0x00,0x70,0x00,0x8C,0x01,0x04,0x01, // 88
0x04,0x00,0x18,0x00,0xE0,0x01,0x18,0x00,0x04, // 89
0x84,0x01,0x44,0x01,0x24,0x01,0x14,0x01,0x0C,0x01, // 90
0x00,0x00,0xFC,0x03,0x04,0x02, // 91
0x0C,0x00,0x70,0x00,0x80,0x01, // 92
0x00,0x00,0x04,0x02,0xFC,0x03, // 93
0x00,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x04,0x00,0x08, // 94
0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04, // 95
0x00,0x00,0x02,0x00,0x04, // 96
0x00,0x00,0xC0,0x01,0x50,0x01,0x50,0x01,0xF0,0x01, // 97
0x00,0x00,0xFE,0x01,0x10,0x01,0x10,0x01,0xE0, // 98
0x00,0x00,0xE0,0x00,0x10,0x01,0x10,0x01,0x10,0x01, // 99
0x00,0x00,0xE0,0x00,0x10,0x01,0x10,0x01,0xFE,0x01, // 100
0x00,0x00,0xE0,0x00,0x50,0x01,0x50,0x01,0x60,0x01, // 101
0x10,0x00,0xFE,0x01,0x12, // 102
0x00,0x00,0xE0,0x00,0x10,0x05,0x10,0x05,0xF0,0x03, // 103
0x00,0x00,0xFE,0x01,0x10,0x00,0x10,0x00,0xF0,0x01, // 104
0x00,0x00,0xF4,0x01, // 105
0x00,0x04,0xF4,0x07, // 106
0x00,0x00,0xFE,0x01,0x40,0x00,0xA0,0x00,0x10,0x01, // 107
0x00,0x00,0xFE,0x01, // 108
0x00,0x00,0xF0,0x01,0x10,0x00,0x10,0x00,0xF0,0x01,0x10,0x00,0x10,0x00,0xF0,0x01, // 109
0x00,0x00,0xF0,0x01,0x10,0x00,0x10,0x00,0xF0,0x01, // 110
0x00,0x00,0xE0,0x00,0x10,0x01,0x10,0x01,0xE0, // 111
0x00,0x00,0xF0,0x07,0x10,0x01,0x10,0x01,0xE0, // 112
0x00,0x00,0xE0,0x00,0x10,0x01,0x10,0x01,0xF0,0x07, // 113
0x00,0x00,0xF0,0x01,0x10,0x00,0x10, // 114
0x00,0x00,0x30,0x01,0x50,0x01,0xD0,0x01, // 115
0x10,0x00,0xF8,0x01,0x10,0x01,0x10,0x01, // 116
0x00,0x00,0xF0,0x01,0x00,0x01,0x00,0x01,0xF0,0x01, // 117
0x30,0x00,0xC0,0x00,0x00,0x01,0xC0,0x00,0x30, // 118
0x70,0x00,0x80,0x01,0x60,0x00,0x10,0x00,0x60,0x00,0x80,0x01,0x70, // 119
0x10,0x01,0xA0,0x00,0x40,0x00,0xA0,0x00,0x10,0x01, // 120
0x30,0x04,0xC0,0x04,0x00,0x03,0xC0,0x00,0x30, // 121
0x00,0x00,0x10,0x01,0x90,0x01,0x50,0x01,0x30,0x01, // 122
0x00,0x00,0x20,0x00,0xDC,0x03,0x04,0x02, // 123
0x00,0x00,0xFC,0x07, // 124
0x00,0x00,0x04,0x02,0xDC,0x03,0x20, // 125
0x00,0x00,0x40,0x00,0x20,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x20, // 126
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 127
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 128
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 129
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 130
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 131
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 132
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 133
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 134
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 135
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 136
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 137
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 138
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 139
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 140
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 141
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 142
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 143
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 144
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 145
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 146
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 147
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 148
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 149
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 150
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 151
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 152
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 153
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 154
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 155
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 156
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 157
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 158
0xF8,0x07,0x08,0x04,0x08,0x04,0x08,0x04,0xF8,0x07, // 159
0x00,0x00,0xD0,0x07, // 161
0x00,0x00,0xE0,0x00,0x10,0x01,0xF8,0x03,0x10,0x01, // 162
0x00,0x00,0x20,0x01,0xF8,0x01,0x24,0x01,0x24,0x01, // 163
0x08,0x01,0xF0,0x00,0x90,0x00,0x90,0x00,0xF0,0x00,0x08,0x01, // 164
0xA4,0x00,0xB8,0x00,0xE0,0x01,0xB8,0x00,0xA4, // 165
0x00,0x00,0xB8,0x03, // 166
0x00,0x00,0x6C,0x02,0x54,0x02,0xB4,0x02,0x64,0x03, // 167
0x00,0x00,0x04,0x00,0x00,0x00,0x04, // 168
0x00,0x00,0x70,0x00,0x88,0x00,0x74,0x01,0x54,0x01,0x54,0x01,0x88,0x00,0x70, // 169
0x00,0x00,0xB0,0x00,0xB4,0x00,0xBC, // 170
0x00,0x00,0x60,0x00,0xF0,0x00,0x60,0x00,0xF0, // 171
0x00,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0xE0, // 172
0x00,0x00,0x40,0x00,0x40, // 173
0x00,0x00,0x70,0x00,0x88,0x00,0x74,0x01,0x34,0x01,0x74,0x01,0x88,0x00,0x70, // 174
0x00,0x00,0x02,0x00,0x02,0x00,0x02, // 175
0x00,0x00,0x1C,0x00,0x14,0x00,0x1C, // 176
0x00,0x00,0x20,0x01,0x20,0x01,0xF8,0x01,0x20,0x01,0x20,0x01, // 177
0x00,0x00,0x24,0x00,0x34,0x00,0x2C, // 178
0x00,0x00,0x24,0x00,0x34,0x00,0x2C, // 179
0x00,0x00,0x00,0x00,0x04,0x00,0x02, // 180
0x00,0x00,0xF0,0x07,0x00,0x01,0x00,0x01,0xF0,0x01,0x00,0x01, // 181
0x00,0x00,0x3C,0x00,0xFC,0x03,0x04,0x00,0xFC,0x03, // 182
0x00,0x00,0x20, // 183
0x00,0x00,0x00,0x04,0x00,0x06, // 184
0x00,0x00,0x24,0x00,0x3C,0x00,0x20, // 185
0x00,0x00,0xBC,0x00,0xA4,0x00,0xBC, // 186
0x00,0x00,0xF0,0x00,0x60,0x00,0xF0,0x00,0x60, // 187
0x00,0x00,0x24,0x00,0x3C,0x01,0xE0,0x00,0x70,0x00,0x18,0x00,0xC4,0x00,0xE0,0x01,0x80, // 188
0x00,0x00,0x24,0x00,0x3C,0x01,0xE0,0x00,0x70,0x00,0x18,0x00,0x24,0x01,0xA0,0x01,0x60,0x01, // 189
0x00,0x00,0x24,0x00,0x34,0x01,0xEC,0x00,0x70,0x00,0x18,0x00,0xC4,0x00,0xE0,0x01,0x80, // 190
0x00,0x00,0x00,0x06,0x00,0x05,0xD0,0x04,0x00,0x04, // 191
0x80,0x01,0x70,0x00,0x4C,0x00,0x4D,0x00,0x70,0x00,0x80,0x01, // 192
0x80,0x01,0x70,0x00,0x4D,0x00,0x4C,0x00,0x70,0x00,0x80,0x01, // 193
0x80,0x01,0x71,0x00,0x4C,0x00,0x4C,0x00,0x71,0x00,0x80,0x01, // 194
0x80,0x01,0x70,0x00,0x4D,0x00,0x4D,0x00,0x70,0x00,0x80,0x01, // 195
0x80,0x01,0x71,0x00,0x4C,0x00,0x4C,0x00,0x71,0x00,0x80,0x01, // 196
0x00,0x01,0xC0,0x00,0xBF,0x00,0xBB,0x00,0xC0,0x00,0x00,0x01, // 197
0x00,0x01,0xE0,0x00,0x98,0x00,0x84,0x00,0xFC,0x01,0x24,0x01,0x24,0x01,0x24,0x01, // 198
0x00,0x00,0xF8,0x00,0x8C,0x05,0x04,0x07,0x04,0x01,0x08,0x01, // 199
0x00,0x00,0xFC,0x01,0x24,0x01,0x25,0x01,0x24,0x01, // 200
0x00,0x00,0xFC,0x01,0x25,0x01,0x24,0x01,0x24,0x01, // 201
0x00,0x00,0xFD,0x01,0x24,0x01,0x25,0x01,0x24,0x01, // 202
0x00,0x00,0xFD,0x01,0x24,0x01,0x25,0x01,0x24,0x01, // 203
0x00,0x00,0xFC,0x01,0x01, // 204
0x00,0x00,0xFD,0x01, // 205
0x01,0x00,0xFC,0x01,0x01, // 206
0x01,0x00,0xFC,0x01,0x01, // 207
0x20,0x00,0xFC,0x01,0x24,0x01,0x04,0x01,0x8C,0x01,0xF8, // 208
0x00,0x00,0xFC,0x01,0x19,0x00,0x21,0x00,0xC1,0x00,0xFC,0x01, // 209
0x00,0x00,0xF8,0x00,0x8C,0x01,0x04,0x01,0x8D,0x01,0xF8, // 210
0x00,0x00,0xF8,0x00,0x8C,0x01,0x05,0x01,0x8C,0x01,0xF8, // 211
0x00,0x00,0xF8,0x00,0x8D,0x01,0x04,0x01,0x8D,0x01,0xF8, // 212
0x00,0x00,0xF8,0x00,0x8D,0x01,0x05,0x01,0x8D,0x01,0xF8, // 213
0x00,0x00,0xF8,0x00,0x8D,0x01,0x04,0x01,0x8D,0x01,0xF8, // 214
0x00,0x00,0x10,0x01,0xA0,0x00,0x40,0x00,0xA0,0x00,0x10,0x01, // 215
0x00,0x00,0x78,0x01,0xCC,0x01,0x34,0x01,0x8C,0x01,0xF4, // 216
0x00,0x00,0xFC,0x00,0x00,0x01,0x00,0x01,0x01,0x01,0xFC, // 217
0x00,0x00,0xFC,0x00,0x00,0x01,0x01,0x01,0x00,0x01,0xFC, // 218
0x00,0x00,0xFC,0x00,0x01,0x01,0x00,0x01,0x01,0x01,0xFC, // 219
0x00,0x00,0xFC,0x00,0x01,0x01,0x00,0x01,0x01,0x01,0xFC, // 220
0x04,0x00,0x18,0x00,0xE1,0x01,0x18,0x00,0x04, // 221
0x00,0x00,0xFC,0x01,0x48,0x00,0x48,0x00,0x30, // 222
0x00,0x00,0xFE,0x01,0x12,0x00,0x2A,0x01,0xC4,0x01, // 223
0x00,0x00,0xC2,0x01,0x54,0x01,0x50,0x01,0xF0,0x01, // 224
0x00,0x00,0xC0,0x01,0x54,0x01,0x52,0x01,0xF0,0x01, // 225
0x00,0x00,0xC0,0x01,0x56,0x01,0x54,0x01,0xF0,0x01, // 226
0x00,0x00,0xC6,0x01,0x52,0x01,0x54,0x01,0xF6,0x01, // 227
0x00,0x00,0xC4,0x01,0x50,0x01,0x50,0x01,0xF4,0x01, // 228
0x00,0x00,0xC0,0x01,0x57,0x01,0x55,0x01,0xF7,0x01, // 229
0x00,0x00,0xC0,0x01,0x50,0x01,0x50,0x01,0xE0,0x00,0x50,0x01,0x50,0x01,0x60,0x01, // 230
0x00,0x00,0xE0,0x00,0x10,0x05,0x10,0x07,0x10,0x01, // 231
0x00,0x00,0xE0,0x00,0x52,0x01,0x54,0x01,0x60,0x01, // 232
0x00,0x00,0xE0,0x00,0x50,0x01,0x54,0x01,0x62,0x01, // 233
0x00,0x00,0xE4,0x00,0x52,0x01,0x52,0x01,0x64,0x01, // 234
0x00,0x00,0xE0,0x00,0x54,0x01,0x50,0x01,0x64,0x01, // 235
0x02,0x00,0xF4,0x01, // 236
0x00,0x00,0xF4,0x01,0x02, // 237
0x04,0x00,0xF2,0x01,0x04, // 238
0x04,0x00,0xF0,0x01,0x04, // 239
0x00,0x00,0xE0,0x00,0x14,0x01,0x1C,0x01,0xE0, // 240
0x00,0x00,0xF6,0x01,0x12,0x00,0x14,0x00,0xF6,0x01, // 241
0x00,0x00,0xE0,0x00,0x12,0x01,0x14,0x01,0xE0, // 242
0x00,0x00,0xE0,0x00,0x10,0x01,0x14,0x01,0xE2, // 243
0x00,0x00,0xE0,0x00,0x16,0x01,0x14,0x01,0xE0, // 244
0x00,0x00,0xE6,0x00,0x12,0x01,0x14,0x01,0xE6, // 245
0x00,0x00,0xE4,0x00,0x10,0x01,0x10,0x01,0xE4, // 246
0x00,0x00,0x40,0x00,0x40,0x00,0x50,0x01,0x40,0x00,0x40, // 247
0x00,0x00,0xE0,0x01,0xD0,0x01,0x30,0x01,0xF0, // 248
0x00,0x00,0xF0,0x01,0x02,0x01,0x04,0x01,0xF0,0x01, // 249
0x00,0x00,0xF0,0x01,0x00,0x01,0x04,0x01,0xF2,0x01, // 250
0x00,0x00,0xF0,0x01,0x06,0x01,0x04,0x01,0xF0,0x01, // 251
0x00,0x00,0xF4,0x01,0x00,0x01,0x00,0x01,0xF4,0x01, // 252
0x30,0x04,0xC0,0x04,0x04,0x03,0xC2,0x00,0x30, // 253
0x00,0x00,0xFE,0x07,0x10,0x01,0x10,0x01,0xE0, // 254
0x30,0x04,0xC4,0x04,0x00,0x03,0xC4,0x00,0x30 // 255
};

3
main/font.h 100644
Wyświetl plik

@ -0,0 +1,3 @@
#pragma once
extern const uint8_t Custom_Font[] PROGMEM;

Wyświetl plik

@ -1,423 +0,0 @@
const uint8_t Custom_ArialMT_Plain_10[] PROGMEM = {
0x0A, // Width: 10
0x0A, // Height: 10
0x20, // First Char: 32
0xE0, // Numbers of Chars: 224
// Jump Table:
0xFF, 0xFF, 0x00, 0x03, // 32:65535
0x00, 0x00, 0x04, 0x03, // 33:0
0x00, 0x04, 0x05, 0x04, // 34:4
0x00, 0x09, 0x09, 0x06, // 35:9
0x00, 0x12, 0x0A, 0x06, // 36:18
0x00, 0x1C, 0x10, 0x09, // 37:28
0x00, 0x2C, 0x0E, 0x07, // 38:44
0x00, 0x3A, 0x01, 0x02, // 39:58
0x00, 0x3B, 0x06, 0x03, // 40:59
0x00, 0x41, 0x06, 0x03, // 41:65
0x00, 0x47, 0x05, 0x04, // 42:71
0x00, 0x4C, 0x09, 0x06, // 43:76
0x00, 0x55, 0x04, 0x03, // 44:85
0x00, 0x59, 0x03, 0x03, // 45:89
0x00, 0x5C, 0x04, 0x03, // 46:92
0x00, 0x60, 0x05, 0x03, // 47:96
0x00, 0x65, 0x0A, 0x06, // 48:101
0x00, 0x6F, 0x08, 0x06, // 49:111
0x00, 0x77, 0x0A, 0x06, // 50:119
0x00, 0x81, 0x0A, 0x06, // 51:129
0x00, 0x8B, 0x0B, 0x06, // 52:139
0x00, 0x96, 0x0A, 0x06, // 53:150
0x00, 0xA0, 0x0A, 0x06, // 54:160
0x00, 0xAA, 0x09, 0x06, // 55:170
0x00, 0xB3, 0x0A, 0x06, // 56:179
0x00, 0xBD, 0x0A, 0x06, // 57:189
0x00, 0xC7, 0x04, 0x03, // 58:199
0x00, 0xCB, 0x04, 0x03, // 59:203
0x00, 0xCF, 0x0A, 0x06, // 60:207
0x00, 0xD9, 0x09, 0x06, // 61:217
0x00, 0xE2, 0x09, 0x06, // 62:226
0x00, 0xEB, 0x0B, 0x06, // 63:235
0x00, 0xF6, 0x14, 0x0A, // 64:246
0x01, 0x0A, 0x0E, 0x07, // 65:266
0x01, 0x18, 0x0C, 0x07, // 66:280
0x01, 0x24, 0x0C, 0x07, // 67:292
0x01, 0x30, 0x0B, 0x07, // 68:304
0x01, 0x3B, 0x0C, 0x07, // 69:315
0x01, 0x47, 0x09, 0x06, // 70:327
0x01, 0x50, 0x0D, 0x08, // 71:336
0x01, 0x5D, 0x0C, 0x07, // 72:349
0x01, 0x69, 0x04, 0x03, // 73:361
0x01, 0x6D, 0x08, 0x05, // 74:365
0x01, 0x75, 0x0E, 0x07, // 75:373
0x01, 0x83, 0x0C, 0x06, // 76:387
0x01, 0x8F, 0x10, 0x08, // 77:399
0x01, 0x9F, 0x0C, 0x07, // 78:415
0x01, 0xAB, 0x0E, 0x08, // 79:427
0x01, 0xB9, 0x0B, 0x07, // 80:441
0x01, 0xC4, 0x0E, 0x08, // 81:452
0x01, 0xD2, 0x0C, 0x07, // 82:466
0x01, 0xDE, 0x0C, 0x07, // 83:478
0x01, 0xEA, 0x0B, 0x06, // 84:490
0x01, 0xF5, 0x0C, 0x07, // 85:501
0x02, 0x01, 0x0D, 0x07, // 86:513
0x02, 0x0E, 0x11, 0x09, // 87:526
0x02, 0x1F, 0x0E, 0x07, // 88:543
0x02, 0x2D, 0x0D, 0x07, // 89:557
0x02, 0x3A, 0x0C, 0x06, // 90:570
0x02, 0x46, 0x06, 0x03, // 91:582
0x02, 0x4C, 0x06, 0x03, // 92:588
0x02, 0x52, 0x04, 0x03, // 93:594
0x02, 0x56, 0x09, 0x05, // 94:598
0x02, 0x5F, 0x0C, 0x06, // 95:607
0x02, 0x6B, 0x03, 0x03, // 96:619
0x02, 0x6E, 0x0A, 0x06, // 97:622
0x02, 0x78, 0x0A, 0x06, // 98:632
0x02, 0x82, 0x0A, 0x05, // 99:642
0x02, 0x8C, 0x0A, 0x06, // 100:652
0x02, 0x96, 0x0A, 0x06, // 101:662
0x02, 0xA0, 0x05, 0x03, // 102:672
0x02, 0xA5, 0x0A, 0x06, // 103:677
0x02, 0xAF, 0x0A, 0x06, // 104:687
0x02, 0xB9, 0x04, 0x02, // 105:697
0x02, 0xBD, 0x04, 0x02, // 106:701
0x02, 0xC1, 0x08, 0x05, // 107:705
0x02, 0xC9, 0x04, 0x02, // 108:713
0x02, 0xCD, 0x10, 0x08, // 109:717
0x02, 0xDD, 0x0A, 0x06, // 110:733
0x02, 0xE7, 0x0A, 0x06, // 111:743
0x02, 0xF1, 0x0A, 0x06, // 112:753
0x02, 0xFB, 0x0A, 0x06, // 113:763
0x03, 0x05, 0x05, 0x03, // 114:773
0x03, 0x0A, 0x08, 0x05, // 115:778
0x03, 0x12, 0x06, 0x03, // 116:786
0x03, 0x18, 0x0A, 0x06, // 117:792
0x03, 0x22, 0x09, 0x05, // 118:802
0x03, 0x2B, 0x0E, 0x07, // 119:811
0x03, 0x39, 0x0A, 0x05, // 120:825
0x03, 0x43, 0x09, 0x05, // 121:835
0x03, 0x4C, 0x0A, 0x05, // 122:844
0x03, 0x56, 0x06, 0x03, // 123:854
0x03, 0x5C, 0x04, 0x03, // 124:860
0x03, 0x60, 0x05, 0x03, // 125:864
0x03, 0x65, 0x09, 0x06, // 126:869
0xFF, 0xFF, 0x00, 0x00, // 127:65535
0xFF, 0xFF, 0x00, 0x0A, // 128:65535
0xFF, 0xFF, 0x00, 0x0A, // 129:65535
0xFF, 0xFF, 0x00, 0x0A, // 130:65535
0xFF, 0xFF, 0x00, 0x0A, // 131:65535
0xFF, 0xFF, 0x00, 0x0A, // 132:65535
0xFF, 0xFF, 0x00, 0x0A, // 133:65535
0xFF, 0xFF, 0x00, 0x0A, // 134:65535
0xFF, 0xFF, 0x00, 0x0A, // 135:65535
0xFF, 0xFF, 0x00, 0x0A, // 136:65535
0xFF, 0xFF, 0x00, 0x0A, // 137:65535
0xFF, 0xFF, 0x00, 0x0A, // 138:65535
0xFF, 0xFF, 0x00, 0x0A, // 139:65535
0xFF, 0xFF, 0x00, 0x0A, // 140:65535
0xFF, 0xFF, 0x00, 0x0A, // 141:65535
0xFF, 0xFF, 0x00, 0x0A, // 142:65535
0xFF, 0xFF, 0x00, 0x0A, // 143:65535
0xFF, 0xFF, 0x00, 0x0A, // 144:65535
0xFF, 0xFF, 0x00, 0x0A, // 145:65535
0xFF, 0xFF, 0x00, 0x0A, // 146:65535
0xFF, 0xFF, 0x00, 0x0A, // 147:65535
0xFF, 0xFF, 0x00, 0x0A, // 148:65535
0xFF, 0xFF, 0x00, 0x0A, // 149:65535
0xFF, 0xFF, 0x00, 0x0A, // 150:65535
0xFF, 0xFF, 0x00, 0x0A, // 151:65535
0xFF, 0xFF, 0x00, 0x0A, // 152:65535
0xFF, 0xFF, 0x00, 0x0A, // 153:65535
0xFF, 0xFF, 0x00, 0x0A, // 154:65535
0xFF, 0xFF, 0x00, 0x0A, // 155:65535
0xFF, 0xFF, 0x00, 0x0A, // 156:65535
0xFF, 0xFF, 0x00, 0x0A, // 157:65535
0xFF, 0xFF, 0x00, 0x0A, // 158:65535
0xFF, 0xFF, 0x00, 0x0A, // 159:65535
0xFF, 0xFF, 0x00, 0x03, // 160:65535
0x03, 0x6E, 0x04, 0x03, // 161:878
0x03, 0x72, 0x0A, 0x06, // 162:882
0x03, 0x7C, 0x0C, 0x06, // 163:892
0x03, 0x88, 0x0A, 0x06, // 164:904
0x03, 0x92, 0x0A, 0x06, // 165:914
0x03, 0x9C, 0x04, 0x03, // 166:924
0x03, 0xA0, 0x0A, 0x06, // 167:928
0x03, 0xAA, 0x05, 0x03, // 168:938
0x03, 0xAF, 0x0D, 0x07, // 169:943
0x03, 0xBC, 0x07, 0x04, // 170:956
0x03, 0xC3, 0x0A, 0x06, // 171:963
0x03, 0xCD, 0x09, 0x06, // 172:973
0x03, 0xD6, 0x03, 0x03, // 173:982
0x03, 0xD9, 0x0D, 0x07, // 174:985
0x03, 0xE6, 0x0B, 0x06, // 175:998
0x03, 0xF1, 0x07, 0x04, // 176:1009
0x03, 0xF8, 0x0A, 0x05, // 177:1016
0x04, 0x02, 0x05, 0x03, // 178:1026
0x04, 0x07, 0x05, 0x03, // 179:1031
0x04, 0x0C, 0x05, 0x03, // 180:1036
0x04, 0x11, 0x0A, 0x06, // 181:1041
0x04, 0x1B, 0x09, 0x05, // 182:1051
0x04, 0x24, 0x03, 0x03, // 183:1060
0x04, 0x27, 0x06, 0x03, // 184:1063
0x04, 0x2D, 0x05, 0x03, // 185:1069
0x04, 0x32, 0x07, 0x04, // 186:1074
0x04, 0x39, 0x0A, 0x06, // 187:1081
0x04, 0x43, 0x10, 0x08, // 188:1091
0x04, 0x53, 0x10, 0x08, // 189:1107
0x04, 0x63, 0x10, 0x08, // 190:1123
0x04, 0x73, 0x0A, 0x06, // 191:1139
0x04, 0x7D, 0x0E, 0x07, // 192:1149
0x04, 0x8B, 0x0E, 0x07, // 193:1163
0x04, 0x99, 0x0E, 0x07, // 194:1177
0x04, 0xA7, 0x0E, 0x07, // 195:1191
0x04, 0xB5, 0x0E, 0x07, // 196:1205
0x04, 0xC3, 0x0E, 0x07, // 197:1219
0x04, 0xD1, 0x12, 0x0A, // 198:1233
0x04, 0xE3, 0x0C, 0x07, // 199:1251
0x04, 0xEF, 0x0C, 0x07, // 200:1263
0x04, 0xFB, 0x0C, 0x07, // 201:1275
0x05, 0x07, 0x0C, 0x07, // 202:1287
0x05, 0x13, 0x0C, 0x07, // 203:1299
0x05, 0x1F, 0x05, 0x03, // 204:1311
0x05, 0x24, 0x04, 0x03, // 205:1316
0x05, 0x28, 0x04, 0x03, // 206:1320
0x05, 0x2C, 0x05, 0x03, // 207:1324
0x05, 0x31, 0x0B, 0x07, // 208:1329
0x05, 0x3C, 0x0C, 0x07, // 209:1340
0x05, 0x48, 0x0E, 0x08, // 210:1352
0x05, 0x56, 0x0E, 0x08, // 211:1366
0x05, 0x64, 0x0E, 0x08, // 212:1380
0x05, 0x72, 0x0E, 0x08, // 213:1394
0x05, 0x80, 0x0E, 0x08, // 214:1408
0x05, 0x8E, 0x0A, 0x06, // 215:1422
0x05, 0x98, 0x0D, 0x08, // 216:1432
0x05, 0xA5, 0x0C, 0x07, // 217:1445
0x05, 0xB1, 0x0C, 0x07, // 218:1457
0x05, 0xBD, 0x0C, 0x07, // 219:1469
0x05, 0xC9, 0x0C, 0x07, // 220:1481
0x05, 0xD5, 0x0D, 0x07, // 221:1493
0x05, 0xE2, 0x0B, 0x07, // 222:1506
0x05, 0xED, 0x0C, 0x06, // 223:1517
0x05, 0xF9, 0x0A, 0x06, // 224:1529
0x06, 0x03, 0x0A, 0x06, // 225:1539
0x06, 0x0D, 0x0A, 0x06, // 226:1549
0x06, 0x17, 0x0A, 0x06, // 227:1559
0x06, 0x21, 0x0A, 0x06, // 228:1569
0x06, 0x2B, 0x0A, 0x06, // 229:1579
0x06, 0x35, 0x10, 0x09, // 230:1589
0x06, 0x45, 0x0A, 0x05, // 231:1605
0x06, 0x4F, 0x0A, 0x06, // 232:1615
0x06, 0x59, 0x0A, 0x06, // 233:1625
0x06, 0x63, 0x0A, 0x06, // 234:1635
0x06, 0x6D, 0x0A, 0x06, // 235:1645
0x06, 0x77, 0x05, 0x03, // 236:1655
0x06, 0x7C, 0x04, 0x03, // 237:1660
0x06, 0x80, 0x05, 0x03, // 238:1664
0x06, 0x85, 0x05, 0x03, // 239:1669
0x06, 0x8A, 0x0A, 0x06, // 240:1674
0x06, 0x94, 0x0A, 0x06, // 241:1684
0x06, 0x9E, 0x0A, 0x06, // 242:1694
0x06, 0xA8, 0x0A, 0x06, // 243:1704
0x06, 0xB2, 0x0A, 0x06, // 244:1714
0x06, 0xBC, 0x0A, 0x06, // 245:1724
0x06, 0xC6, 0x0A, 0x06, // 246:1734
0x06, 0xD0, 0x09, 0x05, // 247:1744
0x06, 0xD9, 0x0A, 0x06, // 248:1753
0x06, 0xE3, 0x0A, 0x06, // 249:1763
0x06, 0xED, 0x0A, 0x06, // 250:1773
0x06, 0xF7, 0x0A, 0x06, // 251:1783
0x07, 0x01, 0x0A, 0x06, // 252:1793
0x07, 0x0B, 0x09, 0x05, // 253:1803
0x07, 0x14, 0x0A, 0x06, // 254:1812
0x07, 0x1E, 0x09, 0x05, // 255:1822
// Font Data:
0x00,0x00,0xF8,0x02, // 33
0x38,0x00,0x00,0x00,0x38, // 34
0xA0,0x03,0xE0,0x00,0xB8,0x03,0xE0,0x00,0xB8, // 35
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
0x80,0x01,0x50,0x02,0x68,0x02,0xA8,0x02,0x18,0x01,0x80,0x03,0x80,0x02, // 38
0x38, // 39
0xE0,0x03,0x10,0x04,0x08,0x08, // 40
0x08,0x08,0x10,0x04,0xE0,0x03, // 41
0x28,0x00,0x18,0x00,0x28, // 42
0x40,0x00,0x40,0x00,0xF0,0x01,0x40,0x00,0x40, // 43
0x00,0x00,0x00,0x06, // 44
0x80,0x00,0x80, // 45
0x00,0x00,0x00,0x02, // 46
0x00,0x03,0xE0,0x00,0x18, // 47
0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 48
0x00,0x00,0x20,0x00,0x10,0x00,0xF8,0x03, // 49
0x10,0x02,0x08,0x03,0x88,0x02,0x48,0x02,0x30,0x02, // 50
0x10,0x01,0x08,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 51
0xC0,0x00,0xA0,0x00,0x90,0x00,0x88,0x00,0xF8,0x03,0x80, // 52
0x60,0x01,0x38,0x02,0x28,0x02,0x28,0x02,0xC8,0x01, // 53
0xF0,0x01,0x28,0x02,0x28,0x02,0x28,0x02,0xD0,0x01, // 54
0x08,0x00,0x08,0x03,0xC8,0x00,0x38,0x00,0x08, // 55
0xB0,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 56
0x70,0x01,0x88,0x02,0x88,0x02,0x88,0x02,0xF0,0x01, // 57
0x00,0x00,0x20,0x02, // 58
0x00,0x00,0x20,0x06, // 59
0x00,0x00,0x40,0x00,0xA0,0x00,0xA0,0x00,0x10,0x01, // 60
0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0, // 61
0x00,0x00,0x10,0x01,0xA0,0x00,0xA0,0x00,0x40, // 62
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,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,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,0x48,0x02,0x48,0x02,0x48,0x02,0x48,0x02, // 69
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,0xF8,0x03,0x40,0x00,0x40,0x00,0x40,0x00,0xF8,0x03, // 72
0x00,0x00,0xF8,0x03, // 73
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,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,0x40,0x00,0x80,0x01,0xF8,0x03, // 78
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,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,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,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
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
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
0x00,0x00,0xF8,0x0F,0x08,0x08, // 91
0x18,0x00,0xE0,0x00,0x00,0x03, // 92
0x08,0x08,0xF8,0x0F, // 93
0x40,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x40, // 94
0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08, // 95
0x08,0x00,0x10, // 96
0x00,0x00,0x00,0x03,0xA0,0x02,0xA0,0x02,0xE0,0x03, // 97
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,0xF8,0x03, // 100
0x00,0x00,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 101
0x20,0x00,0xF0,0x03,0x28, // 102
0x00,0x00,0xC0,0x05,0x20,0x0A,0x20,0x0A,0xE0,0x07, // 103
0x00,0x00,0xF8,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 104
0x00,0x00,0xE8,0x03, // 105
0x00,0x08,0xE8,0x07, // 106
0xF8,0x03,0x80,0x00,0xC0,0x01,0x20,0x02, // 107
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,0xC0,0x03, // 110
0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xC0,0x01, // 111
0x00,0x00,0xE0,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 112
0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xE0,0x0F, // 113
0x00,0x00,0xE0,0x03,0x20, // 114
0x40,0x02,0xA0,0x02,0xA0,0x02,0x20,0x01, // 115
0x20,0x00,0xF8,0x03,0x20,0x02, // 116
0x00,0x00,0xE0,0x01,0x00,0x02,0x00,0x02,0xE0,0x03, // 117
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
0x20,0x02,0x40,0x01,0x80,0x00,0x40,0x01,0x20,0x02, // 120
0x20,0x00,0xC0,0x09,0x00,0x06,0xC0,0x01,0x20, // 121
0x20,0x02,0x20,0x03,0xA0,0x02,0x60,0x02,0x20,0x02, // 122
0x80,0x00,0x78,0x0F,0x08,0x08, // 123
0x00,0x00,0xF8,0x0F, // 124
0x08,0x08,0x78,0x0F,0x80, // 125
0xC0,0x00,0x40,0x00,0xC0,0x00,0x80,0x00,0xC0, // 126
0x00,0x00,0xA0,0x0F, // 161
0x00,0x00,0xC0,0x01,0xA0,0x0F,0x78,0x02,0x40,0x01, // 162
0x40,0x02,0x70,0x03,0xC8,0x02,0x48,0x02,0x08,0x02,0x10,0x02, // 163
0x00,0x00,0xE0,0x01,0x20,0x01,0x20,0x01,0xE0,0x01, // 164
0x48,0x01,0x70,0x01,0xC0,0x03,0x70,0x01,0x48,0x01, // 165
0x00,0x00,0x38,0x0F, // 166
0xD0,0x04,0x28,0x09,0x48,0x09,0x48,0x0A,0x90,0x05, // 167
0x08,0x00,0x00,0x00,0x08, // 168
0xE0,0x00,0x10,0x01,0x48,0x02,0xA8,0x02,0xA8,0x02,0x10,0x01,0xE0, // 169
0x68,0x00,0x68,0x00,0x68,0x00,0x78, // 170
0x00,0x00,0x80,0x01,0x40,0x02,0x80,0x01,0x40,0x02, // 171
0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0xE0, // 172
0x80,0x00,0x80, // 173
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
0x00,0x00,0x38,0x00,0x28,0x00,0x38, // 176
0x40,0x02,0x40,0x02,0xF0,0x03,0x40,0x02,0x40,0x02, // 177
0x48,0x00,0x68,0x00,0x58, // 178
0x48,0x00,0x58,0x00,0x68, // 179
0x00,0x00,0x10,0x00,0x08, // 180
0x00,0x00,0xE0,0x0F,0x00,0x02,0x00,0x02,0xE0,0x03, // 181
0x70,0x00,0xF8,0x0F,0x08,0x00,0xF8,0x0F,0x08, // 182
0x00,0x00,0x40, // 183
0x00,0x00,0x00,0x14,0x00,0x18, // 184
0x00,0x00,0x10,0x00,0x78, // 185
0x30,0x00,0x48,0x00,0x48,0x00,0x30, // 186
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,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
0x00,0x00,0x00,0x06,0x00,0x09,0xA0,0x09,0x00,0x04, // 191
0x00,0x02,0xC0,0x01,0xB0,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 192
0x00,0x02,0xC0,0x01,0xB0,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 193
0x00,0x02,0xC0,0x01,0xB2,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 194
0x00,0x02,0xC2,0x01,0xB1,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 195
0x00,0x02,0xC0,0x01,0xB2,0x00,0x88,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 196
0x00,0x02,0xC0,0x01,0xBE,0x00,0x8A,0x00,0xBE,0x00,0xC0,0x01,0x00,0x02, // 197
0x00,0x03,0xC0,0x00,0xE0,0x00,0x98,0x00,0x88,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02, // 198
0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x16,0x08,0x1A,0x10,0x01, // 199
0x00,0x00,0xF8,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 200
0x00,0x00,0xF8,0x03,0x48,0x02,0x4A,0x02,0x49,0x02,0x48,0x02, // 201
0x00,0x00,0xFA,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 202
0x00,0x00,0xF8,0x03,0x4A,0x02,0x48,0x02,0x4A,0x02,0x48,0x02, // 203
0x00,0x00,0xF9,0x03,0x02, // 204
0x02,0x00,0xF9,0x03, // 205
0x01,0x00,0xFA,0x03, // 206
0x02,0x00,0xF8,0x03,0x02, // 207
0x40,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x10,0x01,0xE0, // 208
0x00,0x00,0xFA,0x03,0x31,0x00,0x42,0x00,0x81,0x01,0xF8,0x03, // 209
0x00,0x00,0xF0,0x01,0x08,0x02,0x09,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 210
0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x08,0x02,0xF0,0x01, // 211
0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x0A,0x02,0xF0,0x01, // 212
0x00,0x00,0xF0,0x01,0x0A,0x02,0x09,0x02,0x0A,0x02,0x09,0x02,0xF0,0x01, // 213
0x00,0x00,0xF0,0x01,0x0A,0x02,0x08,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 214
0x10,0x01,0xA0,0x00,0xE0,0x00,0xA0,0x00,0x10,0x01, // 215
0x00,0x00,0xF0,0x02,0x08,0x03,0xC8,0x02,0x28,0x02,0x18,0x03,0xE8, // 216
0x00,0x00,0xF8,0x01,0x01,0x02,0x02,0x02,0x00,0x02,0xF8,0x01, // 217
0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x00,0x02,0xF8,0x01, // 218
0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x02,0x02,0xF8,0x01, // 219
0x00,0x00,0xF8,0x01,0x02,0x02,0x00,0x02,0x02,0x02,0xF8,0x01, // 220
0x08,0x00,0x10,0x00,0x20,0x00,0xC2,0x03,0x21,0x00,0x10,0x00,0x08, // 221
0x00,0x00,0xF8,0x03,0x10,0x01,0x10,0x01,0x10,0x01,0xE0, // 222
0x00,0x00,0xF0,0x03,0x08,0x01,0x48,0x02,0xB0,0x02,0x80,0x01, // 223
0x00,0x00,0x00,0x03,0xA4,0x02,0xA8,0x02,0xE0,0x03, // 224
0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE0,0x03, // 225
0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE8,0x03, // 226
0x00,0x00,0x08,0x03,0xA4,0x02,0xA8,0x02,0xE4,0x03, // 227
0x00,0x00,0x00,0x03,0xA8,0x02,0xA0,0x02,0xE8,0x03, // 228
0x00,0x00,0x00,0x03,0xAE,0x02,0xAA,0x02,0xEE,0x03, // 229
0x00,0x00,0x40,0x03,0xA0,0x02,0xA0,0x02,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 230
0x00,0x00,0xC0,0x01,0x20,0x16,0x20,0x1A,0x40,0x01, // 231
0x00,0x00,0xC0,0x01,0xA4,0x02,0xA8,0x02,0xC0,0x02, // 232
0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC0,0x02, // 233
0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC8,0x02, // 234
0x00,0x00,0xC0,0x01,0xA8,0x02,0xA0,0x02,0xC8,0x02, // 235
0x00,0x00,0xE4,0x03,0x08, // 236
0x08,0x00,0xE4,0x03, // 237
0x08,0x00,0xE4,0x03,0x08, // 238
0x08,0x00,0xE0,0x03,0x08, // 239
0x00,0x00,0xC0,0x01,0x28,0x02,0x38,0x02,0xE0,0x01, // 240
0x00,0x00,0xE8,0x03,0x24,0x00,0x28,0x00,0xC4,0x03, // 241
0x00,0x00,0xC0,0x01,0x24,0x02,0x28,0x02,0xC0,0x01, // 242
0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC0,0x01, // 243
0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC8,0x01, // 244
0x00,0x00,0xC8,0x01,0x24,0x02,0x28,0x02,0xC4,0x01, // 245
0x00,0x00,0xC0,0x01,0x28,0x02,0x20,0x02,0xC8,0x01, // 246
0x40,0x00,0x40,0x00,0x50,0x01,0x40,0x00,0x40, // 247
0x00,0x00,0xC0,0x02,0xA0,0x03,0x60,0x02,0xA0,0x01, // 248
0x00,0x00,0xE0,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 249
0x00,0x00,0xE0,0x01,0x08,0x02,0x04,0x02,0xE0,0x03, // 250
0x00,0x00,0xE8,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 251
0x00,0x00,0xE0,0x01,0x08,0x02,0x00,0x02,0xE8,0x03, // 252
0x20,0x00,0xC0,0x09,0x08,0x06,0xC4,0x01,0x20, // 253
0x00,0x00,0xF8,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 254
0x20,0x00,0xC8,0x09,0x00,0x06,0xC8,0x01,0x20 // 255
};

Wyświetl plik

@ -11,49 +11,18 @@
HardwareSerial gpsSerial(GPS_SERIAL_NUM);
SFE_UBLOX_GPS myGNSS;
TinyGPSPlus _gps;
TinyGPSPlus tGPS;
void gps_time(char* buffer, uint8_t size) {
snprintf(buffer, size, "%02d:%02d:%02d", _gps.time.hour(), _gps.time.minute(), _gps.time.second());
}
float gps_latitude() {
return _gps.location.lat();
}
float gps_distanceBetween(float last_lat, float last_lon, float lat, float lon) {
return _gps.distanceBetween(last_lat, last_lon, lat, lon);
}
float gps_longitude() {
return _gps.location.lng();
}
float gps_altitude() {
return _gps.altitude.meters();
}
float gps_hdop() {
return _gps.hdop.hdop();
}
uint8_t gps_sats() {
return _gps.satellites.value();
}
float gps_speed() {
return _gps.speed.kmph();
}
uint32_t gps_sentencesWithFix() {
return _gps.sentencesWithFix();
snprintf(buffer, size, "%02d:%02d:%02d", tGPS.time.hour(), tGPS.time.minute(), tGPS.time.second());
}
void gps_end(void) {
gpsSerial.end();
}
void gps_setup(void) {
void gps_setup(boolean first_init) {
static boolean serial_ready = false;
if (serial_ready) {
gpsSerial.updateBaudRate(GPS_BAUDRATE);
@ -65,18 +34,14 @@ void gps_setup(void) {
// Drain any waiting garbage
while (gpsSerial.read() != -1)
;
// Flush out line noise from our side
//char zeros[] = {0, 0, 0, 0, 0, 0};
//gpsSerial.write(zeros, sizeof(zeros));
if (0)
myGNSS.enableDebugging();
// myGNSS.enableDebugging();
bool changed_speed = false;
bool changed_speed = false; // Assume we're already at the right speed
// Check all the possible GPS bitrates to get in sync
do {
gpsSerial.updateBaudRate(GPS_BAUDRATE);
gpsSerial.updateBaudRate(GPS_BAUDRATE); // Try the desired speed first
if (myGNSS.begin(gpsSerial)) {
Serial.println("GPS connected.");
break;
@ -120,12 +85,11 @@ void gps_setup(void) {
Serial.println("Could not connect to GPS. Retrying all speeds...");
} while (1);
myGNSS.setUART1Output(COM_TYPE_NMEA); // We do want NMEA
// Configure NMEA messages only once, save to flash
if (first_init) {
myGNSS.setUART1Output(COM_TYPE_NMEA); // We do want NMEA
if (0)
myGNSS.factoryReset();
myGNSS.setNavigationFrequency(2); // Produce X solutions per second
myGNSS.setNavigationFrequency(2); // Produce X solutions per second
#if 0
// On the Neo6M, these are all off by default anyway:
@ -133,7 +97,6 @@ void gps_setup(void) {
myGNSS.disableNMEAMessage(UBX_NMEA_GAQ, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GBQ, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GBS, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GLQ, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GNQ, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GNS, COM_PORT_UART1);
@ -141,21 +104,39 @@ void gps_setup(void) {
myGNSS.disableNMEAMessage(UBX_NMEA_GRS, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GST, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_TXT, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_VLW, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_ZDA, COM_PORT_UART1);
#endif
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_UART1); // Don't need SV list (on by default)
myGNSS.enableNMEAMessage(UBX_NMEA_RMC, COM_PORT_UART1); // For Speed
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_UART1); // For Time & Location & SV count
// Disable these messages that are on after factory reset
myGNSS.disableNMEAMessage(UBX_NMEA_GSV, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GLL, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_VTG, COM_PORT_UART1);
myGNSS.disableNMEAMessage(UBX_NMEA_GSA, COM_PORT_UART1);
myGNSS.enableNMEAMessage(UBX_NMEA_RMC, COM_PORT_UART1); // For Speed
myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_UART1); // For Time & Location & SV count
}
if (changed_speed)
if (first_init || changed_speed)
myGNSS.saveConfiguration(); // Save the current settings to flash and BBR
}
void gps_full_reset(void) {
Serial.println("Resetting GPS...");
myGNSS.factoryReset();
delay(5000);
Serial.println("Reconfiguring GPS...");
gps_setup(true);
delay(1000);
// Drain any waiting garbage
while (gpsSerial.read() != -1)
;
// gps_passthrough();
// ESP.restart();
}
void gps_passthrough(void) {
Serial.println("GPS Passthrough forever...");
while (1) {
@ -171,6 +152,6 @@ void gps_loop(boolean print_it) {
char c = gpsSerial.read();
if (print_it)
Serial.print(c);
_gps.encode(c);
tGPS.encode(c);
}
}

Wyświetl plik

@ -2,18 +2,13 @@
#pragma once
#include <Arduino.h>
#include <TinyGPS++.h>
extern TinyGPSPlus tGPS;
void gps_loop(boolean print_it);
void gps_setup(void);
void gps_setup(boolean first_init);
void gps_time(char *buffer, uint8_t size);
float gps_latitude(void);
float gps_distanceBetween(float lat1, float long1, float lat2, float long2);
float gps_longitude(void);
float gps_altitude(void);
float gps_hdop(void);
uint8_t gps_sats(void);
float gps_hdop(void);
float gps_speed(void);
void gps_passthrough(void);
uint32_t gps_sentencesWithFix(void);
void gps_end(void);
void gps_full_reset(void);

Wyświetl plik

@ -48,7 +48,10 @@
#define FPORT_MAPPER 2 // FPort for Uplink messages -- must match Helium Console Decoder script!
#define FPORT_STATUS 5
#define FPORT_GPSLOST 6
#define STATUS_BOOT 1
#define STATUS_USB_ON 2
#define STATUS_USB_OFF 3
// Defined in ttn.ino
void ttn_register(void (*callback)(uint8_t message));
@ -57,14 +60,14 @@ bool justSendNow = true; // Send one at boot, regardless of deadz
unsigned long int last_send_ms = 0; // Time of last uplink
unsigned long int last_moved_ms = 0; // Time of last movement
unsigned long int last_gpslost_ms = 0; // Time of last gps-lost packet
float last_send_lat = 0; // Last known location
float last_send_lon = 0; //
float dist_moved = 0; // Distance in m from last uplink
double last_send_lat = 0; // Last known location
double last_send_lon = 0; //
double dist_moved = 0; // Distance in m from last uplink
// Deadzone (no uplink) location and radius
float deadzone_lat = DEADZONE_LAT;
float deadzone_lon = DEADZONE_LON;
float deadzone_radius_m = DEADZONE_RADIUS_M;
double deadzone_lat = DEADZONE_LAT;
double deadzone_lon = DEADZONE_LON;
double deadzone_radius_m = DEADZONE_RADIUS_M;
boolean in_deadzone = false;
/* Defaults that can be overwritten by downlink messages */
@ -75,10 +78,20 @@ unsigned int rest_tx_interval_s; // prefs REST_TX_INTERVAL
unsigned int tx_interval_s; // Currently-active time interval
enum activity_state { ACTIVITY_MOVING, ACTIVITY_REST, ACTIVITY_SLEEP, ACTIVITY_GPS_LOST, ACTIVITY_WOKE };
enum activity_state active_state = ACTIVITY_MOVING;
enum activity_state {
ACTIVITY_MOVING,
ACTIVITY_REST,
ACTIVITY_SLEEP,
ACTIVITY_GPS_LOST,
ACTIVITY_WOKE,
ACTIVITY_INVALID
};
enum activity_state active_state = ACTIVITY_INVALID;
boolean never_rest = NEVER_REST;
// Return status from mapper uplink, since we care about the flavor of the failure
enum mapper_uplink_result { MAPPER_UPLINK_SUCCESS, MAPPER_UPLINK_BADFIX, MAPPER_UPLINK_NOLORA, MAPPER_UPLINK_NOTYET };
/* Maybe these moves to prefs eventually? */
unsigned int sleep_wait_s = SLEEP_WAIT;
unsigned int sleep_tx_interval_s = SLEEP_TX_INTERVAL;
@ -93,8 +106,9 @@ float min_dist_moved = MIN_DIST;
AXP20X_Class axp;
bool pmu_irq = false; // true when PMU IRQ pending
bool ssd1306_found = false;
bool oled_found = false;
bool axp192_found = false;
uint8_t oled_addr = 0; // i2c address of OLED controller
bool packetQueued;
bool isJoined = false;
@ -105,6 +119,8 @@ bool is_screen_on = true;
int screen_idle_off_s = SCREEN_IDLE_OFF_S;
uint32_t screen_last_active_ms = 0;
boolean in_menu = false;
boolean have_usb_power = true;
uint8_t usb_power_count = 0;
// Buffer for Payload frame
static uint8_t txBuffer[11];
@ -122,7 +138,7 @@ unsigned long int ack_req = 0;
unsigned long int ack_rx = 0;
// Store Lat & Long in six bytes of payload
void pack_lat_lon(float lat, float lon) {
void pack_lat_lon(double lat, double lon) {
uint32_t LatitudeBinary;
uint32_t LongitudeBinary;
LatitudeBinary = ((lat + 90) / 180.0) * 16777215;
@ -143,25 +159,26 @@ uint8_t battery_byte(void) {
// Prepare a packet for the Mapper
void build_mapper_packet() {
float lat;
float lon;
double lat;
double lon;
uint16_t altitudeGps;
// uint8_t hdopGps;
uint8_t sats;
uint16_t speed;
lat = gps_latitude();
lon = gps_longitude();
lat = tGPS.location.lat();
lon = tGPS.location.lng();
pack_lat_lon(lat, lon);
altitudeGps = (uint16_t)gps_altitude();
speed = (uint16_t)gps_speed(); // convert from float
sats = gps_sats();
altitudeGps = (uint16_t)tGPS.altitude.meters();
speed = (uint16_t)tGPS.speed.kmph(); // convert from double
if (speed > 255)
speed = 255; // don't wrap around.
sats = tGPS.satellites.value();
sprintf(buffer, "Lat: %f, ", lat);
Serial.print(buffer);
sprintf(buffer, "Long: %f, ", lon);
Serial.print(buffer);
sprintf(buffer, "Alt: %f, ", gps_altitude());
sprintf(buffer, "Alt: %f, ", tGPS.altitude.meters());
Serial.print(buffer);
sprintf(buffer, "Sats: %d", sats);
Serial.println(buffer);
@ -169,12 +186,16 @@ void build_mapper_packet() {
txBuffer[6] = (altitudeGps >> 8) & 0xFF;
txBuffer[7] = altitudeGps & 0xFF;
txBuffer[8] = ((unsigned char *)(&speed))[0];
txBuffer[8] = speed & 0xFF;
txBuffer[9] = battery_byte();
txBuffer[10] = sats & 0xFF;
}
// Helium requires a FCount reset sometime before hitting 0xFFFF
// 50,000 makes it obvious it was intentional
#define MAX_FCOUNT 50000
boolean send_uplink(uint8_t *txBuffer, uint8_t length, uint8_t fport, boolean confirmed) {
if (confirmed) {
Serial.println("ACK requested");
@ -189,10 +210,28 @@ boolean send_uplink(uint8_t *txBuffer, uint8_t length, uint8_t fport, boolean co
Serial.println("Surprise send failure!");
return false;
}
// Helium requires a re-join / reset of count to avoid 16bit count rollover
// Hopefully a device reboot every 50k uplinks is no problem.
if (ttn_get_count() > MAX_FCOUNT) {
Serial.println("FCount Rollover!");
// I don't understand why this doesn't show at all
screen_print("\n\nRollover Reset!\n");
screen_update();
delay(1000); // Give some time to read the screen
ttn_erase_prefs();
ESP.restart();
}
return true;
}
bool status_uplink(uint8_t status, uint8_t value) {
if (!SEND_STATUS_UPLINKS)
return false;
pack_lat_lon(last_send_lat, last_send_lon);
txBuffer[6] = battery_byte();
txBuffer[7] = status;
@ -205,10 +244,13 @@ bool status_uplink(uint8_t status, uint8_t value) {
bool gpslost_uplink(void) {
uint16_t minutes_lost;
if (!SEND_GPSLOST_UPLINKS)
return false;
minutes_lost = (last_fix_time - millis()) / 1000 / 60;
pack_lat_lon(last_send_lat, last_send_lon);
txBuffer[6] = battery_byte();
txBuffer[7] = gps_sats();
txBuffer[7] = tGPS.satellites.value();
txBuffer[8] = (minutes_lost >> 8) & 0xFF;
txBuffer[9] = minutes_lost & 0xFF;
Serial.printf("Tx: GPSLOST %d\n", minutes_lost);
@ -217,35 +259,44 @@ bool gpslost_uplink(void) {
}
// Send a packet, if one is warranted
bool mapper_uplink() {
float now_lat = gps_latitude();
float now_long = gps_longitude();
enum mapper_uplink_result mapper_uplink() {
double now_lat = tGPS.location.lat();
double now_lon = tGPS.location.lng();
unsigned long int now = millis();
// Here we try to filter out bogus GPS readings.
// It's not correct, and there should be a better indication from GPS that the
// fix is invalid
if (gps_hdop() <= 0 || gps_hdop() > 50 || now_lat == 0.0 // Not fair to the whole equator
|| now_lat > 90.0 || now_lat < -90.0 || now_long == 0.0 // Not fair to King George
|| now_long < -180.0 || now_long > 180.0 || gps_altitude() == 0.0 // Not fair to the ocean
|| gps_sats() < 4)
return false; // Rejected as bogus GPS reading.
if (!(tGPS.location.isValid() && tGPS.time.isValid() && tGPS.satellites.isValid() && tGPS.hdop.isValid() &&
tGPS.altitude.isValid() && tGPS.speed.isValid()))
return MAPPER_UPLINK_BADFIX;
// Filter out any reports while we have low satellite count. The receiver can old a fix on 3, but it's poor.
if (tGPS.satellites.value() < 4)
return MAPPER_UPLINK_BADFIX;
// HDOP is only a hint as to accuracy, but we can assume very bad HDOP is not worth mapping.
// https://en.wikipedia.org/wiki/Dilution_of_precision_(navigation) suggests 5 is a good cutoff.
if (tGPS.hdop.hdop() > 5.0)
return MAPPER_UPLINK_BADFIX;
// With the exception of a few places, a perfectly zero lat or long probably means we got a bad reading
if (now_lat == 0.0 || now_lon == 0.0)
return MAPPER_UPLINK_BADFIX;
// Don't attempt to send or update until we join Helium
if (!isJoined)
return false;
return MAPPER_UPLINK_NOLORA;
// LoRa is not ready for a new packet, maybe still sending the last one.
if (!LMIC_queryTxReady())
return false;
return MAPPER_UPLINK_NOLORA;
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND)
return false;
return MAPPER_UPLINK_NOLORA;
// distance from last transmitted location
float dist_moved = gps_distanceBetween(last_send_lat, last_send_lon, now_lat, now_long);
float deadzone_dist = gps_distanceBetween(deadzone_lat, deadzone_lon, now_lat, now_long);
double dist_moved = tGPS.distanceBetween(last_send_lat, last_send_lon, now_lat, now_lon);
double deadzone_dist = tGPS.distanceBetween(deadzone_lat, deadzone_lon, now_lat, now_lon);
in_deadzone = (deadzone_dist <= deadzone_radius_m);
/*
@ -255,7 +306,7 @@ bool mapper_uplink() {
// Deadzone means we don't send unless asked
if (in_deadzone && !justSendNow)
return false;
return MAPPER_UPLINK_NOTYET;
char because = '?';
if (justSendNow) {
@ -270,12 +321,9 @@ bool mapper_uplink() {
Serial.println("** TIME");
because = 'T';
} else {
return false; // Nothing to do, go home early
return MAPPER_UPLINK_NOTYET; // Nothing to do, go home early
}
// SEND a Packet!
// digitalWrite(RED_LED, LOW);
// The first distance-moved is crazy, since has no origin.. don't put it on
// screen.
if (dist_moved > 1000000)
@ -293,14 +341,14 @@ bool mapper_uplink() {
// Send it!
if (!send_uplink(txBuffer, 11, FPORT_MAPPER, confirmed))
return false;
return MAPPER_UPLINK_NOLORA;
last_send_ms = now;
last_send_lat = now_lat;
last_send_lon = now_long;
last_send_lon = now_lon;
screen_last_active_ms = now;
return true; // We did it!
return MAPPER_UPLINK_SUCCESS; // We did it!
}
void mapper_restore_prefs(void) {
@ -483,7 +531,7 @@ void lora_msg_callback(uint8_t message) {
void scanI2Cdevice(void) {
byte err, addr;
int nDevices = 0;
for (addr = 1; addr < 127; addr++) {
for (addr = 1; addr < 0x7F; addr++) {
Wire.beginTransmission(addr);
err = Wire.endTransmission();
if (err == 0) {
@ -496,16 +544,17 @@ void scanI2Cdevice(void) {
#endif
nDevices++;
if (addr == SSD1306_ADDRESS) {
ssd1306_found = true;
Serial.println("SSD1306 OLED display");
if (addr == 0x3C || addr == 0x78 || addr == 0x7E) {
oled_addr = addr;
oled_found = true;
Serial.printf("OLED at %02X\n", oled_addr);
}
if (addr == AXP192_SLAVE_ADDRESS) {
axp192_found = true;
Serial.println("AXP192 PMU");
}
} else if (err == 4) {
Serial.print("Unknow i2c device at 0x");
Serial.print("Unknown i2c device at 0x");
if (addr < 16)
Serial.print("0");
Serial.println(addr, HEX);
@ -582,8 +631,8 @@ void axp192Init() {
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio 200mA "LORA_VCC"
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS power 200mA "GPS_VCC"
axp.setLDO3Voltage(3300); // Voltage for GPS Power. (Neo-6 can take 2.7v to 3.6v)
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED power, 1200mA max
axp.setDCDC1Voltage(3300); // Voltage or the OLED SSD1306
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED power, 1200mA max "VCC_2.5V"
axp.setDCDC1Voltage(2500); // Voltage or the OLED SSD1306
axp.setPowerOutPut(AXP192_DCDC2, AXP202_OFF); // Unconnected
axp.setPowerOutPut(AXP192_EXTEN, AXP202_OFF); // "EXTEN" pin, unused
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_550MA); // Default 0x1000 = 780mA, more than we can get from USB
@ -602,16 +651,19 @@ void axp192Init() {
Serial.printf("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
#endif
// Fire an interrupt on falling edge. Note that some IRQs repeat/persist.
pinMode(PMU_IRQ, INPUT_PULLUP);
pinMode(PMU_IRQ, INPUT);
gpio_pullup_en((gpio_num_t)PMU_IRQ);
attachInterrupt(
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
// Configure REG 36H: PEK press key parameter set. Index values for
// argument!
axp.setStartupTime(2); // "Power on time": 512mS
axp.setlongPressTime(2); // "Long time key press time": 2S
axp.setShutdownTime(2); // "Power off time" = 8S
axp.setTimeOutShutdown(1); // "When key press time is longer than power off time, auto power off"
axp.setStartupTime(2); // "Power on time": 512mS
axp.setlongPressTime(2); // "Long time key press time": 2S
axp.setShutdownTime(2); // "Power off time" = 8S
axp.setTimeOutShutdown(1); // "When key press time is longer than power off time, auto power off"
axp.setVWarningLevel1(2950); // These warning IRQs do not clear until charged, and inhibit other IRQs!
axp.setVWarningLevel2(2900); // We effectively disable them by setting them lower than we'd run
// Serial.printf("AC IN: %fv\n", axp.getAcinVoltage());
// Serial.printf("Vbus: %fv\n", axp.getVbusVoltage());
@ -623,6 +675,7 @@ void axp192Init() {
Serial.printf("Battery: %0.3fv\n", axp.getBattVoltage() / 1000.0);
Serial.printf("SysIPSOut: %0.3fv\n", axp.getSysIPSOUTVoltage() / 1000.0);
Serial.printf("isVBUSPlug? %s\n", axp.isVBUSPlug() ? "Yes" : "No");
have_usb_power = axp.isVBUSPlug();
Serial.printf("isChargingEnable? %s\n", axp.isChargeingEnable() ? "Yes" : "No");
// Doesn't work on AXP192 because it has a different charge current curve:
// Serial.printf("ChargeCurrent: %.2fmA\n", axp.getSettingChargeCurrent());
@ -649,7 +702,14 @@ void axp192Init() {
// @Kenny_PDY discovered that low-battery voltage inhibits detecting the menu button.
// Disable these two IRQs until we figure out why it blocks the PEK button IRQs.
axp.enableIRQ(APX202_APS_LOW_VOL_LEVEL1_IRQ | AXP202_APS_LOW_VOL_LEVEL2_IRQ, 0);
// Low battery also seems to inhibit the USB present/lost signal we use to wake up.
axp.enableIRQ(APX202_APS_LOW_VOL_LEVEL1_IRQ, 0);
axp.enableIRQ(AXP202_APS_LOW_VOL_LEVEL2_IRQ, 0);
// The Charging Current available is less than requested for battery charging.
// Another Persistent IRQ. Clear it after showing it once?
// TODO: Show it every X minutes? Adjust charge current request?
axp.enableIRQ(AXP202_CHARGE_LOW_CUR_IRQ, 0);
axp.clearIRQ();
} else {
@ -677,8 +737,8 @@ void setup() {
wakeup();
// Make sure WiFi and BT are off
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
// WiFi.disconnect(true);
WiFi.mode(WIFI_MODE_NULL);
btStop();
Wire.begin(I2C_SDA, I2C_SCL);
@ -692,7 +752,8 @@ void setup() {
axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // GPS power off
// Buttons & LED
pinMode(MIDDLE_BUTTON_PIN, INPUT_PULLUP);
pinMode(MIDDLE_BUTTON_PIN, INPUT);
gpio_pullup_en((gpio_num_t)MIDDLE_BUTTON_PIN);
pinMode(RED_LED, OUTPUT);
digitalWrite(RED_LED, HIGH); // Off
@ -704,14 +765,14 @@ void setup() {
// Don't init display if we don't have one or we are waking headless due to a
// timer event
if (0 && wakeCause == ESP_SLEEP_WAKEUP_TIMER)
ssd1306_found = false; // forget we even have the hardware
oled_found = false; // forget we even have the hardware
// This creates the display object, so if we don't call it.. all screen ops are do-nothing.
if (ssd1306_found)
screen_setup();
if (oled_found)
screen_setup(oled_addr);
is_screen_on = true;
// GPS power on, so it has time to setttle.
// GPS power on, so it has time to settle.
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON);
// Show logo on first boot (as opposed to wake)
@ -736,7 +797,11 @@ void setup() {
// Might have to add a longer delay here for GPS boot-up. Takes longer to sync if we talk to it too early.
delay(100);
gps_setup(); // Init GPS baudrate and messages
gps_setup(true); // Init GPS baudrate and messages
// This is bad.. we can't find the AXP192 PMIC, so no menu key detect:
if (!axp192_found)
screen_print("** Missing AXP192! **\n");
Serial.printf("Deadzone: %f.0m @ %f, %f\n", deadzone_radius_m, deadzone_lat, deadzone_lon);
}
@ -744,43 +809,55 @@ void setup() {
// Should be around 0.5mA ESP32 consumption, plus OLED controller and PMIC overhead.
void low_power_sleep(uint32_t seconds) {
boolean was_screen_on = is_screen_on;
if (is_screen_on) {
screen_off();
is_screen_on = false;
}
digitalWrite(RED_LED, HIGH); // Off
Serial.printf("Sleep %d..\n", seconds);
Serial.flush();
screen_off();
digitalWrite(RED_LED, HIGH); // LED Off
if (axp192_found) {
axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // GPS power
axp.setChgLEDMode(AXP20X_LED_OFF);
axp.setChgLEDMode(AXP20X_LED_OFF); // Blue LED off
// Turning off DCDC1 consumes MORE power, for reasons unknown
// axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); // OLED power off
// pinMode(I2C_SCL, OUTPUT);
// digitalWrite(I2C_SCL, HIGH); // Must enable pull-up on SCL to keep AXP accessible
}
// Wake on either button press
gpio_wakeup_enable((gpio_num_t)MIDDLE_BUTTON_PIN, GPIO_INTR_LOW_LEVEL);
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_sleep_enable_timer_wakeup(seconds * 1000ULL * 1000ULL); // call expects usecs
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Some GPIOs need this to stay on?
// esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
esp_light_sleep_start();
// If we woke by keypress (7) then turn on the screen
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_GPIO) {
// Try not to puke, but we pretend we moved if they hit a key, to exit SLEEP
// Try not to puke, but we pretend we moved if they hit a key, to exit SLEEP and restart timers
last_moved_ms = screen_last_active_ms = millis();
was_screen_on = true; // Lies
Serial.println("(GPIO)");
}
Serial.println("..woke");
if (was_screen_on)
screen_on();
if (axp192_found) {
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS power
// axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED power
// if (oled_found)
// screen_setup();
}
if (was_screen_on) {
screen_on();
is_screen_on = true;
}
delay(100); // GPS doesn't respond right away.. not ready for baud-rate test.
gps_setup();
delay(100); // GPS doesn't respond right away.. not ready for baud-rate test.
gps_setup(false); // Resync with GPS
}
// Power OFF -- does not return
@ -803,7 +880,7 @@ uint32_t woke_fix_count = 0;
/* Determine the current activity state */
void update_activity() {
static enum activity_state last_active_state = ACTIVITY_MOVING;
static enum activity_state last_active_state = ACTIVITY_INVALID;
if (active_state != last_active_state) {
switch (active_state) {
@ -840,22 +917,24 @@ void update_activity() {
clean_shutdown();
}
// Here we just woke from a GPS-off long sleep.
// When we have a fresh GPS fix, and the fix qualifies for mapper report, we can resume
// either mapping or going back to sleep. Until then, we loop in Wake looking for a good GPS signal.
// Note that we have to be sensitive to "good fix, but not interesting" and go right back to sleep.
// We're only staying awake until we got a good GPS fix or gave up, NOT until we send a mapper report.
if (active_state == ACTIVITY_WOKE) {
if (gps_sentencesWithFix() != woke_fix_count) {
mapper_uplink();
if (tGPS.sentencesWithFix() != woke_fix_count && mapper_uplink() != MAPPER_UPLINK_BADFIX)
active_state = ACTIVITY_REST;
} else if (now - woke_time_ms > gps_lost_wait_s * 1000) {
else if (now - woke_time_ms > gps_lost_wait_s * 1000)
active_state = ACTIVITY_GPS_LOST;
}
return; // else stay in WOKE
return; // else stay in WOKE until we make a good report
}
if (active_state == ACTIVITY_SLEEP && !in_menu) {
Serial.printf("Sleep %d...", tx_interval_s);
low_power_sleep(tx_interval_s);
active_state = ACTIVITY_WOKE;
woke_time_ms = millis();
woke_fix_count = gps_sentencesWithFix();
woke_fix_count = tGPS.sentencesWithFix();
return;
}
@ -872,8 +951,21 @@ void update_activity() {
active_state = ACTIVITY_MOVING;
}
{
boolean had_usb_power = have_usb_power;
have_usb_power = (axp192_found && axp.isVBUSPlug());
if (have_usb_power && !had_usb_power) {
usb_power_count++;
status_uplink(STATUS_USB_ON, usb_power_count);
}
if (!have_usb_power && had_usb_power) {
status_uplink(STATUS_USB_OFF, usb_power_count);
}
}
// If we have USB power, keep GPS on all the time; don't sleep
if (1 && axp192_found && axp.isVBUSPlug()) {
if (have_usb_power) {
if (active_state == ACTIVITY_SLEEP)
active_state = ACTIVITY_REST;
}
@ -924,9 +1016,9 @@ const char *find_irq_name(void) {
else if (axp.isVbusOverVoltageIRQ())
irq_name = "VbusOverVoltage";
else if (axp.isVbusPlugInIRQ())
irq_name = "VbusPlugIn";
irq_name = "USB Connected"; // "VbusPlugIn";
else if (axp.isVbusRemoveIRQ())
irq_name = "VbusRemove";
irq_name = "USB Removed"; // "VbusRemove";
else if (axp.isVbusLowVHOLDIRQ())
irq_name = "VbusLowVHOLD";
else if (axp.isBattPlugInIRQ())
@ -949,10 +1041,6 @@ const char *find_irq_name(void) {
irq_name = "ChipOvertemperature";
else if (axp.isChargingCurrentLessIRQ()) {
irq_name = "ChargingCurrentLess";
// The Charging Current (770mA max feed) is less than requested
// Persistent IRQ. Clear it after showing it once.
// TODO: Show it every X minutes? Adjust charge current request?
axp.enableIRQ(AXP202_CHARGE_LOW_CUR_IRQ, 0);
} else if (axp.isDC2VoltageLessIRQ())
irq_name = "DC2VoltageLess";
else if (axp.isDC3VoltageLessIRQ())
@ -1039,25 +1127,27 @@ void menu_gps_passthrough(void) {
gps_passthrough();
// Does not return.
}
void menu_experiment(void) {
#if 0
static boolean power_toggle = true;
static int gps_mv = 3300;
Serial.printf("%f mA %f mW\n", axp.getBattChargeCurrent() - axp.getBattDischargeCurrent(), axp.getBattInpower());
gps_mv += 100;
if (gps_mv > 3600)
gps_mv = 2700;
axp.setPowerOutPut(AXP192_LDO3,
power_toggle ? AXP202_ON : AXP202_OFF); // GPS main power
power_toggle = !power_toggle;
#endif
Serial.printf("GPS Voltage: %d\n", gps_mv);
snprintf(buffer, sizeof(buffer), "\nGPS %dmv", gps_mv);
screen_print(buffer);
Serial.println("Sleeping for 5...");
low_power_sleep(5);
Serial.println("Woke.");
axp.setLDO3Voltage(gps_mv); // Voltage for GPS Power. (Neo-6 can take 2.7v to 3.6v)
}
void menu_deadzone_here(void) {
deadzone_lat = gps_latitude();
deadzone_lon = gps_longitude();
deadzone_radius_m = DEADZONE_RADIUS_M;
if (tGPS.location.isValid()) {
deadzone_lat = tGPS.location.lat();
deadzone_lon = tGPS.location.lng();
deadzone_radius_m = DEADZONE_RADIUS_M;
}
}
void menu_no_deadzone(void) {
deadzone_radius_m = 0.0;
@ -1067,6 +1157,10 @@ void menu_stay_on(void) {
screen_stay_on = !screen_stay_on;
}
void menu_gps_reset(void) {
gps_full_reset();
}
dr_t sf_list[] = {DR_SF7, DR_SF8, DR_SF9, DR_SF10};
#define SF_ENTRIES (sizeof(sf_list) / sizeof(sf_list[0]))
uint8_t sf_index = 0;
@ -1087,7 +1181,7 @@ struct menu_entry menu[] = {
{"Distance -", menu_distance_minus}, {"Time +", menu_time_plus}, {"Time -", menu_time_minus},
{"Change SF", menu_change_sf}, {"Full Reset", menu_flush_prefs}, {"USB GPS", menu_gps_passthrough},
{"Deadzone Here", menu_deadzone_here}, {"No Deadzone", menu_no_deadzone}, {"Stay On", menu_stay_on},
{"Experiment", menu_experiment}};
{"GPS Reset", menu_gps_reset}, {"Experiment", menu_experiment}};
#define MENU_ENTRIES (sizeof(menu) / sizeof(menu[0]))
const char *menu_prev;
@ -1116,7 +1210,7 @@ void menu_selected(void) {
}
void update_screen(void) {
screen_header(tx_interval_s, min_dist_moved, sf_name, gps_sats(), in_deadzone, screen_stay_on, never_rest);
screen_header(tx_interval_s, min_dist_moved, sf_name, in_deadzone, screen_stay_on, never_rest);
screen_body(in_menu, menu_prev, menu_cur, menu_next, is_highlighted);
}
@ -1127,10 +1221,10 @@ void loop() {
uint32_t now = millis();
gps_loop(0 /* active_state == ACTIVITY_WOKE */); // Update GPS
now_fix_count = gps_sentencesWithFix();
now_fix_count = tGPS.sentencesWithFix(); // Did we get a new fix?
if (now_fix_count != last_fix_count) {
last_fix_count = now_fix_count;
last_fix_time = now;
last_fix_time = now; // Note the time of most recent fix
}
ttn_loop();
@ -1154,7 +1248,7 @@ void loop() {
else if (axp.isPEKLongtPressIRQ()) // want to turn OFF
menu_power_off();
else {
snprintf(buffer, sizeof(buffer), "\n* %s ", irq_name);
snprintf(buffer, sizeof(buffer), "\n* %s ", irq_name);
screen_print(buffer);
}
axp.clearIRQ();
@ -1208,7 +1302,7 @@ void loop() {
last_gpslost_ms = 0; // Reset if we regained GPS
}
if (mapper_uplink()) {
if (mapper_uplink() == MAPPER_UPLINK_SUCCESS) {
// Good send, light Blue LED
if (axp192_found)
axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL);

Wyświetl plik

@ -25,18 +25,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <Wire.h>
#include "OLEDDisplay.h"
#include "SH1106Wire.h"
#include "SSD1306Wire.h"
#include "configuration.h"
#include "credentials.h"
#include "fonts.h"
#include "gps.h"
#include "images.h"
#include "font.h"
#define SCREEN_HEADER_HEIGHT 24
#define SCREEN_HEADER_HEIGHT 23
SSD1306Wire *display;
OLEDDisplay *display;
uint8_t _screen_line = SCREEN_HEADER_HEIGHT - 1;
enum display_types { DISPLAY_UNKNOWN, DISPLAY_SSD1306, DISPLAY_SH1106 };
enum display_types display_type = DISPLAY_UNKNOWN;
void screen_show_logo() {
if (!display)
return;
@ -99,58 +103,154 @@ void screen_update() {
display->display();
}
void screen_setup() {
/*
* The SSD1306 and SH1106 controllers are almost the same, but different.
* Most importantly here, the SH1106 allows reading from the frame buffer, while the SSD1306 does not.
* We exploit this by writing two bytes and reading them back. A mismatch probably means SSD1306.
* Probably.
*/
enum display_types display_get_type(uint8_t id) {
uint8_t err;
uint8_t b1, b2;
Wire.begin(I2C_SDA, I2C_SCL);
Wire.setClock(7000000);
Wire.beginTransmission(id);
uint8_t a[] = {
0, // co=0 DC=0 000000 to start command
0x00, // Lower Column Address = 0
0x10, // Higher Column Address = 0
0xB0 // Set Page Address 0
};
Wire.write(a, sizeof(a));
if ((err = Wire.endTransmission(false)) != 0) {
Serial.printf("err=%d EndTransmission=%d(%s)", err, Wire.lastError(), Wire.getErrorText(Wire.lastError()));
return DISPLAY_UNKNOWN;
}
Wire.beginTransmission(id);
uint8_t b[] = {0x40, 'M', 'P'}; // co=0 DC=1 & 000000, then two bytes of data
Wire.write(b, sizeof(b));
if ((err = Wire.endTransmission(false)) != 0) {
Serial.printf("err=%d EndTransmission=%d(%s)", err, Wire.lastError(), Wire.getErrorText(Wire.lastError()));
return DISPLAY_UNKNOWN;
}
Wire.beginTransmission(id);
uint8_t c[] = {0, 0, 0x10}; // Back to Lower & Higher Column address 0
Wire.write(c, sizeof(c));
if ((err = Wire.endTransmission(false)) != 0) {
Serial.printf("err=%d EndTransmission=%d(%s)", err, Wire.lastError(), Wire.getErrorText(Wire.lastError()));
return DISPLAY_UNKNOWN;
}
Wire.beginTransmission(id);
Wire.write(0x40); // Data next
if ((err = Wire.endTransmission(false)) != 0) {
Serial.printf("err=%d EndTransmission=%d(%s)", err, Wire.lastError(), Wire.getErrorText(Wire.lastError()));
return DISPLAY_UNKNOWN;
}
err = Wire.requestFrom((int)id, (int)3, (int)1);
if (err != 3) {
return DISPLAY_UNKNOWN;
}
Wire.read(); // Must discard 1 byte
b1 = Wire.read();
b2 = Wire.read();
Wire.endTransmission();
// If we read back what we wrote, memory is readable:
if (b1 == 'M' && b2 == 'P')
return DISPLAY_SH1106;
else
return DISPLAY_SSD1306;
}
void screen_setup(uint8_t addr) {
/* Attempt to determine which kind of display we're dealing with */
if (display_type == DISPLAY_UNKNOWN)
display_type = display_get_type(addr);
// Display instance
display = new SSD1306Wire(SSD1306_ADDRESS, I2C_SDA, I2C_SCL);
if (display_type == DISPLAY_SSD1306)
display = new SSD1306Wire(addr, I2C_SDA, I2C_SCL);
else if (display_type == DISPLAY_SH1106)
display = new SH1106Wire(addr, I2C_SDA, I2C_SCL);
else
return;
display->init();
display->flipScreenVertically();
display->setFont(Custom_ArialMT_Plain_10);
display->setFont(Custom_Font);
// Scroll buffer
display->setLogBuffer(4, 30);
}
void screen_end() {
if (display) {
screen_off();
display->end();
delete display;
}
}
#include <axp20x.h>
extern AXP20X_Class axp; // TODO: This is evil
void screen_header(unsigned int tx_interval_s, float min_dist_moved, char *cached_sf_name, int sats,
boolean in_deadzone, boolean stay_on, boolean never_rest) {
void screen_header(unsigned int tx_interval_s, float min_dist_moved, char *cached_sf_name, boolean in_deadzone,
boolean stay_on, boolean never_rest) {
if (!display)
return;
char buffer[40];
uint32_t sats = tGPS.satellites.value();
boolean no_gps = (sats < 3);
uint16_t devid_hint = ((DEVEUI[7] << 4) | (DEVEUI[6] & 0xF0) >> 4);
display->clear();
// Cycle display every 3 seconds
if (millis() % 6000 < 3000) {
// 2 bytes of Device EUI with Voltage and Current
snprintf(buffer, sizeof(buffer), "#%03X", ((DEVEUI[7] << 4) | (DEVEUI[6] & 0xF0) >> 4));
// Voltage and Current
snprintf(buffer, sizeof(buffer), "%.2fV %.0fmA", axp.getBattVoltage() / 1000,
axp.getBattChargeCurrent() - axp.getBattDischargeCurrent());
// display->setTextAlignment(TEXT_ALIGN_CENTER);
// display->drawString(display->getWidth() / 2, 2, buffer);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(0, 2, buffer);
snprintf(buffer, sizeof(buffer), "%.2fV %.0fmA", axp.getBattVoltage() / 1000,
axp.getBattChargeCurrent() - axp.getBattDischargeCurrent());
} else {
// Message count and time
// snprintf(buffer, sizeof(buffer), "%4d", ttn_get_count() % 10000);
// display->setTextAlignment(TEXT_ALIGN_LEFT);
// display->drawString(0, 2, buffer);
// ID & Time
if (no_gps) {
snprintf(buffer, sizeof(buffer), "#%03X", devid_hint);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(0, 2, buffer);
if (sats < 3)
snprintf(buffer, sizeof(buffer), "*** NO GPS ***");
else
gps_time(buffer, sizeof(buffer));
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(display->getWidth() / 2, 2, "*** NO GPS ***");
snprintf(buffer, sizeof(buffer), "(%d)", sats);
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(display->getWidth(), 2, buffer);
} else {
snprintf(buffer, sizeof(buffer), "#%03X %02d:%02d:%02d", devid_hint, tGPS.time.hour(), tGPS.time.minute(),
tGPS.time.second());
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(0, 2, buffer);
}
}
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(display->getWidth() / 2, 2, buffer);
// Satellite count
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(display->getWidth() - SATELLITE_IMAGE_WIDTH - 4, 2, itoa(sats, buffer, 10));
display->drawXbm(display->getWidth() - SATELLITE_IMAGE_WIDTH, 0, SATELLITE_IMAGE_WIDTH, SATELLITE_IMAGE_HEIGHT,
SATELLITE_IMAGE);
// HDOP & Satellite count
if (!no_gps) {
snprintf(buffer, sizeof(buffer), "%2.1f %d", tGPS.hdop.hdop(), sats);
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(display->getWidth() - SATELLITE_IMAGE_WIDTH - 4, 2, buffer);
display->drawXbm(display->getWidth() - SATELLITE_IMAGE_WIDTH, 0, SATELLITE_IMAGE_WIDTH, SATELLITE_IMAGE_HEIGHT,
SATELLITE_IMAGE);
}
// Second status row:
snprintf(buffer, sizeof(buffer), "%us %.0fm %c%c%c", tx_interval_s, min_dist_moved, in_deadzone ? 'D' : ' ',

Wyświetl plik

@ -9,11 +9,12 @@ void screen_update(void);
void screen_body(boolean in_menu, const char *menu_prev, const char *menu_cur, const char *menu_next,
boolean highlighted);
void screen_header(unsigned int tx_interval_s, float min_dist_moved, char *cached_sf_name, int sats,
boolean in_deadzone, boolean stay_on, boolean never_rest);
void screen_header(unsigned int tx_interval_s, float min_dist_moved, char *cached_sf_name, boolean in_deadzone,
boolean stay_on, boolean never_rest);
void screen_off(void);
void screen_on(void);
void screen_show_logo(void);
void screen_setup(void);
void screen_setup(uint8_t addr);
void screen_end(void);

Wyświetl plik

@ -105,7 +105,8 @@ void forceTxSingleChannelDr() {
ttn_sf(ttn_tx_sf);
}
// DevEUI generator using devices's MAC address - from https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/lorawan.cpp
// DevEUI generator using devices's MAC address - from
// https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/lorawan.cpp
void gen_lora_deveui(uint8_t* pdeveui) {
uint8_t *p = pdeveui, dmac[6];
int i = 0;
@ -275,7 +276,7 @@ bool ttn_setup() {
return (1 == os_init_ex((const void*)&lmic_pins));
}
void ttn_join() {
void ttn_join(void) {
// Reset the MAC state. Session and pending data transfers will be discarded.
LMIC_reset();
@ -324,9 +325,21 @@ void ttn_join() {
// other regions, this will need to be changed.
LMIC_selectSubBand(1);
#elif defined(CFG_au915) && defined(CFG_au915_fsb6)
// AU915 FSB6
// CH 40-47 (923.2 - 924.6 MHz)
// Helium DualPlan was live in Australia 2022-11-17 to 2023-04-18
// DualPlan allowed AU915 devices to co-exist with AS923 by taking advantage
// of some AU915 FSB6 frequencies overlapping AS923 frequencies.
// If a device joined on the first two channels of FSB6, it was treated as
// an AS923 device. On the last 6 channels, an AU915 device.
LMIC_selectSubBand(5);
#elif defined(CFG_au915)
// set sub band for AU915
// AU915 FSB2
// CH 8-15 (916.8 - 918.2 MHz uplink)
// https://github.com/TheThingsNetwork/gateway-conf/blob/master/AU-global_conf.json
LMIC_selectSubBand(1);
@ -382,14 +395,16 @@ void ttn_join() {
uint32_t netId = p.getUInt("netId", UINT32_MAX);
uint32_t devAddr = p.getUInt("devAddr", UINT32_MAX);
uint8_t nwkKey[16], artKey[16];
bool keysgood = p.getBytes("nwkKey", nwkKey, sizeof(nwkKey)) == sizeof(nwkKey) && p.getBytes("artKey", artKey, sizeof(artKey)) == sizeof(artKey);
bool keysgood = devAddr != UINT32_MAX && netId != UINT32_MAX &&
p.getBytes("nwkKey", nwkKey, sizeof(nwkKey)) == sizeof(nwkKey) &&
p.getBytes("artKey", artKey, sizeof(artKey)) == sizeof(artKey);
p.end(); // close our prefs
if (!keysgood) {
// We have not yet joined a network, start a full join attempt
// Make LMiC initialize the default channels, choose a channel, and
// schedule the OTAA join
Serial.println("No session saved, joining from scratch");
Serial.println("Joining from scratch");
screen_print("Joining...\n");
LMIC_startJoining();
} else {
@ -410,7 +425,7 @@ void ttn_get_sf_name(char* b, size_t len) {
u1_t sf, bw;
sf = getSf(txrps) + 6; // 1 == SF7
bw = getBw(txrps);
/*
snprintf(b, len, "%3d.%02d SF%d BW%d",
LMIC.freq / 1000000,
@ -454,9 +469,10 @@ void ttn_write_prefs() {
static void ttn_set_cnt() {
LMIC_setSeqnoUp(count);
// We occasionally mirror our count to flash, to ensure that if we lose power we will at least start with a count that is almost correct
// (otherwise the TNN network will discard packets until count once again reaches the value they've seen). We limit these writes to a max rate
// of one write every 5 minutes. Which should let the FLASH last for 300 years (given the ESP32 NVS algoritm)
// We occasionally mirror our count to flash, to ensure that if we lose power we will at least start with a count that
// is almost correct (otherwise the TNN network will discard packets until count once again reaches the value they've
// seen). We limit these writes to a max rate of one write every 5 minutes. Which should let the FLASH last for 300
// years (given the ESP32 NVS algorithm)
static uint32_t lastWriteMsec = UINT32_MAX; // Ensure we write at least once
uint32_t now = millis();
if (now < lastWriteMsec || (now - lastWriteMsec) > 5 * 60 * 1000L) { // write if we roll over (50 days) or 5 mins

Wyświetl plik

@ -12,31 +12,35 @@
src_dir = main
[env]
platform = espressif32
platform = espressif32@^3.5.0
board = ttgo-t-beam
framework = arduino
build_flags = -Wall
-Wextra
-Wno-missing-field-initializers -O3
-D CFG_us915=1
; -D CFG_eu868=1
; -D CFG_eu868=1
; -D CFG_au915=1
; -D CFG_au915_fsb6=1
; -D CFG_as923=1
-D CFG_sx1276_radio=1
-D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS
-D ARDUINO_TTGO_LoRa32_V1
lib_deps =
mcci-catena/MCCI LoRaWAN LMIC library
thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays
lewisxhe/AXP202X_Library
mikalhart/TinyGPSPlus
mcci-catena/MCCI LoRaWAN LMIC library@^4.1.1
thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@^4.3.0
lewisxhe/AXP202X_Library@^1.1.3
mikalhart/TinyGPSPlus@^1.0.3
monitor_speed = 115200
; monitor_port = COM17
; upload_speed = 921600
; upload_port = COM17
[env:release]
[env:debug]
debug_build_flags =
-D DEBUG
-D DEBUG
-D LMIC_DEBUG_LEVEL=3

Wyświetl plik

@ -13,7 +13,8 @@
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"initializer_list": "cpp"
"initializer_list": "cpp",
"*.tcc": "cpp"
}
}
}