diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/LICENSE b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/LICENSE new file mode 100644 index 0000000..7b1bcda --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 timmbogner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/README.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/README.md new file mode 100644 index 0000000..d02fa04 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/README.md @@ -0,0 +1,80 @@ +

+ +#

Farm Data Relay System + +#####

[***In loving memory of Gay Holman, an extraordinary woman.***](https://www.facebook.com/CFECI/posts/2967989419953119) ##### + +Farm Data Relay System is an easy way to communicate with remote IoT devices without relying on WiFi or LoRaWAN infrastructure. It establishes a series of inexpensive, low-power access points and repeaters to provide ESP-NOW and LoRa coverage for remote devices. FDRS can be used to transport sensor readings and control messages in situations where it would be too cumbersome to provide full WiFi/LoRaWAN coverage. While the system was designed with farming in mind, FDRS could also be beneficial in a classroom, home, or research setting. + +Devices are classified into two types: **Gateways** and **Nodes**. Gateways comprise the infrastructure of the network, moving data along pre-directed routes and providing coverage to all devices. Nodes allow the user to exchange data with a gateway. Each gateway is identified with an 8-bit physical hex address (MAC), while nodes use 16-bit integers to identify datapoints as they move through the system. + + +If you are having fun with FDRS, **[please consider supporting me](https://www.buymeacoffee.com/TimmB)** so that I can spend more time building it. + +## Getting Started +**Libraries Required:** +- [ArduinoJson](https://arduinojson.org/) +- [RadioLib](https://github.com/jgromes/RadioLib) for LoRa +- [PubSubClient](https://github.com/knolleary/pubsubclient/) for MQTT + +**Included:** +- [ThingPulse OLED Library for ESP](https://github.com/ThingPulse/esp8266-oled-ssd1306) +# +**To install FDRS:** +1. Download or clone this repository and copy it into your Arduino **'libraries'** folder. + +2. After installing, edit the **'src/fdrs_globals.h'** file with your WiFi credentials and other global parameters. + +3. The first sketch you'll want to try is the **1_UART_Gateway.ino** example. This device will listen for incoming ESP-NOW packets, then route them to the serial port (and vice versa). Next, flash the **ESPNOW_Sensor.ino** example to see how to send data to the gateway. + +4. To use MQTT: Connect the second gateway to the first via the Rx and Tx pins (crossed), and flash it with the **0_MQTT_Gateway.ino** example. If your WiFi and MQTT configurations are correct, data will be published to the topic 'fdrs/data'. + +5. To extend your range, try the **2_ESPNOW_Repeater.ino** or **3_LoRa_Repeater.ino**. Just change the *GTWY_MAC* of your sensor to the address of your new repeater. + + +## Nodes +**[Node Documentation](/extras/Node.md)** + +Nodes can be described as *sensors, controllers, or both*: +- A **Sensor node** aggregates data into a packet, then sends it to a gateway via ESP-NOW or LoRa. +- A **Controller node** subscribes to one or more reading IDs. When data arrives from an ID the device is subscribed to, a callback function is called where the user can access the incoming data. + +## Gateways +**[Gateway Documentation](extras/Gateway.md)** + +Gateways are modular and configurable microcontroller devices that can perform a variety of useful functions including collecting, distributing, and relaying wireless data. They provide a flexible and cohesive interface between various wired and wireless protocols, and are generally arranged in a line or star topology. As a general rule, the gateway that uses MQTT always has the address 0x00, and ESP-NOW and LoRa gateways start at 0x01. + +In its most common usage, an FDRS gateway is deployed as an access point for remote ESP-NOW and LoRa user nodes. If it receives a packet from an unknown ESP-NOW or LoRa address, the gateway assumes that these are sensor readings and passes them downstream towards the front-end. The gateway will also broadcast packets coming *from* the front-end out to any controller nodes that are registered/listening. + +Gateways can also be configured as simple repeaters; passing data from one neighbor directly to another neighbor or vice versa. This can create a data wormhole that will carry packets upstream or downstream ad infinitum. You can configure your gateways to share data headed upstream with connected peers, thus providing them with any data being sent from the front-end. + +## Front-end + The front-end is where all data is entered or consumed by another application. This could be anything from a microcontroller communicating through UART and displaying data on a screen to a server/database platform logging the data via MQTT. + +My recommended method of accessing your data is using a computer, server, or Raspberry Pi linked to an FDRS Gateway device via either MQTT or UART. Node-RED is my favorite platform for accessing/manipulating data on the front-end, and InfluxDB+Grafana is the dream team for storage and visualization. + + +## Future Plans +Upcoming goals for FDRS include: +- A method for FDRS gateways to keep track of the time via NTP or an RTC module, then seamlessly distribute it amongst its neighbors and connected nodes. +- More sensor and controller examples. If you are using a device or sensor that is not covered in the examples, feel free to contribute an example of its basic usage! +- Support for cellular radios with [TinyGSM](https://github.com/vshymanskyy/TinyGSM). +- Channel Activity Detection (CAD) for LoRa. + +## Thank you +**...very much for checking out my project!** I truly appreciate everyone who has reached out with contributions and assistance, especially those featured in the "Contributors" section. If you have any questions, comments, issues, or suggestions please don't hesitate to contact me at timmbogner@gmail.com or open a discussion here on Github. + +Many thanks go to the ever-instructional [**Andreas Spiess**](https://www.youtube.com/channel/UCu7_D0o48KbfhpEohoP7YSQ). His insight and ideas took this project from a roughly-hewn stone to the "[diamond](https://youtu.be/6JI5wZABWmA)" you see today. + +Thanks to [**LilyGo**](https://www.lilygo.cc/) for sending me new [LoRa32 modules](https://www.lilygo.cc/products/lora32-v1-0-lora-868mhz-915mhz) when mine were damaged. Much of this project was [created](https://github.com/timmbogner/Farm-Data-Relay-System/tree/main/examples/Sensor_Examples/LilyGo_HiGrow_32) using [TTGO devices](https://www.lilygo.cc/products/t-higrow), and I highly recommend their [products](https://www.lilygo.cc/products/lilygo%C2%AE-ttgo-t-display-1-14-inch-lcd-esp32-control-board)! + +It is a great honor to have been [featured on **Hackaday**](https://hackaday.com/2022/07/02/farm-data-relay-system/) and [**hackster.io!**](https://www.hackster.io/news/timm-bogner-s-farm-data-relay-system-uses-esp8266-esp32-nodes-and-gateways-for-sensor-networks-b87a75c69f46) + +I started this project with instructions from [**Random Nerd Tutorials**](https://randomnerdtutorials.com/). If you are a beginner and trying to learn more about microcontrollers, I highly recommend starting there. + + # +![Basic - UART](extras/basic-UART.png) +# +# +![Basic - MQTT](extras/basic-MQTT.png) +# diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/0_MQTT_Gateway/0_MQTT_Gateway.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/0_MQTT_Gateway/0_MQTT_Gateway.ino new file mode 100644 index 0000000..bd0030a --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/0_MQTT_Gateway/0_MQTT_Gateway.ino @@ -0,0 +1,17 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY 2.000 +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// + +#include "fdrs_gateway_config.h" +#include + +void setup() { +beginFDRS(); +} + +void loop() { +loopFDRS(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/0_MQTT_Gateway/fdrs_gateway_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/0_MQTT_Gateway/fdrs_gateway_config.h new file mode 100644 index 0000000..3a3fda7 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/0_MQTT_Gateway/fdrs_gateway_config.h @@ -0,0 +1,73 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY CONFIGURATION + +//Addresses +#define UNIT_MAC 0x00 // The address of this gateway + +#define ESPNOW_NEIGHBOR_1 0x00 // Address of ESP-NOW neighbor #1 +#define ESPNOW_NEIGHBOR_2 0x00 // Address of ESP-NOW neighbor #2 +#define LORA_NEIGHBOR_1 0x00 // Address of LoRa neighbor #1 +#define LORA_NEIGHBOR_2 0x00 // Address of LoRa neighbor #2 + +// Interfaces +//#define USE_ESPNOW +//#define USE_LORA +#define USE_WIFI // Will cause errors if used with ESP-NOW. Use a serial link instead! +//#define USE_ETHERNET + +// Routing +// Options: sendESPNowNbr(1 or 2); sendESPNowPeers(); sendLoRaNbr(1 or 2); broadcastLoRa(); sendSerial(); sendMQTT(); +#define ESPNOWG_ACT +#define LORAG_ACT +#define SERIAL_ACT sendMQTT(); +#define MQTT_ACT sendSerial(); +#define INTERNAL_ACT sendMQTT(); +#define ESPNOW1_ACT +#define ESPNOW2_ACT +#define LORA1_ACT +#define LORA2_ACT + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) + +//#define CUSTOM_SPI +#define LORA_SPI_SCK 5 +#define LORA_SPI_MISO 19 +#define LORA_SPI_MOSI 27 + +#define FDRS_DEBUG // Enable USB-Serial debugging + +// OLED -- Displays console debugging messages on an SSD1306 I²C OLED +///#define USE_OLED +#define OLED_HEADER "FDRS" +#define OLED_PAGE_SECS 30 +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 + +// UART data interface pins (if available) +#define RXD2 14 +#define TXD2 15 + +//#define USE_LR // Use ESP-NOW LR mode (ESP32 only) + +// WiFi and MQTT Credentials -- These will override the global settings +//#define WIFI_SSID "Your SSID" +//#define WIFI_PASS "Your Password" + +//#define MQTT_ADDR "192.168.0.8" +//#define MQTT_PORT 1883 // Default MQTT port is 1883 +//#define MQTT_AUTH //Enable MQTT authentication +//#define MQTT_USER "Your MQTT Username" +//#define MQTT_PASS "Your MQTT Password" + + + diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/1_UART_Gateway/1_UART_Gateway.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/1_UART_Gateway/1_UART_Gateway.ino new file mode 100644 index 0000000..bd0030a --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/1_UART_Gateway/1_UART_Gateway.ino @@ -0,0 +1,17 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY 2.000 +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// + +#include "fdrs_gateway_config.h" +#include + +void setup() { +beginFDRS(); +} + +void loop() { +loopFDRS(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/1_UART_Gateway/fdrs_gateway_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/1_UART_Gateway/fdrs_gateway_config.h new file mode 100644 index 0000000..37b428f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/1_UART_Gateway/fdrs_gateway_config.h @@ -0,0 +1,73 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY CONFIGURATION + +//Addresses +#define UNIT_MAC 0x01 // The address of this gateway + +#define ESPNOW_NEIGHBOR_1 0x00 // Address of ESP-NOW neighbor #1 +#define ESPNOW_NEIGHBOR_2 0x02 // Address of ESP-NOW neighbor #2 +#define LORA_NEIGHBOR_1 0x00 // Address of LoRa neighbor #1 +#define LORA_NEIGHBOR_2 0x03 // Address of LoRa neighbor #2 + +// Interfaces +#define USE_ESPNOW +//#define USE_LORA +//#define USE_WIFI // Will cause errors if used with ESP-NOW. Use a serial link instead! +//#define USE_ETHERNET + +// Routing +// Options: sendESPNowNbr(1 or 2); sendESPNowPeers(); sendLoRaNbr(1 or 2); broadcastLoRa(); sendSerial(); sendMQTT(); +#define ESPNOWG_ACT sendSerial(); +#define LORAG_ACT sendSerial(); +#define SERIAL_ACT sendESPNowNbr(2); sendESPNowPeers(); sendLoRaNbr(2); broadcastLoRa(); +#define MQTT_ACT +#define INTERNAL_ACT sendSerial(); +#define ESPNOW1_ACT +#define ESPNOW2_ACT sendSerial(); +#define LORA1_ACT +#define LORA2_ACT sendSerial(); + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) + +//#define CUSTOM_SPI +#define LORA_SPI_SCK 5 +#define LORA_SPI_MISO 19 +#define LORA_SPI_MOSI 27 + +#define FDRS_DEBUG // Enable USB-Serial debugging + +// OLED -- Displays console debugging messages on an SSD1306 I²C OLED +///#define USE_OLED +#define OLED_HEADER "FDRS" +#define OLED_PAGE_SECS 30 +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 + +// UART data interface pins (if available) +#define RXD2 14 +#define TXD2 15 + +//#define USE_LR // Use ESP-NOW LR mode (ESP32 only) + +// WiFi and MQTT Credentials -- These will override the global settings +//#define WIFI_SSID "Your SSID" +//#define WIFI_PASS "Your Password" + +//#define MQTT_ADDR "192.168.0.8" +//#define MQTT_PORT 1883 // Default MQTT port is 1883 +//#define MQTT_AUTH //Enable MQTT authentication +//#define MQTT_USER "Your MQTT Username" +//#define MQTT_PASS "Your MQTT Password" + + + diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/2_ESPNOW_Repeater/2_ESPNOW_Repeater.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/2_ESPNOW_Repeater/2_ESPNOW_Repeater.ino new file mode 100644 index 0000000..bd0030a --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/2_ESPNOW_Repeater/2_ESPNOW_Repeater.ino @@ -0,0 +1,17 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY 2.000 +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// + +#include "fdrs_gateway_config.h" +#include + +void setup() { +beginFDRS(); +} + +void loop() { +loopFDRS(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/2_ESPNOW_Repeater/fdrs_gateway_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/2_ESPNOW_Repeater/fdrs_gateway_config.h new file mode 100644 index 0000000..0d190dc --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/2_ESPNOW_Repeater/fdrs_gateway_config.h @@ -0,0 +1,73 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY CONFIGURATION + +//Addresses +#define UNIT_MAC 0x02 // The address of this gateway + +#define ESPNOW_NEIGHBOR_1 0x01 // Address of ESP-NOW neighbor #1 +#define ESPNOW_NEIGHBOR_2 0x04 // Address of ESP-NOW neighbor #2 +#define LORA_NEIGHBOR_1 0x00 // Address of LoRa neighbor #1 +#define LORA_NEIGHBOR_2 0x00 // Address of LoRa neighbor #2 + +// Interfaces +#define USE_ESPNOW +//#define USE_LORA +//#define USE_WIFI // Will cause errors if used with ESP-NOW. Use a serial link instead! +//#define USE_ETHERNET + +// Routing +// Options: sendESPNowNbr(1 or 2); sendESPNowPeers(); sendLoRaNbr(1 or 2); broadcastLoRa(); sendSerial(); sendMQTT(); +#define ESPNOWG_ACT sendESPNowNbr(1); +#define LORAG_ACT +#define SERIAL_ACT +#define MQTT_ACT +#define INTERNAL_ACT sendESPNowNbr(1); +#define ESPNOW1_ACT sendESPNowNbr(2); sendESPNowPeers(); +#define ESPNOW2_ACT sendESPNowNbr(1); +#define LORA1_ACT +#define LORA2_ACT + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) + +//#define CUSTOM_SPI +#define LORA_SPI_SCK 5 +#define LORA_SPI_MISO 19 +#define LORA_SPI_MOSI 27 + +#define FDRS_DEBUG // Enable USB-Serial debugging + +// OLED -- Displays console debugging messages on an SSD1306 I²C OLED +///#define USE_OLED +#define OLED_HEADER "FDRS" +#define OLED_PAGE_SECS 30 +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 + +// UART data interface pins (if available) +#define RXD2 14 +#define TXD2 15 + +//#define USE_LR // Use ESP-NOW LR mode (ESP32 only) + +// WiFi and MQTT Credentials -- These will override the global settings +//#define WIFI_SSID "Your SSID" +//#define WIFI_PASS "Your Password" + +//#define MQTT_ADDR "192.168.0.8" +//#define MQTT_PORT 1883 // Default MQTT port is 1883 +//#define MQTT_AUTH //Enable MQTT authentication +//#define MQTT_USER "Your MQTT Username" +//#define MQTT_PASS "Your MQTT Password" + + + diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/3_LoRa_Repeater/3_LoRa_Repeater.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/3_LoRa_Repeater/3_LoRa_Repeater.ino new file mode 100644 index 0000000..bd0030a --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/3_LoRa_Repeater/3_LoRa_Repeater.ino @@ -0,0 +1,17 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY 2.000 +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// + +#include "fdrs_gateway_config.h" +#include + +void setup() { +beginFDRS(); +} + +void loop() { +loopFDRS(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/3_LoRa_Repeater/fdrs_gateway_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/3_LoRa_Repeater/fdrs_gateway_config.h new file mode 100644 index 0000000..9987cce --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/3_LoRa_Repeater/fdrs_gateway_config.h @@ -0,0 +1,73 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY CONFIGURATION + +//Addresses +#define UNIT_MAC 0x03 // The address of this gateway + +#define ESPNOW_NEIGHBOR_1 0x00 // Address of ESP-NOW neighbor #1 +#define ESPNOW_NEIGHBOR_2 0x00 // Address of ESP-NOW neighbor #2 +#define LORA_NEIGHBOR_1 0x01 // Address of LoRa neighbor #1 +#define LORA_NEIGHBOR_2 0x05 // Address of LoRa neighbor #2 + +// Interfaces +//#define USE_ESPNOW +#define USE_LORA +//#define USE_WIFI // Will cause errors if used with ESP-NOW. Use a serial link instead! +//#define USE_ETHERNET + +// Routing +// Options: sendESPNowNbr(1 or 2); sendESPNowPeers(); sendLoRaNbr(1 or 2); broadcastLoRa(); sendSerial(); sendMQTT(); +#define ESPNOWG_ACT +#define LORAG_ACT sendLoRaNbr(1); +#define SERIAL_ACT +#define MQTT_ACT +#define INTERNAL_ACT sendLoRaNbr(1); +#define ESPNOW1_ACT +#define ESPNOW2_ACT +#define LORA1_ACT sendLoRaNbr(2); broadcastLoRa(); +#define LORA2_ACT sendLoRaNbr(1); + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) + +//#define CUSTOM_SPI +#define LORA_SPI_SCK 5 +#define LORA_SPI_MISO 19 +#define LORA_SPI_MOSI 27 + +#define FDRS_DEBUG // Enable USB-Serial debugging + +// OLED -- Displays console debugging messages on an SSD1306 I²C OLED +///#define USE_OLED +#define OLED_HEADER "FDRS" +#define OLED_PAGE_SECS 30 +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 + +// UART data interface pins (if available) +#define RXD2 14 +#define TXD2 15 + +//#define USE_LR // Use ESP-NOW LR mode (ESP32 only) + +// WiFi and MQTT Credentials -- These will override the global settings +//#define WIFI_SSID "Your SSID" +//#define WIFI_PASS "Your Password" + +//#define MQTT_ADDR "192.168.0.8" +//#define MQTT_PORT 1883 // Default MQTT port is 1883 +//#define MQTT_AUTH //Enable MQTT authentication +//#define MQTT_USER "Your MQTT Username" +//#define MQTT_PASS "Your MQTT Password" + + + diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/FastLED/FastLED.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/FastLED/FastLED.ino new file mode 100644 index 0000000..93dcde5 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/FastLED/FastLED.ino @@ -0,0 +1,139 @@ +// FARM DATA RELAY SYSTEM +// +// FastLED Lantern +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// Rest in Peace, Daniel Garcia. Thank you for bringing so much light to the world! + +// Type +// 1: Red +// 2: Green +// 3: Blue +// 4: Hue +// 5: Saturation +// 6: Brightness +// +// + +#define USE_PWM //If using an RGB LED +// #define PIN_R 18 //ESP32 WeMos +// #define PIN_G 19 +// #define PIN_B 23 + +#define PIN_R 14 //8266 WeMos +#define PIN_G 12 +#define PIN_B 13 + +#define PIN_DATA 4 // If using a NeoPixel +#define NUM_LEDS 24 // Number of physical LEDs. + +#include +#include "fdrs_node_config.h" +#include + +CRGB rgb_color = CRGB::Black; +CHSV hsv_color(0, 255, 255); +bool hsv_mode = false; +bool new_data = false; + +CRGB leds[NUM_LEDS]; + +void fdrs_recv_cb(DataReading theData) { + + new_data = true; + int id = (int)theData.id; + uint8_t type = (uint8_t)theData.t; + uint8_t data = (uint8_t)theData.d; + switch (type) { + case 1: + rgb_color.red = data; + hsv_mode = false; + break; + case 2: + rgb_color.green = data; + hsv_mode = false; + break; + case 3: + rgb_color.blue = data; + hsv_mode = false; + break; + case 4: + hsv_color.hue = data; + hsv_mode = true; + break; + case 5: + hsv_color.sat = data; + hsv_mode = true; + break; + case 6: + hsv_color.val = data; + hsv_mode = true; + break; + } +} +void setup_pwm() { +#ifdef ESP8266 + pinMode(PIN_R, OUTPUT); + pinMode(PIN_G, OUTPUT); + pinMode(PIN_B, OUTPUT); +#endif +#ifdef ESP32 + ledcSetup(0, 5000, 8); + ledcSetup(1, 5000, 8); + ledcSetup(2, 5000, 8); + ledcAttachPin(PIN_R, 0); + ledcAttachPin(PIN_G, 1); + ledcAttachPin(PIN_B, 2); +#endif +} + +void set_color(CRGB new_color) { +#ifdef USE_PWM +#ifdef ESP8266 + analogWrite(PIN_R, new_color.r); + analogWrite(PIN_G, new_color.g); + analogWrite(PIN_B, new_color.b); +#elif ESP32 + ledcWrite(0, new_color.r); + ledcWrite(1, new_color.g); + ledcWrite(2, new_color.b); +#endif +#else + fill_solid(leds, NUM_LEDS, new_color); + FastLED.show(); +#endif // USE_PWM +} + +void color_bars() { + set_color(CRGB::Red); + delay(250); + set_color(CRGB::Green); + delay(250); + set_color(CRGB::Blue); + delay(250); + set_color(CRGB::Black); +} + +void setup() { +#ifdef USE_PWM + setup_pwm(); +#else + FastLED.addLeds(leds, NUM_LEDS); +#endif + color_bars(); + + beginFDRS(); + addFDRS(1000, fdrs_recv_cb); + subscribeFDRS(READING_ID); +} +void loop() { + loopFDRS(); + if (new_data) { + new_data = false; + if (hsv_mode) { + set_color(hsv_color); + } else { + set_color(rgb_color); + } + } +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/FastLED/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/FastLED/fdrs_node_config.h new file mode 100644 index 0000000..330146e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/FastLED/fdrs_node_config.h @@ -0,0 +1,23 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 104 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Irrigation/Irrigation.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Irrigation/Irrigation.ino new file mode 100644 index 0000000..ddc9649 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Irrigation/Irrigation.ino @@ -0,0 +1,179 @@ +// FARM DATA RELAY SYSTEM +// +// Irrigation Controller +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// +// +#include "fdrs_node_config.h" +#include + +#define CONTROL_1 101 //Address for controller 1 +#define CONTROL_2 102 //Address for controller 2 +#define CONTROL_3 103 //Address for controller 3 +#define CONTROL_4 104 //Address for controller 4 + +#define COIL_1 4 //Coil Pin 1 +#define COIL_2 5 //Coil Pin 2 +#define COIL_3 13 //Coil Pin 3 +#define COIL_4 14 //Coil Pin 4 + +int status_1 = 0; +int status_2 = 0; +int status_3 = 0; +int status_4 = 0; + +bool isData = false; +bool newStatus = false; + +void fdrs_recv_cb(DataReading theData) { + DBG(String(theData.id)); + switch (theData.t) { + case 0: // Incoming command is to SET a value + + switch (theData.id) { + case CONTROL_1: + status_1 = (int)theData.d; + isData = true; + break; + case CONTROL_2: + status_2 = (int)theData.d; + isData = true; + break; + case CONTROL_3: + status_3 = (int)theData.d; + isData = true; + break; + case CONTROL_4: + status_4 = (int)theData.d; + isData = true; + break; + } + break; + + case 1: // Incoming command is to GET a value + switch (theData.id) { + case CONTROL_1: + if (digitalRead(COIL_1) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_1); + } else { + loadFDRS(0, STATUS_T, CONTROL_1); + } + break; + case CONTROL_2: + if (digitalRead(COIL_2) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_2); + } else { + loadFDRS(0, STATUS_T, CONTROL_2); + } + break; + case CONTROL_3: + if (digitalRead(COIL_3) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_3); + } else { + loadFDRS(0, STATUS_T, CONTROL_3); + } + break; + case CONTROL_4: + if (digitalRead(COIL_4) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_4); + } else { + loadFDRS(0, STATUS_T, CONTROL_4); + } + break; + } + newStatus = true; + break; + } +} + +void checkCoils() { // Sends back a status report for each coil pin. + if (digitalRead(COIL_1) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_1); + } else { + loadFDRS(0, STATUS_T, CONTROL_1); + } + if (digitalRead(COIL_2) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_2); + } else { + loadFDRS(0, STATUS_T, CONTROL_2); + } + if (digitalRead(COIL_3) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_3); + } else { + loadFDRS(0, STATUS_T, CONTROL_3); + } + if (digitalRead(COIL_4) == HIGH) { + loadFDRS(1, STATUS_T, CONTROL_4); + } else { + loadFDRS(0, STATUS_T, CONTROL_4); + } + if (sendFDRS()) { + DBG("Packet received by gateway"); + } else { + DBG("Unable to communicate with gateway!"); + } +} + +void updateCoils() { //These are set up for relay module which are active-LOW. Swap 'HIGH'and 'LOW' in this function to use the inverse. + if (status_1) { + digitalWrite(COIL_1, LOW); + } else { + digitalWrite(COIL_1, HIGH); + } + if (status_2) { + digitalWrite(COIL_2, LOW); + } else { + digitalWrite(COIL_2, HIGH); + } + if (status_3) { + digitalWrite(COIL_3, LOW); + } else { + digitalWrite(COIL_3, HIGH); + } + if (status_4) { + digitalWrite(COIL_4, LOW); + } else { + digitalWrite(COIL_4, HIGH); + } +} + +void setup() { + beginFDRS(); + pingFDRS(1000); + if (addFDRS(1000, fdrs_recv_cb)) { + subscribeFDRS(CONTROL_1); + subscribeFDRS(CONTROL_2); + subscribeFDRS(CONTROL_3); + subscribeFDRS(CONTROL_4); + } else { + DBG("Not Connected"); + } + pinMode(COIL_1, OUTPUT); + digitalWrite(COIL_1, HIGH); + pinMode(COIL_2, OUTPUT); + digitalWrite(COIL_2, HIGH); + pinMode(COIL_3, OUTPUT); + digitalWrite(COIL_3, HIGH); + pinMode(COIL_4, OUTPUT); + digitalWrite(COIL_4, HIGH); + + DBG("FARM DATA RELAY SYSTEM :: Irrigation Module"); +} + +void loop() { + loopFDRS(); + if (isData) { + isData = false; + updateCoils(); + checkCoils(); + } + if (newStatus) { + newStatus = false; + if (sendFDRS()) { + DBG("Packet received by gateway"); + } else { + DBG("Unable to communicate with gateway!"); + } + } +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Irrigation/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Irrigation/fdrs_node_config.h new file mode 100644 index 0000000..1cb8fe0 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Irrigation/fdrs_node_config.h @@ -0,0 +1,23 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Single_Channel_Relay/Single_Channel_Relay.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Single_Channel_Relay/Single_Channel_Relay.ino new file mode 100644 index 0000000..9d20d10 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/Single_Channel_Relay/Single_Channel_Relay.ino @@ -0,0 +1,23 @@ +#define READING_ID 31 +#define GTWY_MAC 0x01 +#define USE_ESPNOW +#define COIL_PIN 5 + +#include + +bool status = 0; + +void fdrs_recv_cb(DataReading theData) { + status = (bool)theData.d; +} +void setup() { + beginFDRS(); + if (addFDRS(1000, fdrs_recv_cb)) + subscribeFDRS(READING_ID); + pinMode(COIL_PIN, OUTPUT); +} +void loop() { + loopFDRS(); + if (status) digitalWrite(COIL_PIN, HIGH); + else digitalWrite(COIL_PIN, LOW); +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/TFT_eSPI_fdrs/TFT_eSPI_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/TFT_eSPI_fdrs/TFT_eSPI_fdrs.ino new file mode 100644 index 0000000..81df132 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/TFT_eSPI_fdrs/TFT_eSPI_fdrs.ino @@ -0,0 +1,88 @@ +// FARM DATA RELAY SYSTEM +// +// TFT_eSPI Example +// +// Listens for datareading IDs #101-104 and displays their data in the four corners of a TFT screen. +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// +// +#include "fdrs_node_config.h" +#include +#include + +TFT_eSPI tft = TFT_eSPI(); + +#define CONTROL_A 101 //ID for datapoint A +#define CONTROL_B 102 //ID for datapoint B +#define CONTROL_C 103 //ID for datapoint C +#define CONTROL_D 104 //ID for datapoint D + +float value_A = 0; +float value_B = 0; +float value_C = 0; +float value_D = 0; + +bool newData = false; + +void fdrs_recv_cb(DataReading theData) { + switch (theData.id) { + case CONTROL_A: + value_A = theData.d; + newData = true; + break; + case CONTROL_B: + value_B = theData.d; + newData = true; + break; + case CONTROL_C: + value_C = theData.d; + newData = true; + break; + case CONTROL_D: + value_D = theData.d; + newData = true; + break; + } +} +void updateScreen() { + tft.fillScreen(TFT_BLACK); + tft.setTextDatum(TL_DATUM); + tft.drawFloat(value_A, 3, 0, 0, 2); + tft.setTextDatum(TR_DATUM); + tft.drawFloat(value_B, 3, tft.width(), 0, 2); + tft.setTextDatum(BL_DATUM); + tft.drawFloat(value_C, 3, 0, tft.height(), 2); + tft.setTextDatum(BR_DATUM); + tft.drawFloat(value_D, 3, tft.width(), tft.height(), 2); + +} + +void setup() { + tft.init(); + tft.setRotation(1); + tft.fillScreen(TFT_BLACK); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(2); + updateScreen(); + beginFDRS(); + DBG("W:" +String(tft.width())); + DBG("FARM DATA RELAY SYSTEM :: TFT_eSPI Example -- thanks Bodmer!"); + if (addFDRS(1000, fdrs_recv_cb)) { + subscribeFDRS(CONTROL_A); + subscribeFDRS(CONTROL_B); + subscribeFDRS(CONTROL_C); + subscribeFDRS(CONTROL_D); + } else { + DBG("Not Connected"); + } + +} + +void loop() +{ + loopFDRS(); + if (newData) { + newData = false; + updateScreen(); + } +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/TFT_eSPI_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/TFT_eSPI_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..1cb8fe0 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Controller_examples/TFT_eSPI_fdrs/fdrs_node_config.h @@ -0,0 +1,23 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Controller/ESPNOW_Controller.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Controller/ESPNOW_Controller.ino new file mode 100644 index 0000000..4391bd2 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Controller/ESPNOW_Controller.ino @@ -0,0 +1,29 @@ +// FARM DATA RELAY SYSTEM +// +// ESP-NOW Sensor Example +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// An example of how to send data via ESP-NOW using FDRS. +// + +#include "fdrs_node_config.h" +#include + +void fdrs_recv_cb(DataReading theData) +{ + DBG("ID: " + String(theData.id)); + DBG("Type: " + String(theData.t)); + DBG("Data: " + String(theData.d)); +} + +void setup() +{ + beginFDRS(); + pingFDRS(1000); + addFDRS(fdrs_recv_cb); + subscribeFDRS(READING_ID); +} +void loop() +{ + loopFDRS(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Controller/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Controller/fdrs_node_config.h new file mode 100644 index 0000000..00f200c --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Controller/fdrs_node_config.h @@ -0,0 +1,25 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Sensor/ESPNOW_Sensor.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Sensor/ESPNOW_Sensor.ino new file mode 100644 index 0000000..a544b03 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Sensor/ESPNOW_Sensor.ino @@ -0,0 +1,38 @@ +// FARM DATA RELAY SYSTEM +// +// ESP-NOW Sensor Example +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// An example of how to send data via ESP-NOW using FDRS. +// + +#include "fdrs_node_config.h" +#include + +float data1; +float data2; + +void setup() { + beginFDRS(); +} +void loop() { + data1 = readHum(); + loadFDRS(data1, HUMIDITY_T); + data2 = readTemp(); + loadFDRS(data2, TEMP_T); +// DBG(sendFDRS()); + if(sendFDRS()){ + DBG("Big Success!"); + } else { + DBG("Nope, not so much."); + } + sleepFDRS(10); //Sleep time in seconds +} + +float readTemp() { + return 22.069; +} + +float readHum() { + return random(0, 100); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Sensor/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Sensor/fdrs_node_config.h new file mode 100644 index 0000000..fe95bcd --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Sensor/fdrs_node_config.h @@ -0,0 +1,25 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Stress_Test/ESPNOW_Stress_Test.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Stress_Test/ESPNOW_Stress_Test.ino new file mode 100644 index 0000000..c710e2e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Stress_Test/ESPNOW_Stress_Test.ino @@ -0,0 +1,20 @@ +// FARM DATA RELAY SYSTEM +// +// ESP-NOW Stress Tester or "Spammer" +// +// Sends ESP-NOW packets at approximately 60Hz. +// + +#include "fdrs_node_config.h" +#include + +void setup() { + beginFDRS(); +} +void loop() { + for (uint8_t i=0; i < 255; i++) { + loadFDRS(float(i), IT_T); + sendFDRS(); + delay(15); + } +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Stress_Test/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Stress_Test/fdrs_node_config.h new file mode 100644 index 0000000..fd0d29b --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/ESPNOW_Stress_Test/fdrs_node_config.h @@ -0,0 +1,23 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 3 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +//#define FDRS_DEBUG +// +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Gateway_Examples/1_MQTT_Gateway_Ethernet/1_MQTT_Gateway_Ethernet.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Gateway_Examples/1_MQTT_Gateway_Ethernet/1_MQTT_Gateway_Ethernet.ino new file mode 100644 index 0000000..c447004 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Gateway_Examples/1_MQTT_Gateway_Ethernet/1_MQTT_Gateway_Ethernet.ino @@ -0,0 +1,22 @@ +// FARM DATA RELAY SYSTEM +// +// ETHERNET GATEWAY +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. + +// Configuration for the ThingPulse ESPGateway: https://thingpulse.com/product/espgateway-ethernet-esp32-wifi-ble-gateway-with-rj45-ethernet-connector/ +#define ETH_CLK_MODE ETH_CLOCK_GPIO16_OUT +#define ETH_POWER_PIN 5 + +#include "fdrs_gateway_config.h" +#include + +void setup() { + pinMode(ETH_POWER_PIN, OUTPUT); + digitalWrite(ETH_POWER_PIN, HIGH); + beginFDRS(); +} + +void loop() { + loopFDRS(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Gateway_Examples/1_MQTT_Gateway_Ethernet/fdrs_gateway_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Gateway_Examples/1_MQTT_Gateway_Ethernet/fdrs_gateway_config.h new file mode 100644 index 0000000..5ac2d5e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Gateway_Examples/1_MQTT_Gateway_Ethernet/fdrs_gateway_config.h @@ -0,0 +1,73 @@ +// FARM DATA RELAY SYSTEM +// +// ETHERNET GATEWAY CONFIGURATION + +//Addresses +#define UNIT_MAC 0x01 // The address of this gateway + +#define ESPNOW_NEIGHBOR_1 0x00 // Address of ESP-NOW neighbor #1 +#define ESPNOW_NEIGHBOR_2 0x02 // Address of ESP-NOW neighbor #2 +#define LORA_NEIGHBOR_1 0x00 // Address of LoRa neighbor #1 +#define LORA_NEIGHBOR_2 0x00 // Address of LoRa neighbor #2 + +// Interfaces +#define USE_ESPNOW +//#define USE_LORA +//#define USE_WIFI // Will cause errors if used with ESP-NOW. Use a serial link instead! +#define USE_ETHERNET + +// Routing +// Options: sendESPNowNbr(1 or 2); sendESPNowPeers(); sendLoRaNbr(1 or 2); broadcastLoRa(); sendSerial(); sendMQTT(); +#define ESPNOWG_ACT sendMQTT(); +#define LORAG_ACT +#define SERIAL_ACT sendESPNowNbr(2); sendESPNowPeers(); +#define MQTT_ACT +#define INTERNAL_ACT sendMQTT(); +#define ESPNOW1_ACT +#define ESPNOW2_ACT sendMQTT(); +#define LORA1_ACT +#define LORA2_ACT + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) + +//#define CUSTOM_SPI +#define LORA_SPI_SCK 5 +#define LORA_SPI_MISO 19 +#define LORA_SPI_MOSI 27 + +#define FDRS_DEBUG // Enable USB-Serial debugging + +// OLED -- Displays console debugging messages on an SSD1306 I²C OLED +///#define USE_OLED +#define OLED_HEADER "FDRS" +#define OLED_PAGE_SECS 30 +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 + +// UART data interface pins (if available) +#define RXD2 14 +#define TXD2 15 + +//#define USE_LR // Use ESP-NOW LR mode (ESP32 only) + +// WiFi and MQTT Credentials -- These will override the global settings +//#define WIFI_SSID "Your SSID" +//#define WIFI_PASS "Your Password" + +#define MQTT_ADDR "192.168.2.3" +//#define MQTT_PORT 1883 // Default MQTT port is 1883 +//#define MQTT_AUTH //Enable MQTT authentication +//#define MQTT_USER "Your MQTT Username" +//#define MQTT_PASS "Your MQTT Password" + + + diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Controller/LoRa_Controller.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Controller/LoRa_Controller.ino new file mode 100644 index 0000000..4391bd2 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Controller/LoRa_Controller.ino @@ -0,0 +1,29 @@ +// FARM DATA RELAY SYSTEM +// +// ESP-NOW Sensor Example +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// An example of how to send data via ESP-NOW using FDRS. +// + +#include "fdrs_node_config.h" +#include + +void fdrs_recv_cb(DataReading theData) +{ + DBG("ID: " + String(theData.id)); + DBG("Type: " + String(theData.t)); + DBG("Data: " + String(theData.d)); +} + +void setup() +{ + beginFDRS(); + pingFDRS(1000); + addFDRS(fdrs_recv_cb); + subscribeFDRS(READING_ID); +} +void loop() +{ + loopFDRS(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Controller/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Controller/fdrs_node_config.h new file mode 100644 index 0000000..eeca52d --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Controller/fdrs_node_config.h @@ -0,0 +1,24 @@ +// FARM DATA RELAY SYSTEM +// Node Configuration + +#include + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +//#define USE_ESPNOW +#define USE_LORA +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Sensor/LoRa_Sensor.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Sensor/LoRa_Sensor.ino new file mode 100644 index 0000000..6761282 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Sensor/LoRa_Sensor.ino @@ -0,0 +1,33 @@ +// FARM DATA RELAY SYSTEM +// +// LoRa Sensor Example +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// An example of how to send data via LoRa using FDRS. +// + +#include "fdrs_node_config.h" +#include + +float data1; +float data2; + +void setup() { + beginFDRS(); +} +void loop() { + data1 = readHum(); + loadFDRS(data1, HUMIDITY_T); + data2 = readTemp(); + loadFDRS(data2, TEMP_T); + sendFDRS(); + sleepFDRS(10); //Sleep time in seconds +} + +float readTemp() { + return 21.0; +} + +float readHum() { + return random(0,100); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Sensor/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Sensor/fdrs_node_config.h new file mode 100644 index 0000000..017f77b --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/LoRa_Sensor/fdrs_node_config.h @@ -0,0 +1,30 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 2 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +//#define USE_ESPNOW +#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. + +//#define CUSTOM_SPI +#define LORA_SPI_SCK 5 +#define LORA_SPI_MISO 19 +#define LORA_SPI_MOSI 27 \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/AHT20_fdrs/AHT20_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/AHT20_fdrs/AHT20_fdrs.ino new file mode 100644 index 0000000..cfa1632 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/AHT20_fdrs/AHT20_fdrs.ino @@ -0,0 +1,29 @@ +// FARM DATA RELAY SYSTEM +// +// AHT20 SENSOR MODULE +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. + +#include "fdrs_node_config.h" +#include +#include + +Adafruit_AHTX0 aht; + +void setup() { + Serial.begin(115200); + beginFDRS(); + if (! aht.begin()) { + Serial.println("Could not find AHT? Check wiring"); + while (1) delay(10); + } +} + +void loop() { + sensors_event_t humidity, temp; + aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data + loadFDRS(temp.temperature, TEMP_T); + loadFDRS(humidity.relative_humidity, HUMIDITY_T); + sendFDRS(); + sleepFDRS(60); //Sleep time in seconds +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/AHT20_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/AHT20_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/AHT20_fdrs/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BME280_fdrs/BME280_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BME280_fdrs/BME280_fdrs.ino new file mode 100644 index 0000000..228c2e9 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BME280_fdrs/BME280_fdrs.ino @@ -0,0 +1,28 @@ +// FARM DATA RELAY SYSTEM +// +// BME280 SENSOR MODULE +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. + +#include "fdrs_node_config.h" +#include +#include + +Adafruit_BME280 bme; + +void setup() { + //Serial.begin(115200); + beginFDRS(); + while (!bme.begin(0x76)) { + //Serial.println("BME not initializing!"); + delay(10); + } +} + +void loop() { + loadFDRS(bme.readTemperature(), TEMP_T); + loadFDRS(bme.readHumidity(), HUMIDITY_T); + loadFDRS(bme.readPressure() / 100.0F, PRESSURE_T); + sendFDRS(); + sleepFDRS(60); //Sleep time in seconds +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BME280_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BME280_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BME280_fdrs/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BMP280_fdrs/BMP280_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BMP280_fdrs/BMP280_fdrs.ino new file mode 100644 index 0000000..055abaa --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BMP280_fdrs/BMP280_fdrs.ino @@ -0,0 +1,28 @@ +// FARM DATA RELAY SYSTEM +// +// BMP280 SENSOR MODULE +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// Connect sensor SDA and SCL pins to those of the ESP. + +#include "fdrs_node_config.h" +#include +#include + +Adafruit_BMP280 bmp; + +void setup() { + //Serial.begin(115200); + beginFDRS(); + while (!bmp.begin(0x76)) { + //Serial.println("BMP not initializing!"); + delay(10); + } +} + +void loop() { + loadFDRS(bmp.readTemperature(), TEMP_T); + loadFDRS(bmp.readPressure() / 100.0F, PRESSURE_T); + sendFDRS(); + sleepFDRS(60); //Sleep time in seconds +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BMP280_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BMP280_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/BMP280_fdrs/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/CapacitiveSoil/CapacitiveSoil.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/CapacitiveSoil/CapacitiveSoil.ino new file mode 100644 index 0000000..b6fdced --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/CapacitiveSoil/CapacitiveSoil.ino @@ -0,0 +1,26 @@ +// FARM DATA RELAY SYSTEM +// +// CAPACITIVE SOIL MOISTURE SENSOR MODULE +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// Connect the sensor to an analog pin of your MCU. +// + +#define SOIL_PIN 36 // Ignored on ESP8266 + +#include "fdrs_node_config.h" +#include +void setup() { + beginFDRS(); + delay(50); //let the sensor warm up +} +void loop() { +#ifdef ESP8266 + uint16_t s = analogRead(0); +#else + uint16_t s = analogRead(SOIL_PIN); +#endif + loadFDRS(s, SOIL_T); + sendFDRS(); + sleepFDRS(60 * 5); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/CapacitiveSoil/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/CapacitiveSoil/fdrs_node_config.h new file mode 100644 index 0000000..ae64058 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/CapacitiveSoil/fdrs_node_config.h @@ -0,0 +1,38 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 21 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +//#define USE_ESPNOW +#define USE_LORA + +//#define USE_LR // Enables 802.11LR on ESP32 ESP-NOW devices + +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 // ESP32 SX1276 (TTGO) +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 +//#define USE_SX126X + +//#define CUSTOM_SPI +#define LORA_SPI_SCK 5 +#define LORA_SPI_MISO 19 +#define LORA_SPI_MOSI 27 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. + +//#define USE_OLED +#define OLED_HEADER "FDRS" +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DHT22_fdrs/DHT22_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DHT22_fdrs/DHT22_fdrs.ino new file mode 100644 index 0000000..dc0e22f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DHT22_fdrs/DHT22_fdrs.ino @@ -0,0 +1,42 @@ +// Example testing sketch for various DHT humidity/temperature sensors +// Written by ladyada, public domain + +// Modified by Timm Bogner for Farm Data Relay System -- Untested because I don't have a DHT sensor onhand. + +#include "fdrs_node_config.h" +#include +#include "DHT.h" + +#define DHTPIN 2 // Digital pin connected to the DHT sensor + +// Uncomment whatever type you're using! +//#define DHTTYPE DHT11 // DHT 11 +#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 +//#define DHTTYPE DHT21 // DHT 21 (AM2301) + +DHT dht(DHTPIN, DHTTYPE); + +void setup() { + beginFDRS(); + DBG("DHTxx Sketch!"); + dht.begin(); +} + +void loop() { + // Wait a few seconds between measurements. + // Reading temperature or humidity takes about 250 milliseconds! + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + float h = dht.readHumidity(); + // Read temperature as Celsius (the default) + float t = dht.readTemperature(); + + // Check if any reads failed and exit early (to try again). + if (isnan(h) || isnan(t)) { + DBG("Failed to read from DHT sensor!"); + return; + } + loadFDRS(h, HUMIDITY_T); + loadFDRS(t, TEMP_T); + sendFDRS(); + sleepFDRS(10); //Sleep time in seconds +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DHT22_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DHT22_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DHT22_fdrs/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DS18B20_fdrs/DS18B20_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DS18B20_fdrs/DS18B20_fdrs.ino new file mode 100644 index 0000000..6e29cff --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DS18B20_fdrs/DS18B20_fdrs.ino @@ -0,0 +1,27 @@ +// FARM DATA RELAY SYSTEM +// +// DS18B20 SENSOR MODULE +// + +#define ONE_WIRE_BUS 13 //Pin that the DS18B20 is connected to + +#include "fdrs_node_config.h" +#include +#include +#include + +OneWire oneWire(ONE_WIRE_BUS); +DallasTemperature sensors(&oneWire); + +void setup() { + beginFDRS(); + sensors.begin(); +} + +void loop() { + sensors.requestTemperatures(); // Send the command to get temperatures + float tempC = sensors.getTempCByIndex(0); + loadFDRS(tempC, TEMP_T); + sendFDRS(); + sleepFDRS(60); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DS18B20_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DS18B20_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/DS18B20_fdrs/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/FrequencyCounter/FrequencyCounter.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/FrequencyCounter/FrequencyCounter.ino new file mode 100644 index 0000000..39fade8 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/FrequencyCounter/FrequencyCounter.ino @@ -0,0 +1,51 @@ +// FARM DATA RELAY SYSTEM +// +// Gypsum-based Soil Moisture Sensor +// +// Uses a Ezsbc.com Dev board, a Kisssys moisture sensor board, and a DS3231 RTC. +// Deep sleep current is less than 20µA. +// https://www.printables.com/model/176752-gypson-water-sensor + + +#include "fdrs_node_config.h" +#include + + +#define BUTTON_PIN_BITMASK 0x100000000 // 2^32 in hex the pin that is connected to SQW +#define CLOCK_INTERRUPT_PIN 32 // yep same pin + + +const int FreqIn1 = 5; +volatile uint16_t Freq1 = 0; +static uint16_t FreqOut1; + +ICACHE_RAM_ATTR void SensorInt1() { + // If the pin is Rising, increment counter + Freq1++; +}; + + +void readFrequency() { + // for one second we count the pulses and get our moisture reading in pps + attachInterrupt(FreqIn1, SensorInt1, RISING); + delay(1000); // delay in ms is 1 seconds-- not the most accurate way to do this but more than accurate enough for our low frequency and 10 second read + FreqOut1 = Freq1; // our frequency * 10 + detachInterrupt(FreqIn1); // only reading once so turn off the interrupt + +} +void setup() { + beginFDRS(); + pinMode(FreqIn1, INPUT); + Freq1 = 0; + delay(50); + readFrequency(); + //DBG("Frequency = " + String(FreqOut1)); + loadFDRS(FreqOut1, SOIL_T); + sendFDRS(); + sleepFDRS(1800); //Sleep time in seconds + +} + +void loop() { + // nuttin honey +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/FrequencyCounter/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/FrequencyCounter/fdrs_node_config.h new file mode 100644 index 0000000..dc71550 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/FrequencyCounter/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 23 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +#define POWER_CTRL 22 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/GenericGPS_fdrs/GenericGPS_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/GenericGPS_fdrs/GenericGPS_fdrs.ino new file mode 100644 index 0000000..5df3f6d --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/GenericGPS_fdrs/GenericGPS_fdrs.ino @@ -0,0 +1,172 @@ +// FARM DATA RELAY SYSTEM +// +// Generic GPS Sensor +// +// Developed by Sascha Juch (sascha.juch@gmail.com). +// Reads in GPS data from serial and sends latitude, longitude and altitude to a gateway. +// + +#include "fdrs_node_config.h" +#include + +#define SERIAL1_RX 13 // TX pin of GPS sensor +#define SERIAL1_TX 12 // RX pin of GPS sensor +#define MAX_NMEA_LENGTH 82 //maximum allowed length of a NMEA 0183 sentences. + +char currentNMEALine[MAX_NMEA_LENGTH]; + +void setup() { + // ToDo: This works well on a board with a second hardware serial port like the ESP32. But what if there is no hardware serial on the device? + // Unfortunately I do not have a GPS (standalone) sensor atm with which I could test. Help and advice appreciated. + Serial1.begin(9600, SERIAL_8N1, SERIAL1_RX, SERIAL1_TX); + + beginFDRS(); +} + +void loop() { + // read in line by line of the NMEA input and get rid of trailing whitespaces + Serial1.readBytesUntil('\n', currentNMEALine, MAX_NMEA_LENGTH); + trimwhitespace(currentNMEALine); + + // we are only interested in GPGGA (U-Blox M6N) or GNGGA (U-Blox M8N)lines. + if (startsWith(currentNMEALine, "$GNGGA") || startsWith(currentNMEALine, "$GPGGA")) { + + DBG(currentNMEALine); + + // just in case someone needs UTC, quality or #satelites, just uncomment and do what you have to do with them. :) + //char * gpsUTC = getNthValueOf(currentNMEALine, ',', 1); + char * gpsLatitude = getNthValueOf(currentNMEALine, ',', 2); + char * gpsLatitudeOrientation = getNthValueOf(currentNMEALine, ',', 3); + char * gpsLongitude = getNthValueOf(currentNMEALine, ',', 4); + char * gpsLongitudeOrientation = getNthValueOf(currentNMEALine, ',', 5); + //char * gpsQuality = getNthValueOf(currentNMEALine, ',', 6); + char * gpsAltitude = getNthValueOf(currentNMEALine, ',', 7); + //char * gpsNoOfSatelites = getNthValueOf(currentNMEALine, ',', 9); + + // convert latitude and altitude to decimal degree values (as used in most maps programs) + // negative values mean "S" or "W", positive values mean "N" and "E" + float latitude = convertGpsCoordinates(atof(gpsLatitude), gpsLatitudeOrientation); + float longitude = convertGpsCoordinates(atof(gpsLongitude), gpsLongitudeOrientation); + + float altitude = atof(gpsAltitude); + +/* + loadFDRS(latitude, HUMIDITY_T); + loadFDRS(longitude, TEMP_T); + loadFDRS(altitude, TEMP2_T); +*/ + + // extended sensor types - not officially atm! + loadFDRS(latitude, LATITUDE_T); + loadFDRS(longitude, LONGITUDE_T); + loadFDRS(altitude, ALTITUDE_T); + if(sendFDRS()){ + DBG("Big Success!"); + } else { + DBG("Nope, not so much."); + } + sleepFDRS(10); //Sleep time in seconds + } + +} + +// cudos for the trimming function go to: https://stackoverflow.com/questions/122616/how-do-i-trim-leading-trailing-whitespace-in-a-standard-way +// Thanks! That was a time saver. :) +// Note: This function returns a pointer to a substring of the original string. +// If the given string was allocated dynamically, the caller must not overwrite +// that pointer with the returned value, since the original pointer must be +// deallocated using the same allocator with which it was allocated. The return +// value must NOT be deallocated using free() etc. +char *trimwhitespace(char *str) +{ + char *end; + + // Trim leading space + while(isspace((unsigned char)*str)) str++; + + if(*str == 0) // All spaces? + return str; + + // Trim trailing space + end = str + strlen(str) - 1; + while(end > str && isspace((unsigned char)*end)) end--; + + // Write new null terminator character + end[1] = '\0'; + + return str; +} + +// check, if a given char* fullString starts with a given char* startString. +// If that's the case, return true, false otherwise +bool startsWith(const char *fullString, const char *startString) +{ + if (strncmp(fullString, startString, strlen(startString)) == 0) return 1; + return 0; +} + +// Cudos for the substr function go to: https://www.techiedelight.com/implement-substr-function-c/ +// Thanks! That helped a lot :) +// Following function extracts characters present in `src` +// between `m` and `n` (excluding `n`) +char* substr(const char *src, int m, int n) +{ + // get the length of the destination string + int len = n - m; + + // allocate (len + 1) chars for destination (+1 for extra null character) + char *dest = (char*)malloc(sizeof(char) * (len + 1)); + + // extracts characters between m'th and n'th index from source string + // and copy them into the destination string + for (int i = m; i < n && (*(src + i) != '\0'); i++) + { + *dest = *(src + i); + dest++; + } + + // null-terminate the destination string + *dest = '\0'; + + // return the destination string + return dest - len; +} + +// returns the value of the n-th occurance within a delimiter-separated string +char * getNthValueOf (char *inputString, const char delimiter, uint8_t index) { + uint8_t i = 0; + uint8_t currentIndex = 0; + uint8_t startOfValue = 0; + uint8_t endOfValue = 0; + + while (i < strlen(inputString) && inputString[i] && currentIndex < index) { + if (inputString[i] == delimiter) { + currentIndex++; + } + i++; + } + startOfValue = i; + + while (i < strlen(inputString) && inputString[i] && currentIndex <= index) { + if (inputString[i] == delimiter) { + currentIndex++; + } + i++; + } + endOfValue = i; + + char* valueAtIndex = substr(inputString, startOfValue, endOfValue-1); + + return valueAtIndex; +} + +// convert NMEA0183 degrees minutes coordinates to decimal degrees +float convertGpsCoordinates(float degreesMinutes, char* orientation) { + double gpsMinutes = fmod((double)degreesMinutes, 100.0); + uint8_t gpsDegrees = degreesMinutes / 100; + double decimalDegrees = gpsDegrees + ( gpsMinutes / 60 ); + if (strcmp(orientation, "W") == 0 || strcmp(orientation, "S") == 0) { + decimalDegrees = 0 - decimalDegrees; + } + return decimalDegrees; +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/GenericGPS_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/GenericGPS_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..7e7391d --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/GenericGPS_fdrs/fdrs_node_config.h @@ -0,0 +1,28 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x03 //Address of the nearest gateway + +//#define USE_ESPNOW +#define USE_LORA +//#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 // ESP32 SX1276 (TTGO) +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 +#define LORA_BUSY 33 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. + +#define USE_OLED +#define OLED_HEADER "FDRS" +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/KisssysGypsum/KisssysGypsum.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/KisssysGypsum/KisssysGypsum.ino new file mode 100644 index 0000000..5899b6d --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/KisssysGypsum/KisssysGypsum.ino @@ -0,0 +1,117 @@ +// FARM DATA RELAY SYSTEM +// +// Gypsum-based Soil Moisture Sensor +// +// Uses a Ezsbc.com Dev board, a Kisssys moisture sensor board, and a DS3231 RTC. +// Deep sleep current is less than 20µA. +// https://www.printables.com/model/176752-gypson-water-sensor + +#define DEBUG +#define CREDENTIALS + +#include "fdrs_node_config.h" +#include +#include +RTC_DS3231 rtc; + +#define BUTTON_PIN_BITMASK 0x100000000 // 2^32 in hex the pin that is connected to SQW +#define CLOCK_INTERRUPT_PIN 32 // yep same pin +//#define SupplyPin 33 // power to DS3231 +#define BatteryReadPin 35 // read battey voltage on this pin + + +const int FreqIn1 = 18; // gpio32 +const int FreqPower = 19; +const int RTCPower = 25; +volatile uint16_t Freq1 = 0; +static uint16_t FreqOut1; + +void SensorInt1() { + // If the pin is Rising, increment counter + Freq1++; +}; + + + +void setup() { + beginFDRS(); + DBG(__FILE__); + pinMode(FreqIn1, INPUT); + pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP); // On deep sleep the pin needs and external 330k pullup + pinMode(RTCPower, OUTPUT); // for the SQW pin to stay high when RTC power is removed + digitalWrite(RTCPower, HIGH); // DS3231 needs the SQW pullup removed on the board to + pinMode(FreqPower, OUTPUT); // lower the deepsleep current by 60ua's + digitalWrite(FreqPower, HIGH); + + Freq1 = 0; + delay(50); + + Wire.begin(13, 14); // moved from 21 22 normal I2C pins because it allows me to set these pins to input later + if (!rtc.begin()) { + DBG("Couldn't find RTC!"); + Serial.flush(); + while (1) delay(10); + } + + if (rtc.lostPower()) { + // this will adjust to the date and time at compilation + rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + } + + rtc.disable32K(); // Don't use this so we shut it down on the board + // set alarm 1, 2 flag to false (so alarm 1, 2 didn't happen so far) + // if not done, this easily leads to problems, as both register aren't reset on reboot/recompile + rtc.clearAlarm(1); + rtc.clearAlarm(2); + + // stop oscillating signals at SQW Pin + // otherwise setAlarm1 will fail + rtc.writeSqwPinMode(DS3231_OFF); + + // turn off alarm 2 (in case it isn't off already) + // again, this isn't done at reboot, so a previously set alarm could easily go overlooked + rtc.disableAlarm(2); + + + // for one second we count the pulses and get our moisture reading in pps + attachInterrupt(FreqIn1, SensorInt1, RISING); + delay(1000); // delay in ms is 1 seconds-- not the most accurate way to do this but more than accurate enough for our low frequency and 10 second read + FreqOut1 = Freq1; // our frequency * 10 + detachInterrupt(FreqIn1); // only reading once so turn off the interrupt + + + if (rtc.alarmFired(1)) { + rtc.clearAlarm(1); + Serial.println("Alarm cleared"); + } + + float supply = analogRead(BatteryReadPin) * .001833; + DBG("WaterSensor 1 = " + String(FreqOut1)); + DBG("Supply Voltage = " + String(supply)); + + loadFDRS(FreqOut1, SOIL_T); + loadFDRS(supply, VOLTAGE_T); + sendFDRS(); + // Lowers SQW int pin32 every 10 seconds + rtc.setAlarm1(DateTime(0, 0, 0, 00, 00, 10), DS3231_A1_Second); //DateTime (year,month,day,hour,min,sec) + + esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, ESP_EXT1_WAKEUP_ALL_LOW); + + char date[10] = "hh:mm:ss"; + rtc.now().toString(date); + Serial.println(date); // Print the time + Serial.println("Going to sleep soon"); + + // prepare for low current shutdown + digitalWrite(FreqPower, LOW); + digitalWrite(RTCPower, LOW); + pinMode(13, INPUT); + pinMode(14, INPUT); + + esp_deep_sleep_start(); + +} + +void loop() { + // nuttin honey +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/KisssysGypsum/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/KisssysGypsum/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/KisssysGypsum/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/LilyGo_HiGrow_32/LilyGo_HiGrow_32.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/LilyGo_HiGrow_32/LilyGo_HiGrow_32.ino new file mode 100644 index 0000000..fd62a39 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/LilyGo_HiGrow_32/LilyGo_HiGrow_32.ino @@ -0,0 +1,117 @@ +// FARM DATA RELAY SYSTEM +// +// LILYGO HIGROW SENSOR MODULE +// + + +#define I2C_SDA 25 +#define I2C_SCL 26 +#define DHT12_PIN 16 +#define BAT_ADC 33 +#define SALT_PIN 34 +#define SOIL_PIN 32 +#define BOOT_PIN 0 +#define USER_BUTTON 35 +#define DS18B20_PIN 21 + +#include "fdrs_node_config.h" +#include +#include +#include + +BH1750 lightMeter(0x23); //0x23 +Adafruit_BME280 bme; //0x77 +RTC_DATA_ATTR int the_count = 0; + +void setup() { + beginFDRS(); + + //Init Sensors + Wire.begin(I2C_SDA, I2C_SCL); + // while (!bme.begin()) { + // Serial.println("bme"); + // delay(10); + // } + + if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) { + Serial.println(F("BH1750 Advanced begin")); + } else { + Serial.println(F("Error initialising BH1750")); + } + +} + +void loadData() { + float s_battery = readBattery(); + // float bme_temp = bme.readTemperature(); + // float bme_pressure = (bme.readPressure() / 100.0F); + //float bme_altitude = bme.readAltitude(1013.25); + // float bme_humidity = bme.readHumidity(); + float s_soil = readSoil(); + float s_salt = readSalt(); + while (! lightMeter.measurementReady()) { + delay(10); + } + float lux = lightMeter.readLightLevel(); + the_count++; + + Serial.println(); + // Serial.println("Temp: " + String(bme_temp)); + // Serial.println("Humidity: " + String(bme_humidity)); + Serial.println("Light: " + String(lux)); + // Serial.println("Pressure: " + String(bme_pressure)); + Serial.println("Salt: " + String(s_salt)); + Serial.println("Soil: " + String(s_soil)); + Serial.println("Voltage: " + String(s_battery)); + Serial.println("Count: " + String(the_count)); + + // loadFDRS(bme_temp, TEMP_T); + + // loadFDRS(bme_humidity, HUMIDITY_T); + + + loadFDRS(lux, LIGHT_T); + // loadFDRS(bme_pressure, PRESSURE_T); + loadFDRS(s_salt, SOILR_T); + loadFDRS(s_soil, SOIL_T); + loadFDRS(s_battery, VOLTAGE_T); + loadFDRS(float(the_count), IT_T); + +} + +uint32_t readSalt() //Soil Electrodes: This code came from the LilyGo documentation. +{ + uint8_t samples = 120; + uint32_t humi = 0; + uint16_t array[120]; + for (int i = 0; i < samples; i++) { + array[i] = analogRead(SALT_PIN); + delay(2); + } + std::sort(array, array + samples); + for (int i = 0; i < samples; i++) { + if (i == 0 || i == samples - 1)continue; + humi += array[i]; + } + humi /= samples - 2; + return humi; +} + +uint16_t readSoil() //Soil Capacitance +{ + uint16_t soil = analogRead(SOIL_PIN); + return soil; +} + +float readBattery() //Battery Voltage: This code came from the LilyGo documentation. +{ + int vref = 1100; + uint16_t volt = analogRead(BAT_ADC); + float battery_voltage = ((float)volt / 4095.0) * 2.0 * 3.3 * (vref); + return battery_voltage; +} +void loop() { + loadData(); + sendFDRS(); + sleepFDRS(30); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/LilyGo_HiGrow_32/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/LilyGo_HiGrow_32/fdrs_node_config.h new file mode 100644 index 0000000..1930791 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/LilyGo_HiGrow_32/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +#define POWER_CTRL 4 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MESB_fdrs/MESB_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MESB_fdrs/MESB_fdrs.ino new file mode 100644 index 0000000..1ea3e4f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MESB_fdrs/MESB_fdrs.ino @@ -0,0 +1,45 @@ +// FARM DATA RELAY SYSTEM +// +// Multifunction ESP8266 Sensor Board by Phil Grant +// +// https://github.com/gadjet/Multifunction-ESP8266-Sensor-board +// + +#include "fdrs_node_config.h" +#include +#include + +Adafruit_AHT10 aht; + +const int reedSwitch = 13; + +void setup() { + aht.begin(); + + // Init Serial Monitor + //Serial.begin(115200); + // initialize the reed switch pin as an input: + pinMode(reedSwitch, INPUT); + // initialize the wakeup pin as an input: + pinMode(16, WAKEUP_PULLUP); + beginFDRS(); +} + + +void loop() { + sensors_event_t humidity, temp; + aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data + // Read the state of the reed switch and send open or closed + if (digitalRead(reedSwitch) == HIGH) { + loadFDRS(1.0, MOTION_T); + } + else { + loadFDRS(0.0, MOTION_T); + } + loadFDRS((analogRead(A0) * 4.2 * 10 / 1023), VOLTAGE_T); + loadFDRS(humidity.relative_humidity, HUMIDITY_T); + loadFDRS(temp.temperature, TEMP_T); + // Send message via FDRS + sendFDRS(); + sleepFDRS(15); //15 Min's sleep +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MESB_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MESB_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MESB_fdrs/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MLX90614_fdrs/MLX90614_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MLX90614_fdrs/MLX90614_fdrs.ino new file mode 100644 index 0000000..7550bb5 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MLX90614_fdrs/MLX90614_fdrs.ino @@ -0,0 +1,28 @@ +// FARM DATA RELAY SYSTEM +// +// MLX90614 INFRARED TEMPERATURE SENSOR MODULE +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. + +#include "fdrs_node_config.h" +#include +#include + +Adafruit_MLX90614 mlx = Adafruit_MLX90614(); + +void setup() { + beginFDRS(); + delay(250); + DBG("Adafruit MLX90614 test"); + if (!mlx.begin()) { + DBG("Error connecting to MLX sensor. Check wiring."); + while (1); + }; +} + +void loop() { + loadFDRS(mlx.readAmbientTempC(), TEMP_T); + loadFDRS(mlx.readObjectTempC(), TEMP2_T); + sendFDRS(); + sleepFDRS(60); //Sleep time in seconds +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MLX90614_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MLX90614_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..77f3ed7 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/MLX90614_fdrs/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/README.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/README.md new file mode 100644 index 0000000..99fc996 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/README.md @@ -0,0 +1,19 @@ +# FDRS Sensors +Out of date- Needs improvement. +| Sensor Name | Maker/Datasheet | Library | +| --- | --- | --- | +| AHT20 | [ASAIR](http://www.aosong.com/userfiles/files/media/Data%20Sheet%20AHT20.pdf) | [Adafruit](https://github.com/adafruit/Adafruit_AHTX0) | +| BME280 | [Bosch](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf) | [Adafruit](https://github.com/adafruit/Adafruit_BME280_Library) | +| BMP280 | [Bosch](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) | [Adafruit](https://github.com/adafruit/Adafruit_BMP280_Library) | +| DHT22 | [Aosong](https://www.sparkfun.com/datasheets/Sensors/Temperature/DHT22.pdf) |[adafruit](https://github.com/adafruit/DHT-sensor-library) | +| DS18B20 | [Dallas](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) | [milesburton](https://github.com/adafruit/Adafruit_AHTX0) | +| LilyGo HiGrow | [TTGO](http://www.lilygo.cn/prod_view.aspx?TypeId=50033&Id=1172) | +| Multifunction ESP8266 Sensor Board | [Phil Grant](https://github.com/gadjet/Multifunction-ESP8266-Sensor-board) | + + + + + + + +I would like to use this spot as a showcase for open source PCB designs. If **you** have designed a sensor module that runs on either ESP8266 or ESP32, please contact me at timmbogner@gmail.com. I will make a custom sensor sketch, along with a link and description available here. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/SHT20_fdrs/SHT20_fdrs.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/SHT20_fdrs/SHT20_fdrs.ino new file mode 100644 index 0000000..2bf9940 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/SHT20_fdrs/SHT20_fdrs.ino @@ -0,0 +1,24 @@ +// FARM DATA RELAY SYSTEM +// +// SENSIRION SHT20 SENSOR MODULE +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// If you are using the sensor for air monitoring, change SOIL_T to HUMIDITY_T. +// +#include "DFRobot_SHT20.h" +#include "fdrs_node_config.h" +#include + +DFRobot_SHT20 sht20(&Wire, SHT20_I2C_ADDR); + + +void setup() { + beginFDRS(); + sht20.initSHT20(); +} +void loop() { + loadFDRS(sht20.readHumidity(), SOIL_T); + loadFDRS(sht20.readTemperature(), TEMP_T); + sendFDRS(); + sleepFDRS(300); //Sleep time in seconds +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/SHT20_fdrs/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/SHT20_fdrs/fdrs_node_config.h new file mode 100644 index 0000000..16a92d4 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/SHT20_fdrs/fdrs_node_config.h @@ -0,0 +1,22 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 11 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TSL2561/TSL2561.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TSL2561/TSL2561.ino new file mode 100644 index 0000000..6f0cf08 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TSL2561/TSL2561.ino @@ -0,0 +1,67 @@ +#include +#include +#include + +#include "fdrs_node_config.h" +#include + +Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345); + + +/**************************************************************************/ +/* + Configures the gain and integration time for the TSL2561 +*/ +/**************************************************************************/ +void configureSensor(void) +{ + /* You can also manually set the gain or enable auto-gain support */ + // tsl.setGain(TSL2561_GAIN_1X); /* No gain ... use in bright light to avoid sensor saturation */ + // tsl.setGain(TSL2561_GAIN_16X); /* 16x gain ... use in low light to boost sensitivity */ + tsl.enableAutoRange(true); /* Auto-gain ... switches automatically between 1x and 16x */ + + /* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */ + tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS); /* fast but low resolution */ + // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS); /* medium resolution and speed */ + // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS); /* 16-bit data but slowest conversions */ + +} + +/**************************************************************************/ +/* + Arduino setup function (automatically called at startup) +*/ +/**************************************************************************/ +void setup(void) +{ + beginFDRS(); + /* Initialise the sensor */ + //use tsl.begin() to default to Wire, + //tsl.begin(&Wire2) directs api to use Wire2, etc. + if(!tsl.begin()) + { + DBG("Ooops, no TSL2561 detected ... Check your wiring or I2C ADDR!"); + while(1); + } + configureSensor(); +} + +void loop(void) +{ + sensors_event_t event; + tsl.getEvent(&event); + if (event.light) + { + DBG(String(event.light) + " lux"); + loadFDRS(float(event.light), LIGHT_T); + sendFDRS(); + sleepFDRS(10); //Sleep time in seconds + } + else + { + /* If event.light = 0 lux the sensor is probably saturated + and no reliable data could be generated! */ + Serial.println("Sensor overload"); + } + delay(250); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TSL2561/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TSL2561/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TSL2561/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TippingBucket/TippingBucket.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TippingBucket/TippingBucket.ino new file mode 100644 index 0000000..5679a1b --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TippingBucket/TippingBucket.ino @@ -0,0 +1,37 @@ +// FARM DATA RELAY SYSTEM +// +// TIPPING BUCKET RAINFALL SENSOR MODULE +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. + +#define REED_PIN 2 + +#include "fdrs_node_config.h" +#include + +unsigned int theCount = 0; +unsigned long lastTrigger = 0; +boolean clicked = false; + +ICACHE_RAM_ATTR void detectsMovement() { + clicked = true; + lastTrigger = millis(); +} + +void setup() { +beginFDRS(); + pinMode(REED_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(REED_PIN), detectsMovement, FALLING); +} + +void loop() { + if (clicked && millis() - lastTrigger > 100) { + theCount++; + Serial.print("DINK."); + Serial.println(theCount); + clicked = false; + loadFDRS(theCount, RAINFALL_T); + sendFDRS(); + } + +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TippingBucket/fdrs_node_config.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TippingBucket/fdrs_node_config.h new file mode 100644 index 0000000..7fbb7ea --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/examples/Sensor_Examples/TippingBucket/fdrs_node_config.h @@ -0,0 +1,21 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG +// LoRa Configuration +#define RADIOLIB_MODULE SX1276 //Tested on SX1276 +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO 26 + +#define LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) +#define LORA_ACK // Request LoRa acknowledgment. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Advanced_Setup.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Advanced_Setup.png new file mode 100644 index 0000000..68a2906 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Advanced_Setup.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Advanced_Setup_LoRa.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Advanced_Setup_LoRa.png new file mode 100644 index 0000000..cb3783c Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Advanced_Setup_LoRa.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Basic_LoRa_Setup.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Basic_LoRa_Setup.png new file mode 100644 index 0000000..aaa2684 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Basic_LoRa_Setup.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Basic_Setup.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Basic_Setup.png new file mode 100644 index 0000000..3316991 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Basic_Setup.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Gateway.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Gateway.md new file mode 100644 index 0000000..1b03c12 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Gateway.md @@ -0,0 +1,196 @@ +# FDRS Gateway + +The FDRS Gateway listens for packets over ESP-NOW, UART, LoRa, and/or MQTT, then retransmits the packets over these interfaces using rules defined in the "Routing" section of the configuration file. + +A basic FDRS gateway's sketch file (.ino) will look like this: + +``` cpp +#include "fdrs_gateway_config.h" +#include + +void setup() { + beginFDRS(); +} + +void loop() { + loopFDRS(); +} +``` + +## Addresses +#### ```#define UNIT_MAC 0xNN``` +The ESP-NOW and LoRa address of the gateway. This is the address that nodes and other gateways will use to pass data to this device. +#### ```#define ESPNOW_NEIGHBOR_1 0xNN```, ```ESPNOW_NEIGHBOR_2 0xNN``` +The addresses of any ESP-NOW repeaters neighboring this gateway. +#### ```#define LORA_NEIGHBOR_1 0xNN```, ```LORA_NEIGHBOR_2 0xNN``` +The addresses of any LoRa repeaters neighboring this gateway. +## Interfaces +#### ```#define USE_ESPNOW``` +Enables ESP-NOW. + +#### ```#define USE_LORA``` +Enables LoRa. Ensure your pins are configured correctly. +#### ```#define USE_WIFI``` +Enables WiFi for use by MQTT. Do not enable WiFi and ESP-NOW simultaneously. +#### ```#define USE_ETHERNET``` +Enables ethernet to be used by MQTT. + +## Routing +**Events** occur when data arrives at the gateway via its various interfaces. When an event occurs it triggers one or more **actions**, which are functions that re-send the incoming data over the same or different interfaces. + +**Example:** In the following configuration, a packet that arrives at the serial port will be sent to the gateway's neighbor #2, and then to all ESP-NOW nodes that are connected: +``` +#define SERIAL_ACT sendESPNowNbr(2); sendESPNowPeers(); +``` +# +### Events + +#### ```#define ESPNOWG_ACT ``` +Actions that occur when data arrives from an ESP-NOW device that is *not* listed as a neighbor. +#### ```#define LORAG_ACT ``` +Actions that occur when data arrives from a LoRa device that is *not* listed as a neighbor. +#### ```#define SERIAL_ACT ``` +Actions that occur when JSON data arrives over UART. +#### ```#define MQTT_ACT ``` +Actions that occur when JSON data is posted to the MQTT topic defined by ```TOPIC_COMMAND``` in 'src/fdrs_globals.h'. +#### ```#define INTERNAL_ACT ``` +Actions that occur when data is entered by a user-defined function. Used for sending the gateway's own voltage or temperature. +#### ```#define ESPNOW1_ACT ``` and ```ESPNOW2_ACT ``` +Actions that occur when data arrives from the devices defined by ```ESPNOW_NEIGHBOR_1``` and ```ESPNOW_NEIGHBOR_2```. +#### ```#define LORA1_ACT ``` and ```LORA2_ACT ``` +Actions that occur when data arrives from the devices defined by ```LORA_NEIGHBOR_1``` and ```LORA_NEIGHBOR_2```. +# +### Actions +#### ```sendSerial();``` +Transmits the data in JSON format via both the debugging terminal as well as a second UART interface. (If available. See below.) +#### ```sendMQTT();``` +Posts the data in JSON format to the MQTT topic defined by ```TOPIC_DATA``` +#### ```sendESPNowNbr(1 or 2);``` +Sends the data to the address defined by ```ESPNOW_NEIGHBOR_1``` or ```ESPNOW_NEIGHBOR_2``` +#### ```sendESPNowPeers();``` +Sends the data to any ESP-NOW controller node that has registered with this gateway as a peer. +#### ```sendLoRaNbr(1 or 2);``` +Sends the data to the address defined by ```LORA_NEIGHBOR_1``` or ```LORA_NEIGHBOR_2``` +#### ```broadcastLoRa();``` +Broadcasts the data to any LoRa controller node that is listening to this gateway. No registration is needed to pair with a LoRa controller. +#### ```sendESPNow(0xNN);``` +Sends the data directly to the ESP-NOW gateway address provided. There is no LoRa equivalent of this function. + +# +## LoRa Configuration +#### ```#define RADIOLIB_MODULE cccc``` +The name of the RadioLib module being used. Tested modules: SX1276, SX1278, SX1262. +#### ```#define LORA_SS n``` +LoRa chip select pin. +#### ```#define LORA_RST n``` +LoRa reset pin. +#### ```#define LORA_DIO n``` +LoRa DIO pin. This refers to DIO0 on SX127x chips and DIO1 on SX126x chips. +#### ```#define LORA_BUSY n``` +For SX126x chips: LoRa BUSY pin. For SX127x: DIO1 pin, or "RADIOLIB_NC" to leave it blank. +#### ```#define LORA_TXPWR n``` +LoRa TX power in dBm. +#### ```#define USE_SX126X``` +Enable this if using the SX126x series of LoRa chips. + +#### ```#define CUSTOM_SPI``` +Enable this to define non-default SPI pins. +#### ```#define LORA_SPI_SCK n```, ```LORA_SPI_MISO n```, ```LORA_SPI_MOSI n``` +Custom SPI pin definitions. + +# +**LoRa radio parameters are generally configured in the 'src/fdrs_globals.h' file.** The following values may be set in the gateway configuration file if the user wishes to override the global value: + +The actual allowed values may vary by chip. Check the datasheet and/or RadioLib documentation. +# +#### ```#define LORA_FREQUENCY n``` +LoRa frequency in MHz. Allowed values range from 137.0 MHz to 1020.0 MHz. +#### ```#define LORA_SF n``` +LoRa spreading factor. Allowed values range from 6 to 12. +#### ```#define LORA_BANDWIDTH n``` +LoRa bandwidth in kHz. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz. +#### ```#define LORA_CR n``` +LoRa coding rate denominator. Allowed values range from 5 to 8. +#### ```#define LORA_SYNCWORD n``` +LoRa sync word. Can be used to distinguish different networks. Note that 0x34 is reserved for LoRaWAN. +#### ```#define LORA_INTERVAL n``` +Interval between LoRa buffer releases. Must be longer than transmission time-on-air. + +## WiFi and MQTT Configuration +WiFi and MQTT parameters are generally configured in the 'src/fdrs_globals.h' file. The following values may be set in the gateway configuration file if the user wishes to override the global value: +#### ```#define WIFI_SSID "cccc"``` and ``` WIFI_PASS "cccc" ``` +WiFi credentials +#### ```#define MQTT_ADDR "n.n.n.n"``` or ```MQTT_ADDR "cccc"``` +The address of the MQTT server, either the IP address or domain name. +#### ```#define MQTT_PORT n ``` +The port of the MQTT server. +#### ```#define MQTT_AUTH ``` +Enable this if using MQTT authentication +#### ```#define MQTT_USER "cccc"``` and ```MQTT_PASS "cccc"``` + +# +### SSD1306 OLED Display +Built on the [ThingPulse OLED SSD1306 Library](https://github.com/ThingPulse/esp8266-oled-ssd1306) +#### ```#define OLED_HEADER "cccc"``` +The message to be displayed at the top of the screen. +#### ```#define OLED_SDA n``` and ```OLED_SCL n``` +OLED I²C pins. +#### ```#define OLED_RST n``` +OLED reset pin. Use '-1' if not present or known. +# +### Miscellaneous +#### ```#define FDRS_DEBUG``` +Enables debugging messages to be sent over the serial port and OLED display. +#### ```#define RXD2 (pin)``` and ```TXD2 (pin)``` +Configures a second, data-only UART interface on ESP32. The ESP8266 serial interface is not configurable, and thus these options don't apply. + +#### ```#define USE_LR``` +Enables ESP-NOW Long-Range mode. Requires ESP32. +## Neighbors +*To-do: Describe neighbors and how to use them to make repeaters.* + +## User-Defined Functions +This feature allows the user to send data from the gateway itself. For example: the battery level or ambient temperature at the gateway. + +Calling ```scheduleFDRS(function, interval);``` after initializing FDRS will schedule ```function()``` to be called every ```interval``` milliseconds. + +Within this function, the user may utilize the same ```loadFDRS()``` and ```sendFDRS()``` commands used by sensors. After the data is sent, it triggers ```INTERNAL_ACT``` where it can be routed to the front-end. + +#### ```loadFDRS(float data, uint8_t type, uint16_t id);``` +Loads some data into the current packet. 'data' is a float, 'type' is the data type, and 'id' is the DataReading id. +#### ```sendFDRS();``` +Sends the current packet using actions defined by ```INTERNAL_ACT```. Does not return any value. + +**Example:** +``` cpp +#define GTWY_READING_ID 42 +#define INTERVAL_SECONDS 60 + +#include "fdrs_gateway_config.h" +#include +#include + +void sendReading() { + float v = bms.readVoltage(); + loadFDRS(v, VOLTAGE_T, GTWY_READING_ID); + sendFDRS(); +} + +void setup() { + beginFDRS(); + scheduleFDRS(sendReading, INTERVAL_SECONDS * 1000); +} + +void loop() { + loopFDRS(); +} +``` + +# +![Basic](Basic_Setup.png) + +![Advanced](Advanced_Setup.png) + +![Basic LoRa](Basic_LoRa_Setup.png) + +![Advanced LoRa](Advanced_Setup_LoRa.png) diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Node.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Node.md new file mode 100644 index 0000000..6206646 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/Node.md @@ -0,0 +1,166 @@ +# FDRS Node +A node is a device that sends and receives data from a nearby gateway. A node can be a **sensor**, **controller**, ***or both***. +## Addresses +#### ```#define READING_ID  n``` +The unique ID that the node will use when sending sensor values. Can be any integer 0 - 65535. Nodes are not necessarily tied to this parameter. They can be subscribed to up to 256 different IDs or send using several different IDs. +#### ```#define GTWY_MAC  0xnn``` +The ```UNIT_MAC``` of the gateway that this device will be paired with. + +## Usage +#### ```beginFDRS();``` +Initializes FDRS, powers up the sensor array, and begins ESP-NOW and/or LoRa. +#### ```uint32_t pingFDRS(timeout);``` +Sends a ping request to the device's paired gateway with a timeout in ms. Returns the ping time in ms as well as displaying it on the debugging console. + +## Sensor API +#### ```loadFDRS(float data, uint8_t type, uint16_t id);``` +Loads some data into the current packet. 'data' is a float, 'type' is the data type (see below), and 'id' is the DataReading id. +#### ```loadFDRS((float data, uint8_t type);``` +Same as above, but the 'id' is preset to the node's ```READING_ID```. +#### ```bool sendFDRS();``` +Sends the current packet using ESP-NOW and/or LoRa. Returns true if packet is confirmed to have been recieved successfully by the gateway. +#### ```sleepFDRS(seconds)``` +Time to sleep in seconds. If ```DEEP_SLEEP``` is enabled, the device will enter sleep. Otherwise it will use a simple ```delay()```. + +## Controller API +#### ```addFDRS(void callback);``` +Initializes controller functionality by selecting the function to be called when incoming commands are recieved. If using LoRa, the controller will automatically recieve any packets sent with broadcastLoRa(), provided they were sent by the paired gateway. ESP-NOW requires the device to register with its gateway before it will recieve incoming commands. This is done automatically, and the ESP-NOW node will continue recieving data until the paired gateway is reset. A maximum of 16 ESP-NOW controllers can recieve data from a single gateway. There is no limit to how many LoRa controllers can listen to the same gateway. +#### ```subscribeFDRS(uint16_t sub_id);``` +Sets the device to listen for a specific DataReading id. When a DataReading with id ```sub_id``` is received, the callback function will be called and given the full DataReading as a parameter. +#### ```unsubscribeFDRS(uint16_t sub_id);``` +Removes ```sub_id``` from subscription list. +#### ```loopFDRS();``` +Always add this to ```loop()``` to handle the controller's listening capabilities. + +## Basic Examples: +### Sensor +Sensors load a packet with data, then send the packet to the gateway that they are addressed to. +``` cpp +#include "fdrs_node_config.h" +#include + +void setup() { +  beginFDRS(); // Start the system + pingFDRS(2000); // Send ping and wait 2000ms for response +} +void loop() { +  loadFDRS(21.0, TEMP_T); // Load a temperature of 21.0 into the queued packet +  sendFDRS(); // Send the queued packet +  sleepFDRS(60); // Sleep for 60 seconds +} +``` + +### Controller +Controllers register with the gateway they are addressed to, then receive data from it. + +``` cpp +#include "fdrs_node_config.h" +#include + +void fdrs_recv_cb(DataReading theData) { + DBG("ID: " + String(theData.id)); + DBG("Type: " + String(theData.t)); + DBG("Data: " + String(theData.d)); +} + +void setup() { +  beginFDRS(); // Start the system +  addFDRS(fdrs_recv_cb); // Call fdrs_recv_cb() when data arrives. +  subscribeFDRS(READING_ID); // Subscribe to DataReadings with ID matching READING_ID +} +void loop() { + loopFDRS(); // Listen for data +} +``` + +## Configuration + +#### ```#define USE_ESPNOW``` +Enables/disables ESP-NOW. +#### ```#define USE_LORA``` +Enables/disables LoRa. +#### ```#define LORA_ACK``` +Enables LoRa packet acknowledgement. The device will use CRC to ensure that the data arrived at its destination correctly. If disabled, ```sendFDRS()``` will always return true when sending LoRa packets. + +Thanks to [aviateur17](https://github.com/aviateur17) for this feature! +#### ```#define FDRS_DEBUG``` +This definition enables debug messages to be sent over the serial port. If disabled, no serial debug interface will be initialized. +#### ```#define DEEP_SLEEP``` +If enabled, device will enter deep-sleep when the sleepFDRS() command is used. If using ESP8266, be sure that you connect the WAKE pin (GPIO 16) to RST or your device will not wake up. +#### ```#define POWER_CTRL n``` +If defined, power control will bring a GPIO pin high when FDRS is initialized. This is useful for powering sensors while running on battery. +# +## LoRa Configuration +#### ```#define RADIOLIB_MODULE cccc``` +The name of the RadioLib module being used. Tested modules: SX1276, SX1278, SX1262. +#### ```#define LORA_SS n``` +LoRa chip select pin. +#### ```#define LORA_RST n``` +LoRa reset pin. +#### ```#define LORA_DIO n``` +LoRa DIO pin. This refers to DIO1 on SX127x chips and DIO1 on SX126x chips. +#### ```#define LORA_BUSY n``` +For SX126x chips: LoRa BUSY pin. For SX127x: DIO1 pin, or "RADIOLIB_NC" to leave it blank. +#### ```#define LORA_TXPWR n``` +LoRa TX power in dBm. +#### ```#define USE_SX126X``` +Enable this if using the SX126x series of LoRa chips. +# +### SSD1306 OLED Display +Built on the [ThingPulse OLED SSD1306 Library](https://github.com/ThingPulse/esp8266-oled-ssd1306) +##### ```#define OLED_HEADER "cccc"``` +The message to be displayed at the top of the screen. +#### ```#define OLED_SDA n``` and ```OLED_SCL n``` +OLED I²C pins. +#### ```#define OLED_RST n``` +OLED reset pin. Use '-1' if not present or known. +# +## Callback Function +The callback function is executed when data arrives with an ID that the controller is subscribed to. Inside of this function, the user has access to the incoming DataReading. If multiple readings are recieved, the function will be called for each of them. While you should always be brief in interrupt callbacks (ISRs), it's okay to do more in this one. +## Type Definitions +For the moment, my thought is to reserve the first two bits of the type. I might use them in the future to indicate the data size or type (bool, char,  int, float, etc?). This leaves us with 64 possible type definitions. If you have more types to add, please get in touch! +``` +#define STATUS_T 0 // Status +#define TEMP_T 1 // Temperature +#define TEMP2_T 2 // Temperature #2 +#define HUMIDITY_T 3 // Relative Humidity +#define PRESSURE_T 4 // Atmospheric Pressure +#define LIGHT_T 5 // Light (lux) +#define SOIL_T 6 // Soil Moisture +#define SOIL2_T 7 // Soil Moisture #2 +#define SOILR_T 8 // Soil Resistance +#define SOILR2_T 9 // Soil Resistance #2 +#define OXYGEN_T 10 // Oxygen +#define CO2_T 11 // Carbon Dioxide +#define WINDSPD_T 12 // Wind Speed +#define WINDHDG_T 13 // Wind Direction +#define RAINFALL_T 14 // Rainfall +#define MOTION_T 15 // Motion +#define VOLTAGE_T 16 // Voltage +#define VOLTAGE2_T 17 // Voltage #2 +#define CURRENT_T 18 // Current +#define CURRENT2_T 19 // Current #2 +#define IT_T 20 // Iterations +#define LATITUDE_T 21 // GPS Latitude +#define LONGITUDE_T 22 // GPS Longitude +#define ALTITUDE_T 23 // GPS Altitude +#define HDOP_T 24 // GPS HDOP +#define LEVEL_T 25 // Fluid Level +#define UV_T 26 // UV +#define PM1_T 27 // 1 Particles +#define PM2_5_T 28 // 2.5 Particles +#define PM10_T 29 // 10 Particles +#define POWER_T 30 // Power +#define POWER2_T 31 // Power #2 +#define ENERGY_T 32 // Energy +#define ENERGY2_T 33 // Energy #2 +``` +## Under the Hood +``` +typedef struct __attribute__((packed)) DataReading { +  float d; +  uint16_t id; +  uint8_t t; +} DataReading; +``` +Each node in the system sends its data inside of a structure called a DataReading. Its global sensor address is represented by an integer 'id', and each type of reading is represented by a single byte 't'.  If a sensor or gateway needs to send multiple DataReadings, then they are sent in an array. A single DataReading.id may have readings of multiple types ('t') associated with it. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/basic-MQTT.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/basic-MQTT.png new file mode 100644 index 0000000..37a1240 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/basic-MQTT.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/basic-UART.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/basic-UART.png new file mode 100644 index 0000000..40a4e73 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/basic-UART.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/fdrs_logo.svg b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/fdrs_logo.svg new file mode 100644 index 0000000..061828e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/extras/fdrs_logo.svg @@ -0,0 +1,8 @@ + + + + svgOutput + made with tinkercad + + \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/keywords.txt b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/keywords.txt new file mode 100644 index 0000000..55f064f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/keywords.txt @@ -0,0 +1,87 @@ +####################################################################### +# Syntax Coloring Map For Farm-Data-Relay-System +####################################################################### +# +# Format for each line: +# KEYWORD KEYWORD_TOKENTYPE REFERENCE_LINK RSYNTAXTEXTAREA_TOKENTYPE +# (only the first two are used) +# +####################################################################### + + +####################################################################### +# Datatypes and Class names (KEYWORD1) +####################################################################### +DataReading KEYWORD1 +SystemPacket KEYWORD1 +FDRSBase KEYWORD1 +FDRSLoRa KEYWORD1 +FDRS_EspNow KEYWORD1 + +####################################################################### +# Methods and Functions (KEYWORD2) +####################################################################### +FDRS_DBG KEYWORD2 +begin KEYWORD2 +beginFDRS KEYWORD2 +begin_espnow KEYWORD2 +begin_FS KEYWORD2 +begin_lora KEYWORD2 +begin_SD KEYWORD2 +bufferESPNOW KEYWORD2 +bufferLoRa KEYWORD2 +bufferMQTT KEYWORD2 +bufferSerial KEYWORD2 +crc16_update KEYWORD2 +getLoRa KEYWORD2 +getSerial KEYWORD2 +handleCommands KEYWORD2 +load KEYWORD2 +loadFDRS KEYWORD2 +OnDataSent KEYWORD2 +OnDataRecv KEYWORD2 +pingFDRS KEYWORD2 +printLoraPacket KEYWORD2 +reconnect KEYWORD2 +releaseLogBuffer KEYWORD2 +releaseMQTT KEYWORD2 +sendESPNOW KEYWORD2 +sendFDRS KEYWORD2 +sendLog KEYWORD2 +sendMQTT KEYWORD2 +sendSerial KEYWORD2 +sleep KEYWORD2 +sleepFDRS KEYWORD2 +transmitLoRa KEYWORD2 + +####################################################################### +# structures (KEYWORD3) +####################################################################### + + +####################################################################### +# Literals (LITERAL1) +####################################################################### +DEBUG_CONFIG LITERAL1 +DEEP_SLEEP LITERAL1 +FDRS_BAND LITERAL1 +FDRS_DEBUG LITERAL1 +FDRS_GLOBALS LITERAL1 +FDRS_SF LITERAL1 +GTWY_MAC LITERAL1 +GTWY_MAC LITERAL1 +LORA_DIO0 LITERAL1 +LORA_RST LITERAL1 +LORA_SS LITERAL1 +MAC_PREFIX LITERAL1 +POWER_CTRL LITERAL1 +READING_ID LITERAL1 +SPI_MISO LITERAL1 +SPI_MOSI LITERAL1 +SPI_SCK LITERAL1 +UNIT_MAC LITERAL1 +USE_LORA LITERAL1 + +####################################################################### +# ? (LITERAL2) +####################################################################### diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/library.properties b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/library.properties new file mode 100644 index 0000000..f25bfe4 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/library.properties @@ -0,0 +1,10 @@ +name=Farm-Data-Relay-System +version=2.0.1 +author=Timm Bogner +maintainer=Timm Bogner +sentence=Farm-Data-Relay-System - a infrastructure and cloud-less network +paragraph=FDRS is a highly configurable cloud-agnostic network utilizing LoRa and ESPNow. Easily define nodes as sensors or routers and easily configure the way different protocols are forwarded. +category=Communication +url=https://github.com/timmbogner/Farm-Data-Relay-System +architectures=* +depends=ArduinoJson diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/CMakeLists.txt b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/CMakeLists.txt new file mode 100644 index 0000000..705d9a0 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_ADD_INCLUDEDIRS src) +set(COMPONENT_PRIV_REQUIRES arduino-esp32) +set(COMPONENT_SRCDIRS src) +register_component() diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/CONTRIBUTING.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/CONTRIBUTING.md new file mode 100644 index 0000000..6ed01ad --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/CONTRIBUTING.md @@ -0,0 +1,70 @@ +# Contributing to ThingPulse OLED SSD1306 + +:+1::tada: First off, thanks for taking the time to contribute! :tada::+1: + +The following is a set of guidelines for contributing to the ThingPulse OLED SSD1306 library on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. + +It is appreciated if you raise an issue _before_ you start changing the code, discussing the proposed change; emphasizing that you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. + +### Table Of Contents +* [General remarks](#general-remarks) +* [Writing Documentation](#writing-documentation) +* [Working with Git and GitHub](#working-with-git-and-github) + * [General flow](#general-flow) + * [Keeping your fork in sync](#keeping-your-fork-in-sync) + * [Commit messages](#commit-messages) + +## General remarks +We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of code. +If you made something really cool but won't spend the time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issue we'll take a look at it anyway. + +## Writing Documentation +ThingPulse maintains documentation for its products at [https://github.com/thingpulse/docs/](https://github.com/thingpulse/docs/). If you contribute features for this project that require altering the respective product guide then we ask you to prepare a pull request with the necessary documentation changes as well. + +## Working with Git and GitHub + +Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) your feature branch onto `master` to pull updates and verify your local changes against them before placing the pull request. + +### General flow +1. [Fork](https://help.github.com/articles/fork-a-repo) this repository on GitHub. +1. [Create a branch](https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/#creating-a-branch) in your fork on GitHub **based on the `master` branch**. +1. Clone the fork on your machine with `git clone https://github.com//.git` +1. `cd ` then run `git remote add upstream https://github.com/ThingPulse/esp8266-oled-ssd1306` +1. `git checkout ` +1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'` +1. When you're done bring your fork up-to-date with the upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `master` running `git rebase master`. +1. `git push` +1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. + +This is just one way of doing things. If you're proficient in Git matters you're free to choose your own. If you want to read more then the [GitHub chapter in the Git book](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project#The-GitHub-Flow) is a way to start. [GitHub's own documentation](https://help.github.com/categories/collaborating/) contains a wealth of information as well. + +### Keeping your fork in sync +You need to sync your fork with the upstream repository from time to time, latest before you rebase (see flow above). + +1. `git fetch upstream` +1. `git checkout master` +1. `git merge upstream/master` + +### Commit messages + +From: [http://git-scm.com/book/ch5-2.html](http://git-scm.com/book/ch5-2.html) +

+Short (50 chars or less) summary of changes
+
+More detailed explanatory text, if necessary.  Wrap it to about 72
+characters or so.  In some contexts, the first line is treated as the
+subject of an email and the rest of the text as the body.  The blank
+line separating the summary from the body is critical (unless you omit
+the body entirely); tools like rebase can get confused if you run the
+two together.
+
+Further paragraphs come after blank lines.
+
+- Bullet points are okay, too
+- Typically a hyphen or asterisk is used for the bullet, preceded by a
+   single space, with blank lines in between, but conventions vary here
+
+ +Don't forget to [reference affected issues](https://help.github.com/articles/closing-issues-via-commit-messages/) in the commit message to have them closed automatically on GitHub. + +[Amend](https://help.github.com/articles/changing-a-commit-message/) your commit messages if necessary to make sure what the world sees on GitHub is as expressive and meaningful as possible. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/README.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/README.md new file mode 100644 index 0000000..98ad975 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/README.md @@ -0,0 +1,441 @@ +[![Build Status](https://github.com/ThingPulse/esp8266-oled-ssd1306/actions/workflows/main.yml/badge.svg)](https://github.com/ThingPulse/esp8266-oled-ssd1306/actions) + +# ThingPulse OLED SSD1306 (ESP8266/ESP32/Mbed-OS) + +This is a driver for SSD1306 128x64, 128x32, 64x48 and 64x32 OLED displays running on the Arduino/ESP8266 & ESP32 and mbed-os platforms. +Can be used with either the I2C or SPI version of the display. + +This library drives the OLED display included in the [ThingPulse IoT starter kit](https://thingpulse.com/product/esp8266-iot-electronics-starter-kit-weatherstation-planespotter-worldclock/) aka classic kit aka weather station kit. + +[![ThingPulse ESP8266 WeatherStation Classic Kit](https://github.com/ThingPulse/esp8266-weather-station/blob/master/resources/ThingPulse-ESP8266-Weather-Station.jpeg?raw=true)](https://thingpulse.com/product/esp8266-iot-electronics-starter-kit-weatherstation-planespotter-worldclock/) + +You can either download this library as a zip file and unpack it to your Arduino/libraries folder or find it in the Arduino library manager under "ESP8266 and ESP32 Oled Driver for SSD1306 display". For mbed-os a copy of the files are available as an mbed-os library. + +It is also available as a [PlatformIO library](https://platformio.org/lib/show/2978/ESP8266%20and%20ESP32%20OLED%20driver%20for%20SSD1306%20displays/examples). Just execute the following command: +``` +platformio lib install 2978 +``` + +## Service level promise + +
+This is a ThingPulse prime project. See our open-source commitment declaration for what this means.
+ +## Credits + +This library has initially been written by [Daniel Eichhorn](https://github.com/squix78). Many thanks go to [Fabrice Weinberg](https://github.com/FWeinb) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs. Mbed-OS support and other improvements were contributed by [Helmut Tschemernjak](https://github.com/helmut64). + +The init sequence for the SSD1306 was inspired by Adafruit's library for the same display. + +## mbed-os +This library has been adopted to support the ARM mbed-os environment. A copy of this library is available in mbed-os under the name OLED_SSD1306 by Helmut Tschemernjak. An alternate installation option is to copy the following files into your mbed-os project: OLEDDisplay.cpp OLEDDisplay.h OLEDDisplayFonts.h OLEDDisplayUi.cpp OLEDDisplayUi.h SSD1306I2C.h + +## Usage + +Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the [ESP8266 Weather Station](https://github.com/ThingPulse/esp8266-weather-station) library which uses the OLED library to display beautiful weather information. + +## Upgrade + +The API changed a lot with the 3.0 release. If you were using this library with older versions please have a look at the [Upgrade Guide](UPGRADE-3.0.md). + +Going from 3.x version to 4.0 a lot of internals changed and compatibility for more displays was added. Please read the [Upgrade Guide](UPGRADE-4.0.md). + +## Features + +* Draw pixels at given coordinates +* Draw lines from given coordinates to given coordinates +* Draw or fill a rectangle with given dimensions +* Draw Text at given coordinates: + * Define Alignment: Left, Right and Center + * Set the Fontface you want to use (see section Fonts below) + * Limit the width of the text by an amount of pixels. Before this widths will be reached, the renderer will wrap the text to a new line if possible +* Display content in automatically side scrolling carousel + * Define transition cycles + * Define how long one frame will be displayed + * Draw the different frames in callback methods + * One indicator per frame will be automatically displayed. The active frame will be displayed from inactive once + +## Fonts + +Fonts are defined in a proprietary but open format. You can create new font files by choosing from a given list +of open sourced Fonts from this web app: http://oleddisplay.squix.ch +Choose the font family, style and size, check the preview image and if you like what you see click the "Create" button. This will create the font array in a text area form where you can copy and paste it into a new or existing header file. + + +![FontTool](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/FontTool.png) + +## Hardware Abstraction + +The library supports different protocols to access the OLED display. Currently there is support for I2C using the built in Wire.h library, I2C by using the much faster [BRZO I2C library](https://github.com/pasko-zh/brzo_i2c) written in assembler and it also supports displays which come with the SPI interface. + +### I2C with Wire.h + +```C++ +#include +#include "SSD1306Wire.h" + +// for 128x64 displays: +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +// for 128x32 displays: +// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, GEOMETRY_128_32 (or 128_64) +// for using 2nd Hardware I2C (if available) +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_TWO); //default value is I2C_ONE if not mentioned +// By default SD1306Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency +``` + +for a SH1106: +```C++ +#include +#include "SH1106Wire.h" + +SH1106Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +// By default SH1106Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value +// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz +// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency +``` + +### I2C with brzo_i2c + +```C++ +#include +#include "SSD1306Brzo.h" + +SSD1306Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +``` +or for the SH1106: +```C++ +#include +#include "SH1106Brzo.h" + +SH1106Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +``` + +### SPI + +```C++ +#include +#include "SSD1306Spi.h" + +SSD1306Spi display(D0, D2, D8); // RES, DC, CS +``` +or for the SH1106: +```C++ +#include +#include "SH1106Spi.h" + +SH1106Spi display(D0, D2); // RES, DC +``` + +## API + +### Display Control + +```C++ +// Initialize the display +void init(); + +// Free the memory used by the display +void end(); + +// Cycle through the initialization +void resetDisplay(void); + +// Connect again to the display through I2C +void reconnect(void); + +// Turn the display on +void displayOn(void); + +// Turn the display offs +void displayOff(void); + +// Clear the local pixel buffer +void clear(void); + +// Write the buffer to the display memory +void display(void); + +// Inverted display mode +void invertDisplay(void); + +// Normal display mode +void normalDisplay(void); + +// Set display contrast +// really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 +// normal brightness & contrast: contrast = 100 +void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); + +// Convenience method to access +void setBrightness(uint8_t); + +// Turn the display upside down +void flipScreenVertically(); + +// Draw the screen mirrored +void mirrorScreen(); +``` + +## Pixel drawing + +```C++ + +/* Drawing functions */ +// Sets the color of all pixel operations +// color : BLACK, WHITE, INVERSE +void setColor(OLEDDISPLAY_COLOR color); + +// Draw a pixel at given position +void setPixel(int16_t x, int16_t y); + +// Draw a line from position 0 to position 1 +void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + +// Draw the border of a rectangle at the given location +void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Fill the rectangle +void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Draw the border of a circle +void drawCircle(int16_t x, int16_t y, int16_t radius); + +// Fill circle +void fillCircle(int16_t x, int16_t y, int16_t radius); + +// Draw an empty triangle i.e. only the outline +void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2); + +// Draw a solid triangle i.e. filled +void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2); + +// Draw a line horizontally +void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + +// Draw a lin vertically +void drawVerticalLine(int16_t x, int16_t y, int16_t length); + +// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is +// a unsigned byte value between 0 and 100 +void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + +// Draw a bitmap in the internal image format +void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); + +// Draw a XBM +void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm); +``` + +## Text operations + +``` C++ +// Draws a string at the given location, returns how many chars have been written +uint16_t drawString(int16_t x, int16_t y, const String &text); + +// Draws a String with a maximum width at the given location. +// If the given String is wider than the specified width +// The text will be wrapped to the next line at a space or dash +// returns 0 if everything fits on the screen or the numbers of characters in the +// first line if not +uint16_t drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, const String &text); + +// Returns the width of the const char* with the current +// font settings +uint16_t getStringWidth(const char* text, uint16_t length, bool utf8 = false); + +// Convencience method for the const char version +uint16_t getStringWidth(const String &text); + +// Specifies relative to which anchor point +// the text is rendered. Available constants: +// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH +void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + +// Sets the current font. Available default fonts +// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 +// Or create one with the font tool at http://oleddisplay.squix.ch +void setFont(const uint8_t* fontData); +``` + +## Ui Library (OLEDDisplayUi) + +The Ui Library is used to provide a basic set of user interface elements called `Frames` and `Overlays`. A `Frame` is used to provide +information to the user. The default behaviour is to display a `Frame` for a defined time and than move to the next `Frame`. The library also +provides an `Indicator` element that will be updated accordingly. An `Overlay` on the other hand is a piece of information (e.g. a clock) that +is always displayed at the same position. + +```C++ +/** + * Initialise the display + */ +void init(); + +/** + * Configure the internal used target FPS + */ +void setTargetFPS(uint8_t fps); + +/** + * Enable automatic transition to next frame after the some time can be configured with + * `setTimePerFrame` and `setTimePerTransition`. + */ +void enableAutoTransition(); + +/** + * Disable automatic transition to next frame. + */ +void disableAutoTransition(); + +/** + * Set the direction if the automatic transitioning + */ +void setAutoTransitionForwards(); +void setAutoTransitionBackwards(); + +/** + * Set the approx. time a frame is displayed + */ +void setTimePerFrame(uint16_t time); + +/** + * Set the approx. time a transition will take + */ +void setTimePerTransition(uint16_t time); + +/** + * Draw the indicator. + * This is the default state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ +void enableIndicator(); + +/** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ +void disableIndicator(); + +/** + * Enable drawing of all indicators. + */ +void enableAllIndicators(); + +/** + * Disable drawing of all indicators. + */ +void disableAllIndicators(); + +/** + * Set the position of the indicator bar. + */ +void setIndicatorPosition(IndicatorPosition pos); + +/** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ +void setIndicatorDirection(IndicatorDirection dir); + +/** + * Set the symbol to indicate an active frame in the indicator bar. + */ +void setActiveSymbol(const uint8_t* symbol); + +/** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ +void setInactiveSymbol(const uint8_t* symbol); + +/** + * Configure what animation is used to transition from one frame to another + */ +void setFrameAnimation(AnimationDirection dir); + +/** + * Add frame drawing functions + */ +void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + +/** + * Add overlays drawing functions that are draw independent of the Frames + */ +void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + +/** + * Set the function that will draw each step + * in the loading animation + */ +void setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction); + +/** + * Run the loading process + */ +void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + +// Manual control +void nextFrame(); +void previousFrame(); + +/** + * Switch without transition to frame `frame`. + */ +void switchToFrame(uint8_t frame); + +/** + * Transition to frame `frame`. When the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ +void transitionToFrame(uint8_t frame); + +// State Info +OLEDDisplayUiState* getUiState(); + +// This needs to be called in the main loop +// the returned value is the remaining time (in ms) +// you have to draw after drawing to keep the frame budget. +int8_t update(); +``` + +## Example: SSD1306Demo + +### Frame 1 +![DemoFrame1](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame1.jpg) + +This frame shows three things: + * How to draw an XMB image + * How to draw static text which is not moved by the frame transition + * The active/inactive frame indicators + +### Frame 2 +![DemoFrame2](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame2.jpg) + +Currently there are one fontface with three sizes included in the library: Arial 10, 16 and 24. Once the converter is published you will be able to convert any ttf font into the used format. + +### Frame 3 + +![DemoFrame3](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame3.jpg) + +This frame demonstrates the text alignment. The coordinates in the frame show relative to which position the texts have been rendered. + +### Frame 4 + +![DemoFrame4](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame4.jpg) + +This shows how to use define a maximum width after which the driver automatically wraps a word to the next line. This comes in very handy if you have longer texts to display. + +### SPI version + +![SPIVersion](https://github.com/neptune2/esp8266-oled-ssd1306/raw/master/resources/SPI_version.jpg) + +This shows the code working on the SPI version of the display. See demo code for ESP8266 pins used. + +## Selection of projects using this library + + * [QRCode ESP8266](https://github.com/anunpanya/ESP8266_QRcode) (by @anunpanya) + * [Scan I2C](https://github.com/hallard/Scan-I2C-WiFi) (by @hallard) + * [ThingPulse Weather Station](https://github.com/ThingPulse/esp8266-weather-station) + * [Meshtastic](https://www.meshtastic.org/) - an open source GPS communicator mesh radio + * Yours? diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/README_GEOMETRY_64_48.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/README_GEOMETRY_64_48.md new file mode 100644 index 0000000..9b9bf91 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/README_GEOMETRY_64_48.md @@ -0,0 +1,20 @@ +# GEOMETRY_64_48 + +The 64x48 geometry setting are working with the `Wire.h` and `brzo_i2c` libraries. + +I've tested it successfully with a WEMOS D1 mini Lite and a WEMOS OLED shield + +Initialization code: + +- Wire +``` +#include +#include +SSD1306Wire display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED shield +``` + +- BRZO i2c +``` +#include +SSD1306Brzo display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED Shield +``` diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/UPGRADE-3.0.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/UPGRADE-3.0.md new file mode 100644 index 0000000..e7a315b --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/UPGRADE-3.0.md @@ -0,0 +1,125 @@ +# Upgrade from 2.0 to 3.0 + +While developing version 3.0 we made some breaking changes to the public +API of this library. This document will help you update your code to work with +version 3.0 + +## Font Definitions + +To get better performance and a smaller font definition format, we change the memory +layout of the font definition format. If you are using custom fonts not included in +this library we updated the font generator [here](http://oleddisplay.squix.ch/#/home). +Please update your fonts to be working with 3.0 by selecting the respective version in the dropdown. + + +## Architectural Changes + +To become a more versatile library for the SSD1306 chipset we abstracted the +hardware connection into subclasses of the base display class now called `OLEDDisplay`. +This library is currently shipping with three implementations: + + * `SSD1306Wire` implementing the I2C protocol using the Wire Library. + * `SSD1306Brzo` implementing the I2C protocol using the faster [`brzo_i2c`](https://github.com/pasko-zh/brzo_i2c) library. + * `SSD1306Spi` implementing the SPI protocol. + +To keep backwards compatiblity with the old API `SSD1306` is an alias of `SSD1306Wire`. +If you are not using the UI components you don't have to change anything to keep your code working. + +## Name Changes + +[Naming things is hard](http://martinfowler.com/bliki/TwoHardThings.html), to better reflect our intention with this library +we changed the name of the base class to `OLEDDisplay` and the UI library accordingly to `OLEDDisplayUi`. +As a consequence the type definitions of all frame and overlay related functions changed. +This means that you have to update all your frame drawing callbacks from: + +```c +bool frame1(SSD1306 *display, SSD1306UiState* state, int x, int y); +``` + +too + +```c +void frame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +``` + +And your overlay drawing functions from: + +```c +bool overlay1(SSD1306 *display, SSD1306UiState* state); +``` + +too + +```c +void overlay1(OLEDDisplay *display, OLEDDisplayUiState* state); +``` + +## New Features + +### Loading Animation + +While using this library ourself we noticed a pattern emerging. We want to drawing +a loading progress while connecting to WiFi and updating weather data etc. + +The simplest thing was to add the function `drawProgressBar(x, y, width, height, progress)` +,where `progress` is between `0` and `100`, right to the `OLEDDisplay` class. + +But we didn't stop there. We added a new feature to the `OLEDDisplayUi` called `LoadingStages`. +You can define your loading process like this: + +```c++ +LoadingStage loadingStages[] = { + { + .process = "Connect to WiFi", + .callback = []() { + // Connect to WiFi + } + }, + { + .process = "Get time from NTP", + .callback = []() { + // Get current time via NTP + } + } + // more steps +}; + +int LOADING_STAGES_COUNT = sizeof(loadingStages) / sizeof(LoadingStage); +``` + +After defining your array of `LoadingStages` you can then run the loading process by using +`ui.runLoadingProcess(loadingStages, LOADING_STAGES_COUNT)`. This will give you a +nice little loading animation you can see in the beginning of [this](https://vimeo.com/168362918) +video. + +To further customize this you are free to define your own `LoadingDrawFunction` like this: + +```c +void myLoadingDraw(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + // stage->process contains the text of the current progress e.q. "Connect to WiFi" + display->drawString(64, 18, stage->process); + // you could just print the current process without the progress bar + display->drawString(64, 28, progress); +} +``` + +After defining a function like that, you can pass it to the Ui library by use +`ui.setLoadingDrawFunction(myLoadingDraw)`. + + +### Text Logging + +It is always useful to display some text on the display without worrying to much +where it goes and managing it. In 3.0 we made the `OLEDDisplay` class implement +[`Print`](https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h) +so you can use it like you would use `Serial`. We calls this feature `LogBuffer` +and the only thing you have to do is to define how many lines you want to display +and how many characters there are on average on each. This is done by calling +`setLogBuffer(lines, chars);`. If there is not enough memory the function will +return false. + +After that you can draw the `LogBuffer` anywhere you want by calling `drawLogBuffer(x, y)`. +(Note: You have to call `display()` to update the screen) +We made a [video](https://www.youtube.com/watch?v=8Fiss77A3TE) showing this feature in action. diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/UPGRADE-4.0.md b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/UPGRADE-4.0.md new file mode 100644 index 0000000..4b17693 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/UPGRADE-4.0.md @@ -0,0 +1,27 @@ +# Upgrade from 3.x to 4.0 + +There are changes that breaks compatibility with older versions. + +1. You'll have to change data type for all your binary resources such as images and fonts from + + ```c + const char MySymbol[] PROGMEM = { + ``` + + to + + ```c + const uint8_t MySymbol[] PROGMEM = { + ``` + +1. Arguments of `setContrast` from `char` to `uint8_t` + + ```c++ + void OLEDDisplay::setContrast(char contrast, char precharge, char comdetect); + ``` + + to + + ```c++ + void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect); + ``` diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/component.mk b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/component.mk new file mode 100644 index 0000000..23a01a0 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src +CXXFLAGS += -Wno-ignored-qualifiers diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino new file mode 100644 index 0000000..b5eb3dc --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino @@ -0,0 +1,212 @@ +/** + The MIT License (MIT) + + Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + ThingPulse invests considerable time and money to develop these open source libraries. + Please support us by buying our products (and not the clones) from + https://thingpulse.com + +*/ + +// Install https://github.com/PaulStoffregen/Time +#include + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h e.g. https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h +// SH1106Wire display(0x3c, SDA, SCL); + +OLEDDisplayUi ui ( &display ); + +int screenW = 128; +int screenH = 64; +int clockCenterX = screenW / 2; +int clockCenterY = ((screenH - 16) / 2) + 16; // top yellow part is 16 px height +int clockRadius = 23; + +// utility function for digital clock display: prints leading 0 +String twoDigits(int digits) { + if (digits < 10) { + String i = '0' + String(digits); + return i; + } + else { + return String(digits); + } +} + +void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + +} + +void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // ui.disableIndicator(); + + // Draw the clock face + // display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius); + display->drawCircle(clockCenterX + x, clockCenterY + y, 2); + // + //hour ticks + for ( int z = 0; z < 360; z = z + 30 ) { + //Begin at 0° and stop at 360° + float angle = z ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) ); + int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) ); + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y); + } + + // display second hand + float angle = second() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display minute hand + angle = minute() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display hour hand + angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); +} + +void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + String timenow = String(hour()) + ":" + twoDigits(minute()) + ":" + twoDigits(second()); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_24); + display->drawString(clockCenterX + x , clockCenterY + y, timenow ); +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { analogClockFrame, digitalClockFrame }; + +// how many frames are there? +int frameCount = 2; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { clockOverlay }; +int overlaysCount = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(TOP); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + + unsigned long secsSinceStart = millis(); + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + unsigned long epoch = secsSinceStart - seventyYears * SECS_PER_HOUR; + setTime(epoch); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + } +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ClockDemo/images.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ClockDemo/images.h new file mode 100644 index 0000000..0943f57 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ClockDemo/images.h @@ -0,0 +1,21 @@ +const uint8_t activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const uint8_t inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino new file mode 100644 index 0000000..d684e7d --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino @@ -0,0 +1,233 @@ +/** + The MIT License (MIT) + + Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + Copyright (c) 2018 by Fabrice Weinberg + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + ThingPulse invests considerable time and money to develop these open source libraries. + Please support us by buying our products (and not the clones) from + https://thingpulse.com + +*/ + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h e.g. https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h +// SH1106Wire display(0x3c, SDA, SCL); + +// Adapted from Adafruit_SSD1306 +void drawLines() { + for (int16_t i = 0; i < display.getWidth(); i += 4) { + display.drawLine(0, 0, i, display.getHeight() - 1); + display.display(); + delay(10); + } + for (int16_t i = 0; i < display.getHeight(); i += 4) { + display.drawLine(0, 0, display.getWidth() - 1, i); + display.display(); + delay(10); + } + delay(250); + + display.clear(); + for (int16_t i = 0; i < display.getWidth(); i += 4) { + display.drawLine(0, display.getHeight() - 1, i, 0); + display.display(); + delay(10); + } + for (int16_t i = display.getHeight() - 1; i >= 0; i -= 4) { + display.drawLine(0, display.getHeight() - 1, display.getWidth() - 1, i); + display.display(); + delay(10); + } + delay(250); + + display.clear(); + for (int16_t i = display.getWidth() - 1; i >= 0; i -= 4) { + display.drawLine(display.getWidth() - 1, display.getHeight() - 1, i, 0); + display.display(); + delay(10); + } + for (int16_t i = display.getHeight() - 1; i >= 0; i -= 4) { + display.drawLine(display.getWidth() - 1, display.getHeight() - 1, 0, i); + display.display(); + delay(10); + } + delay(250); + display.clear(); + for (int16_t i = 0; i < display.getHeight(); i += 4) { + display.drawLine(display.getWidth() - 1, 0, 0, i); + display.display(); + delay(10); + } + for (int16_t i = 0; i < display.getWidth(); i += 4) { + display.drawLine(display.getWidth() - 1, 0, i, display.getHeight() - 1); + display.display(); + delay(10); + } + delay(250); +} + +// Adapted from Adafruit_SSD1306 +void drawRect(void) { + for (int16_t i = 0; i < display.getHeight() / 2; i += 2) { + display.drawRect(i, i, display.getWidth() - 2 * i, display.getHeight() - 2 * i); + display.display(); + delay(10); + } +} + +// Adapted from Adafruit_SSD1306 +void fillRect(void) { + uint8_t color = 1; + for (int16_t i = 0; i < display.getHeight() / 2; i += 3) { + display.setColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors + display.fillRect(i, i, display.getWidth() - i * 2, display.getHeight() - i * 2); + display.display(); + delay(10); + color++; + } + // Reset back to WHITE + display.setColor(WHITE); +} + +// Adapted from Adafruit_SSD1306 +void drawCircle(void) { + for (int16_t i = 0; i < display.getHeight(); i += 2) { + display.drawCircle(display.getWidth() / 2, display.getHeight() / 2, i); + display.display(); + delay(10); + } + delay(1000); + display.clear(); + + // This will draw the part of the circel in quadrant 1 + // Quadrants are numberd like this: + // 0010 | 0001 + // ------|----- + // 0100 | 1000 + // + display.drawCircleQuads(display.getWidth() / 2, display.getHeight() / 2, display.getHeight() / 4, 0b00000001); + display.display(); + delay(200); + display.drawCircleQuads(display.getWidth() / 2, display.getHeight() / 2, display.getHeight() / 4, 0b00000011); + display.display(); + delay(200); + display.drawCircleQuads(display.getWidth() / 2, display.getHeight() / 2, display.getHeight() / 4, 0b00000111); + display.display(); + delay(200); + display.drawCircleQuads(display.getWidth() / 2, display.getHeight() / 2, display.getHeight() / 4, 0b00001111); + display.display(); +} + +void printBuffer(void) { + // Initialize the log buffer + // allocate memory to store 8 lines of text and 30 chars per line. + display.setLogBuffer(5, 30); + + // Some test data + const char* test[] = { + "Hello", + "World" , + "----", + "Show off", + "how", + "the log buffer", + "is", + "working.", + "Even", + "scrolling is", + "working" + }; + + for (uint8_t i = 0; i < 11; i++) { + display.clear(); + // Print to the screen + display.println(test[i]); + // Draw it to the internal screen buffer + display.drawLogBuffer(0, 0); + // Display it on the screen + display.display(); + delay(500); + } +} + +void setup() { + display.init(); + + // display.flipScreenVertically(); + + display.setContrast(255); + + drawLines(); + delay(1000); + display.clear(); + + drawRect(); + delay(1000); + display.clear(); + + fillRect(); + delay(1000); + display.clear(); + + drawCircle(); + delay(1000); + display.clear(); + + printBuffer(); + delay(1000); + display.clear(); +} + +void loop() { } diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306OTADemo/SSD1306OTADemo.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306OTADemo/SSD1306OTADemo.ino new file mode 100644 index 0000000..2f51f2e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306OTADemo/SSD1306OTADemo.ino @@ -0,0 +1,127 @@ +/** + The MIT License (MIT) + + Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + Copyright (c) 2018 by Fabrice Weinberg + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + ThingPulse invests considerable time and money to develop these open source libraries. + Please support us by buying our products (and not the clones) from + https://thingpulse.com + +*/ + +#if defined(ESP8266) +#include +#include +#elif defined(ESP32) +#include +#include +#include +#endif + +#include + +const char *ssid = "[Your SSID]"; +const char *password = "[Your Password]"; + + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h e.g. https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h +// SH1106Wire display(0x3c, SDA, SCL); + + +void setup() { + WiFi.begin ( ssid, password ); + + // Wait for connection + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 10 ); + } + + display.init(); + display.flipScreenVertically(); + display.setContrast(255); + + ArduinoOTA.begin(); + ArduinoOTA.onStart([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(display.getWidth() / 2, display.getHeight() / 2 - 10, "OTA Update"); + display.display(); + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) ); + display.display(); + }); + + ArduinoOTA.onEnd([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(display.getWidth() / 2, display.getHeight() / 2, "Restart"); + display.display(); + }); + + // Align text vertical/horizontal center + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.setFont(ArialMT_Plain_10); + display.drawString(display.getWidth() / 2, display.getHeight() / 2, "Ready for OTA:\n" + WiFi.localIP().toString()); + display.display(); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ScrollVerticalDemo/SSD1306ScrollVerticalDemo.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ScrollVerticalDemo/SSD1306ScrollVerticalDemo.ino new file mode 100644 index 0000000..7ee86e5 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306ScrollVerticalDemo/SSD1306ScrollVerticalDemo.ino @@ -0,0 +1,91 @@ +/** + The MIT License (MIT) + + Copyright (c) 2022 by Stefan Seyfried + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106Spi.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h e.g. https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h +// SH1106Wire display(0x3c, SDA, SCL); + +// UTF-8 sprinkled within, because it tests special conditions in the char-counting code +const String loremipsum = "Lorem ipsum dolor sit ämet, " + "consetetur sadipscing elitr, sed diam nonümy eirmöd " + "tempor invidunt ut labore et dolore mägnä aliquyam erat, " + "sed diam voluptua. At vero eos et accusam et justo duo " + "dolores et ea rebum. Stet clita kasd gubergren, no sea " + "takimata sanctus est Lorem ipsum dolor sit amet. " + "äöü-ÄÖÜ/߀é/çØ."; + +void setup() { + display.init(); + display.setContrast(255); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_16); + display.display(); +} + +void loop() { + static uint16_t start_at = 0; + display.clear(); + uint16_t firstline = display.drawStringMaxWidth(0, 0, 128, loremipsum.substring(start_at)); + display.display(); + if (firstline != 0) { + start_at += firstline; + } else { + start_at = 0; + delay(1000); // additional pause before going back to start + } + delay(1000); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino new file mode 100644 index 0000000..e871362 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino @@ -0,0 +1,198 @@ +/** + The MIT License (MIT) + + Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + Copyright (c) 2018 by Fabrice Weinberg + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + ThingPulse invests considerable time and money to develop these open source libraries. + Please support us by buying our products (and not the clones) from + https://thingpulse.com + +*/ + +// Include the correct display library + +// For a connection via I2C using the Arduino Wire include: +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy: #include "SSD1306.h" +// OR #include "SH1106Wire.h" // legacy: #include "SH1106.h" + +// For a connection via I2C using brzo_i2c (must be installed) include: +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// OR #include "SH1106Brzo.h" + +// For a connection via SPI include: +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// OR #include "SH1106SPi.h" + + +// Optionally include custom images +#include "images.h" + + +// Initialize the OLED display using Arduino Wire: +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h e.g. https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h +// SSD1306Wire display(0x3c, D3, D5); // ADDRESS, SDA, SCL - If not, they can be specified manually. +// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, OLEDDISPLAY_GEOMETRY - Extra param required for 128x32 displays. +// SH1106Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL + +// Initialize the OLED display using brzo_i2c: +// SSD1306Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL +// or +// SH1106Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL + +// Initialize the OLED display using SPI: +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); // RES, DC, CS +// or +// SH1106Spi display(D0, D2); // RES, DC + + +#define DEMO_DURATION 3000 +typedef void (*Demo)(void); + +int demoMode = 0; +int counter = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + + // Initialising the UI will init the display too. + display.init(); + + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + +} + +void drawFontFaceDemo() { + // Font Demo1 + // create more fonts at http://oleddisplay.squix.ch/ + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + display.drawString(0, 0, "Hello world"); + display.setFont(ArialMT_Plain_16); + display.drawString(0, 10, "Hello world"); + display.setFont(ArialMT_Plain_24); + display.drawString(0, 26, "Hello world"); +} + +void drawTextFlowDemo() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawStringMaxWidth(0, 0, 128, + "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." ); +} + +void drawTextAlignmentDemo() { + // Text alignment demo + display.setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 10, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 22, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 33, "Right aligned (128,33)"); +} + +void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + display.setPixel(i, i); + display.setPixel(10 - i, i); + } + display.drawRect(12, 12, 20, 20); + + // Fill the rectangle + display.fillRect(14, 14, 17, 17); + + // Draw a line horizontally + display.drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + display.drawVerticalLine(40, 0, 20); +} + +void drawCircleDemo() { + for (int i = 1; i < 8; i++) { + display.setColor(WHITE); + display.drawCircle(32, 32, i * 3); + if (i % 2 == 0) { + display.setColor(BLACK); + } + display.fillCircle(96, 32, 32 - i * 3); + } +} + +void drawProgressBarDemo() { + int progress = (counter / 5) % 100; + // draw the progress bar + display.drawProgressBar(0, 32, 120, 10, progress); + + // draw the percentage as String + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 15, String(progress) + "%"); +} + +void drawImageDemo() { + // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html + // on how to create xbm files + display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo}; +int demoLength = (sizeof(demos) / sizeof(Demo)); +long timeSinceLastModeSwitch = 0; + +void loop() { + // clear the display + display.clear(); + // draw the current demo method + demos[demoMode](); + + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 54, String(millis())); + // write the buffer to the display + display.display(); + + if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) { + demoMode = (demoMode + 1) % demoLength; + timeSinceLastModeSwitch = millis(); + } + counter++; + delay(10); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306SimpleDemo/images.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306SimpleDemo/images.h new file mode 100644 index 0000000..5041799 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306SimpleDemo/images.h @@ -0,0 +1,28 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino new file mode 100644 index 0000000..27265e6 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino @@ -0,0 +1,75 @@ +/** + The MIT License (MIT) + + Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + ThingPulse invests considerable time and money to develop these open source libraries. + Please support us by buying our products (and not the clones) from + https://thingpulse.com + +*/ + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +#include "images.h" + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, 0, 14); +SSD1306Wire display2(0x3c, 5, 4); + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + + // Initialising the UI will init the display too. + display.init(); + display2.init(); + + // This will make sure that multiple instances of a display driver + // running on different ports will work together transparently + display.setI2cAutoInit(true); + display2.setI2cAutoInit(true); + + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + + display2.flipScreenVertically(); + display2.setFont(ArialMT_Plain_10); + display2.setTextAlignment(TEXT_ALIGN_LEFT); + +} + +void loop() { + display.clear(); + display.drawString(0, 0, "Hello world: " + String(millis())); + display.display(); + + display2.clear(); + display2.drawString(0, 0, "Hello world: " + String(millis())); + display2.display(); + + delay(10); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306TwoScreenDemo/images.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306TwoScreenDemo/images.h new file mode 100644 index 0000000..5041799 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306TwoScreenDemo/images.h @@ -0,0 +1,28 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306UiDemo/SSD1306UiDemo.ino b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306UiDemo/SSD1306UiDemo.ino new file mode 100644 index 0000000..a7bd6cd --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306UiDemo/SSD1306UiDemo.ino @@ -0,0 +1,194 @@ +/** + The MIT License (MIT) + + Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + Copyright (c) 2018 by Fabrice Weinberg + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + ThingPulse invests considerable time and money to develop these open source libraries. + Please support us by buying our products (and not the clones) from + https://thingpulse.com + +*/ + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images + +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h e.g. https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h +// SH1106Wire display(0x3c, SDA, SCL); + +OLEDDisplayUi ui ( &display ); + +void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->setFont(ArialMT_Plain_10); + display->drawString(128, 0, String(millis())); +} + +void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // draw an xbm image. + // Please note that everything that should be transitioned + // needs to be drawn relative to x and y + + display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file + // Besides the default fonts there will be a program to convert TrueType fonts into this format + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawString(0 + x, 10 + y, "Arial 10"); + + display->setFont(ArialMT_Plain_16); + display->drawString(0 + x, 20 + y, "Arial 16"); + + display->setFont(ArialMT_Plain_24); + display->drawString(0 + x, 34 + y, "Arial 24"); +} + +void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Text alignment demo + display->setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(0 + x, 11 + y, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64 + x, 22 + y, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128 + x, 33 + y, "Right aligned (128,33)"); +} + +void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demo for drawStringMaxWidth: + // with the third parameter you can define the width after which words will be wrapped. + // Currently only spaces and "-" are allowed for wrapping + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore."); +} + +void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 }; + +// how many frames are there? +int frameCount = 5; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { msOverlay }; +int overlaysCount = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(BOTTOM); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + } +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306UiDemo/images.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306UiDemo/images.h new file mode 100644 index 0000000..2489d1e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/examples/SSD1306UiDemo/images.h @@ -0,0 +1,50 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +const uint8_t activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const uint8_t inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/keywords.txt b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/keywords.txt new file mode 100644 index 0000000..db59d6e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/keywords.txt @@ -0,0 +1,100 @@ +####################################### +# Syntax Coloring Map List +####################################### + + +####################################### +# Constants (LITERAL1) +####################################### +INVERSE LITERAL1 + +TEXT_ALIGN_LEFT LITERAL1 +TEXT_ALIGN_RIGHT LITERAL1 +TEXT_ALIGN_CENTER LITERAL1 +TEXT_ALIGN_CENTER_BOTH LITERAL1 + +GEOMETRY_128_64 LITERAL1 +GEOMETRY_128_32 LITERAL1 +GEOMETRY_RAWMODE LITERAL1 + +ArialMT_Plain_10 LITERAL1 +ArialMT_Plain_16 LITERAL1 +ArialMT_Plain_24 LITERAL1 + +SLIDE_UP LITERAL1 +SLIDE_DOWN LITERAL1 +SLIDE_LEFT LITERAL1 +SLIDE_RIGHT LITERAL1 + +TOP LITERAL1 +RIGHT LITERAL1 +BOTTOM LITERAL1 +LEFT LITERAL1 + +LEFT_RIGHT LITERAL1 +RIGHT_LEFT LITERAL1 + +IN_TRANSITION LITERAL1 +FIXED LITERAL1 + + +####################################### +# Datatypes (KEYWORD1) +####################################### +OLEDDisplay KEYWORD1 +OLEDDisplayUi KEYWORD1 + +SH1106Wire KEYWORD1 +SH1106Brzo KEYWORD1 +SH1106Spi KEYWORD1 + +SSD1306Wire KEYWORD1 +SSD1306Brzo KEYWORD1 +SSD1306I2C KEYWORD1 +SSD1306Spi KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +allocateBuffer KEYWORD2 +init KEYWORD2 +resetDisplay KEYWORD2 +setColor KEYWORD2 +getColor KEYWORD2 +setPixel KEYWORD2 +setPixelColor KEYWORD2 +clearPixel KEYWORD2 +drawLine KEYWORD2 +drawRect KEYWORD2 +fillRect KEYWORD2 +drawCircle KEYWORD2 +drawCircleQuads KEYWORD2 +fillCircle KEYWORD2 +fillRing KEYWORD2 +drawHorizontalLine KEYWORD2 +drawVerticalLine KEYWORD2 +drawProgressBar KEYWORD2 +drawFastImage KEYWORD2 +drawXbm KEYWORD2 +drawIco16x16 KEYWORD2 +drawString KEYWORD2 +drawStringMaxWidth KEYWORD2 +getStringWidth KEYWORD2 +setTextAlignment KEYWORD2 +setFont KEYWORD2 +setFontTableLookupFunction KEYWORD2 +displayOn KEYWORD2 +displayOff KEYWORD2 +invertDisplay KEYWORD2 +normalDisplay KEYWORD2 +setContrast KEYWORD2 +setBrightness KEYWORD2 +resetOrientation KEYWORD2 +flipScreenVertically KEYWORD2 +mirrorScreen KEYWORD2 +display KEYWORD2 +setLogBuffer KEYWORD2 +drawLogBuffer KEYWORD2 +getWidth KEYWORD2 +getHeight KEYWORD2 diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/library.json b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/library.json new file mode 100644 index 0000000..18f59fd --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/library.json @@ -0,0 +1,30 @@ +{ + "name": "ESP8266 and ESP32 OLED driver for SSD1306 displays", + "version": "4.3.0", + "keywords": "ssd1306, oled, display, i2c", + "description": "I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS", + "license": "MIT", + "repository": + { + "type": "git", + "url": "https://github.com/ThingPulse/esp8266-oled-ssd1306" + }, + "authors": + [ + { + "name": "Daniel Eichhorn, ThingPulse", + "email": "squix78@gmail.com", + "url": "https://thingpulse.com" + }, + { + "name": "Fabrice Weinberg", + "email": "fabrice@weinberg.me" + } + ], + "frameworks": "arduino", + "platforms": [ + "espressif8266", + "espressif32", + "nordicnrf52" + ] +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/library.properties b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/library.properties new file mode 100644 index 0000000..42fb10a --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/library.properties @@ -0,0 +1,10 @@ +name=ESP8266 and ESP32 OLED driver for SSD1306 displays +version=4.3.0 +author=ThingPulse, Fabrice Weinberg +maintainer=ThingPulse +sentence=I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS +paragraph=The following geometries are currently supported: 128x64, 128x32, 64x48. The init sequence was inspired by Adafruit's library for the same display. +category=Display +url=https://github.com/ThingPulse/esp8266-oled-ssd1306 +architectures=esp8266,esp32 +license=MIT diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/license b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/license new file mode 100644 index 0000000..706c10f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/license @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2016 by Daniel Eichhorn +Copyright (c) 2016 by Fabrice Weinberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +See more at http://blog.squix.ch diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/platformio.ini b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/platformio.ini new file mode 100644 index 0000000..530c046 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/page/projectconf.html + +[env:d1_mini] +platform = espressif8266 +board = d1_mini +framework = arduino +upload_speed = 921600 +board_build.f_cpu = 160000000L +upload_port = /dev/cu.SLAB_USBtoUART +monitor_port = /dev/cu.SLAB_USBtoUART +lib_deps = diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame1.jpg b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame1.jpg new file mode 100644 index 0000000..536b570 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame1.jpg differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame2.jpg b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame2.jpg new file mode 100644 index 0000000..8dccbcd Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame2.jpg differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame3.jpg b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame3.jpg new file mode 100644 index 0000000..49e07ab Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame3.jpg differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame4.jpg b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame4.jpg new file mode 100644 index 0000000..99cbe1b Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/DemoFrame4.jpg differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/FontTool.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/FontTool.png new file mode 100644 index 0000000..c7bb222 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/FontTool.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/SPI_version.jpg b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/SPI_version.jpg new file mode 100644 index 0000000..115c9f3 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/SPI_version.jpg differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/glyphEditor.html b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/glyphEditor.html new file mode 100644 index 0000000..31fbd90 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/glyphEditor.html @@ -0,0 +1,664 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Font array name: +
First char code:
Width:
Height:
+ + + + +
+
+
+
+
+

+ +
+
+ +
+
+
+
+ + + diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/glyphEditor.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/glyphEditor.png new file mode 100644 index 0000000..800efc5 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/glyphEditor.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/xbmPreview.png b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/xbmPreview.png new file mode 100644 index 0000000..70ea3a5 Binary files /dev/null and b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/resources/xbmPreview.png differ diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplay.cpp b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplay.cpp new file mode 100644 index 0000000..5001d55 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplay.cpp @@ -0,0 +1,1216 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + + /* + * TODO Helmut + * - test/finish dislplay.printf() on mbed-os + * - Finish _putc with drawLogBuffer when running display + */ + +#include "OLEDDisplay.h" + +OLEDDisplay::OLEDDisplay() { + + displayWidth = 128; + displayHeight = 64; + displayBufferSize = displayWidth * displayHeight / 8; + color = WHITE; + geometry = GEOMETRY_128_64; + textAlignment = TEXT_ALIGN_LEFT; + fontData = ArialMT_Plain_10; + fontTableLookupFunction = DefaultFontTableLookup; + buffer = NULL; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + buffer_back = NULL; +#endif +} + +OLEDDisplay::~OLEDDisplay() { + end(); +} + +bool OLEDDisplay::allocateBuffer() { + + logBufferSize = 0; + logBufferFilled = 0; + logBufferLine = 0; + logBufferMaxLines = 0; + logBuffer = NULL; + + if (!connect()) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n"); + return false; + } + + if(this->buffer==NULL) { + this->buffer = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + BufferOffset); + this->buffer += BufferOffset; + + if(!this->buffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n"); + return false; + } + } + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if(this->buffer_back==NULL) { + this->buffer_back = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + BufferOffset); + this->buffer_back += BufferOffset; + + if(!this->buffer_back) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n"); + free(this->buffer - BufferOffset); + return false; + } + } + #endif + + return true; +} + +bool OLEDDisplay::init() { + + BufferOffset = getBufferOffset(); + + if(!allocateBuffer()) { + return false; + } + + sendInitCommands(); + resetDisplay(); + + return true; +} + +void OLEDDisplay::end() { + if (this->buffer) { free(this->buffer - BufferOffset); this->buffer = NULL; } + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if (this->buffer_back) { free(this->buffer_back - BufferOffset); this->buffer_back = NULL; } + #endif + if (this->logBuffer != NULL) { free(this->logBuffer); this->logBuffer = NULL; } +} + +void OLEDDisplay::resetDisplay(void) { + clear(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + memset(buffer_back, 1, displayBufferSize); + #endif + display(); +} + +void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) { + this->color = color; +} + +OLEDDISPLAY_COLOR OLEDDisplay::getColor() { + return this->color; +} + +void OLEDDisplay::setPixel(int16_t x, int16_t y) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + +void OLEDDisplay::setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + +void OLEDDisplay::clearPixel(int16_t x, int16_t y) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case BLACK: buffer[x + (y >> 3) * this->width()] |= (1 << (y & 7)); break; + case WHITE: buffer[x + (y >> 3) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y >> 3) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + + +// Bresenham's algorithm - thx wikipedia and Adafruit_GFX +void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + setPixel(y0, x0); + } else { + setPixel(x0, y0); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) { + drawHorizontalLine(x, y, width); + drawVerticalLine(x, y, height); + drawVerticalLine(x + width - 1, y, height); + drawHorizontalLine(x, y + height - 1, width); +} + +void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) { + for (int16_t x = xMove; x < xMove + width; x++) { + drawVerticalLine(x, yMove, height); + } +} + +void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + + setPixel(x0 + x, y0 + y); //For the 8 octants + setPixel(x0 - x, y0 + y); + setPixel(x0 + x, y0 - y); + setPixel(x0 - x, y0 - y); + setPixel(x0 + y, y0 + x); + setPixel(x0 - y, y0 + x); + setPixel(x0 + y, y0 - x); + setPixel(x0 - y, y0 - x); + + } while (x < y); + + setPixel(x0 + radius, y0); + setPixel(x0, y0 + radius); + setPixel(x0 - radius, y0); + setPixel(x0, y0 - radius); +} + +void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + while (x < y) { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + if (quads & 0x1) { + setPixel(x0 + x, y0 - y); + setPixel(x0 + y, y0 - x); + } + if (quads & 0x2) { + setPixel(x0 - y, y0 - x); + setPixel(x0 - x, y0 - y); + } + if (quads & 0x4) { + setPixel(x0 - y, y0 + x); + setPixel(x0 - x, y0 + y); + } + if (quads & 0x8) { + setPixel(x0 + x, y0 + y); + setPixel(x0 + y, y0 + x); + } + } + if (quads & 0x1 && quads & 0x8) { + setPixel(x0 + radius, y0); + } + if (quads & 0x4 && quads & 0x8) { + setPixel(x0, y0 + radius); + } + if (quads & 0x2 && quads & 0x4) { + setPixel(x0 - radius, y0); + } + if (quads & 0x1 && quads & 0x2) { + setPixel(x0, y0 - radius); + } +} + + +void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + + drawHorizontalLine(x0 - x, y0 - y, 2*x); + drawHorizontalLine(x0 - x, y0 + y, 2*x); + drawHorizontalLine(x0 - y, y0 - x, 2*y); + drawHorizontalLine(x0 - y, y0 + x, 2*y); + + + } while (x < y); + drawHorizontalLine(x0 - radius, y0, 2 * radius); + +} + +void OLEDDisplay::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2) { + drawLine(x0, y0, x1, y1); + drawLine(x1, y1, x2, y2); + drawLine(x2, y2, x0, y0); +} + +void OLEDDisplay::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2) { + int16_t a, b, y, last; + + if (y0 > y1) { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } + if (y1 > y2) { + _swap_int16_t(y2, y1); + _swap_int16_t(x2, x1); + } + if (y0 > y1) { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } + + if (y0 == y2) { + a = b = x0; + if (x1 < a) { + a = x1; + } else if (x1 > b) { + b = x1; + } + if (x2 < a) { + a = x2; + } else if (x2 > b) { + b = x2; + } + drawHorizontalLine(a, y0, b - a + 1); + return; + } + + int16_t + dx01 = x1 - x0, + dy01 = y1 - y0, + dx02 = x2 - x0, + dy02 = y2 - y0, + dx12 = x2 - x1, + dy12 = y2 - y1; + int32_t + sa = 0, + sb = 0; + + if (y1 == y2) { + last = y1; // Include y1 scanline + } else { + last = y1 - 1; // Skip it + } + + for (y = y0; y <= last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + + if (a > b) { + _swap_int16_t(a, b); + } + drawHorizontalLine(a, y, b - a + 1); + } + + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + for (; y <= y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + + if (a > b) { + _swap_int16_t(a, b); + } + drawHorizontalLine(a, y, b - a + 1); + } +} + +void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) { + if (y < 0 || y >= this->height()) { return; } + + if (x < 0) { + length += x; + x = 0; + } + + if ( (x + length) > this->width()) { + length = (this->width() - x); + } + + if (length <= 0) { return; } + + uint8_t * bufferPtr = buffer; + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + uint8_t drawBit = 1 << (y & 7); + + switch (color) { + case WHITE: while (length--) { + *bufferPtr++ |= drawBit; + }; break; + case BLACK: drawBit = ~drawBit; while (length--) { + *bufferPtr++ &= drawBit; + }; break; + case INVERSE: while (length--) { + *bufferPtr++ ^= drawBit; + }; break; + } +} + +void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) { + if (x < 0 || x >= this->width()) return; + + if (y < 0) { + length += y; + y = 0; + } + + if ( (y + length) > this->height()) { + length = (this->height() - y); + } + + if (length <= 0) return; + + + uint8_t yOffset = y & 7; + uint8_t drawBit; + uint8_t *bufferPtr = buffer; + + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + if (yOffset) { + yOffset = 8 - yOffset; + drawBit = ~(0xFF >> (yOffset)); + + if (length < yOffset) { + drawBit &= (0xFF >> (yOffset - length)); + } + + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + + if (length < yOffset) return; + + length -= yOffset; + bufferPtr += this->width(); + } + + if (length >= 8) { + switch (color) { + case WHITE: + case BLACK: + drawBit = (color == WHITE) ? 0xFF : 0x00; + do { + *bufferPtr = drawBit; + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + case INVERSE: + do { + *bufferPtr = ~(*bufferPtr); + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + } + } + + if (length > 0) { + drawBit = (1 << (length & 7)) - 1; + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + } +} + +void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) { + uint16_t radius = height / 2; + uint16_t xRadius = x + radius; + uint16_t yRadius = y + radius; + uint16_t doubleRadius = 2 * radius; + uint16_t innerRadius = radius - 2; + + setColor(WHITE); + drawCircleQuads(xRadius, yRadius, radius, 0b00000110); + drawHorizontalLine(xRadius, y, width - doubleRadius + 1); + drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1); + drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001); + + uint16_t maxProgressWidth = (width - doubleRadius + 1) * progress / 100; + + fillCircle(xRadius, yRadius, innerRadius); + fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3); + fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius); +} + +void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *image) { + drawInternal(xMove, yMove, width, height, image, 0, 0); +} + +void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *xbm) { + int16_t widthInXbm = (width + 7) / 8; + uint8_t data = 0; + + for(int16_t y = 0; y < height; y++) { + for(int16_t x = 0; x < width; x++ ) { + if (x & 7) { + data >>= 1; // Move a bit + } else { // Read new data every 8 bit + data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm); + } + // if there is a bit draw it + if (data & 0x01) { + setPixel(xMove + x, yMove + y); + } + } + } +} + +void OLEDDisplay::drawIco16x16(int16_t xMove, int16_t yMove, const uint8_t *ico, bool inverse) { + uint16_t data; + + for(int16_t y = 0; y < 16; y++) { + data = pgm_read_byte(ico + (y << 1)) + (pgm_read_byte(ico + (y << 1) + 1) << 8); + for(int16_t x = 0; x < 16; x++ ) { + if ((data & 0x01) ^ inverse) { + setPixelColor(xMove + x, yMove + y, WHITE); + } else { + setPixelColor(xMove + x, yMove + y, BLACK); + } + data >>= 1; // Move a bit + } + } +} + +uint16_t OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, const char* text, uint16_t textLength, uint16_t textWidth, bool utf8) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES; + + uint16_t cursorX = 0; + uint16_t cursorY = 0; + uint16_t charCount = 0; + + switch (textAlignment) { + case TEXT_ALIGN_CENTER_BOTH: + yMove -= textHeight >> 1; + // Fallthrough + case TEXT_ALIGN_CENTER: + xMove -= textWidth >> 1; // divide by 2 + break; + case TEXT_ALIGN_RIGHT: + xMove -= textWidth; + break; + case TEXT_ALIGN_LEFT: + break; + } + + // Don't draw anything if it is not on the screen. + if (xMove + textWidth < 0 || xMove >= this->width() ) {return 0;} + if (yMove + textHeight < 0 || yMove >= this->height()) {return 0;} + + for (uint16_t j = 0; j < textLength; j++) { + int16_t xPos = xMove + cursorX; + int16_t yPos = yMove + cursorY; + if (xPos > this->width()) + break; // no need to continue + charCount++; + + uint8_t code; + if (utf8) { + code = (this->fontTableLookupFunction)(text[j]); + if (code == 0) + continue; + } else + code = text[j]; + if (code >= firstChar) { + uint8_t charCode = code - firstChar; + + // 4 Bytes per char code + uint8_t msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress + uint8_t lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB / + uint8_t charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size + uint8_t currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width + + // Test if the char is drawable + if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) { + // Get the position of the char data + uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar); + drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize); + } + + cursorX += currentCharWidth; + } + } + return charCount; +} + + +uint16_t OLEDDisplay::drawString(int16_t xMove, int16_t yMove, const String &strUser) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + // char* text must be freed! + char* text = strdup(strUser.c_str()); + if (!text) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][drawString] Can't allocate char array.\n"); + return 0; + } + + uint16_t yOffset = 0; + // If the string should be centered vertically too + // we need to now how heigh the string is. + if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { + uint16_t lb = 0; + // Find number of linebreaks in text + for (uint16_t i=0;text[i] != 0; i++) { + lb += (text[i] == 10); + } + // Calculate center + yOffset = (lb * lineHeight) / 2; + } + + uint16_t charDrawn = 0; + uint16_t line = 0; + char* textPart = strtok(text,"\n"); + while (textPart != NULL) { + uint16_t length = strlen(textPart); + charDrawn += drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length, true), true); + textPart = strtok(NULL, "\n"); + } + free(text); + return charDrawn; +} + +void OLEDDisplay::drawStringf( int16_t x, int16_t y, char* buffer, String format, ... ) +{ + va_list myargs; + va_start(myargs, format); + vsprintf(buffer, format.c_str(), myargs); + va_end(myargs); + drawString( x, y, buffer ); +} + +uint16_t OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, const String &strUser) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + const char* text = strUser.c_str(); + + uint16_t length = strlen(text); + uint16_t lastDrawnPos = 0; + uint16_t lineNumber = 0; + uint16_t strWidth = 0; + + uint16_t preferredBreakpoint = 0; + uint16_t widthAtBreakpoint = 0; + uint16_t firstLineChars = 0; + uint16_t drawStringResult = 1; // later tested for 0 == error, so initialize to 1 + + for (uint16_t i = 0; i < length; i++) { + char c = (this->fontTableLookupFunction)(text[i]); + if (c == 0) + continue; + strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (c - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + + // Always try to break on a space, dash or slash + if (text[i] == ' ' || text[i]== '-' || text[i] == '/') { + preferredBreakpoint = i + 1; + widthAtBreakpoint = strWidth; + } + + if (strWidth >= maxLineWidth) { + if (preferredBreakpoint == 0) { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + drawStringResult = drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint, true); + if (firstLineChars == 0) + firstLineChars = preferredBreakpoint; + lastDrawnPos = preferredBreakpoint; + // It is possible that we did not draw all letters to i so we need + // to account for the width of the chars from `i - preferredBreakpoint` + // by calculating the width we did not draw yet. + strWidth = strWidth - widthAtBreakpoint; + preferredBreakpoint = 0; + if (drawStringResult == 0) // we are past the display already? + break; + } + } + + // Draw last part if needed + if (drawStringResult != 0 && lastDrawnPos < length) { + drawStringResult = drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos, true), true); + } + + if (drawStringResult == 0 || (yMove + lineNumber * lineHeight) >= this->height()) // text did not fit on screen + return firstLineChars; + return 0; // everything was drawn +} + +uint16_t OLEDDisplay::FDRS_drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, const String &strUser) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + const char* text = strUser.c_str(); + + uint16_t length = strlen(text); + uint16_t lastDrawnPos = 0; + uint16_t lineNumber = 0; + uint16_t strWidth = 0; + + uint16_t preferredBreakpoint = 0; + uint16_t widthAtBreakpoint = 0; + uint16_t firstLineChars = 0; + uint16_t drawStringResult = 1; // later tested for 0 == error, so initialize to 1 + + for (uint16_t i = 0; i < length; i++) { + char c = (this->fontTableLookupFunction)(text[i]); + if (c == 0) + continue; + strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (c - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + + // Always try to break on a space, dash or slash + if (text[i] == ' ' || text[i]== '-' || text[i] == '/') { + preferredBreakpoint = i + 1; + widthAtBreakpoint = strWidth; + } + + if (strWidth >= maxLineWidth) { + if (preferredBreakpoint == 0) { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + drawStringResult = drawStringInternal(xMove, yMove + (lineNumber++) * (lineHeight-5) , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint, true); + if (firstLineChars == 0) + firstLineChars = preferredBreakpoint; + lastDrawnPos = preferredBreakpoint; + // It is possible that we did not draw all letters to i so we need + // to account for the width of the chars from `i - preferredBreakpoint` + // by calculating the width we did not draw yet. + strWidth = strWidth - widthAtBreakpoint; + preferredBreakpoint = 0; + if (drawStringResult == 0) // we are past the display already? + break; + } + } + + // Draw last part if needed + if (drawStringResult != 0 && lastDrawnPos < length) { + drawStringResult = drawStringInternal(xMove, yMove + (lineNumber++) * (lineHeight -5) , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos, true), true); + } + + return lineNumber; + +} + +uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length, bool utf8) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + + uint16_t stringWidth = 0; + uint16_t maxWidth = 0; + + for (uint16_t i = 0; i < length; i++) { + char c = text[i]; + if (utf8) { + c = (this->fontTableLookupFunction)(c); + if (c == 0) + continue; + } + stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (c - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + if (c == 10) { + maxWidth = max(maxWidth, stringWidth); + stringWidth = 0; + } + } + + return max(maxWidth, stringWidth); +} + +uint16_t OLEDDisplay::getStringWidth(const String &strUser) { + uint16_t width = getStringWidth(strUser.c_str(), strUser.length()); + return width; +} + +void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) { + this->textAlignment = textAlignment; +} + +void OLEDDisplay::setFont(const uint8_t *fontData) { + this->fontData = fontData; +} + +void OLEDDisplay::displayOn(void) { + sendCommand(DISPLAYON); +} + +void OLEDDisplay::displayOff(void) { + sendCommand(DISPLAYOFF); +} + +void OLEDDisplay::invertDisplay(void) { + sendCommand(INVERTDISPLAY); +} + +void OLEDDisplay::normalDisplay(void) { + sendCommand(NORMALDISPLAY); +} + +void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect) { + sendCommand(SETPRECHARGE); //0xD9 + sendCommand(precharge); //0xF1 default, to lower the contrast, put 1-1F + sendCommand(SETCONTRAST); + sendCommand(contrast); // 0-255 + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(comdetect); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(DISPLAYON); +} + +void OLEDDisplay::setBrightness(uint8_t brightness) { + uint8_t contrast = brightness; + if (brightness < 128) { + // Magic values to get a smooth/ step-free transition + contrast = brightness * 1.171; + } else { + contrast = brightness * 1.171 - 43; + } + + uint8_t precharge = 241; + if (brightness == 0) { + precharge = 0; + } + uint8_t comdetect = brightness / 8; + + setContrast(contrast, precharge, comdetect); +} + +void OLEDDisplay::resetOrientation() { + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); //Reset screen rotation or mirroring +} + +void OLEDDisplay::flipScreenVertically() { + sendCommand(SEGREMAP | 0x01); + sendCommand(COMSCANDEC); //Rotate screen 180 Deg +} + +void OLEDDisplay::mirrorScreen() { + sendCommand(SEGREMAP); + sendCommand(COMSCANDEC); //Mirror screen +} + +void OLEDDisplay::clear(void) { + memset(buffer, 0, displayBufferSize); +} + +void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + // Always align left + setTextAlignment(TEXT_ALIGN_LEFT); + + // State values + uint16_t length = 0; + uint16_t line = 0; + uint16_t lastPos = 0; + + for (uint16_t i=0;ilogBufferFilled;i++){ + // Everytime we have a \n print + if (this->logBuffer[i] == 10) { + length++; + // Draw string on line `line` from lastPos to length + // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT + drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0, false); + // Remember last pos + lastPos = i; + // Reset length + length = 0; + } else { + // Count chars until next linebreak + length++; + } + } + // Draw the remaining string + if (length > 0) { + drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0, false); + } +} + +uint16_t OLEDDisplay::getWidth(void) { + return displayWidth; +} + +uint16_t OLEDDisplay::getHeight(void) { + return displayHeight; +} + +bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){ + if (logBuffer != NULL) free(logBuffer); + uint16_t size = lines * chars; + if (size > 0) { + this->logBufferLine = 0; // Lines printed + this->logBufferFilled = 0; // Nothing stored yet + this->logBufferMaxLines = lines; // Lines max printable + this->logBufferSize = size; // Total number of characters the buffer can hold + this->logBuffer = (char *) malloc(size * sizeof(uint8_t)); + if(!this->logBuffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n"); + return false; + } + } + return true; +} + +size_t OLEDDisplay::write(uint8_t c) { + if (this->logBufferSize > 0) { + // Don't waste space on \r\n line endings, dropping \r + if (c == 13) return 1; + + // convert UTF-8 character to font table index + c = (this->fontTableLookupFunction)(c); + // drop unknown character + if (c == 0) return 1; + + bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines; + bool bufferNotFull = this->logBufferFilled < this->logBufferSize; + + // Can we write to the buffer? + if (bufferNotFull && maxLineNotReached) { + this->logBuffer[logBufferFilled] = c; + this->logBufferFilled++; + // Keep track of lines written + if (c == 10) this->logBufferLine++; + } else { + // Max line number is reached + if (!maxLineNotReached) this->logBufferLine--; + + // Find the end of the first line + uint16_t firstLineEnd = 0; + for (uint16_t i=0;ilogBufferFilled;i++) { + if (this->logBuffer[i] == 10){ + // Include last char too + firstLineEnd = i + 1; + break; + } + } + // If there was a line ending + if (firstLineEnd > 0) { + // Calculate the new logBufferFilled value + this->logBufferFilled = logBufferFilled - firstLineEnd; + // Now we move the lines infront of the buffer + memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled); + } else { + // Let's reuse the buffer if it was full + if (!bufferNotFull) { + this->logBufferFilled = 0; + }// else { + // Nothing to do here + //} + } + write(c); + } + } + // We are always writing all uint8_t to the buffer + return 1; +} + +size_t OLEDDisplay::write(const char* str) { + if (str == NULL) return 0; + size_t length = strlen(str); + for (size_t i = 0; i < length; i++) { + write(str[i]); + } + return length; +} + +#ifdef __MBED__ +int OLEDDisplay::_putc(int c) { + + if (!fontData) + return 1; + if (!logBufferSize) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint16_t lines = this->displayHeight / textHeight; + uint16_t chars = 2 * (this->displayWidth / textHeight); + + if (this->displayHeight % textHeight) + lines++; + if (this->displayWidth % textHeight) + chars++; + setLogBuffer(lines, chars); + } + + return this->write((uint8_t)c); +} +#endif + +// Private functions +void OLEDDisplay::setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) { + this->geometry = g; + + switch (g) { + case GEOMETRY_128_64: + this->displayWidth = 128; + this->displayHeight = 64; + break; + case GEOMETRY_128_32: + this->displayWidth = 128; + this->displayHeight = 32; + break; + case GEOMETRY_64_48: + this->displayWidth = 64; + this->displayHeight = 48; + break; + case GEOMETRY_64_32: + this->displayWidth = 64; + this->displayHeight = 32; + break; + case GEOMETRY_RAWMODE: + this->displayWidth = width > 0 ? width : 128; + this->displayHeight = height > 0 ? height : 64; + break; + } + this->displayBufferSize = displayWidth * displayHeight / 8; +} + +void OLEDDisplay::sendInitCommands(void) { + if (geometry == GEOMETRY_RAWMODE) + return; + sendCommand(DISPLAYOFF); + sendCommand(SETDISPLAYCLOCKDIV); + sendCommand(0xF0); // Increase speed of the display max ~96Hz + sendCommand(SETMULTIPLEX); + sendCommand(this->height() - 1); + sendCommand(SETDISPLAYOFFSET); + sendCommand(0x00); + if(geometry == GEOMETRY_64_32) + sendCommand(0x00); + else + sendCommand(SETSTARTLINE); + sendCommand(CHARGEPUMP); + sendCommand(0x14); + sendCommand(MEMORYMODE); + sendCommand(0x00); + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); + sendCommand(SETCOMPINS); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32) { + sendCommand(0x12); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x02); + } + + sendCommand(SETCONTRAST); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32) { + sendCommand(0xCF); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x8F); + } + + sendCommand(SETPRECHARGE); + sendCommand(0xF1); + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(0x40); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(0x2e); // stop scroll + sendCommand(DISPLAYON); +} + +void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) { + if (width < 0 || height < 0) return; + if (yMove + height < 0 || yMove > this->height()) return; + if (xMove + width < 0 || xMove > this->width()) return; + + uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0) + int8_t yOffset = yMove & 7; + + bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData; + + int16_t initYMove = yMove; + int8_t initYOffset = yOffset; + + + for (uint16_t i = 0; i < bytesInData; i++) { + + // Reset if next horizontal drawing phase is started. + if ( i % rasterHeight == 0) { + yMove = initYMove; + yOffset = initYOffset; + } + + uint8_t currentByte = pgm_read_byte(data + offset + i); + + int16_t xPos = xMove + (i / rasterHeight); + int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * this->width(); + +// int16_t yScreenPos = yMove + yOffset; + int16_t dataPos = xPos + yPos; + + if (dataPos >= 0 && dataPos < displayBufferSize && + xPos >= 0 && xPos < this->width() ) { + + if (yOffset >= 0) { + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte << yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break; + } + + if (dataPos < (displayBufferSize - this->width())) { + switch (this->color) { + case WHITE: buffer[dataPos + this->width()] |= currentByte >> (8 - yOffset); break; + case BLACK: buffer[dataPos + this->width()] &= ~(currentByte >> (8 - yOffset)); break; + case INVERSE: buffer[dataPos + this->width()] ^= currentByte >> (8 - yOffset); break; + } + } + } else { + // Make new offset position + yOffset = -yOffset; + + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte >> yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break; + } + + // Prepare for next iteration by moving one block up + yMove -= 8; + + // and setting the new yOffset + yOffset = 8 - yOffset; + } +#ifndef __MBED__ + yield(); +#endif + } + } +} + +// You need to free the char! +char* OLEDDisplay::utf8ascii(const String &str) { + uint16_t k = 0; + uint16_t length = str.length() + 1; + + // Copy the string into a char array + char* s = (char*) malloc(length * sizeof(char)); + if(!s) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n"); + return (char*) str.c_str(); + } + str.toCharArray(s, length); + + length--; + + for (uint16_t i=0; i < length; i++) { + char c = (this->fontTableLookupFunction)(s[i]); + if (c!=0) { + s[k++]=c; + } + } + + s[k]=0; + + // This will leak 's' be sure to free it in the calling function. + return s; +} + +void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) { + this->fontTableLookupFunction = function; +} + + +char DefaultFontTableLookup(const uint8_t ch) { + // UTF-8 to font table index converter + // Code form http://playground.arduino.cc/Main/Utf8ascii + static uint8_t LASTCHAR; + + if (ch < 128) { // Standard ASCII-set 0..0x7F handling + LASTCHAR = 0; + return ch; + } + + uint8_t last = LASTCHAR; // get last char + LASTCHAR = ch; + + switch (last) { // conversion depnding on first UTF8-character + case 0xC2: return (uint8_t) ch; + case 0xC3: return (uint8_t) (ch | 0xC0); + case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol + } + + return (uint8_t) 0; // otherwise: return zero, if character has to be ignored +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplay.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplay.h new file mode 100644 index 0000000..7d670a6 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplay.h @@ -0,0 +1,397 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAY_h +#define OLEDDISPLAY_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + +#include +#define delay(x) wait_ms(x) +#define yield() void() + +/* + * This is a little Arduino String emulation to keep the OLEDDisplay + * library code in common between Arduino and mbed-os + */ +class String { +public: + String(const char *s) { _str = s; }; + int length() { return strlen(_str); }; + const char *c_str() { return _str; }; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { + memcpy(buf, _str + index, std::min(bufsize, strlen(_str))); + }; +private: + const char *_str; +}; + +#else +#error "Unkown operating system" +#endif + +#include "OLEDDisplayFonts.h" + +//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ ) +//#define DEBUG_OLEDDISPLAY(...) dprintf("%s", __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAY +#define DEBUG_OLEDDISPLAY(...) +#endif + +// Use DOUBLE BUFFERING by default +#ifndef OLEDDISPLAY_REDUCE_MEMORY +#define OLEDDISPLAY_DOUBLE_BUFFER +#endif + +// Header Values +#define JUMPTABLE_BYTES 4 + +#define JUMPTABLE_LSB 1 +#define JUMPTABLE_SIZE 2 +#define JUMPTABLE_WIDTH 3 +#define JUMPTABLE_START 4 + +#define WIDTH_POS 0 +#define HEIGHT_POS 1 +#define FIRST_CHAR_POS 2 +#define CHAR_NUM_POS 3 + + +// Display commands +#define CHARGEPUMP 0x8D +#define COLUMNADDR 0x21 +#define COMSCANDEC 0xC8 +#define COMSCANINC 0xC0 +#define DISPLAYALLON 0xA5 +#define DISPLAYALLON_RESUME 0xA4 +#define DISPLAYOFF 0xAE +#define DISPLAYON 0xAF +#define EXTERNALVCC 0x1 +#define INVERTDISPLAY 0xA7 +#define MEMORYMODE 0x20 +#define NORMALDISPLAY 0xA6 +#define PAGEADDR 0x22 +#define SEGREMAP 0xA0 +#define SETCOMPINS 0xDA +#define SETCONTRAST 0x81 +#define SETDISPLAYCLOCKDIV 0xD5 +#define SETDISPLAYOFFSET 0xD3 +#define SETHIGHCOLUMN 0x10 +#define SETLOWCOLUMN 0x00 +#define SETMULTIPLEX 0xA8 +#define SETPRECHARGE 0xD9 +#define SETSEGMENTREMAP 0xA1 +#define SETSTARTLINE 0x40 +#define SETVCOMDETECT 0xDB +#define SWITCHCAPVCC 0x2 + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } +#endif + +enum OLEDDISPLAY_COLOR { + BLACK = 0, + WHITE = 1, + INVERSE = 2 +}; + +enum OLEDDISPLAY_TEXT_ALIGNMENT { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_RIGHT = 1, + TEXT_ALIGN_CENTER = 2, + TEXT_ALIGN_CENTER_BOTH = 3 +}; + + +enum OLEDDISPLAY_GEOMETRY { + GEOMETRY_128_64 = 0, + GEOMETRY_128_32 = 1, + GEOMETRY_64_48 = 2, + GEOMETRY_64_32 = 3, + GEOMETRY_RAWMODE = 4 +}; + +enum HW_I2C { + I2C_ONE, + I2C_TWO +}; + +typedef char (*FontTableLookupFunction)(const uint8_t ch); +char DefaultFontTableLookup(const uint8_t ch); + + +#ifdef ARDUINO +class OLEDDisplay : public Print { +#elif __MBED__ +class OLEDDisplay : public Stream { +#else +#error "Unkown operating system" +#endif + + public: + OLEDDisplay(); + virtual ~OLEDDisplay(); + + uint16_t width(void) const { return displayWidth; }; + uint16_t height(void) const { return displayHeight; }; + + // Use this to resume after a deep sleep without resetting the display (what init() would do). + // Returns true if connection to the display was established and the buffer allocated, false otherwise. + bool allocateBuffer(); + + // Allocates the buffer and initializes the driver & display. Resets the display! + // Returns false if buffer allocation failed, true otherwise. + bool init(); + + // Free the memory used by the display + void end(); + + // Cycle through the initialization + void resetDisplay(void); + + /* Drawing functions */ + // Sets the color of all pixel operations + void setColor(OLEDDISPLAY_COLOR color); + + // Returns the current color. + OLEDDISPLAY_COLOR getColor(); + + // Draw a pixel at given position + void setPixel(int16_t x, int16_t y); + + // Draw a pixel at given position and color + void setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color); + + // Clear a pixel at given position FIXME: INVERSE is untested with this function + void clearPixel(int16_t x, int16_t y); + + // Draw a line from position 0 to position 1 + void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + + // Draw the border of a rectangle at the given location + void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Fill the rectangle + void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Draw the border of a circle + void drawCircle(int16_t x, int16_t y, int16_t radius); + + // Draw all Quadrants specified in the quads bit mask + void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads); + + // Fill circle + void fillCircle(int16_t x, int16_t y, int16_t radius); + + // Draw an empty triangle i.e. only the outline + void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2); + + // Draw a solid triangle i.e. filled + void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2); + + // Draw a line horizontally + void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + + // Draw a line vertically + void drawVerticalLine(int16_t x, int16_t y, int16_t length); + + // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is + // a unsigned byte value between 0 and 100 + void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + + // Draw a bitmap in the internal image format + void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); + + // Draw a XBM + void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm); + + // Draw icon 16x16 xbm format + void drawIco16x16(int16_t x, int16_t y, const uint8_t *ico, bool inverse = false); + + /* Text functions */ + + // Draws a string at the given location, returns how many chars have been written + uint16_t drawString(int16_t x, int16_t y, const String &text); + + // Draws a formatted string (like printf) at the given location + void drawStringf(int16_t x, int16_t y, char* buffer, String format, ... ); + + // Draws a String with a maximum width at the given location. + // If the given String is wider than the specified width + // The text will be wrapped to the next line at a space or dash + // returns 0 if everything fits on the screen or the numbers of characters in the + // first line if not + uint16_t drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, const String &text); + + + // Modified version with closer spacing. Also returns number of lines used. + + uint16_t FDRS_drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, const String &text); + + // Returns the width of the const char* with the current + // font settings + uint16_t getStringWidth(const char* text, uint16_t length, bool utf8 = false); + + // Convencience method for the const char version + uint16_t getStringWidth(const String &text); + + // Specifies relative to which anchor point + // the text is rendered. Available constants: + // TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH + void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + + // Sets the current font. Available default fonts + // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 + void setFont(const uint8_t *fontData); + + // Set the function that will convert utf-8 to font table index + void setFontTableLookupFunction(FontTableLookupFunction function); + + /* Display functions */ + + // Turn the display on + void displayOn(void); + + // Turn the display offs + void displayOff(void); + + // Inverted display mode + void invertDisplay(void); + + // Normal display mode + void normalDisplay(void); + + // Set display contrast + // really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 + // normal brightness & contrast: contrast = 100 + void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); + + // Convenience method to access + void setBrightness(uint8_t); + + // Reset display rotation or mirroring + void resetOrientation(); + + // Turn the display upside down + void flipScreenVertically(); + + // Mirror the display (to be used in a mirror or as a projector) + void mirrorScreen(); + + // Write the buffer to the display memory + virtual void display(void) = 0; + + // Clear the local pixel buffer + void clear(void); + + // Log buffer implementation + + // This will define the lines and characters you can + // print to the screen. When you exeed the buffer size (lines * chars) + // the output may be truncated due to the size constraint. + bool setLogBuffer(uint16_t lines, uint16_t chars); + + // Draw the log buffer at position (x, y) + void drawLogBuffer(uint16_t x, uint16_t y); + + // Get screen geometry + uint16_t getWidth(void); + uint16_t getHeight(void); + + // Implement needed function to be compatible with Print class + size_t write(uint8_t c); + size_t write(const char* s); + + // Implement needed function to be compatible with Stream class +#ifdef __MBED__ + int _putc(int c); + int _getc() { return -1; }; +#endif + + + uint8_t *buffer; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t *buffer_back; + #endif + + protected: + + OLEDDISPLAY_GEOMETRY geometry; + + uint16_t displayWidth; + uint16_t displayHeight; + uint16_t displayBufferSize; + + // Set the correct height, width and buffer for the geometry + void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width = 0, uint16_t height = 0); + + OLEDDISPLAY_TEXT_ALIGNMENT textAlignment; + OLEDDISPLAY_COLOR color; + + const uint8_t *fontData; + + // State values for logBuffer + uint16_t logBufferSize; + uint16_t logBufferFilled; + uint16_t logBufferLine; + uint16_t logBufferMaxLines; + char *logBuffer; + + + // the header size of the buffer used, e.g. for the SPI command header + int BufferOffset; + virtual int getBufferOffset(void) = 0; + + // Send a command to the display (low level function) + virtual void sendCommand(uint8_t com) {(void)com;}; + + // Connect to the display + virtual bool connect() { return false; }; + + // Send all the init commands + void sendInitCommands(); + + // converts utf8 characters to extended ascii + char* utf8ascii(const String &s); + + void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline)); + + uint16_t drawStringInternal(int16_t xMove, int16_t yMove, const char* text, uint16_t textLength, uint16_t textWidth, bool utf8); + + FontTableLookupFunction fontTableLookupFunction; +}; + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayFonts.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayFonts.h new file mode 100644 index 0000000..abc61ba --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayFonts.h @@ -0,0 +1,1278 @@ +#ifndef OLEDDISPLAYFONTS_h +#define OLEDDISPLAYFONTS_h + +#ifdef __MBED__ +#define PROGMEM +#endif + +const uint8_t ArialMT_Plain_10[] PROGMEM = { + 0x0A, // Width: 10 + 0x0D, // Height: 13 + 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 +}; + +const uint8_t ArialMT_Plain_16[] PROGMEM = { + 0x10, // Width: 16 + 0x13, // Height: 19 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x04, // 32:65535 + 0x00, 0x00, 0x08, 0x04, // 33:0 + 0x00, 0x08, 0x0D, 0x06, // 34:8 + 0x00, 0x15, 0x1A, 0x09, // 35:21 + 0x00, 0x2F, 0x17, 0x09, // 36:47 + 0x00, 0x46, 0x26, 0x0E, // 37:70 + 0x00, 0x6C, 0x1D, 0x0B, // 38:108 + 0x00, 0x89, 0x04, 0x03, // 39:137 + 0x00, 0x8D, 0x0C, 0x05, // 40:141 + 0x00, 0x99, 0x0B, 0x05, // 41:153 + 0x00, 0xA4, 0x0D, 0x06, // 42:164 + 0x00, 0xB1, 0x17, 0x09, // 43:177 + 0x00, 0xC8, 0x09, 0x04, // 44:200 + 0x00, 0xD1, 0x0B, 0x05, // 45:209 + 0x00, 0xDC, 0x08, 0x04, // 46:220 + 0x00, 0xE4, 0x0A, 0x04, // 47:228 + 0x00, 0xEE, 0x17, 0x09, // 48:238 + 0x01, 0x05, 0x11, 0x09, // 49:261 + 0x01, 0x16, 0x17, 0x09, // 50:278 + 0x01, 0x2D, 0x17, 0x09, // 51:301 + 0x01, 0x44, 0x17, 0x09, // 52:324 + 0x01, 0x5B, 0x17, 0x09, // 53:347 + 0x01, 0x72, 0x17, 0x09, // 54:370 + 0x01, 0x89, 0x16, 0x09, // 55:393 + 0x01, 0x9F, 0x17, 0x09, // 56:415 + 0x01, 0xB6, 0x17, 0x09, // 57:438 + 0x01, 0xCD, 0x05, 0x04, // 58:461 + 0x01, 0xD2, 0x06, 0x04, // 59:466 + 0x01, 0xD8, 0x17, 0x09, // 60:472 + 0x01, 0xEF, 0x17, 0x09, // 61:495 + 0x02, 0x06, 0x17, 0x09, // 62:518 + 0x02, 0x1D, 0x16, 0x09, // 63:541 + 0x02, 0x33, 0x2F, 0x10, // 64:563 + 0x02, 0x62, 0x1D, 0x0B, // 65:610 + 0x02, 0x7F, 0x1D, 0x0B, // 66:639 + 0x02, 0x9C, 0x20, 0x0C, // 67:668 + 0x02, 0xBC, 0x20, 0x0C, // 68:700 + 0x02, 0xDC, 0x1D, 0x0B, // 69:732 + 0x02, 0xF9, 0x19, 0x0A, // 70:761 + 0x03, 0x12, 0x20, 0x0C, // 71:786 + 0x03, 0x32, 0x1D, 0x0C, // 72:818 + 0x03, 0x4F, 0x05, 0x04, // 73:847 + 0x03, 0x54, 0x14, 0x08, // 74:852 + 0x03, 0x68, 0x1D, 0x0B, // 75:872 + 0x03, 0x85, 0x17, 0x09, // 76:901 + 0x03, 0x9C, 0x23, 0x0D, // 77:924 + 0x03, 0xBF, 0x1D, 0x0C, // 78:959 + 0x03, 0xDC, 0x20, 0x0C, // 79:988 + 0x03, 0xFC, 0x1C, 0x0B, // 80:1020 + 0x04, 0x18, 0x20, 0x0C, // 81:1048 + 0x04, 0x38, 0x1D, 0x0C, // 82:1080 + 0x04, 0x55, 0x1D, 0x0B, // 83:1109 + 0x04, 0x72, 0x19, 0x0A, // 84:1138 + 0x04, 0x8B, 0x1D, 0x0C, // 85:1163 + 0x04, 0xA8, 0x1C, 0x0B, // 86:1192 + 0x04, 0xC4, 0x2B, 0x0F, // 87:1220 + 0x04, 0xEF, 0x20, 0x0B, // 88:1263 + 0x05, 0x0F, 0x19, 0x0B, // 89:1295 + 0x05, 0x28, 0x1A, 0x0A, // 90:1320 + 0x05, 0x42, 0x0C, 0x04, // 91:1346 + 0x05, 0x4E, 0x0B, 0x04, // 92:1358 + 0x05, 0x59, 0x09, 0x04, // 93:1369 + 0x05, 0x62, 0x14, 0x08, // 94:1378 + 0x05, 0x76, 0x1B, 0x09, // 95:1398 + 0x05, 0x91, 0x07, 0x05, // 96:1425 + 0x05, 0x98, 0x17, 0x09, // 97:1432 + 0x05, 0xAF, 0x17, 0x09, // 98:1455 + 0x05, 0xC6, 0x14, 0x08, // 99:1478 + 0x05, 0xDA, 0x17, 0x09, // 100:1498 + 0x05, 0xF1, 0x17, 0x09, // 101:1521 + 0x06, 0x08, 0x0A, 0x04, // 102:1544 + 0x06, 0x12, 0x17, 0x09, // 103:1554 + 0x06, 0x29, 0x14, 0x09, // 104:1577 + 0x06, 0x3D, 0x05, 0x04, // 105:1597 + 0x06, 0x42, 0x06, 0x04, // 106:1602 + 0x06, 0x48, 0x17, 0x08, // 107:1608 + 0x06, 0x5F, 0x05, 0x04, // 108:1631 + 0x06, 0x64, 0x23, 0x0D, // 109:1636 + 0x06, 0x87, 0x14, 0x09, // 110:1671 + 0x06, 0x9B, 0x17, 0x09, // 111:1691 + 0x06, 0xB2, 0x17, 0x09, // 112:1714 + 0x06, 0xC9, 0x18, 0x09, // 113:1737 + 0x06, 0xE1, 0x0D, 0x05, // 114:1761 + 0x06, 0xEE, 0x14, 0x08, // 115:1774 + 0x07, 0x02, 0x0B, 0x04, // 116:1794 + 0x07, 0x0D, 0x14, 0x09, // 117:1805 + 0x07, 0x21, 0x13, 0x08, // 118:1825 + 0x07, 0x34, 0x1F, 0x0C, // 119:1844 + 0x07, 0x53, 0x14, 0x08, // 120:1875 + 0x07, 0x67, 0x13, 0x08, // 121:1895 + 0x07, 0x7A, 0x14, 0x08, // 122:1914 + 0x07, 0x8E, 0x0F, 0x05, // 123:1934 + 0x07, 0x9D, 0x06, 0x04, // 124:1949 + 0x07, 0xA3, 0x0E, 0x05, // 125:1955 + 0x07, 0xB1, 0x17, 0x09, // 126:1969 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x10, // 128:65535 + 0xFF, 0xFF, 0x00, 0x10, // 129:65535 + 0xFF, 0xFF, 0x00, 0x10, // 130:65535 + 0xFF, 0xFF, 0x00, 0x10, // 131:65535 + 0xFF, 0xFF, 0x00, 0x10, // 132:65535 + 0xFF, 0xFF, 0x00, 0x10, // 133:65535 + 0xFF, 0xFF, 0x00, 0x10, // 134:65535 + 0xFF, 0xFF, 0x00, 0x10, // 135:65535 + 0xFF, 0xFF, 0x00, 0x10, // 136:65535 + 0xFF, 0xFF, 0x00, 0x10, // 137:65535 + 0xFF, 0xFF, 0x00, 0x10, // 138:65535 + 0xFF, 0xFF, 0x00, 0x10, // 139:65535 + 0xFF, 0xFF, 0x00, 0x10, // 140:65535 + 0xFF, 0xFF, 0x00, 0x10, // 141:65535 + 0xFF, 0xFF, 0x00, 0x10, // 142:65535 + 0xFF, 0xFF, 0x00, 0x10, // 143:65535 + 0xFF, 0xFF, 0x00, 0x10, // 144:65535 + 0xFF, 0xFF, 0x00, 0x10, // 145:65535 + 0xFF, 0xFF, 0x00, 0x10, // 146:65535 + 0xFF, 0xFF, 0x00, 0x10, // 147:65535 + 0xFF, 0xFF, 0x00, 0x10, // 148:65535 + 0xFF, 0xFF, 0x00, 0x10, // 149:65535 + 0xFF, 0xFF, 0x00, 0x10, // 150:65535 + 0xFF, 0xFF, 0x00, 0x10, // 151:65535 + 0xFF, 0xFF, 0x00, 0x10, // 152:65535 + 0xFF, 0xFF, 0x00, 0x10, // 153:65535 + 0xFF, 0xFF, 0x00, 0x10, // 154:65535 + 0xFF, 0xFF, 0x00, 0x10, // 155:65535 + 0xFF, 0xFF, 0x00, 0x10, // 156:65535 + 0xFF, 0xFF, 0x00, 0x10, // 157:65535 + 0xFF, 0xFF, 0x00, 0x10, // 158:65535 + 0xFF, 0xFF, 0x00, 0x10, // 159:65535 + 0xFF, 0xFF, 0x00, 0x04, // 160:65535 + 0x07, 0xC8, 0x09, 0x05, // 161:1992 + 0x07, 0xD1, 0x17, 0x09, // 162:2001 + 0x07, 0xE8, 0x17, 0x09, // 163:2024 + 0x07, 0xFF, 0x14, 0x09, // 164:2047 + 0x08, 0x13, 0x1A, 0x09, // 165:2067 + 0x08, 0x2D, 0x06, 0x04, // 166:2093 + 0x08, 0x33, 0x17, 0x09, // 167:2099 + 0x08, 0x4A, 0x07, 0x05, // 168:2122 + 0x08, 0x51, 0x23, 0x0C, // 169:2129 + 0x08, 0x74, 0x0E, 0x06, // 170:2164 + 0x08, 0x82, 0x14, 0x09, // 171:2178 + 0x08, 0x96, 0x17, 0x09, // 172:2198 + 0x08, 0xAD, 0x0B, 0x05, // 173:2221 + 0x08, 0xB8, 0x23, 0x0C, // 174:2232 + 0x08, 0xDB, 0x19, 0x09, // 175:2267 + 0x08, 0xF4, 0x0D, 0x06, // 176:2292 + 0x09, 0x01, 0x17, 0x09, // 177:2305 + 0x09, 0x18, 0x0E, 0x05, // 178:2328 + 0x09, 0x26, 0x0D, 0x05, // 179:2342 + 0x09, 0x33, 0x0A, 0x05, // 180:2355 + 0x09, 0x3D, 0x17, 0x09, // 181:2365 + 0x09, 0x54, 0x19, 0x09, // 182:2388 + 0x09, 0x6D, 0x08, 0x05, // 183:2413 + 0x09, 0x75, 0x0C, 0x05, // 184:2421 + 0x09, 0x81, 0x0B, 0x05, // 185:2433 + 0x09, 0x8C, 0x0D, 0x06, // 186:2444 + 0x09, 0x99, 0x17, 0x09, // 187:2457 + 0x09, 0xB0, 0x26, 0x0D, // 188:2480 + 0x09, 0xD6, 0x26, 0x0D, // 189:2518 + 0x09, 0xFC, 0x26, 0x0D, // 190:2556 + 0x0A, 0x22, 0x1A, 0x0A, // 191:2594 + 0x0A, 0x3C, 0x1D, 0x0B, // 192:2620 + 0x0A, 0x59, 0x1D, 0x0B, // 193:2649 + 0x0A, 0x76, 0x1D, 0x0B, // 194:2678 + 0x0A, 0x93, 0x1D, 0x0B, // 195:2707 + 0x0A, 0xB0, 0x1D, 0x0B, // 196:2736 + 0x0A, 0xCD, 0x1D, 0x0B, // 197:2765 + 0x0A, 0xEA, 0x2C, 0x10, // 198:2794 + 0x0B, 0x16, 0x20, 0x0C, // 199:2838 + 0x0B, 0x36, 0x1D, 0x0B, // 200:2870 + 0x0B, 0x53, 0x1D, 0x0B, // 201:2899 + 0x0B, 0x70, 0x1D, 0x0B, // 202:2928 + 0x0B, 0x8D, 0x1D, 0x0B, // 203:2957 + 0x0B, 0xAA, 0x05, 0x04, // 204:2986 + 0x0B, 0xAF, 0x07, 0x04, // 205:2991 + 0x0B, 0xB6, 0x0A, 0x04, // 206:2998 + 0x0B, 0xC0, 0x07, 0x04, // 207:3008 + 0x0B, 0xC7, 0x20, 0x0C, // 208:3015 + 0x0B, 0xE7, 0x1D, 0x0C, // 209:3047 + 0x0C, 0x04, 0x20, 0x0C, // 210:3076 + 0x0C, 0x24, 0x20, 0x0C, // 211:3108 + 0x0C, 0x44, 0x20, 0x0C, // 212:3140 + 0x0C, 0x64, 0x20, 0x0C, // 213:3172 + 0x0C, 0x84, 0x20, 0x0C, // 214:3204 + 0x0C, 0xA4, 0x17, 0x09, // 215:3236 + 0x0C, 0xBB, 0x20, 0x0C, // 216:3259 + 0x0C, 0xDB, 0x1D, 0x0C, // 217:3291 + 0x0C, 0xF8, 0x1D, 0x0C, // 218:3320 + 0x0D, 0x15, 0x1D, 0x0C, // 219:3349 + 0x0D, 0x32, 0x1D, 0x0C, // 220:3378 + 0x0D, 0x4F, 0x19, 0x0B, // 221:3407 + 0x0D, 0x68, 0x1D, 0x0B, // 222:3432 + 0x0D, 0x85, 0x17, 0x0A, // 223:3461 + 0x0D, 0x9C, 0x17, 0x09, // 224:3484 + 0x0D, 0xB3, 0x17, 0x09, // 225:3507 + 0x0D, 0xCA, 0x17, 0x09, // 226:3530 + 0x0D, 0xE1, 0x17, 0x09, // 227:3553 + 0x0D, 0xF8, 0x17, 0x09, // 228:3576 + 0x0E, 0x0F, 0x17, 0x09, // 229:3599 + 0x0E, 0x26, 0x29, 0x0E, // 230:3622 + 0x0E, 0x4F, 0x14, 0x08, // 231:3663 + 0x0E, 0x63, 0x17, 0x09, // 232:3683 + 0x0E, 0x7A, 0x17, 0x09, // 233:3706 + 0x0E, 0x91, 0x17, 0x09, // 234:3729 + 0x0E, 0xA8, 0x17, 0x09, // 235:3752 + 0x0E, 0xBF, 0x05, 0x04, // 236:3775 + 0x0E, 0xC4, 0x07, 0x04, // 237:3780 + 0x0E, 0xCB, 0x0A, 0x04, // 238:3787 + 0x0E, 0xD5, 0x07, 0x04, // 239:3797 + 0x0E, 0xDC, 0x17, 0x09, // 240:3804 + 0x0E, 0xF3, 0x14, 0x09, // 241:3827 + 0x0F, 0x07, 0x17, 0x09, // 242:3847 + 0x0F, 0x1E, 0x17, 0x09, // 243:3870 + 0x0F, 0x35, 0x17, 0x09, // 244:3893 + 0x0F, 0x4C, 0x17, 0x09, // 245:3916 + 0x0F, 0x63, 0x17, 0x09, // 246:3939 + 0x0F, 0x7A, 0x17, 0x09, // 247:3962 + 0x0F, 0x91, 0x17, 0x0A, // 248:3985 + 0x0F, 0xA8, 0x14, 0x09, // 249:4008 + 0x0F, 0xBC, 0x14, 0x09, // 250:4028 + 0x0F, 0xD0, 0x14, 0x09, // 251:4048 + 0x0F, 0xE4, 0x14, 0x09, // 252:4068 + 0x0F, 0xF8, 0x13, 0x08, // 253:4088 + 0x10, 0x0B, 0x17, 0x09, // 254:4107 + 0x10, 0x22, 0x13, 0x08, // 255:4130 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x5F, // 33 + 0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, // 34 + 0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08, // 35 + 0x00,0x00,0x00,0xE0,0x10,0x00,0x10,0x21,0x00,0x08,0x41,0x00,0xFC,0xFF,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 36 + 0x00,0x00,0x00,0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x61,0x00,0xF0,0x18,0x00,0x00,0x06,0x00,0xC0,0x01,0x00,0x30,0x3C,0x00,0x08,0x42,0x00,0x00,0x42,0x00,0x00,0x42,0x00,0x00,0x3C, // 37 + 0x00,0x00,0x00,0x00,0x1C,0x00,0x70,0x22,0x00,0x88,0x41,0x00,0x08,0x43,0x00,0x88,0x44,0x00,0x70,0x28,0x00,0x00,0x10,0x00,0x00,0x28,0x00,0x00,0x44, // 38 + 0x00,0x00,0x00,0x78, // 39 + 0x00,0x00,0x00,0x80,0x3F,0x00,0x70,0xC0,0x01,0x08,0x00,0x02, // 40 + 0x00,0x00,0x00,0x08,0x00,0x02,0x70,0xC0,0x01,0x80,0x3F, // 41 + 0x10,0x00,0x00,0xD0,0x00,0x00,0x38,0x00,0x00,0xD0,0x00,0x00,0x10, // 42 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0xC0,0x1F,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01, // 44 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40, // 46 + 0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 47 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xE0,0x1F, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0xF8,0x7F, // 49 + 0x00,0x00,0x00,0x20,0x40,0x00,0x10,0x60,0x00,0x08,0x50,0x00,0x08,0x48,0x00,0x08,0x44,0x00,0x10,0x43,0x00,0xE0,0x40, // 50 + 0x00,0x00,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x88,0x41,0x00,0xF0,0x22,0x00,0x00,0x1C, // 51 + 0x00,0x0C,0x00,0x00,0x0A,0x00,0x00,0x09,0x00,0xC0,0x08,0x00,0x20,0x08,0x00,0x10,0x08,0x00,0xF8,0x7F,0x00,0x00,0x08, // 52 + 0x00,0x00,0x00,0xC0,0x11,0x00,0xB8,0x20,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x08,0x21,0x00,0x08,0x1E, // 53 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x21,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x10,0x21,0x00,0x20,0x1E, // 54 + 0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x78,0x00,0x08,0x07,0x00,0xC8,0x00,0x00,0x28,0x00,0x00,0x18, // 55 + 0x00,0x00,0x00,0x60,0x1C,0x00,0x90,0x22,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 56 + 0x00,0x00,0x00,0xE0,0x11,0x00,0x10,0x22,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x10,0x22,0x00,0xE0,0x1F, // 57 + 0x00,0x00,0x00,0x40,0x40, // 58 + 0x00,0x00,0x00,0x40,0xC0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x40,0x10, // 60 + 0x00,0x00,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08, // 61 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x00,0x02, // 62 + 0x00,0x00,0x00,0x60,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x08,0x5C,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 63 + 0x00,0x00,0x00,0x00,0x3F,0x00,0xC0,0x40,0x00,0x20,0x80,0x00,0x10,0x1E,0x01,0x10,0x21,0x01,0x88,0x40,0x02,0x48,0x40,0x02,0x48,0x40,0x02,0x48,0x20,0x02,0x88,0x7C,0x02,0xC8,0x43,0x02,0x10,0x40,0x02,0x10,0x20,0x01,0x60,0x10,0x01,0x80,0x8F, // 64 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x08,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 65 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 66 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 67 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 68 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 69 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08, // 70 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x12,0x00,0x00,0x0E, // 71 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0xF8,0x7F, // 72 + 0x00,0x00,0x00,0xF8,0x7F, // 73 + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0xF8,0x3F, // 74 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x80,0x03,0x00,0x40,0x04,0x00,0x20,0x18,0x00,0x10,0x20,0x00,0x08,0x40, // 75 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40, // 76 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0xF8,0x7F, // 77 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x80,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 78 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 79 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 80 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x50,0x00,0x08,0x50,0x00,0x10,0x20,0x00,0x20,0x70,0x00,0xC0,0x4F, // 81 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x06,0x00,0x08,0x1A,0x00,0x10,0x21,0x00,0xE0,0x40, // 82 + 0x00,0x00,0x00,0x60,0x10,0x00,0x90,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 83 + 0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0x7F,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 84 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 85 + 0x00,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x00,0x00,0x07,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x07,0x00,0xE0,0x00,0x00,0x18, // 86 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x03,0x00,0x70,0x00,0x00,0x08,0x00,0x00,0x70,0x00,0x00,0x80,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 87 + 0x00,0x40,0x00,0x08,0x20,0x00,0x10,0x10,0x00,0x60,0x0C,0x00,0x80,0x02,0x00,0x00,0x01,0x00,0x80,0x02,0x00,0x60,0x0C,0x00,0x10,0x10,0x00,0x08,0x20,0x00,0x00,0x40, // 88 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x00,0x7E,0x00,0x80,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 89 + 0x00,0x40,0x00,0x08,0x60,0x00,0x08,0x58,0x00,0x08,0x44,0x00,0x08,0x43,0x00,0x88,0x40,0x00,0x68,0x40,0x00,0x18,0x40,0x00,0x08,0x40, // 90 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x08,0x00,0x02,0x08,0x00,0x02, // 91 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60, // 92 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF8,0xFF,0x03, // 93 + 0x00,0x01,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0x08,0x00,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x01, // 94 + 0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 95 + 0x00,0x00,0x00,0x08,0x00,0x00,0x10, // 96 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 97 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 98 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20, // 99 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xF8,0x7F, // 100 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 101 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x48,0x00,0x00,0x48, // 102 + 0x00,0x00,0x00,0x00,0x1F,0x01,0x80,0x20,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x80,0x20,0x01,0xC0,0xFF, // 103 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 104 + 0x00,0x00,0x00,0xC8,0x7F, // 105 + 0x00,0x00,0x02,0xC8,0xFF,0x01, // 106 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x19,0x00,0x80,0x20,0x00,0x40,0x40, // 107 + 0x00,0x00,0x00,0xF8,0x7F, // 108 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 109 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 110 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 111 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 112 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xC0,0xFF,0x03, // 113 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40, // 114 + 0x00,0x00,0x00,0x80,0x23,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x38, // 115 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x40,0x40,0x00,0x40,0x40, // 116 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 117 + 0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0, // 118 + 0xC0,0x00,0x00,0x00,0x1F,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1F,0x00,0xC0, // 119 + 0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x40,0x40, // 120 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x00,0xE0,0x01,0x00,0x38,0x00,0x00,0x07,0x00,0xC0, // 121 + 0x40,0x40,0x00,0x40,0x60,0x00,0x40,0x58,0x00,0x40,0x44,0x00,0x40,0x43,0x00,0xC0,0x40,0x00,0x40,0x40, // 122 + 0x00,0x04,0x00,0x00,0x04,0x00,0xF0,0xFB,0x01,0x08,0x00,0x02,0x08,0x00,0x02, // 123 + 0x00,0x00,0x00,0xF8,0xFF,0x03, // 124 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF0,0xFB,0x01,0x00,0x04,0x00,0x00,0x04, // 125 + 0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xFF,0x03, // 161 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x03,0x40,0xF0,0x00,0x40,0x4E,0x00,0xC0,0x41,0x00,0xB8,0x20,0x00,0x00,0x11, // 162 + 0x00,0x41,0x00,0xE0,0x31,0x00,0x10,0x2F,0x00,0x08,0x21,0x00,0x08,0x21,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x20,0x20, // 163 + 0x00,0x00,0x00,0x40,0x0B,0x00,0x80,0x04,0x00,0x40,0x08,0x00,0x40,0x08,0x00,0x80,0x04,0x00,0x40,0x0B, // 164 + 0x08,0x0A,0x00,0x10,0x0A,0x00,0x60,0x0A,0x00,0x80,0x0B,0x00,0x00,0x7E,0x00,0x80,0x0B,0x00,0x60,0x0A,0x00,0x10,0x0A,0x00,0x08,0x0A, // 165 + 0x00,0x00,0x00,0xF8,0xF1,0x03, // 166 + 0x00,0x86,0x00,0x70,0x09,0x01,0xC8,0x10,0x02,0x88,0x10,0x02,0x08,0x21,0x02,0x08,0x61,0x02,0x30,0xD2,0x01,0x00,0x0C, // 167 + 0x08,0x00,0x00,0x00,0x00,0x00,0x08, // 168 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xC8,0x47,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x48,0x44,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 169 + 0xD0,0x00,0x00,0x48,0x01,0x00,0x28,0x01,0x00,0x28,0x01,0x00,0xF0,0x01, // 170 + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20, // 171 + 0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x0F, // 172 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 173 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xE8,0x4F,0x00,0x28,0x41,0x00,0x28,0x41,0x00,0x28,0x43,0x00,0x28,0x45,0x00,0xC8,0x48,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 174 + 0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04, // 175 + 0x00,0x00,0x00,0x30,0x00,0x00,0x48,0x00,0x00,0x48,0x00,0x00,0x30, // 176 + 0x00,0x00,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0xE0,0x4F,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41, // 177 + 0x10,0x01,0x00,0x88,0x01,0x00,0x48,0x01,0x00,0x48,0x01,0x00,0x30,0x01, // 178 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x28,0x01,0x00,0xD8, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x08, // 180 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 181 + 0xF0,0x00,0x00,0xF8,0x00,0x00,0xF8,0x01,0x00,0xF8,0x01,0x00,0xF8,0xFF,0x03,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0xFF,0x03,0x08, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02, // 183 + 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x02,0x00,0x00,0x03, // 184 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0xF8,0x01, // 185 + 0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0xF0, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04, // 187 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x21,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x80,0x01,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 188 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x31,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x60,0x44,0x00,0x10,0x62,0x00,0x08,0x52,0x00,0x00,0x52,0x00,0x00,0x4C, // 189 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x41,0x00,0x28,0x21,0x00,0xD8,0x18,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 190 + 0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x10,0x01,0x00,0x08,0x02,0x40,0x07,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0xC0, // 191 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x71,0x04,0x00,0x0A,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 192 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x0A,0x04,0x00,0x71,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 193 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x71,0x04,0x00,0x82,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 194 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x72,0x04,0x00,0x81,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 195 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x08,0x04,0x00,0x72,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 196 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x7E,0x04,0x00,0x0A,0x04,0x00,0x7E,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 197 + 0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x06,0x00,0x80,0x05,0x00,0x60,0x04,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41, // 198 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x02,0x08,0xC0,0x02,0x08,0x40,0x03,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 199 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 200 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 201 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 202 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 203 + 0x01,0x00,0x00,0xFA,0x7F, // 204 + 0x00,0x00,0x00,0xFA,0x7F,0x00,0x01, // 205 + 0x02,0x00,0x00,0xF9,0x7F,0x00,0x01,0x00,0x00,0x02, // 206 + 0x02,0x00,0x00,0xF8,0x7F,0x00,0x02, // 207 + 0x00,0x02,0x00,0xF8,0x7F,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 208 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x82,0x00,0x00,0x01,0x03,0x00,0x02,0x04,0x00,0x01,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 209 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 210 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 211 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 212 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 213 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 214 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x40,0x10, // 215 + 0x00,0x00,0x00,0xC0,0x4F,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x08,0x4C,0x00,0x08,0x42,0x00,0x08,0x41,0x00,0xC8,0x40,0x00,0x30,0x20,0x00,0x30,0x10,0x00,0xC8,0x0F, // 216 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 217 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 218 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 219 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 220 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x02,0x7E,0x00,0x81,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 221 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x40,0x08,0x00,0x80,0x07, // 222 + 0x00,0x00,0x00,0xE0,0x7F,0x00,0x10,0x00,0x00,0x08,0x20,0x00,0x88,0x43,0x00,0x70,0x42,0x00,0x00,0x44,0x00,0x00,0x38, // 223 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 224 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 225 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x42,0x00,0x50,0x22,0x00,0x80,0x7F, // 226 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x48,0x22,0x00,0x80,0x7F, // 227 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 228 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x5C,0x44,0x00,0x54,0x44,0x00,0x5C,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 229 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x3F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 230 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x02,0x40,0xC0,0x02,0x40,0x40,0x03,0x80,0x20, // 231 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x48,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 232 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 233 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x44,0x00,0x90,0x24,0x00,0x00,0x17, // 234 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 235 + 0x08,0x00,0x00,0xD0,0x7F, // 236 + 0x00,0x00,0x00,0xD0,0x7F,0x00,0x08, // 237 + 0x10,0x00,0x00,0xC8,0x7F,0x00,0x08,0x00,0x00,0x10, // 238 + 0x10,0x00,0x00,0xC0,0x7F,0x00,0x10, // 239 + 0x00,0x00,0x00,0x00,0x1F,0x00,0xA0,0x20,0x00,0x68,0x40,0x00,0x58,0x40,0x00,0x70,0x40,0x00,0xE8,0x20,0x00,0x00,0x1F, // 240 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x90,0x00,0x00,0x48,0x00,0x00,0x50,0x00,0x00,0x48,0x00,0x00,0x80,0x7F, // 241 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 242 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 243 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x48,0x40,0x00,0x90,0x20,0x00,0x00,0x1F, // 244 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x88,0x20,0x00,0x00,0x1F, // 245 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 246 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x80,0x0A,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 247 + 0x00,0x00,0x00,0x00,0x5F,0x00,0x80,0x30,0x00,0x40,0x48,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x80,0x21,0x00,0x40,0x1F, // 248 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 249 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x08,0x20,0x00,0xC0,0x7F, // 250 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x10,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xC0,0x7F, // 251 + 0x00,0x00,0x00,0xD0,0x3F,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 252 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x10,0xE0,0x01,0x08,0x38,0x00,0x00,0x07,0x00,0xC0, // 253 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 254 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x10,0x38,0x02,0x00,0xE0,0x01,0x10,0x38,0x00,0x00,0x07,0x00,0xC0 // 255 +}; +const uint8_t ArialMT_Plain_24[] PROGMEM = { + 0x18, // Width: 24 + 0x1C, // Height: 28 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x07, // 32:65535 + 0x00, 0x00, 0x13, 0x07, // 33:0 + 0x00, 0x13, 0x1A, 0x09, // 34:19 + 0x00, 0x2D, 0x33, 0x0D, // 35:45 + 0x00, 0x60, 0x2F, 0x0D, // 36:96 + 0x00, 0x8F, 0x4F, 0x15, // 37:143 + 0x00, 0xDE, 0x3B, 0x10, // 38:222 + 0x01, 0x19, 0x0A, 0x05, // 39:281 + 0x01, 0x23, 0x1C, 0x08, // 40:291 + 0x01, 0x3F, 0x1B, 0x08, // 41:319 + 0x01, 0x5A, 0x21, 0x09, // 42:346 + 0x01, 0x7B, 0x32, 0x0E, // 43:379 + 0x01, 0xAD, 0x10, 0x07, // 44:429 + 0x01, 0xBD, 0x1B, 0x08, // 45:445 + 0x01, 0xD8, 0x0F, 0x07, // 46:472 + 0x01, 0xE7, 0x19, 0x07, // 47:487 + 0x02, 0x00, 0x2F, 0x0D, // 48:512 + 0x02, 0x2F, 0x23, 0x0D, // 49:559 + 0x02, 0x52, 0x2F, 0x0D, // 50:594 + 0x02, 0x81, 0x2F, 0x0D, // 51:641 + 0x02, 0xB0, 0x2F, 0x0D, // 52:688 + 0x02, 0xDF, 0x2F, 0x0D, // 53:735 + 0x03, 0x0E, 0x2F, 0x0D, // 54:782 + 0x03, 0x3D, 0x2D, 0x0D, // 55:829 + 0x03, 0x6A, 0x2F, 0x0D, // 56:874 + 0x03, 0x99, 0x2F, 0x0D, // 57:921 + 0x03, 0xC8, 0x0F, 0x07, // 58:968 + 0x03, 0xD7, 0x10, 0x07, // 59:983 + 0x03, 0xE7, 0x2F, 0x0E, // 60:999 + 0x04, 0x16, 0x2F, 0x0E, // 61:1046 + 0x04, 0x45, 0x2E, 0x0E, // 62:1093 + 0x04, 0x73, 0x2E, 0x0D, // 63:1139 + 0x04, 0xA1, 0x5B, 0x18, // 64:1185 + 0x04, 0xFC, 0x3B, 0x10, // 65:1276 + 0x05, 0x37, 0x3B, 0x10, // 66:1335 + 0x05, 0x72, 0x3F, 0x11, // 67:1394 + 0x05, 0xB1, 0x3F, 0x11, // 68:1457 + 0x05, 0xF0, 0x3B, 0x10, // 69:1520 + 0x06, 0x2B, 0x35, 0x0F, // 70:1579 + 0x06, 0x60, 0x43, 0x13, // 71:1632 + 0x06, 0xA3, 0x3B, 0x11, // 72:1699 + 0x06, 0xDE, 0x0F, 0x07, // 73:1758 + 0x06, 0xED, 0x27, 0x0C, // 74:1773 + 0x07, 0x14, 0x3F, 0x10, // 75:1812 + 0x07, 0x53, 0x2F, 0x0D, // 76:1875 + 0x07, 0x82, 0x43, 0x14, // 77:1922 + 0x07, 0xC5, 0x3B, 0x11, // 78:1989 + 0x08, 0x00, 0x47, 0x13, // 79:2048 + 0x08, 0x47, 0x3A, 0x10, // 80:2119 + 0x08, 0x81, 0x47, 0x13, // 81:2177 + 0x08, 0xC8, 0x3F, 0x11, // 82:2248 + 0x09, 0x07, 0x3B, 0x10, // 83:2311 + 0x09, 0x42, 0x35, 0x0F, // 84:2370 + 0x09, 0x77, 0x3B, 0x11, // 85:2423 + 0x09, 0xB2, 0x39, 0x10, // 86:2482 + 0x09, 0xEB, 0x59, 0x17, // 87:2539 + 0x0A, 0x44, 0x3B, 0x10, // 88:2628 + 0x0A, 0x7F, 0x3D, 0x10, // 89:2687 + 0x0A, 0xBC, 0x37, 0x0F, // 90:2748 + 0x0A, 0xF3, 0x14, 0x07, // 91:2803 + 0x0B, 0x07, 0x1B, 0x07, // 92:2823 + 0x0B, 0x22, 0x18, 0x07, // 93:2850 + 0x0B, 0x3A, 0x2A, 0x0B, // 94:2874 + 0x0B, 0x64, 0x34, 0x0D, // 95:2916 + 0x0B, 0x98, 0x11, 0x08, // 96:2968 + 0x0B, 0xA9, 0x2F, 0x0D, // 97:2985 + 0x0B, 0xD8, 0x33, 0x0D, // 98:3032 + 0x0C, 0x0B, 0x2B, 0x0C, // 99:3083 + 0x0C, 0x36, 0x2F, 0x0D, // 100:3126 + 0x0C, 0x65, 0x2F, 0x0D, // 101:3173 + 0x0C, 0x94, 0x1A, 0x07, // 102:3220 + 0x0C, 0xAE, 0x2F, 0x0D, // 103:3246 + 0x0C, 0xDD, 0x2F, 0x0D, // 104:3293 + 0x0D, 0x0C, 0x0F, 0x05, // 105:3340 + 0x0D, 0x1B, 0x10, 0x05, // 106:3355 + 0x0D, 0x2B, 0x2F, 0x0C, // 107:3371 + 0x0D, 0x5A, 0x0F, 0x05, // 108:3418 + 0x0D, 0x69, 0x47, 0x14, // 109:3433 + 0x0D, 0xB0, 0x2F, 0x0D, // 110:3504 + 0x0D, 0xDF, 0x2F, 0x0D, // 111:3551 + 0x0E, 0x0E, 0x33, 0x0D, // 112:3598 + 0x0E, 0x41, 0x30, 0x0D, // 113:3649 + 0x0E, 0x71, 0x1E, 0x08, // 114:3697 + 0x0E, 0x8F, 0x2B, 0x0C, // 115:3727 + 0x0E, 0xBA, 0x1B, 0x07, // 116:3770 + 0x0E, 0xD5, 0x2F, 0x0D, // 117:3797 + 0x0F, 0x04, 0x2A, 0x0C, // 118:3844 + 0x0F, 0x2E, 0x42, 0x11, // 119:3886 + 0x0F, 0x70, 0x2B, 0x0C, // 120:3952 + 0x0F, 0x9B, 0x2A, 0x0C, // 121:3995 + 0x0F, 0xC5, 0x2B, 0x0C, // 122:4037 + 0x0F, 0xF0, 0x1C, 0x08, // 123:4080 + 0x10, 0x0C, 0x10, 0x06, // 124:4108 + 0x10, 0x1C, 0x1B, 0x08, // 125:4124 + 0x10, 0x37, 0x32, 0x0E, // 126:4151 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x18, // 128:65535 + 0xFF, 0xFF, 0x00, 0x18, // 129:65535 + 0xFF, 0xFF, 0x00, 0x18, // 130:65535 + 0xFF, 0xFF, 0x00, 0x18, // 131:65535 + 0xFF, 0xFF, 0x00, 0x18, // 132:65535 + 0xFF, 0xFF, 0x00, 0x18, // 133:65535 + 0xFF, 0xFF, 0x00, 0x18, // 134:65535 + 0xFF, 0xFF, 0x00, 0x18, // 135:65535 + 0xFF, 0xFF, 0x00, 0x18, // 136:65535 + 0xFF, 0xFF, 0x00, 0x18, // 137:65535 + 0xFF, 0xFF, 0x00, 0x18, // 138:65535 + 0xFF, 0xFF, 0x00, 0x18, // 139:65535 + 0xFF, 0xFF, 0x00, 0x18, // 140:65535 + 0xFF, 0xFF, 0x00, 0x18, // 141:65535 + 0xFF, 0xFF, 0x00, 0x18, // 142:65535 + 0xFF, 0xFF, 0x00, 0x18, // 143:65535 + 0xFF, 0xFF, 0x00, 0x18, // 144:65535 + 0xFF, 0xFF, 0x00, 0x18, // 145:65535 + 0xFF, 0xFF, 0x00, 0x18, // 146:65535 + 0xFF, 0xFF, 0x00, 0x18, // 147:65535 + 0xFF, 0xFF, 0x00, 0x18, // 148:65535 + 0xFF, 0xFF, 0x00, 0x18, // 149:65535 + 0xFF, 0xFF, 0x00, 0x18, // 150:65535 + 0xFF, 0xFF, 0x00, 0x18, // 151:65535 + 0xFF, 0xFF, 0x00, 0x18, // 152:65535 + 0xFF, 0xFF, 0x00, 0x18, // 153:65535 + 0xFF, 0xFF, 0x00, 0x18, // 154:65535 + 0xFF, 0xFF, 0x00, 0x18, // 155:65535 + 0xFF, 0xFF, 0x00, 0x18, // 156:65535 + 0xFF, 0xFF, 0x00, 0x18, // 157:65535 + 0xFF, 0xFF, 0x00, 0x18, // 158:65535 + 0xFF, 0xFF, 0x00, 0x18, // 159:65535 + 0xFF, 0xFF, 0x00, 0x07, // 160:65535 + 0x10, 0x69, 0x14, 0x08, // 161:4201 + 0x10, 0x7D, 0x2B, 0x0D, // 162:4221 + 0x10, 0xA8, 0x2F, 0x0D, // 163:4264 + 0x10, 0xD7, 0x33, 0x0D, // 164:4311 + 0x11, 0x0A, 0x31, 0x0D, // 165:4362 + 0x11, 0x3B, 0x10, 0x06, // 166:4411 + 0x11, 0x4B, 0x2F, 0x0D, // 167:4427 + 0x11, 0x7A, 0x19, 0x08, // 168:4474 + 0x11, 0x93, 0x46, 0x12, // 169:4499 + 0x11, 0xD9, 0x1A, 0x09, // 170:4569 + 0x11, 0xF3, 0x27, 0x0D, // 171:4595 + 0x12, 0x1A, 0x2F, 0x0E, // 172:4634 + 0x12, 0x49, 0x1B, 0x08, // 173:4681 + 0x12, 0x64, 0x46, 0x12, // 174:4708 + 0x12, 0xAA, 0x31, 0x0D, // 175:4778 + 0x12, 0xDB, 0x1E, 0x0A, // 176:4827 + 0x12, 0xF9, 0x33, 0x0D, // 177:4857 + 0x13, 0x2C, 0x1A, 0x08, // 178:4908 + 0x13, 0x46, 0x1A, 0x08, // 179:4934 + 0x13, 0x60, 0x19, 0x08, // 180:4960 + 0x13, 0x79, 0x2F, 0x0E, // 181:4985 + 0x13, 0xA8, 0x31, 0x0D, // 182:5032 + 0x13, 0xD9, 0x12, 0x08, // 183:5081 + 0x13, 0xEB, 0x18, 0x08, // 184:5099 + 0x14, 0x03, 0x16, 0x08, // 185:5123 + 0x14, 0x19, 0x1E, 0x09, // 186:5145 + 0x14, 0x37, 0x2E, 0x0D, // 187:5175 + 0x14, 0x65, 0x4F, 0x14, // 188:5221 + 0x14, 0xB4, 0x4B, 0x14, // 189:5300 + 0x14, 0xFF, 0x4B, 0x14, // 190:5375 + 0x15, 0x4A, 0x33, 0x0F, // 191:5450 + 0x15, 0x7D, 0x3B, 0x10, // 192:5501 + 0x15, 0xB8, 0x3B, 0x10, // 193:5560 + 0x15, 0xF3, 0x3B, 0x10, // 194:5619 + 0x16, 0x2E, 0x3B, 0x10, // 195:5678 + 0x16, 0x69, 0x3B, 0x10, // 196:5737 + 0x16, 0xA4, 0x3B, 0x10, // 197:5796 + 0x16, 0xDF, 0x5B, 0x18, // 198:5855 + 0x17, 0x3A, 0x3F, 0x11, // 199:5946 + 0x17, 0x79, 0x3B, 0x10, // 200:6009 + 0x17, 0xB4, 0x3B, 0x10, // 201:6068 + 0x17, 0xEF, 0x3B, 0x10, // 202:6127 + 0x18, 0x2A, 0x3B, 0x10, // 203:6186 + 0x18, 0x65, 0x11, 0x07, // 204:6245 + 0x18, 0x76, 0x11, 0x07, // 205:6262 + 0x18, 0x87, 0x15, 0x07, // 206:6279 + 0x18, 0x9C, 0x15, 0x07, // 207:6300 + 0x18, 0xB1, 0x3F, 0x11, // 208:6321 + 0x18, 0xF0, 0x3B, 0x11, // 209:6384 + 0x19, 0x2B, 0x47, 0x13, // 210:6443 + 0x19, 0x72, 0x47, 0x13, // 211:6514 + 0x19, 0xB9, 0x47, 0x13, // 212:6585 + 0x1A, 0x00, 0x47, 0x13, // 213:6656 + 0x1A, 0x47, 0x47, 0x13, // 214:6727 + 0x1A, 0x8E, 0x2B, 0x0E, // 215:6798 + 0x1A, 0xB9, 0x47, 0x13, // 216:6841 + 0x1B, 0x00, 0x3B, 0x11, // 217:6912 + 0x1B, 0x3B, 0x3B, 0x11, // 218:6971 + 0x1B, 0x76, 0x3B, 0x11, // 219:7030 + 0x1B, 0xB1, 0x3B, 0x11, // 220:7089 + 0x1B, 0xEC, 0x3D, 0x10, // 221:7148 + 0x1C, 0x29, 0x3A, 0x10, // 222:7209 + 0x1C, 0x63, 0x37, 0x0F, // 223:7267 + 0x1C, 0x9A, 0x2F, 0x0D, // 224:7322 + 0x1C, 0xC9, 0x2F, 0x0D, // 225:7369 + 0x1C, 0xF8, 0x2F, 0x0D, // 226:7416 + 0x1D, 0x27, 0x2F, 0x0D, // 227:7463 + 0x1D, 0x56, 0x2F, 0x0D, // 228:7510 + 0x1D, 0x85, 0x2F, 0x0D, // 229:7557 + 0x1D, 0xB4, 0x53, 0x15, // 230:7604 + 0x1E, 0x07, 0x2B, 0x0C, // 231:7687 + 0x1E, 0x32, 0x2F, 0x0D, // 232:7730 + 0x1E, 0x61, 0x2F, 0x0D, // 233:7777 + 0x1E, 0x90, 0x2F, 0x0D, // 234:7824 + 0x1E, 0xBF, 0x2F, 0x0D, // 235:7871 + 0x1E, 0xEE, 0x11, 0x07, // 236:7918 + 0x1E, 0xFF, 0x11, 0x07, // 237:7935 + 0x1F, 0x10, 0x15, 0x07, // 238:7952 + 0x1F, 0x25, 0x15, 0x07, // 239:7973 + 0x1F, 0x3A, 0x2F, 0x0D, // 240:7994 + 0x1F, 0x69, 0x2F, 0x0D, // 241:8041 + 0x1F, 0x98, 0x2F, 0x0D, // 242:8088 + 0x1F, 0xC7, 0x2F, 0x0D, // 243:8135 + 0x1F, 0xF6, 0x2F, 0x0D, // 244:8182 + 0x20, 0x25, 0x2F, 0x0D, // 245:8229 + 0x20, 0x54, 0x2F, 0x0D, // 246:8276 + 0x20, 0x83, 0x32, 0x0D, // 247:8323 + 0x20, 0xB5, 0x33, 0x0F, // 248:8373 + 0x20, 0xE8, 0x2F, 0x0D, // 249:8424 + 0x21, 0x17, 0x2F, 0x0D, // 250:8471 + 0x21, 0x46, 0x2F, 0x0D, // 251:8518 + 0x21, 0x75, 0x2F, 0x0D, // 252:8565 + 0x21, 0xA4, 0x2A, 0x0C, // 253:8612 + 0x21, 0xCE, 0x2F, 0x0D, // 254:8654 + 0x21, 0xFD, 0x2A, 0x0C, // 255:8701 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x33,0x00,0xE0,0xFF,0x33, // 33 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 34 + 0x00,0x0C,0x03,0x00,0x00,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x03,0x00,0x00,0x0C,0x03, // 35 + 0x00,0x00,0x00,0x00,0x80,0x07,0x06,0x00,0xC0,0x0F,0x1E,0x00,0xC0,0x18,0x1C,0x00,0x60,0x18,0x38,0x00,0x60,0x30,0x30,0x00,0xF0,0xFF,0xFF,0x00,0x60,0x30,0x30,0x00,0x60,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0xC1,0x1F,0x00,0x00,0x81,0x07, // 36 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x60,0x30,0x38,0x00,0xC0,0x1F,0x1E,0x00,0x80,0x8F,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x8F,0x0F,0x00,0xC0,0xC3,0x1F,0x00,0xE0,0x60,0x30,0x00,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 37 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x00,0xC0,0x0F,0x00,0x80,0xE3,0x1C,0x00,0xC0,0x77,0x38,0x00,0xE0,0x3C,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x78,0x30,0x00,0xE0,0xEC,0x38,0x00,0xC0,0x8F,0x1B,0x00,0x80,0x03,0x1F,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xC0,0x38,0x00,0x00,0x00,0x10, // 38 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 39 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0x00,0x00,0xFE,0x7F,0x00,0x80,0x0F,0xF0,0x01,0xC0,0x01,0x80,0x03,0x60,0x00,0x00,0x06,0x20,0x00,0x00,0x04, // 40 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x60,0x00,0x00,0x06,0xC0,0x01,0x80,0x03,0x80,0x0F,0xF0,0x01,0x00,0xFE,0x7F,0x00,0x00,0xF0,0x0F, // 41 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x04,0x00,0x00,0x80,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x80,0x04,0x00,0x00,0x80, // 42 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0xFF,0x0F,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0xF0,0x01, // 44 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 46 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x3F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60, // 47 + 0x00,0x00,0x00,0x00,0x00,0xFE,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x01,0x1C,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x03, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 49 + 0x00,0x00,0x00,0x00,0x00,0x03,0x30,0x00,0xC0,0x03,0x38,0x00,0xC0,0x00,0x3C,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x33,0x00,0x60,0x80,0x31,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x30,0x00,0xC0,0x30,0x30,0x00,0xC0,0x1F,0x30,0x00,0x00,0x0F,0x30, // 50 + 0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0xC0,0x01,0x0E,0x00,0xC0,0x00,0x1C,0x00,0x60,0x00,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x0F,0x00,0x00,0x80,0x07, // 51 + 0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x03,0x00,0x00,0x3C,0x03,0x00,0x00,0x0E,0x03,0x00,0x80,0x07,0x03,0x00,0xC0,0x01,0x03,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03, // 52 + 0x00,0x00,0x00,0x00,0x00,0x30,0x06,0x00,0x80,0x3F,0x0E,0x00,0xE0,0x1F,0x18,0x00,0x60,0x08,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x18,0x1C,0x00,0x60,0xF0,0x0F,0x00,0x00,0xE0,0x03, // 53 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x63,0x1C,0x00,0xC0,0x30,0x38,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0xE0,0x30,0x18,0x00,0xC0,0xF1,0x0F,0x00,0x80,0xC1,0x07, // 54 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x3C,0x00,0x60,0x80,0x3F,0x00,0x60,0xE0,0x03,0x00,0x60,0x78,0x00,0x00,0x60,0x0E,0x00,0x00,0x60,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60, // 55 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x80,0xC7,0x1F,0x00,0xC0,0x6F,0x18,0x00,0xE0,0x38,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xE0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 56 + 0x00,0x00,0x00,0x00,0x00,0x1F,0x0C,0x00,0x80,0x7F,0x1C,0x00,0xC0,0x61,0x38,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x18,0x00,0xC0,0x31,0x1E,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x01, // 57 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 58 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x03,0x00,0x06,0xF0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x04,0x01,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x03,0x06, // 60 + 0x00,0x00,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01, // 61 + 0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x04,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x20, // 62 + 0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x33,0x00,0x60,0xE0,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x00,0x07, // 63 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x1E,0xF0,0x00,0x00,0x07,0xC0,0x01,0x80,0xC3,0x87,0x01,0xC0,0xF1,0x9F,0x03,0xC0,0x38,0x18,0x03,0xC0,0x0C,0x30,0x03,0x60,0x0E,0x30,0x06,0x60,0x06,0x30,0x06,0x60,0x06,0x18,0x06,0x60,0x06,0x0C,0x06,0x60,0x0C,0x1E,0x06,0x60,0xF8,0x3F,0x06,0xE0,0xFE,0x31,0x06,0xC0,0x0E,0x30,0x06,0xC0,0x01,0x18,0x03,0x80,0x03,0x1C,0x03,0x00,0x07,0x8F,0x01,0x00,0xFE,0x87,0x01,0x00,0xF8,0xC1,0x00,0x00,0x00,0x40, // 64 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE0,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 65 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x78,0x30,0x00,0xC0,0xFF,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 66 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 67 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 68 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 69 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60, // 70 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x60,0x30,0x00,0x60,0x60,0x30,0x00,0xE0,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0x61,0x18,0x00,0x80,0xE3,0x0F,0x00,0x00,0xE2,0x0F, // 71 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 72 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 73 + 0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0xE0,0xFF,0x1F,0x00,0xE0,0xFF,0x0F, // 74 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE7,0x01,0x00,0x80,0x83,0x07,0x00,0xC0,0x01,0x0F,0x00,0xE0,0x00,0x1E,0x00,0x60,0x00,0x38,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 75 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 76 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x07,0x00,0x00,0xFE,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 77 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 78 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 79 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0xC0,0x30,0x00,0x00,0xC0,0x3F,0x00,0x00,0x00,0x0F, // 80 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x0C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x36,0x00,0xE0,0x00,0x3C,0x00,0xC0,0x00,0x1C,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x3F,0x00,0x00,0xFF,0x77,0x00,0x00,0xFC,0x61, // 81 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x70,0x00,0x00,0x60,0xF0,0x00,0x00,0x60,0xF0,0x03,0x00,0x60,0xB0,0x07,0x00,0xE0,0x18,0x1F,0x00,0xC0,0x1F,0x3C,0x00,0x80,0x0F,0x30,0x00,0x00,0x00,0x20, // 82 + 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x0F,0x00,0xC0,0x1F,0x1C,0x00,0xC0,0x18,0x18,0x00,0x60,0x38,0x38,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x70,0x30,0x00,0xC0,0x60,0x18,0x00,0xC0,0xE1,0x18,0x00,0x80,0xC3,0x0F,0x00,0x00,0x83,0x07, // 83 + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 84 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 85 + 0x20,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xF8,0x01,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0x20, // 86 + 0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x80,0xFF,0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x80,0x3F,0x00,0x00,0xF8,0x0F,0x00,0x80,0xFF,0x00,0x00,0xE0,0x07,0x00,0x00,0x60, // 87 + 0x00,0x00,0x20,0x00,0x20,0x00,0x30,0x00,0x60,0x00,0x3C,0x00,0xE0,0x01,0x1E,0x00,0xC0,0x83,0x07,0x00,0x00,0xCF,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0xFE,0x01,0x00,0x00,0xCF,0x03,0x00,0xC0,0x03,0x07,0x00,0xE0,0x01,0x1E,0x00,0x60,0x00,0x3C,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 88 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 89 + 0x00,0x00,0x30,0x00,0x60,0x00,0x38,0x00,0x60,0x00,0x3C,0x00,0x60,0x00,0x37,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0x60,0x0E,0x30,0x00,0x60,0x07,0x30,0x00,0xE0,0x01,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x30, // 90 + 0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 91 + 0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 92 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07, // 93 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xE0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x20, // 94 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 95 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x80, // 96 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 97 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 98 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 99 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 100 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 101 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x06,0x00,0x00,0x60,0x06,0x00,0x00,0x60,0x06, // 102 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x83,0x01,0x00,0xF8,0x8F,0x03,0x00,0x1C,0x1C,0x07,0x00,0x0E,0x38,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x0C,0x18,0x07,0x00,0x18,0x8C,0x03,0x00,0xFE,0xFF,0x01,0x00,0xFE,0xFF, // 103 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 104 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F, // 105 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x60,0xFE,0xFF,0x07,0x60,0xFE,0xFF,0x03, // 106 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0x98,0x07,0x00,0x00,0x0C,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x02,0x30,0x00,0x00,0x00,0x20, // 107 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 108 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 109 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 110 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 111 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 112 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07, // 113 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 114 + 0x00,0x00,0x00,0x00,0x00,0x38,0x0C,0x00,0x00,0x7C,0x1C,0x00,0x00,0xEE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x31,0x00,0x00,0xC6,0x31,0x00,0x00,0x8E,0x39,0x00,0x00,0x9C,0x1F,0x00,0x00,0x18,0x0F, // 115 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 116 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 117 + 0x00,0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xF8,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 118 + 0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x7C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xF0,0x03,0x00,0x00,0x7E,0x00,0x00,0x00,0x0E, // 119 + 0x00,0x02,0x20,0x00,0x00,0x06,0x30,0x00,0x00,0x1E,0x3C,0x00,0x00,0x38,0x0E,0x00,0x00,0xF0,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x07,0x00,0x00,0x38,0x0E,0x00,0x00,0x1C,0x3C,0x00,0x00,0x0E,0x30,0x00,0x00,0x02,0x20, // 120 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0x00,0xC0,0x1F,0x00,0x00,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 121 + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x3E,0x00,0x00,0x06,0x37,0x00,0x00,0xC6,0x33,0x00,0x00,0xE6,0x30,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x30,0x00,0x00,0x1E,0x30,0x00,0x00,0x06,0x30, // 122 + 0x00,0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x03,0x00,0xC0,0x7F,0xFE,0x03,0xE0,0x3F,0xFC,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 123 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x0F,0xE0,0xFF,0xFF,0x0F, // 124 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0x3F,0xFC,0x07,0xC0,0x7F,0xFF,0x03,0x00,0xC0,0x03,0x00,0x00,0x80,0x01, // 125 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE6,0xFF,0x07,0x00,0xE6,0xFF,0x07, // 161 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x9C,0x07,0x00,0x0E,0x78,0x00,0x00,0x06,0x3F,0x00,0x00,0xF6,0x30,0x00,0x00,0x0E,0x30,0x00,0xE0,0x0D,0x1C,0x00,0x00,0x1C,0x0E,0x00,0x00,0x10,0x06, // 162 + 0x00,0x60,0x10,0x00,0x00,0x60,0x38,0x00,0x00,0x7F,0x1C,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xE0,0x19,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x30,0x00,0xE0,0x00,0x30,0x00,0xC0,0x01,0x30,0x00,0x80,0x01,0x38,0x00,0x00,0x00,0x10, // 163 + 0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x00,0xF7,0x0E,0x00,0x00,0xFE,0x07,0x00,0x00,0x0C,0x03,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x0C,0x03,0x00,0x00,0xFE,0x07,0x00,0x00,0xF7,0x0E,0x00,0x00,0x02,0x04, // 164 + 0xE0,0x60,0x06,0x00,0xC0,0x61,0x06,0x00,0x80,0x67,0x06,0x00,0x00,0x7E,0x06,0x00,0x00,0x7C,0x06,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x7C,0x06,0x00,0x00,0x7E,0x06,0x00,0x80,0x67,0x06,0x00,0xC0,0x61,0x06,0x00,0xE0,0x60,0x06,0x00,0x20, // 165 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xF8,0x0F,0xE0,0x7F,0xF8,0x0F, // 166 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x80,0xF3,0xC1,0x00,0xC0,0x1F,0xC3,0x03,0xE0,0x0C,0x07,0x03,0x60,0x1C,0x06,0x06,0x60,0x18,0x0C,0x06,0x60,0x30,0x1C,0x06,0xE0,0x70,0x38,0x07,0xC0,0xE1,0xF4,0x03,0x80,0xC1,0xE7,0x01,0x00,0x80,0x03, // 167 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 168 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x79,0x1C,0x00,0xC0,0xFE,0x19,0x00,0x60,0x86,0x31,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x87,0x33,0x00,0xC0,0x86,0x19,0x00,0xC0,0x85,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 169 + 0x00,0x00,0x00,0x00,0xC0,0x1C,0x00,0x00,0xE0,0x3E,0x00,0x00,0x60,0x32,0x00,0x00,0x60,0x32,0x00,0x00,0xE0,0x3F,0x00,0x00,0xC0,0x3F, // 170 + 0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x84,0x10,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x04,0x10, // 171 + 0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFC,0x01, // 172 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 173 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x01,0x1C,0x00,0xC0,0xFE,0x1B,0x00,0x60,0xFE,0x33,0x00,0x60,0x66,0x30,0x00,0x60,0x66,0x30,0x00,0x60,0xE6,0x30,0x00,0x60,0xFE,0x31,0x00,0x60,0x3C,0x33,0x00,0xC0,0x00,0x1A,0x00,0xC0,0x01,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 174 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C, // 175 + 0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x40,0x04,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x40,0x04,0x00,0x00,0x80,0x03, // 176 + 0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0xFF,0x3F,0x00,0x00,0xFF,0x3F,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30, // 177 + 0x40,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x38,0x00,0x00,0x20,0x2C,0x00,0x00,0x20,0x26,0x00,0x00,0xE0,0x23,0x00,0x00,0xC0,0x21, // 178 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x00,0x00,0x20,0x22,0x00,0x00,0xE0,0x3D,0x00,0x00,0xC0,0x1D, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 180 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x00,0x1C,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 181 + 0x00,0x0F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 183 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x01, // 184 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F, // 185 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xE0,0x38,0x00,0x00,0x60,0x30,0x00,0x00,0xE0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x84,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x80, // 187 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x38,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x0C,0x00,0xC0,0x01,0x0E,0x00,0xE0,0x80,0x0B,0x00,0x60,0xC0,0x08,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F,0x00,0x00,0x00,0x08, // 188 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x30,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x4E,0x20,0x00,0x00,0x67,0x30,0x00,0xC0,0x21,0x38,0x00,0xE0,0x20,0x2C,0x00,0x60,0x20,0x26,0x00,0x00,0xE0,0x27,0x00,0x00,0xC0,0x21, // 189 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x20,0x00,0x20,0x22,0x30,0x00,0xE0,0x3D,0x38,0x00,0xC0,0x1D,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x0E,0x0C,0x00,0x00,0x07,0x0E,0x00,0x80,0x83,0x0B,0x00,0xE0,0xC0,0x08,0x00,0x60,0xE0,0x3F,0x00,0x20,0xE0,0x3F,0x00,0x00,0x00,0x08, // 190 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xF8,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x07,0x07,0x00,0xE6,0x03,0x06,0x00,0xE6,0x01,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xC0, // 191 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x82,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE8,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 192 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE8,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x82,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 193 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x88,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x08,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 194 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x0C,0xFE,0x01,0x00,0x8E,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xEC,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0E,0xFE,0x01,0x00,0x06,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 195 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x8C,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0C,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 196 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x9C,0x8F,0x01,0x00,0xE2,0x83,0x01,0x00,0x62,0x80,0x01,0x00,0xE2,0x83,0x01,0x00,0x9C,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 197 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x01,0x00,0x00,0xBC,0x01,0x00,0x00,0x8F,0x01,0x00,0xC0,0x83,0x01,0x00,0xE0,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 198 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x02,0x60,0x00,0x30,0x02,0x60,0x00,0xF0,0x02,0x60,0x00,0xB0,0x03,0x60,0x00,0x30,0x01,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 199 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 200 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 201 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 202 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 203 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xEE,0xFF,0x3F,0x00,0x08, // 204 + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xEE,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x02, // 205 + 0x08,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x08, // 206 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x0C, // 207 + 0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 208 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x8C,0x03,0x00,0x00,0x0E,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x70,0x00,0x00,0x0C,0xE0,0x01,0x00,0x0C,0x80,0x03,0x00,0x0E,0x00,0x0F,0x00,0x06,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 209 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x62,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 210 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x62,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 211 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x68,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xE8,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 212 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xCC,0x00,0x18,0x00,0xEE,0x00,0x38,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0xE6,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 213 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xEC,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 214 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x03,0x00,0x00,0x8E,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0xF8,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x8E,0x03,0x00,0x00,0x06,0x03, // 215 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x21,0x00,0x00,0xFF,0x77,0x00,0x80,0x07,0x3F,0x00,0xC0,0x01,0x1E,0x00,0xC0,0x00,0x1F,0x00,0xE0,0x80,0x3B,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x70,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0xE0,0x0E,0x38,0x00,0xC0,0x07,0x18,0x00,0xC0,0x03,0x1C,0x00,0xE0,0x07,0x0F,0x00,0x70,0xFF,0x07,0x00,0x20,0xFC,0x01, // 216 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x02,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 217 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 218 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x08,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x08,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 219 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 220 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x08,0xF0,0x3F,0x00,0x0E,0xF0,0x3F,0x00,0x06,0x3C,0x00,0x00,0x02,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 221 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x07,0x00,0x00,0x86,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0xF8, // 222 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0x00,0xC0,0xFF,0x3F,0x00,0xC0,0x00,0x00,0x00,0x60,0x00,0x08,0x00,0x60,0x00,0x1C,0x00,0x60,0x00,0x38,0x00,0xE0,0x78,0x30,0x00,0xC0,0x7F,0x30,0x00,0x80,0xC7,0x30,0x00,0x00,0x80,0x39,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x0F, // 223 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x20,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 224 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x80,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x18,0x00,0x20,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 225 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x80,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0x60,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0x80,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 226 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0xC0,0x1C,0x1F,0x00,0xE0,0x8C,0x39,0x00,0x60,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xC0,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xE0,0xCE,0x0C,0x00,0x60,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 227 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0xC0,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xC0,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 228 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x70,0x86,0x31,0x00,0x88,0x86,0x31,0x00,0x88,0xC6,0x30,0x00,0x88,0xC6,0x18,0x00,0x70,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 229 + 0x00,0x00,0x00,0x00,0x00,0x10,0x0F,0x00,0x00,0x9C,0x1F,0x00,0x00,0xCC,0x39,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0x66,0x18,0x00,0x00,0x6E,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xCC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xE0,0x04, // 230 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x02,0x00,0x06,0x30,0x02,0x00,0x06,0xF0,0x02,0x00,0x06,0xB0,0x03,0x00,0x0E,0x38,0x01,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 231 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x20,0xCE,0x38,0x00,0x60,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 232 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x80,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x20,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 233 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x80,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0x80,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 234 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 235 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0xE0,0xFE,0x3F,0x00,0x80, // 236 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0x20, // 237 + 0x80,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0x80, // 238 + 0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0xC0, // 239 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1D,0x1C,0x00,0xA0,0x0F,0x38,0x00,0xA0,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0F,0x38,0x00,0x20,0x1F,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xE0,0x07, // 240 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0xC0,0xFE,0x3F,0x00,0xE0,0x18,0x00,0x00,0x60,0x0C,0x00,0x00,0x60,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xE0,0x0E,0x00,0x00,0x60,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 241 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x20,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x80,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 242 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x80,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x20,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 243 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x80,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0x80,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 244 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0xC0,0x1C,0x1C,0x00,0xE0,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xE0,0x1C,0x1C,0x00,0x60,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 245 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 246 + 0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0xB6,0x01,0x00,0x00,0xB6,0x01,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 247 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x67,0x00,0x00,0xF8,0x7F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x3F,0x00,0x00,0x86,0x33,0x00,0x00,0xE6,0x31,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xFF,0x0F,0x00,0x00,0xF3,0x07, // 248 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x20,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 249 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x18,0x00,0x20,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 250 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x80,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0x80,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 251 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0xC0,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 252 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x80,0x00,0xFE,0x03,0xE0,0x00,0xFC,0x00,0x60,0xC0,0x1F,0x00,0x20,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 253 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x00,0x1C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x03, // 254 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0xC0,0xF0,0x01,0x06,0xC0,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0xC0,0xC0,0x1F,0x00,0xC0,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06 // 255 +}; +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayUi.cpp b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayUi.cpp new file mode 100644 index 0000000..3ade99f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayUi.cpp @@ -0,0 +1,471 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#include "OLEDDisplayUi.h" + +void LoadingDrawDefault(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + display->drawString(64, 18, stage->process); + display->drawProgressBar(4, 32, 120, 8, progress); +}; + + +OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { + this->display = display; + + indicatorPosition = BOTTOM; + indicatorDirection = LEFT_RIGHT; + activeSymbol = ANIMATION_activeSymbol; + inactiveSymbol = ANIMATION_inactiveSymbol; + frameAnimationDirection = SLIDE_RIGHT; + lastTransitionDirection = 1; + frameCount = 0; + nextFrameNumber = -1; + overlayCount = 0; + indicatorDrawState = 1; + loadingDrawFunction = LoadingDrawDefault; + updateInterval = 33; + state.lastUpdate = 0; + state.ticksSinceLastStateSwitch = 0; + state.frameState = FIXED; + state.currentFrame = 0; + state.frameTransitionDirection = 1; + state.isIndicatorDrawn = true; + state.manualControl = false; + state.userData = NULL; + shouldDrawIndicators = true; + autoTransition = true; + setTimePerFrame(5000); + setTimePerTransition(500); +} + +void OLEDDisplayUi::init() { + this->display->init(); +} + +void OLEDDisplayUi::setTargetFPS(uint8_t fps){ + this->updateInterval = ((float) 1.0 / (float) fps) * 1000; + + this->ticksPerFrame = timePerFrame / updateInterval; + this->ticksPerTransition = timePerTransition / updateInterval; +} + +// -/------ Automatic controll ------\- + +void OLEDDisplayUi::enableAutoTransition(){ + this->autoTransition = true; +} +void OLEDDisplayUi::disableAutoTransition(){ + this->autoTransition = false; +} +void OLEDDisplayUi::setAutoTransitionForwards(){ + this->state.frameTransitionDirection = 1; + this->lastTransitionDirection = 1; +} +void OLEDDisplayUi::setAutoTransitionBackwards(){ + this->state.frameTransitionDirection = -1; + this->lastTransitionDirection = -1; +} +void OLEDDisplayUi::setTimePerFrame(uint16_t time){ + this->timePerFrame = time; + this->ticksPerFrame = timePerFrame / updateInterval; +} +void OLEDDisplayUi::setTimePerTransition(uint16_t time){ + this->timePerTransition = time; + this->ticksPerTransition = timePerTransition / updateInterval; +} + +// -/------ Customize indicator position and style -------\- +void OLEDDisplayUi::enableIndicator(){ + this->state.isIndicatorDrawn = true; +} + +void OLEDDisplayUi::disableIndicator(){ + this->state.isIndicatorDrawn = false; +} + +void OLEDDisplayUi::enableAllIndicators(){ + this->shouldDrawIndicators = true; +} + +void OLEDDisplayUi::disableAllIndicators(){ + this->shouldDrawIndicators = false; +} + +void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) { + this->indicatorPosition = pos; +} +void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) { + this->indicatorDirection = dir; +} +void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) { + this->activeSymbol = symbol; +} +void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) { + this->inactiveSymbol = symbol; +} + + +// -/----- Frame settings -----\- +void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) { + this->frameAnimationDirection = dir; +} +void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) { + this->frameFunctions = frameFunctions; + this->frameCount = frameCount; + this->resetState(); +} + +// -/----- Overlays ------\- +void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){ + this->overlayFunctions = overlayFunctions; + this->overlayCount = overlayCount; +} + +// -/----- Loading Process -----\- + +void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) { + this->loadingDrawFunction = loadingDrawFunction; +} + +void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) { + uint8_t progress = 0; + uint8_t increment = 100 / stagesCount; + + for (uint8_t i = 0; i < stagesCount; i++) { + display->clear(); + this->loadingDrawFunction(this->display, &stages[i], progress); + display->display(); + + stages[i].callback(); + + progress += increment; + yield(); + } + + display->clear(); + this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress); + display->display(); + + delay(150); +} + +// -/----- Manual control -----\- +void OLEDDisplayUi::nextFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manualControl = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = 1; + } +} +void OLEDDisplayUi::previousFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manualControl = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = -1; + } +} + +void OLEDDisplayUi::switchToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->state.frameState = FIXED; + this->state.currentFrame = frame; + this->state.isIndicatorDrawn = true; +} + +void OLEDDisplayUi::transitionToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->nextFrameNumber = frame; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.manualControl = true; + this->state.frameState = IN_TRANSITION; + this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1; +} + + +// -/----- State information -----\- +OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ + return &this->state; +} + +int16_t OLEDDisplayUi::update(){ +#ifdef ARDUINO + unsigned long frameStart = millis(); +#elif __MBED__ + Timer t; + t.start(); + unsigned long frameStart = t.read_ms(); +#else +#error "Unkown operating system" +#endif + int32_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); + if ( timeBudget <= 0) { + // Implement frame skipping to ensure time budget is kept + if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil((double)-timeBudget / (double)this->updateInterval); + + this->state.lastUpdate = frameStart; + this->tick(); + } +#ifdef ARDUINO + return this->updateInterval - (millis() - frameStart); +#elif __MBED__ + return this->updateInterval - (t.read_ms() - frameStart); +#else +#error "Unkown operating system" +#endif +} + + +void OLEDDisplayUi::tick() { + this->state.ticksSinceLastStateSwitch++; + + switch (this->state.frameState) { + case IN_TRANSITION: + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){ + this->state.frameState = FIXED; + this->state.currentFrame = getNextFrameNumber(); + this->state.ticksSinceLastStateSwitch = 0; + this->nextFrameNumber = -1; + } + break; + case FIXED: + // Revert manualControl + if (this->state.manualControl) { + this->state.frameTransitionDirection = this->lastTransitionDirection; + this->state.manualControl = false; + } + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){ + if (this->autoTransition){ + this->state.frameState = IN_TRANSITION; + } + this->state.ticksSinceLastStateSwitch = 0; + } + break; + } + + this->display->clear(); + this->drawFrame(); + if (shouldDrawIndicators) { + this->drawIndicator(); + } + this->drawOverlays(); + this->display->display(); +} + +void OLEDDisplayUi::resetState() { + this->state.lastUpdate = 0; + this->state.ticksSinceLastStateSwitch = 0; + this->state.frameState = FIXED; + this->state.currentFrame = 0; + this->state.isIndicatorDrawn = true; +} + +void OLEDDisplayUi::drawFrame(){ + switch (this->state.frameState){ + case IN_TRANSITION: { + float progress = 0.f; + if (this->ticksPerTransition > 0u) { + progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition; + } + int16_t x = 0, y = 0, x1 = 0, y1 = 0; + switch(this->frameAnimationDirection){ + case SLIDE_LEFT: + x = -this->display->width() * progress; + y = 0; + x1 = x + this->display->width(); + y1 = 0; + break; + case SLIDE_RIGHT: + x = this->display->width() * progress; + y = 0; + x1 = x - this->display->width(); + y1 = 0; + break; + case SLIDE_UP: + x = 0; + y = -this->display->height() * progress; + x1 = 0; + y1 = y + this->display->height(); + break; + case SLIDE_DOWN: + default: + x = 0; + y = this->display->height() * progress; + x1 = 0; + y1 = y - this->display->height(); + break; + } + + // Invert animation if direction is reversed. + int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1; + x *= dir; y *= dir; x1 *= dir; y1 *= dir; + + bool drawnCurrentFrame; + + + // Probe each frameFunction for the indicator drawn state + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y); + drawnCurrentFrame = this->state.isIndicatorDrawn; + + this->enableIndicator(); + (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1); + + // Build up the indicatorDrawState + if (drawnCurrentFrame && !this->state.isIndicatorDrawn) { + // Drawn now but not next + this->indicatorDrawState = 2; + } else if (!drawnCurrentFrame && this->state.isIndicatorDrawn) { + // Not drawn now but next + this->indicatorDrawState = 1; + } else if (!drawnCurrentFrame && !this->state.isIndicatorDrawn) { + // Not drawn in both frames + this->indicatorDrawState = 3; + } + + // If the indicator isn't draw in the current frame + // reflect it in state.isIndicatorDrawn + if (!drawnCurrentFrame) this->state.isIndicatorDrawn = false; + + break; + } + case FIXED: + // Always assume that the indicator is drawn! + // And set indicatorDrawState to "not known yet" + this->indicatorDrawState = 0; + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0); + break; + } +} + +void OLEDDisplayUi::drawIndicator() { + + // Only draw if the indicator is invisible + // for both frames or + // the indiactor is shown and we are IN_TRANSITION + if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawn && this->state.frameState != IN_TRANSITION)) { + return; + } + + uint8_t posOfHighlightFrame = 0; + float indicatorFadeProgress = 0; + + // if the indicator needs to be slided in we want to + // highlight the next frame in the transition + uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame; + + // Calculate the frame that needs to be highlighted + // based on the Direction the indiactor is drawn + switch (this->indicatorDirection){ + case LEFT_RIGHT: + posOfHighlightFrame = frameToHighlight; + break; + case RIGHT_LEFT: + default: + posOfHighlightFrame = this->frameCount - frameToHighlight; + break; + } + + switch (this->indicatorDrawState) { + case 1: // Indicator was not drawn in this frame but will be in next + // Slide IN + indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + case 2: // Indicator was drawn in this frame but not in next + // Slide OUT + indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + } + + //Space between indicators - reduce for small screen sizes + uint16_t indicatorSpacing = 12; + if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) { + indicatorSpacing = 6; + } + + uint16_t frameStartPos = (indicatorSpacing * frameCount / 2); + const uint8_t *image; + + uint16_t x = 0,y = 0; + + + for (uint8_t i = 0; i < this->frameCount; i++) { + + switch (this->indicatorPosition){ + case TOP: + y = 0 - (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case BOTTOM: + y = (this->display->height() - 8) + (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case RIGHT: + x = (this->display->width() - 8) + (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i; + break; + case LEFT: + default: + x = 0 - (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i; + break; + } + + if (posOfHighlightFrame == i) { + image = this->activeSymbol; + } else { + image = this->inactiveSymbol; + } + + this->display->drawFastImage(x, y, 8, 8, image); + } +} + +void OLEDDisplayUi::drawOverlays() { + for (uint8_t i=0;ioverlayCount;i++){ + (this->overlayFunctions[i])(this->display, &this->state); + } +} + +uint8_t OLEDDisplayUi::getNextFrameNumber(){ + if (this->nextFrameNumber != -1) return this->nextFrameNumber; + return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount; +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayUi.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayUi.h new file mode 100644 index 0000000..27a83e8 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/OLEDDisplayUi.h @@ -0,0 +1,315 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAYUI_h +#define OLEDDISPLAYUI_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#include +#else +#error "Unkown operating system" +#endif + +#include "OLEDDisplay.h" + +//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAYUI +#define DEBUG_OLEDDISPLAYUI(...) +#endif + +enum AnimationDirection { + SLIDE_UP, + SLIDE_DOWN, + SLIDE_LEFT, + SLIDE_RIGHT +}; + +enum IndicatorPosition { + TOP, + RIGHT, + BOTTOM, + LEFT +}; + +enum IndicatorDirection { + LEFT_RIGHT, + RIGHT_LEFT +}; + +enum FrameState { + IN_TRANSITION, + FIXED +}; + + +const uint8_t ANIMATION_activeSymbol[] PROGMEM = { + 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 +}; + +const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = { + 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 +}; + + +// Structure of the UiState +struct OLEDDisplayUiState { + uint64_t lastUpdate; + uint16_t ticksSinceLastStateSwitch; + + FrameState frameState; + uint8_t currentFrame; + + bool isIndicatorDrawn; + + // Normal = 1, Inverse = -1; + int8_t frameTransitionDirection; + + bool manualControl; + + // Custom data that can be used by the user + void* userData; +}; + +struct LoadingStage { + const char* process; + void (*callback)(); +}; + +typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state); +typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress); + +class OLEDDisplayUi { + private: + OLEDDisplay *display; + + // Symbols for the Indicator + IndicatorPosition indicatorPosition; + IndicatorDirection indicatorDirection; + + const uint8_t* activeSymbol; + const uint8_t* inactiveSymbol; + + bool shouldDrawIndicators; + + // Values for the Frames + AnimationDirection frameAnimationDirection; + + int8_t lastTransitionDirection; + + uint16_t ticksPerFrame; // ~ 5000ms at 30 FPS + uint16_t ticksPerTransition; // ~ 500ms at 30 FPS + + bool autoTransition; + + FrameCallback* frameFunctions; + uint8_t frameCount; + + // Internally used to transition to a specific frame + int8_t nextFrameNumber; + + // Values for Overlays + OverlayCallback* overlayFunctions; + uint8_t overlayCount; + + // Will the Indicator be drawn + // 3 Not drawn in both frames + // 2 Drawn this frame but not next + // 1 Not drawn this frame but next + // 0 Not known yet + uint8_t indicatorDrawState; + + // Loading screen + LoadingDrawFunction loadingDrawFunction; + + // UI State + OLEDDisplayUiState state; + + // Bookeeping for update + uint16_t updateInterval = 33; + + uint16_t timePerFrame; + uint16_t timePerTransition; + + uint8_t getNextFrameNumber(); + void drawIndicator(); + void drawFrame(); + void drawOverlays(); + void tick(); + void resetState(); + + public: + + OLEDDisplayUi(OLEDDisplay *display); + + /** + * Initialise the display + */ + void init(); + + /** + * Configure the internal used target FPS + */ + void setTargetFPS(uint8_t fps); + + // Automatic Control + /** + * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`. + */ + void enableAutoTransition(); + + /** + * Disable automatic transition to next frame. + */ + void disableAutoTransition(); + + /** + * Set the direction if the automatic transitioning + */ + void setAutoTransitionForwards(); + void setAutoTransitionBackwards(); + + /** + * Set the approximate time a frame is displayed + */ + void setTimePerFrame(uint16_t time); + + /** + * Set the approximate time a transition will take + */ + void setTimePerTransition(uint16_t time); + + // Customize indicator position and style + + /** + * Draw the indicator. + * This is the defaut state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ + void enableIndicator(); + + /** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ + void disableIndicator(); + + /** + * Enable drawing of indicators + */ + void enableAllIndicators(); + + /** + * Disable draw of indicators. + */ + void disableAllIndicators(); + + /** + * Set the position of the indicator bar. + */ + void setIndicatorPosition(IndicatorPosition pos); + + /** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ + void setIndicatorDirection(IndicatorDirection dir); + + /** + * Set the symbol to indicate an active frame in the indicator bar. + */ + void setActiveSymbol(const uint8_t* symbol); + + /** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ + void setInactiveSymbol(const uint8_t* symbol); + + + // Frame settings + + /** + * Configure what animation is used to transition from one frame to another + */ + void setFrameAnimation(AnimationDirection dir); + + /** + * Add frame drawing functions + */ + void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + + // Overlay + + /** + * Add overlays drawing functions that are draw independent of the Frames + */ + void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + + + // Loading animation + /** + * Set the function that will draw each step + * in the loading animation + */ + void setLoadingDrawFunction(LoadingDrawFunction loadingFunction); + + + /** + * Run the loading process + */ + void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + + + // Manual Control + void nextFrame(); + void previousFrame(); + + /** + * Switch without transition to frame `frame`. + */ + void switchToFrame(uint8_t frame); + + /** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ + void transitionToFrame(uint8_t frame); + + // State Info + OLEDDisplayUiState* getUiState(); + + int16_t update(); +}; +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106.h new file mode 100644 index 0000000..47188d1 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106.h @@ -0,0 +1,39 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106_h +#define SH1106_h +#include "SH1106Wire.h" + +// For make SH1106 an alias for SH1106Wire +typedef SH1106Wire SH1106; + + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106Brzo.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106Brzo.h new file mode 100644 index 0000000..be9c0c7 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106Brzo.h @@ -0,0 +1,141 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106Brzo_h +#define SH1106Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SH1106Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + byte k = 0; + uint8_t sendBuffer[17]; + sendBuffer[0] = 0x40; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * displayWidth]; + if (k == 16) { + brzo_i2c_write(sendBuffer, 17, true); + k = 0; + } + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + k = 0; + } + yield(); + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + } + brzo_i2c_end_transaction(); + #else + #endif + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t com) __attribute__((always_inline)){ + uint8_t command[2] = {0x80 /* command mode */, com}; + brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED); + brzo_i2c_write(command, 2, true); + brzo_i2c_end_transaction(); + } +}; + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106Spi.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106Spi.h new file mode 100644 index 0000000..b31dd84 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SH1106Spi.h @@ -0,0 +1,147 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106Spi_h +#define SH1106Spi_h + +#include "OLEDDisplay.h" +#include + +class SH1106Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + uint8_t _cs; + + public: + SH1106Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_rst = _rst; + this->_dc = _dc; + this->_cs = _cs; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_cs, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * displayWidth]); + } + digitalWrite(_cs, HIGH); + yield(); + } + #else + for (uint8_t y=0; y + +#define SH1106_SET_PUMP_VOLTAGE 0X30 +#define SH1106_SET_PUMP_MODE 0XAD +#define SH1106_PUMP_ON 0X8B +#define SH1106_PUMP_OFF 0X8A +//-------------------------------------- + +class SH1106Wire : public OLEDDisplay { + private: + uint8_t _address; + int _sda; + int _scl; + bool _doI2cAutoInit = false; + TwoWire* _wire = NULL; + int _frequency; + + public: + /** + * Create and initialize the Display using Wire library + * + * Beware for retro-compatibility default values are provided for all parameters see below. + * Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to + * ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple + * device on the same bus. + * + * @param _address I2C Display address + * @param _sda I2C SDA pin number, default to -1 to skip Wire begin call + * @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call) + * @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options + * @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE + * @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode + */ + SH1106Wire(uint8_t _address, int _sda = -1, int _scl = -1, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; +#if !defined(ARDUINO_ARCH_ESP32) + this->_wire = &Wire; +#else + this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1; +#endif + this->_frequency = _frequency; + } + + bool connect() { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH8266) + _wire->begin(); +#else + // On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us. + if(this->_sda != -1) + _wire->begin(this->_sda, this->_scl); +#endif + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + if(this->_frequency != -1) + _wire->setClock(this->_frequency); + return true; + } + + void display(void) { + initI2cIfNeccesary(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + _wire->beginTransmission(_address); + _wire->write(0x40); + } + _wire->write(buffer[x + y * displayWidth]); + k++; + if (k == 16) { + _wire->endTransmission(); + k = 0; + } + } + if (k != 0) { + _wire->endTransmission(); + k = 0; + } + yield(); + } + + if (k != 0) { + _wire->endTransmission(); + } + #else + uint8_t * p = &buffer[0]; + for (uint8_t y=0; y<8; y++) { + sendCommand(0xB0+y); + sendCommand(0x02); + sendCommand(0x10); + for( uint8_t x=0; x<8; x++) { + _wire->beginTransmission(_address); + _wire->write(0x40); + for (uint8_t k = 0; k < 16; k++) { + _wire->write(*p++); + } + _wire->endTransmission(); + } + } + #endif + } + + void setI2cAutoInit(bool doI2cAutoInit) { + _doI2cAutoInit = doI2cAutoInit; + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + _wire->beginTransmission(_address); + _wire->write(0x80); + _wire->write(command); + _wire->endTransmission(); + } + + void initI2cIfNeccesary() { + if (_doI2cAutoInit) { +#ifdef ARDUINO_ARCH_AVR + _wire->begin(); +#else + _wire->begin(this->_sda, this->_scl); +#endif + } + } + +}; + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306.h new file mode 100644 index 0000000..f6bd554 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306.h @@ -0,0 +1,39 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306_h +#define SSD1306_h +#include "SSD1306Wire.h" + +// For legacy support make SSD1306 an alias for SSD1306 +typedef SSD1306Wire SSD1306; + + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306Brzo.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306Brzo.h new file mode 100644 index 0000000..fbcffcd --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306Brzo.h @@ -0,0 +1,167 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306Brzo_h +#define SSD1306Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SSD1306Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + const int x_offset = (128 - this->width()) / 2; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); + sendCommand(x_offset + maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + + int buflen = ( this->width() / 8 ) + 1; + + uint8_t sendBuffer[buflen]; + sendBuffer[0] = 0x40; + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * this->width()]; + if (k == (buflen-1)) { + brzo_i2c_write(sendBuffer, buflen, true); + k = 0; + } + } + yield(); + } + brzo_i2c_write(sendBuffer, k + 1, true); + brzo_i2c_end_transaction(); + #else + // No double buffering + sendCommand(COLUMNADDR); + + sendCommand(x_offset); + sendCommand(x_offset + (this->width() - 1)); + + sendCommand(PAGEADDR); + sendCommand(0x0); + sendCommand((this->height() / 8) - 1); + + int buflen = ( this->width() / 8 ) + 1; + + uint8_t sendBuffer[buflen]; + sendBuffer[0] = 0x40; + + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + + for (uint16_t i=0; i + +#ifndef UINT8_MAX + #define UINT8_MAX 0xff +#endif + +class SSD1306I2C : public OLEDDisplay { +public: + SSD1306I2C(uint8_t _address, PinName _sda, PinName _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address << 1; // convert from 7 to 8 bit for mbed. + this->_sda = _sda; + this->_scl = _scl; + _i2c = new I2C(_sda, _scl); + } + + bool connect() { + // mbed supports 100k and 400k some device maybe 1000k +#ifdef TARGET_STM32L4 + _i2c->frequency(1000000); +#else + _i2c->frequency(400000); +#endif + return true; + } + + void display(void) { + const int x_offset = (128 - this->width()) / 2; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = std::min(minBoundY, y); + maxBoundY = std::max(maxBoundY, y); + minBoundX = std::min(minBoundX, x); + maxBoundX = std::max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); // column start address (0 = reset) + sendCommand(x_offset + maxBoundX); // column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(minBoundY); // page start address + sendCommand(maxBoundY); // page end address + + for (y = minBoundY; y <= maxBoundY; y++) { + uint8_t *start = &buffer[(minBoundX + y * this->width())-1]; + uint8_t save = *start; + + *start = 0x40; // control + _i2c->write(_address, (char *)start, (maxBoundX-minBoundX) + 1 + 1); + *start = save; + } +#else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); // column start address (0 = reset) + sendCommand(x_offset + (this->width() - 1));// column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(0x0); // page start address (0 = reset) + + if (geometry == GEOMETRY_128_64) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + buffer[-1] = 0x40; // control + _i2c->write(_address, (char *)&buffer[-1], displayBufferSize + 1); +#endif + } + +private: + int getBufferOffset(void) { + return 0; + } + + inline void sendCommand(uint8_t command) __attribute__((always_inline)) { + char _data[2]; + _data[0] = 0x80; // control + _data[1] = command; + _i2c->write(_address, _data, sizeof(_data)); + } + + uint8_t _address; + PinName _sda; + PinName _scl; + I2C *_i2c; +}; + +#endif + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306Spi.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306Spi.h new file mode 100644 index 0000000..08cb4a8 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/ESP8266_and_ESP32_OLED_driver_for_SSD1306_displays/src/SSD1306Spi.h @@ -0,0 +1,163 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306Spi_h +#define SSD1306Spi_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + uint8_t _cs; + + public: + SSD1306Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_rst = _rst; + this->_dc = _dc; + this->_cs = _cs; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_cs, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(minBoundX); + sendCommand(maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * displayWidth]); + } + yield(); + } + digitalWrite(_cs, HIGH); + #else + // No double buffering + sendCommand(COLUMNADDR); + sendCommand(0x0); + sendCommand(0x7F); + + sendCommand(PAGEADDR); + sendCommand(0x0); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32 ) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (uint16_t i=0; i +#include + +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_STM32) +#define _min min +#define _max max +#endif +//-------------------------------------- + +class SSD1306Wire : public OLEDDisplay { + private: + uint8_t _address; + int _sda; + int _scl; + bool _doI2cAutoInit = false; + TwoWire* _wire = NULL; + int _frequency; + + public: + + /** + * Create and initialize the Display using Wire library + * + * Beware for retro-compatibility default values are provided for all parameters see below. + * Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to + * ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple + * device on the same bus. + * + * @param _address I2C Display address + * @param _sda I2C SDA pin number, default to -1 to skip Wire begin call + * @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call) + * @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options + * @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE + * @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode + */ + SSD1306Wire(uint8_t _address, int _sda = -1, int _scl = -1, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; +#if !defined(ARDUINO_ARCH_ESP32) + this->_wire = &Wire; +#else + this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1; +#endif + this->_frequency = _frequency; + } + + bool connect() { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP8266) + _wire->begin(); +#else + // On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us. + if(this->_sda != -1) + _wire->begin(this->_sda, this->_scl); +#endif + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + if(this->_frequency != -1) + _wire->setClock(this->_frequency); + return true; + } + + void display(void) { + initI2cIfNeccesary(); + const int x_offset = (128 - this->width()) / 2; + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = std::min(minBoundY, y); + maxBoundY = std::max(maxBoundY, y); + minBoundX = std::min(minBoundX, x); + maxBoundX = std::max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); + sendCommand(x_offset + maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + _wire->beginTransmission(_address); + _wire->write(0x40); + } + + _wire->write(buffer[x + y * this->width()]); + k++; + if (k == 16) { + _wire->endTransmission(); + k = 0; + } + } + yield(); + } + + if (k != 0) { + _wire->endTransmission(); + } + #else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); + sendCommand(x_offset + (this->width() - 1)); + + sendCommand(PAGEADDR); + sendCommand(0x0); + + for (uint16_t i=0; i < displayBufferSize; i++) { + _wire->beginTransmission(this->_address); + _wire->write(0x40); + for (uint8_t x = 0; x < 16; x++) { + _wire->write(buffer[i]); + i++; + } + i--; + _wire->endTransmission(); + } + #endif + } + + void setI2cAutoInit(bool doI2cAutoInit) { + _doI2cAutoInit = doI2cAutoInit; + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + initI2cIfNeccesary(); + _wire->beginTransmission(_address); + _wire->write(0x80); + _wire->write(command); + _wire->endTransmission(); + } + + void initI2cIfNeccesary() { + if (_doI2cAutoInit) { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP8266) + _wire->begin(); +#else + _wire->begin(this->_sda, this->_scl); +#endif + } + } + +}; + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_checkConfig.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_checkConfig.h new file mode 100644 index 0000000..19a8f0f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_checkConfig.h @@ -0,0 +1,346 @@ +// FARM DATA RELAY SYSTEM +// +// DETAILED NODES' CONFIGURATION CHECK +// +// Make sure #define DEBUG_CONFIG is not uncommented in your node's config +// (fdrs_node_config.h or fdrs_gateway_config.h). Otherwise check will be ignored. +// When the node powers up, it's full config will be printed to the serial console once. +// Be sure to add further checks as new configuration possibilities are added to FDRS. +// +// Contributed by Sascha Juch (sascha.juch@gmail.com) +// +#ifndef __FDRS_CHECKCONFIG_h__ +#define __FDRS_CHECKCONFIG_h__ + +const char* separatorLine = "--------------------------------------------------------------"; +const char* headerAndFooter = "=============================================================="; + + +// helper function for obfuscating passwords +String obfuscatePassword(String password) { + char obfuscatedPass[password.length()]; + // TO DO: The following line is disabled due to AVR incompatibility. + // std::fill(obfuscatedPass, obfuscatedPass + password.length(), '*'); + return String(obfuscatedPass); +} + + +// helper function for small header above each sub section +void printSmallSectionHeader(const char* headerText) { + DBG(separatorLine); + DBG(headerText); +} + + +// helper function for a nice little header above each section +void printSectionHeader(const char* headerText) { + DBG(separatorLine); + DBG(headerText); + DBG(separatorLine); +} + + +// helper function for a nice little header above each main section +void printConfigHeader(const char* headerText) { + DBG(headerAndFooter); + DBG(headerText); + DBG(headerAndFooter); +} + + +// check which logging method(s) have been activated for a node +void printLoggingInformation() { + printSectionHeader("LOG SETTINGS OF DEVICE"); + +#if defined(USE_SD_LOG) && defined(USE_FS_LOG) + DBG("Logging to SD card AND file system is active! You should better use only one of them at a time"); +#endif + +#ifdef USE_SD_LOG + DBG("Logging to SD-Card : enabled"); +#ifdef LOGBUF_DELAY + DBG("log buffer delay in ms: " + String(LOGBUF_DELAY)); +#else + DBG("log buffer delay in ms: NOT SPECIFIED - check config!"); +#endif +#ifdef LOG_FILENAME + DBG("log filename : " + LOG_FILENAME); +#else + DBG("log filename : NOT SPECIFIED - check config!"); +#endif +#else + DBG("Logging to SD-Card : disabled"); +#endif //USE_SD_LOG + +#ifdef USE_FS_LOG + DBG("Logging to file system: enabled"); +#ifdef LOGBUF_DELAY + DBG("log buffer delay in ms: " + String(LOGBUF_DELAY)); +#else + DBG("log buffer delay in ms: NOT SPECIFIED - check config!"); +#endif +#ifdef LOG_FILENAME + DBG("log filename : " + LOG_FILENAME); +#else + DBG("log filename : NOT SPECIFIED - check config!"); +#endif + DBG("WARNING: Permanently logging to flash memory may destroy the flash memory of your device!"); +#else + DBG("Logging to file system: disabled"); +#endif //USE_FS_LOG +} + + +// check which protocols are activated and which are deactivated +void printActivatedProtocols() { + // current candidates are: ESPNOW, LORA and MQTT (WIFI) + printSectionHeader("ACTIVATED PROTOCOLS"); + +#ifdef USE_LORA + DBG("LoRa : ENABLED"); +#else + DBG("LoRa : DISABLED"); +#endif + +#ifdef USE_ESPNOW + DBG("ESPNow : ENABLED"); +#else + DBG("ESPNow : DISABLED"); +#endif + +#ifdef USE_WIFI + DBG("WiFi : ENABLED"); +#else + DBG("WiFi : DISABLED"); +#endif + +#if defined(USE_WIFI) && defined(USE_ESPNOW) + DBG("WARNING: You must not use USE_ESPNOW and USE_WIFI together! USE_WIFI is only needed for MQTT!"); +#endif +} + + +void printEspnowDetails() { +#ifdef USE_ESPNOW + +#ifdef UNIT_MAC + printSmallSectionHeader("ESP-Now Details:"); + DBG("Neighbor 1 address: " + String(ESPNOW_NEIGHBOR_1, HEX)); + DBG("Neighbor 2 address: " + String(ESPNOW_NEIGHBOR_2, HEX)); +#endif //UNIT_MAC + +#endif //USE_ESPNOW +} + + +void printWifiDetails() { +#ifdef USE_WIFI + printSmallSectionHeader("WiFi Details:"); + +#if defined(WIFI_SSID) + DBG("WiFi SSID used from WIFI_SSID : " + String(FDRS_WIFI_SSID)); +#elif defined (GLOBAL_WIFI_SSID) + DBG("WiFi SSID used from GLOBAL_WIFI_SSID : " + String(FDRS_WIFI_SSID)); +#else + DBG("NO WiFi SSID defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //WIFI_SSID + +#if defined(WIFI_PASS) + DBG("WiFi password used from WIFI_PASS : " + obfuscatePassword(FDRS_WIFI_PASS)); +#elif defined (GLOBAL_WIFI_SSID) + DBG("WiFi password used from GLOBAL_WIFI_PASS : " + obfuscatePassword(FDRS_WIFI_PASS)); +#else + DBG("NO WiFi password defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //WIFI_PASS + + printSmallSectionHeader("MQTT BROKER CONFIG:"); + +#if defined(MQTT_ADDR) + DBG("MQTT address used from MQTT_ADDR : " + String(FDRS_MQTT_ADDR)); +#elif defined (GLOBAL_MQTT_ADDR) + DBG("MQTT address used from GLOBAL_MQTT_ADDR : " + String(FDRS_MQTT_ADDR)); +#else + DBG("NO MQTT address defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //MQTT_ADDR + +#if defined(MQTT_PORT) + DBG("MQTT port used from MQTT_PORT : " + String(FDRS_MQTT_PORT)); +#elif defined (GLOBAL_MQTT_PORT) + DBG("MQTT port used from GLOBAL_MQTT_ADDR : " + String(FDRS_MQTT_PORT)); +#else + DBG("Using default MQTT port : " + String(FDRS_MQTT_PORT)); +#endif //MQTT_PORT + +#ifdef FDRS_MQTT_AUTH + printSmallSectionHeader("MQTT AUTHENTIFICATION CONFIG:"); +//GLOBAL_MQTT_AUTH +#if defined(MQTT_USER) + DBG("MQTT username used from MQTT_USER : " + String(FDRS_MQTT_USER)); +#elif defined (GLOBAL_MQTT_USER) + DBG("MQTT username used from GLOBAL_MQTT_USER : " + String(FDRS_MQTT_USER)); +#else + DBG("NO MQTT username defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //MQTT_USER + +#if defined(MQTT_PASS) + DBG("MQTT password used from MQTT_PASS : " + obfuscatePassword(FDRS_MQTT_PASS)); +#elif defined (GLOBAL_MQTT_PASS) + DBG("MQTT password used from GLOBAL_MQTT_PASS : " + obfuscatePassword(FDRS_MQTT_PASS)); +#else + DBG("NO MQTT password defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //MQTT_PASS + +#endif //FDRS_MQTT_AUTH + +#if defined(TOPIC_DATA) + DBG("MQTT topic (TOPIC_DATA) : " + String(TOPIC_DATA)); +#else + DBG("NO MQTT topic defined! Please define TOPIC_DATA in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //TOPIC_DATA + +#if defined(TOPIC_STATUS) + DBG("MQTT topic (TOPIC_STATUS) : " + String(TOPIC_STATUS)); +#else + DBG("NO MQTT topic defined! Please define TOPIC_STATUS in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //TOPIC_STATUS + +#if defined(TOPIC_COMMAND) + DBG("MQTT topic (TOPIC_COMMAND) : " + String(TOPIC_COMMAND)); +#else + DBG("NO MQTT topic defined! Please define TOPIC_COMMAND in fdrs_globals.h (recommended) or in fdrs_node_config.h / fdrs_gateway_config.h"); + //exit(0); +#endif //TOPIC_COMMAND + + DBG(separatorLine); + DBG(separatorLine); + +#endif //USE_WIFI +} + + +void printLoraDetails() { +#ifdef USE_LORA + printSmallSectionHeader("LoRa Details:"); + +#if defined(FDRS_LORA_FREQUENCY) + DBG("LoRa frequency used from FDRS_LORA_FREQUENCY : " + String(FDRS_LORA_FREQUENCY)); +#elif defined (GLOBAL_FDRS_LORA_FREQUENCY) + DBG("LoRa frequency used from GLOBAL_FDRS_LORA_FREQUENCY : " + String(FDRS_LORA_FREQUENCY)); +#else + DBG("NO FDRS_LORA_FREQUENCY defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); + //exit(0); +#endif //LORA-FREQUENCY + +#if defined(LORA_SF) + DBG("LoRa SF used from LORA_SF : " + String(FDRS_LORA_SF)); +#elif defined (GLOBAL_LORA_SF) + DBG("LoRa SF used from GLOBAL_LORA_SF : " + String(FDRS_LORA_SF)); +#else +// ASSERT("NO LORA-SF defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); + DBG("NO LORA_SF defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); + //exit(0); +#endif //LORA_SF + +#if defined(LORA_TXPWR) + DBG("LoRa TXPWR used from LORA_TXPWR : " + String(FDRS_LORA_TXPWR)); +#elif defined (GLOBAL_LORA_TXPWR) + DBG("LoRa TXPWR used from GLOBAL_LORA_TXPWR : " + String(FDRS_LORA_TXPWR)); +#else +// ASSERT("NO LORA-TXPWR defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); + DBG("NO LORA_TXPWR defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); + //exit(0); +#endif //LORA_TXPWR + +#if defined(LORA_ACK) + DBG("LoRa acknowledgement used from LORA_ACK : enabled"); +#elif defined (GLOBAL_LORA_ACK) + DBG("LoRa acknowledgement used from GLOBAL_LORA_ACK: enabled"); +#else + DBG("LoRa acknowledgement : disabled"); +#endif + +#if defined(LORA_ACK) || defined(GLOBAL_LORA_ACK) + +#if defined(LORA_ACK_TIMEOUT) + DBG("Timeout for Lora acknowledment (LORA_ACK) : " + String(LORA_ACK_TIMEOUT)); +#else + DBG("NO LORA_ACK_TIMEOUT defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); +#endif // LORA_ACK_TIMEOUT + +#if defined(LORA_RETRIES) + DBG("Number of ack retries (LORA_RETRIES) : " + String(LORA_RETRIES)); + + if (LORA_RETRIES >= 0 && LORA_RETRIES <= 3) + { + DBG("Number of ack retries (LORA_RETRIES) : within allowed range."); + } + else { + DBG("Number of ack retries (LORA_RETRIES) : not within allowed range [0 - 3]! Please change to correct value."); + } // LORA_RETRIES RANGE CHECK + +#else + DBG("NO LORA_RETRIES defined! Defaulting to 0. Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); +#endif // LORA_RETRIES + + +#endif //LORA_ACK || GLOBAL_LORA_ACK + +#ifdef UNIT_MAC + DBG("LoRa Neighbors"); + DBG("Neighbor 1 address: " + String(LORA_NEIGHBOR_1, HEX)); + DBG("Neighbor 2 address: " + String(LORA_NEIGHBOR_2, HEX)); +#endif //UNIT_MAC + +#endif //USE_LORA +} + + +void checkConfig() { + printConfigHeader("NODE CONFIGURATION OVERVIEW"); +#ifdef UNIT_MAC + DBG("Device Type : Gateway"); + DBG("Gateway ID : " + String(UNIT_MAC, HEX)); +#elif defined (READING_ID) + DBG("Device Type : Node"); + DBG("Reading ID : " + String(READING_ID)); + DBG("Node's Gateway: " + String(GTWY_MAC, HEX)); +#else + DBG("Device Type : UNKNOWN!"); + DBG("Please check config!"); + DBG("If you have just created a new node type,"); + DBG("please add it's config check to:"); + DGB("fdrs_checkConfig.h"); +#endif + + printActivatedProtocols(); + + printSmallSectionHeader("PROTOCOL DETAILS"); + +#ifdef USE_LORA + printLoraDetails(); +#endif + +#ifdef USE_ESPNOW + printEspnowDetails(); +#endif + +#ifdef USE_WIFI + printWifiDetails(); +#endif + + printLoggingInformation(); + + printConfigHeader("NODE CONFIGURATION OVERVIEW END"); + //DBG(separatorLine); + DBG(""); +} + +#endif //__FDRS_CHECKCONFIG_h__ + diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_datatypes.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_datatypes.h new file mode 100644 index 0000000..2652fb5 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_datatypes.h @@ -0,0 +1,87 @@ +// A list of all datatypes you can use within FDRS. +// If you are missing any data type, please open an issue at: +// https://github.com/timmbogner/Farm-Data-Relay-System/issues + +typedef struct FDRSPeer { + uint8_t mac[6]; + uint32_t last_seen = 0; + +} FDRSPeer; + +typedef struct __attribute__((packed)) DataReading { + float d; + uint16_t id; + uint8_t t; + +} DataReading; + +typedef struct __attribute__((packed)) SystemPacket { + uint8_t cmd; + uint32_t param; +} SystemPacket; + +enum crcResult { + CRC_NULL, + CRC_OK, + CRC_BAD, +} returnCRC; + +enum { + cmd_clear, + cmd_ping, + cmd_add, + cmd_ack, +}; + +enum +{ + event_clear, + event_espnowg, + event_espnow1, + event_espnow2, + event_serial, + event_mqtt, + event_lorag, + event_lora1, + event_lora2, + event_internal +}; +#ifndef FDRS_DATA_TYPES +#define FDRS_DATA_TYPES + +#define STATUS_T 0 // Status +#define TEMP_T 1 // Temperature +#define TEMP2_T 2 // Temperature #2 +#define HUMIDITY_T 3 // Relative Humidity +#define PRESSURE_T 4 // Atmospheric Pressure +#define LIGHT_T 5 // Light (lux) +#define SOIL_T 6 // Soil Moisture +#define SOIL2_T 7 // Soil Moisture #2 +#define SOILR_T 8 // Soil Resistance +#define SOILR2_T 9 // Soil Resistance #2 +#define OXYGEN_T 10 // Oxygen +#define CO2_T 11 // Carbon Dioxide +#define WINDSPD_T 12 // Wind Speed +#define WINDHDG_T 13 // Wind Direction +#define RAINFALL_T 14 // Rainfall +#define MOTION_T 15 // Motion +#define VOLTAGE_T 16 // Voltage +#define VOLTAGE2_T 17 // Voltage #2 +#define CURRENT_T 18 // Current +#define CURRENT2_T 19 // Current #2 +#define IT_T 20 // Iterations +#define LATITUDE_T 21 // GPS Latitude +#define LONGITUDE_T 22 // GPS Longitude +#define ALTITUDE_T 23 // GPS Altitude +#define HDOP_T 24 // GPS HDOP +#define LEVEL_T 25 // Fluid Level +#define UV_T 26 // UV +#define PM1_T 27 // 1 Particles +#define PM2_5_T 28 // 2.5 Particles +#define PM10_T 29 // 10 Particles +#define POWER_T 30 // Power +#define POWER2_T 31 // Power #2 +#define ENERGY_T 32 // Energy +#define ENERGY2_T 33 // Energy #2 + +#endif //FDRS_DATA_TYPES \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_debug.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_debug.h new file mode 100644 index 0000000..470d446 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_debug.h @@ -0,0 +1,14 @@ +#ifdef FDRS_DEBUG +#ifdef USE_OLED +#define DBG(a) debug_OLED(String(a)); \ +Serial.println(a); +#else +#define DBG(a) Serial.println(a); +#endif +#else +#ifdef USE_OLED +#define DBG(a) debug_OLED(String(a)); +#else +#define DBG(a) +#endif +#endif \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway.h new file mode 100644 index 0000000..18c89be --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway.h @@ -0,0 +1,247 @@ +// FARM DATA RELAY SYSTEM +// +// GATEWAY Main Functions +// Developed by Timm Bogner (timmbogner@gmail.com) + +#include "fdrs_datatypes.h" +#include "fdrs_globals.h" +#define FDRS_GATEWAY + +#ifndef ESPNOWG_ACT +#define ESPNOWG_ACT +#endif +#ifndef ESPNOW1_ACT +#define ESPNOW1_ACT +#endif +#ifndef ESPNOW2_ACT +#define ESPNOW2_ACT +#endif +#ifndef SERIAL_ACT +#define SERIAL_ACT +#endif +#ifndef MQTT_ACT +#define MQTT_ACT +#endif +#ifndef LORAG_ACT +#define LORAG_ACT +#endif +#ifndef LORA1_ACT +#define LORA1_ACT +#endif +#ifndef LORA2_ACT +#define LORA2_ACT +#endif +#ifndef INTERNAL_ACT +#define INTERNAL_ACT +#endif +#ifdef USE_ETHERNET +#ifndef USE_WIFI +#define USE_WIFI +#endif +#endif // USE_ETHERNET + +SystemPacket theCmd; +DataReading theData[256]; +uint8_t ln; +uint8_t newData = event_clear; +uint8_t newCmd = cmd_clear; + +DataReading fdrsData[256]; // buffer for loadFDRS() +uint8_t data_count = 0; + +// Function Prototypes needed due to #ifdefs being moved outside of function definitions in header files +void broadcastLoRa(); +void sendLoRaNbr(uint8_t); +void timeFDRSLoRa(uint8_t *); +static uint16_t crc16_update(uint16_t, uint8_t); +void sendESPNowNbr(uint8_t); +void sendESPNowPeers(); +void sendESPNow(uint8_t); + +void sendMQTT(); +void sendLog(); +void resendLog(); +void releaseLogBuffer(); + +#ifdef USE_OLED + #include "fdrs_oled.h" +#endif +#include "fdrs_debug.h" +#include "fdrs_gateway_serial.h" +#include "fdrs_gateway_scheduler.h" +#ifdef USE_ESPNOW + #include "fdrs_gateway_espnow.h" +#endif +#ifdef USE_LORA + #include "fdrs_gateway_lora.h" +#endif +#ifdef USE_WIFI + #include "fdrs_gateway_wifi.h" + #include "fdrs_gateway_mqtt.h" +#endif +#if defined(USE_FS_LOG) || defined(USE_SD_LOG) + #include "fdrs_gateway_filesystem.h" +#endif +#ifdef DEBUG_CONFIG + #include "fdrs_checkConfig.h" +#endif + +void sendFDRS() +{ + for (int i = 0; i < data_count; i++) + { + theData[i].id = fdrsData[i].id; + theData[i].t = fdrsData[i].t; + theData[i].d = fdrsData[i].d; + } + ln = data_count; + data_count = 0; + newData = event_internal; + DBG("Entered internal data."); +} + +void loadFDRS(float d, uint8_t t, uint16_t id) +{ + DBG("Id: " + String(id) + " - Type: " + String(t) + " - Data loaded: " + String(d)); + DataReading dr; + dr.id = id; + dr.t = t; + dr.d = d; + fdrsData[data_count] = dr; + data_count++; +} + +void beginFDRS() +{ +#if defined(ESP8266) + Serial.begin(115200); +#elif defined(ESP32) + Serial.begin(115200); + UART_IF.begin(115200, SERIAL_8N1, RXD2, TXD2); +#endif +#ifdef USE_OLED + init_oled(); + DBG("Display initialized!"); + DBG("Hello, World!"); +#endif + DBG("Address:" + String(UNIT_MAC, HEX)); +#ifdef USE_LORA + begin_lora(); + scheduleFDRS(asyncReleaseLoRaFirst, FDRS_LORA_INTERVAL); +#endif +#ifdef USE_WIFI + begin_wifi(); + DBG("Connected."); + begin_mqtt(); +#endif +#ifdef USE_ESPNOW + begin_espnow(); +#endif +#ifdef USE_SD_LOG + begin_SD(); +#endif +#ifdef USE_FS_LOG + begin_FS(); +#endif + +#ifdef USE_WIFI + client.publish(TOPIC_STATUS, "FDRS initialized"); +#endif +} + +void handleCommands() +{ + switch (theCmd.cmd) + { + case cmd_ping: +#ifdef USE_ESPNOW + pingback_espnow(); +#endif // USE_ESPNOW + + break; + + case cmd_add: +#ifdef USE_ESPNOW + add_espnow_peer(); +#endif // USE_ESPNOW + + break; + } + theCmd.cmd = cmd_clear; + theCmd.param = 0; +} + +void loopFDRS() +{ + handle_schedule(); + handleCommands(); +#if defined(USE_SD_LOG) || defined(USE_FS_LOG) + handleLogger(); +#endif + handleSerial(); +#ifdef USE_LORA + handleLoRa(); +#endif +#ifdef USE_WIFI + handleMQTT(); +#endif +#ifdef USE_OLED + drawPageOLED(true); +#endif + if (newData != event_clear) + { + Serial.print("hum:"); + Serial.print(theData[0].d); + Serial.print(",temp:"); + Serial.print(theData[1].d); + Serial.print(",press:"); + Serial.print(theData[2].d); + Serial.println(); + switch (newData) + { + case event_espnowg: + ESPNOWG_ACT + break; + case event_espnow1: + ESPNOW1_ACT + break; + case event_espnow2: + ESPNOW2_ACT + break; + case event_serial: + SERIAL_ACT + break; + case event_mqtt: + MQTT_ACT + break; + case event_lorag: + LORAG_ACT + break; + case event_lora1: + LORA1_ACT + break; + case event_lora2: + LORA2_ACT + break; + case event_internal: + INTERNAL_ACT + break; + } + newData = event_clear; + } +} + +// "Skeleton Functions related to FDRS Actions" +#ifndef USE_LORA + void broadcastLoRa() {} + void sendLoRaNbr(uint8_t address) {} + void timeFDRSLoRa(uint8_t *address) {} +#endif +#ifndef USE_ESPNOW + void sendESPNowNbr(uint8_t interface) {} + void sendESPNowPeers() {} + void sendESPNow(uint8_t address) {} +#endif +#ifndef USE_WIFI + void sendMQTT() {} +#endif \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_espnow.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_espnow.h new file mode 100644 index 0000000..9caeb4d --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_espnow.h @@ -0,0 +1,349 @@ +#ifdef ESP8266 +#include +#include +#elif defined(ESP32) +#include +#include +#include +#endif + +#define PEER_TIMEOUT 300000 +FDRSPeer peer_list[16]; +const uint8_t espnow_size = 250 / sizeof(DataReading); + +#ifdef ESP32 +esp_now_peer_info_t peerInfo; +#endif + +const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +const uint8_t mac_prefix[] = {MAC_PREFIX}; +uint8_t selfAddress[] = {MAC_PREFIX, UNIT_MAC}; +uint8_t incMAC[6]; + +uint8_t ESPNOW1[] = {MAC_PREFIX, ESPNOW_NEIGHBOR_1}; +uint8_t ESPNOW2[] = {MAC_PREFIX, ESPNOW_NEIGHBOR_2}; + + +// Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 +#if defined(ESP8266) +void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) +{ +} +void OnDataRecv(uint8_t *mac, uint8_t *incomingData, uint8_t len) +{ +#elif defined(ESP32) +void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) +{ +} +void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) +{ +#endif + memcpy(&incMAC, mac, sizeof(incMAC)); + if (len < sizeof(DataReading)) + { + DBG("ESP-NOW System Packet"); + memcpy(&theCmd, incomingData, sizeof(theCmd)); + memcpy(&incMAC, mac, sizeof(incMAC)); + return; + } + memcpy(&theData, incomingData, sizeof(theData)); + DBG("Incoming ESP-NOW."); + ln = len / sizeof(DataReading); + if (memcmp(&incMAC, &ESPNOW1, 6) == 0) + { + newData = event_espnow1; + return; + } + if (memcmp(&incMAC, &ESPNOW2, 6) == 0) + { + newData = event_espnow2; + return; + } + newData = event_espnowg; +} + +void begin_espnow() +{ + DBG("Initializing ESP-NOW!"); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address +#if defined(ESP8266) +#ifdef USE_LR + DBG(" LR mode is only available on ESP32. ESP-NOW will begin in normal mode."); +#endif + wifi_set_macaddr(STATION_IF, selfAddress); + if (esp_now_init() != 0) + { + return; + } + + esp_now_set_self_role(ESP_NOW_ROLE_COMBO); + esp_now_register_send_cb(OnDataSent); + esp_now_register_recv_cb(OnDataRecv); + +#elif defined(ESP32) +#ifdef USE_LR + DBG(" ESP-NOW LR mode is active!"); + esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); +#endif + esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); + if (esp_now_init() != ESP_OK) + { + DBG("Error initializing ESP-NOW"); + return; + } + esp_now_register_send_cb(OnDataSent); + esp_now_register_recv_cb(OnDataRecv); + + peerInfo.channel = 0; + peerInfo.encrypt = false; + + memcpy(peerInfo.peer_addr, broadcast_mac, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer bcast"); + return; + } +#endif // ESP8266 + DBG(" ESP-NOW Initialized."); +} + +// Returns an expired entry in peer_list, -1 if full. +int find_espnow_peer() +{ + for (int i = 0; i < 16; i++) + { + if (peer_list[i].last_seen == 0) + { + // DBG("Using peer entry " + String(i)); + return i; + } + } + for (int i = 0; i < 16; i++) + { + if ((millis() - peer_list[i].last_seen) >= PEER_TIMEOUT) + { + // DBG("Recycling peer entry " + String(i)); + esp_now_del_peer(peer_list[i].mac); + + return i; + } + } + DBG("No open peers"); + return -1; +} + +// Returns the index of the peer list array element that contains the provided MAC address, -1 if not found +int getFDRSPeer(uint8_t *mac) +{ + // DBG("Getting peer #"); + + for (int i = 0; i < 16; i++) + { + if (memcmp(mac, &peer_list[i].mac, 6) == 0) + { + DBG("Peer is entry #" + String(i)); + return i; + } + } + + // DBG("Couldn't find peer"); + return -1; +} + +void add_espnow_peer() +{ + DBG("Device requesting peer registration"); + int peer_num = getFDRSPeer(&incMAC[0]); + if (peer_num == -1) // if the device isn't registered + { + // DBG("Device not yet registered, adding to peer list"); + int open_peer = find_espnow_peer(); // find open spot in peer_list + memcpy(&peer_list[open_peer].mac, &incMAC, 6); // save MAC to open spot + peer_list[open_peer].last_seen = millis(); +#if defined(ESP32) + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, incMAC, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer"); + return; + } +#endif +#if defined(ESP8266) + esp_now_add_peer(incMAC, ESP_NOW_ROLE_COMBO, 0, NULL, 0); + +#endif + SystemPacket sys_packet = {.cmd = cmd_add, .param = PEER_TIMEOUT}; + esp_now_send(incMAC, (uint8_t *)&sys_packet, sizeof(SystemPacket)); + } + else + { + DBG("Refreshing existing peer registration"); + peer_list[peer_num].last_seen = millis(); + + SystemPacket sys_packet = {.cmd = cmd_add, .param = PEER_TIMEOUT}; + esp_now_send(incMAC, (uint8_t *)&sys_packet, sizeof(SystemPacket)); + } +} + +// Sends ping reply to sender +void pingback_espnow() +{ + DBG("Ping back to sender"); + SystemPacket sys_packet; + sys_packet.cmd = cmd_ping; + if (!esp_now_is_peer_exist(incMAC)) + { +#ifdef ESP8266 + esp_now_add_peer(incMAC, ESP_NOW_ROLE_COMBO, 0, NULL, 0); +#endif +#if defined(ESP32) + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, incMAC, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer"); + return; + } +#endif + esp_now_send(incMAC, (uint8_t *)&sys_packet, sizeof(SystemPacket)); + esp_now_del_peer(incMAC); + } + else + { + esp_now_send(incMAC, (uint8_t *)&sys_packet, sizeof(SystemPacket)); + } +} + +void sendESPNowNbr(uint8_t interface) +{ + switch (interface) + { + case 1: + { // These brackets are required! + DBG("Sending to ESP-NOW Neighbor #1"); +#if defined(ESP32) + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, ESPNOW1, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer"); + return; + } +#endif // ESP32 + DataReading thePacket[ln]; + int j = 0; + + for (int i = 0; i < ln; i++) + { + if (j > espnow_size) + { + j = 0; + esp_now_send(ESPNOW1, (uint8_t *)&thePacket, sizeof(thePacket)); + } + thePacket[j] = theData[i]; + j++; + } + esp_now_send(ESPNOW1, (uint8_t *)&thePacket, j * sizeof(DataReading)); + esp_now_del_peer(ESPNOW1); + + break; + } // These brackets are required! + case 2: + { + DBG("Sending to ESP-NOW Neighbor #2"); +#if defined(ESP32) + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, ESPNOW2, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer"); + return; + } +#endif // ESP32 + DataReading thePacket[ln]; + int j = 0; + for (int i = 0; i < ln; i++) + { + if (j > espnow_size) + { + j = 0; + esp_now_send(ESPNOW2, (uint8_t *)&thePacket, sizeof(thePacket)); + } + thePacket[j] = theData[i]; + j++; + } + esp_now_send(ESPNOW2, (uint8_t *)&thePacket, j * sizeof(DataReading)); + esp_now_del_peer(ESPNOW2); + + break; + } + } +} + +void sendESPNowPeers() +{ + DBG("Sending to ESP-NOW peers."); + DataReading thePacket[ln]; + int j = 0; + for (int i = 0; i < ln; i++) + { + if (j > espnow_size) + { + j = 0; + esp_now_send(0, (uint8_t *)&thePacket, sizeof(thePacket)); + } + thePacket[j] = theData[i]; + j++; + } + esp_now_send(0, (uint8_t *)&thePacket, j * sizeof(DataReading)); +} + +void sendESPNow(uint8_t address) +{ + DBG("Sending ESP-NOW."); + uint8_t temp_peer[] = {MAC_PREFIX, address}; +#if defined(ESP32) + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, temp_peer, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer"); + return; + } +#endif // ESP32 + + DataReading thePacket[ln]; + int j = 0; + for (int i = 0; i < ln; i++) + { + if (j > espnow_size) + { + j = 0; + esp_now_send(temp_peer, (uint8_t *)&thePacket, sizeof(thePacket)); + } + thePacket[j] = theData[i]; + j++; + } + + esp_now_send(temp_peer, (uint8_t *)&thePacket, j * sizeof(DataReading)); + esp_now_del_peer(temp_peer); +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_filesystem.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_filesystem.h new file mode 100644 index 0000000..a4ade63 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_filesystem.h @@ -0,0 +1,222 @@ + +#ifdef USE_SD_LOG +#include +#include +#endif +#ifdef USE_FS_LOG +#include +#endif +#include + +#define SD_MAX_FILESIZE 1024 +#define FS_MAX_FILESIZE 1024 + + +char logBuffer[512]; +uint16_t logBufferPos = 0; // datatype depends on size of sdBuffer +uint32_t timeLOGBUF = 0; +time_t last_mqtt_success = 0; +time_t last_log_write = 0; + +void releaseLogBuffer() +{ +#ifdef USE_SD_LOG + DBG("Releasing Log buffer to SD"); + File logfile = SD.open(LOG_FILENAME, FILE_WRITE); + if ((logfile.size() / 1024.0) < SD_MAX_FILESIZE) + { + logfile.print(logBuffer); + } + logfile.close(); +#endif +#ifdef USE_FS_LOG + DBG("Releasing Log buffer to internal flash."); + File logfile = LittleFS.open(LOG_FILENAME, "a"); + if ((logfile.size() / 1024.0) < FS_MAX_FILESIZE) + { + logfile.print(logBuffer); + } + logfile.close(); +#endif + memset(&(logBuffer[0]), 0, sizeof(logBuffer) / sizeof(char)); + logBufferPos = 0; +} + +void handleLogger() +{ + if ((millis() - timeLOGBUF) >= LOGBUF_DELAY) + { + timeLOGBUF = millis(); + if (logBufferPos > 0) + releaseLogBuffer(); + } +} + +#ifndef USE_LORA +// crc16_update used by both LoRa and filesystem + +// CRC16 from https://github.com/4-20ma/ModbusMaster/blob/3a05ff87677a9bdd8e027d6906dc05ca15ca8ade/src/util/crc16.h#L71 + +/** @ingroup util_crc16 + Processor-independent CRC-16 calculation. + Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
+ Initial value: 0xFFFF + This CRC is normally used in disk-drive controllers. + @param uint16_t crc (0x0000..0xFFFF) + @param uint8_t a (0x00..0xFF) + @return calculated CRC (0x0000..0xFFFF) +*/ + +static uint16_t crc16_update(uint16_t crc, uint8_t a) +{ + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} +#endif + +uint16_t stringCrc(const char input[]) +{ + uint16_t calcCRC = 0x0000; + + for (unsigned int i = 0; i < strlen(input); i++) + { + calcCRC = crc16_update(calcCRC, input[i]); + } + return calcCRC; +} + +void sendLog() +{ + DBG("Logging to buffer"); + for (int i = 0; i < ln; i++) + { + StaticJsonDocument<96> doc; + JsonObject doc_0 = doc.createNestedObject(); + doc_0["id"] = theData[i].id; + doc_0["type"] = theData[i].t; + doc_0["data"] = theData[i].d; + doc_0["time"] = time(nullptr); + String outgoingString; + serializeJson(doc, outgoingString); + outgoingString = outgoingString + " " + stringCrc(outgoingString.c_str()) + "\r\n"; + if (logBufferPos + outgoingString.length() >= (sizeof(logBuffer) / sizeof(char))) // if buffer would overflow, release first + { + releaseLogBuffer(); + } + memcpy(&logBuffer[logBufferPos], outgoingString.c_str(), outgoingString.length()); // append line to buffer + logBufferPos += outgoingString.length(); + } + time(&last_log_write); +} + +// Send loged values to MQTT so we depend upon network and MQTT +void resendLog() +{ +#if defined(USE_SD_LOG) && defined(USE_WIFI) + DBG("Resending logged values from SD card."); + File logfile = SD.open(LOG_FILENAME, FILE_READ); + while (1) + { + String line = logfile.readStringUntil('\n'); + if (line.length() > 0) + { // if line contains something + if (!client.publish(TOPIC_DATA_BACKLOG, line.c_str())) + { + break; + } + else + { + time(&last_mqtt_success); + } + } + else + { + logfile.close(); + SD.remove(LOG_FILENAME); // if all values are sent + break; + } + } + DBG(" Done"); +#endif +#if defined(USE_FS_LOG) && defined(USE_WIFI) + DBG("Resending logged values from internal flash."); + File logfile = LittleFS.open(LOG_FILENAME, "r"); + while (1) + { + String line = logfile.readStringUntil('\n'); + if (line.length() > 0) + { // if line contains something + uint16_t readCrc; + char data[line.length()]; + sscanf(line.c_str(), "%s %hd", data, &readCrc); + if (stringCrc(data) != readCrc) + { + continue; + } // if CRCs don't match, skip the line + if (!client.publish(TOPIC_DATA_BACKLOG, line.c_str())) + { + break; + } + else + { + time(&last_mqtt_success); + } + } + else + { + logfile.close(); + LittleFS.remove(LOG_FILENAME); // if all values are sent + break; + } + } + DBG(" Done"); +#endif +} + +void begin_SD() +{ +#ifdef USE_SD_LOG + DBG("Initializing SD card..."); +#ifdef ESP32 + SPI.begin(SCK, MISO, MOSI); +#endif + if (!SD.begin(SD_SS)) + { + DBG(" Initialization failed!"); + while (1) + ; + } + else + { + DBG(" SD initialized."); + } +#endif // USE_SD_LOG +} + +void begin_FS() +{ +#ifdef USE_FS_LOG + DBG("Initializing LittleFS..."); + + if (!LittleFS.begin()) + { + DBG(" initialization failed"); + while (1) + ; + } + else + { + DBG(" LittleFS initialized"); + } +#endif // USE_FS_LOG +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_lora.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_lora.h new file mode 100644 index 0000000..20dac1f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_lora.h @@ -0,0 +1,629 @@ +#include + +#define GLOBAL_ACK_TIMEOUT 400 // LoRa ACK timeout in ms. (Minimum = 200) +#define GLOBAL_LORA_RETRIES 2 // LoRa ACK automatic retries [0 - 3] +#define GLOBAL_LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) + +// select LoRa band configuration +#if defined(LORA_FREQUENCY) +#define FDRS_LORA_FREQUENCY LORA_FREQUENCY +#else +#define FDRS_LORA_FREQUENCY GLOBAL_LORA_FREQUENCY +#endif // LORA_FREQUENCY + +// select LoRa SF configuration +#if defined(LORA_SF) +#define FDRS_LORA_SF LORA_SF +#else +#define FDRS_LORA_SF GLOBAL_LORA_SF +#endif // LORA_SF + +// select LoRa ACK configuration +#if defined(LORA_ACK) || defined(GLOBAL_LORA_ACK) +#define FDRS_LORA_ACK +#endif // LORA_ACK + +// select LoRa ACK Timeout configuration +#if defined(LORA_ACK_TIMEOUT) +#define FDRS_ACK_TIMEOUT LORA_ACK_TIMEOUT +#else +#define FDRS_ACK_TIMEOUT GLOBAL_ACK_TIMEOUT +#endif // LORA_ACK_TIMEOUT + +// select LoRa Retry configuration +#if defined(LORA_RETRIES) +#define FDRS_LORA_RETRIES LORA_RETRIES +#else +#define FDRS_LORA_RETRIES GLOBAL_LORA_RETRIES +#endif // LORA_RETRIES + +// select LoRa Tx Power configuration +#if defined(LORA_TXPWR) +#define FDRS_LORA_TXPWR LORA_TXPWR +#else +#define FDRS_LORA_TXPWR GLOBAL_LORA_TXPWR +#endif // LORA_TXPWR + +// select LoRa BANDWIDTH configuration +#if defined(LORA_BANDWIDTH) +#define FDRS_LORA_BANDWIDTH LORA_BANDWIDTH +#else +#define FDRS_LORA_BANDWIDTH GLOBAL_LORA_BANDWIDTH +#endif // LORA_BANDWIDTH + +// select LoRa Coding Rate configuration +#if defined(LORA_CR) +#define FDRS_LORA_CR LORA_CR +#else +#define FDRS_LORA_CR GLOBAL_LORA_CR +#endif // LORA_CR + +// select LoRa SyncWord configuration +#if defined(LORA_SYNCWORD) +#define FDRS_LORA_SYNCWORD LORA_SYNCWORD +#else +#define FDRS_LORA_SYNCWORD GLOBAL_LORA_SYNCWORD +#endif // LORA_SYNCWORD + +// select LoRa Release Interval configuration +#if defined(LORA_INTERVAL) +#define FDRS_LORA_INTERVAL LORA_INTERVAL +#else +#define FDRS_LORA_INTERVAL GLOBAL_LORA_INTERVAL +#endif // LORA_INTERVAL + +#ifndef LORA_BUSY +#define LORA_BUSY RADIOLIB_NC +#endif + +const uint8_t lora_size = 250 / sizeof(DataReading); + +#ifdef CUSTOM_SPI +#ifdef ARDUINO_ARCH_RP2040 +RADIOLIB_MODULE radio = new Module(LORA_SS, LORA_DIO, LORA_RST, LORA_BUSY, SPI1); +#endif // RP2040 +RADIOLIB_MODULE radio = new Module(LORA_SS, LORA_DIO, LORA_RST, LORA_BUSY, SPI); +#else +RADIOLIB_MODULE radio = new Module(LORA_SS, LORA_DIO, LORA_RST, LORA_BUSY); +#endif // CUSTOM_SPI + + +#ifndef USE_ESPNOW // mac_prefix used for both ESP-NOW and LoRa - avoid redefinition warnings +const uint8_t mac_prefix[] = {MAC_PREFIX}; +const uint8_t selfAddress[] = {MAC_PREFIX, UNIT_MAC}; +#endif + +bool pingFlag = false; +bool transmitFlag = false; // flag to indicate transmission or reception state +volatile bool enableInterrupt = true; // disable interrupt when it's not needed +volatile bool operationDone = false; // flag to indicate that a packet was sent or received + +uint16_t LoRa1 = ((mac_prefix[4] << 8) | LORA_NEIGHBOR_1); // Use 2 bytes for LoRa addressing instead of previous 3 bytes +uint16_t LoRa2 = ((mac_prefix[4] << 8) | LORA_NEIGHBOR_2); +uint16_t loraGwAddress = ((selfAddress[4] << 8) | selfAddress[5]); // last 2 bytes of gateway address +uint16_t loraBroadcast = 0xFFFF; +unsigned long receivedLoRaMsg = 0; // Number of total LoRa packets destined for us and of valid size +unsigned long ackOkLoRaMsg = 0; // Number of total LoRa packets with valid CRC + +typedef struct DataBuffer +{ + DataReading buffer[256]; + uint8_t len = 0; +} DataBuffer; + +DataBuffer LORA1Buffer; +DataBuffer LORA2Buffer; +DataBuffer LORABBuffer; + +enum +{ + TxLoRa1, + TxLoRa2, + TxLoRaB, + TxIdle +} TxStatus = TxIdle; + +uint8_t tx_buffer_position = 0; +uint32_t tx_start_time; + +// Function prototypes +crcResult transmitLoRa(uint16_t *, DataReading *, uint8_t); +crcResult transmitLoRa(uint16_t *, SystemPacket *, uint8_t); +static uint16_t crc16_update(uint16_t, uint8_t); + +#if defined(ESP8266) || defined(ESP32) +ICACHE_RAM_ATTR +#endif +void setFlag(void) +{ + // check if the interrupt is enabled + if (!enableInterrupt) + { + return; + } + // we sent or received packet, set the flag + operationDone = true; +} + +// crc16_update used by both LoRa and filesystem + +// CRC16 from https://github.com/4-20ma/ModbusMaster/blob/3a05ff87677a9bdd8e027d6906dc05ca15ca8ade/src/util/crc16.h#L71 + +/** @ingroup util_crc16 + Processor-independent CRC-16 calculation. + Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
+ Initial value: 0xFFFF + This CRC is normally used in disk-drive controllers. + @param uint16_t crc (0x0000..0xFFFF) + @param uint8_t a (0x00..0xFF) + @return calculated CRC (0x0000..0xFFFF) +*/ + +static uint16_t crc16_update(uint16_t crc, uint8_t a) +{ + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} + +crcResult transmitLoRa(uint16_t *destMac, DataReading *packet, uint8_t len) +{ + crcResult crcReturned = CRC_NULL; + uint16_t calcCRC = 0x0000; + + uint8_t pkt[6 + (len * sizeof(DataReading))]; + + pkt[0] = (*destMac >> 8); // high byte of destination MAC + pkt[1] = (*destMac & 0x00FF); // low byte of destination MAC + pkt[2] = selfAddress[4]; // high byte of source MAC (ourselves) + pkt[3] = selfAddress[5]; // low byte of source MAC + memcpy(&pkt[4], packet, len * sizeof(DataReading)); // copy data portion of packet + for (int i = 0; i < (sizeof(pkt) - 2); i++) + { // Last 2 bytes are CRC so do not include them in the calculation itself + // printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, pkt[i]); + } + //if (*destMac == 0xFFFF) + //{ + calcCRC = crc16_update(calcCRC, 0xA1); + //} + pkt[(len * sizeof(DataReading) + 4)] = (calcCRC >> 8); // Append calculated CRC to the last 2 bytes of the packet + pkt[(len * sizeof(DataReading) + 5)] = (calcCRC & 0x00FF); + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, HEX) + " to LoRa MAC 0x" + String(*destMac, HEX)); + // printLoraPacket(pkt,sizeof(pkt)); + int state = radio.startTransmit(pkt, sizeof(pkt)); + transmitFlag = true; + if (state == RADIOLIB_ERR_NONE) + { + } + else + { + DBG(" failed, code " + String(state)); + while (true) + ; + } + return crcReturned; +} + +crcResult transmitLoRa(uint16_t *destMac, SystemPacket *packet, uint8_t len) +{ + crcResult crcReturned = CRC_NULL; + uint16_t calcCRC = 0x0000; + + uint8_t pkt[6 + (len * sizeof(SystemPacket))]; + + pkt[0] = (*destMac >> 8); // high byte of destination MAC + pkt[1] = (*destMac & 0x00FF); // low byte of destination MAC + pkt[2] = selfAddress[4]; // high byte of source MAC (ourselves) + pkt[3] = selfAddress[5]; // low byte of source MAC + memcpy(&pkt[4], packet, len * sizeof(SystemPacket)); // copy data portion of packet + for (int i = 0; i < (sizeof(pkt) - 2); i++) + { // Last 2 bytes are CRC so do not include them in the calculation itself + // printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, pkt[i]); + } + calcCRC = crc16_update(calcCRC, 0xA1); // No ACK for SystemPacket messages so generate new CRC with 0xA1 + pkt[(len * sizeof(SystemPacket) + 4)] = (calcCRC >> 8); // Append calculated CRC to the last 2 bytes of the packet + pkt[(len * sizeof(SystemPacket) + 5)] = (calcCRC & 0x00FF); + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, HEX) + " to LoRa MAC 0x" + String(*destMac, HEX)); + // printLoraPacket(pkt,sizeof(pkt)); + int state = radio.startTransmit(pkt, sizeof(pkt)); + transmitFlag = true; + if (state == RADIOLIB_ERR_NONE) + { + } + else + { + DBG(" failed, code " + String(state)); + while (true) + ; + } + return crcReturned; +} + +void printLoraPacket(uint8_t *p, int size) +{ + printf("Printing packet of size %d.", size); + for (int i = 0; i < size; i++) + { + if (i % 2 == 0) + printf("\n%02d: ", i); + printf("%02X ", p[i]); + } + printf("\n"); +} + +void begin_lora() +{ +#ifdef CUSTOM_SPI +#ifdef ESP32 + SPI.begin(LORA_SPI_SCK, LORA_SPI_MISO, LORA_SPI_MOSI); +#endif // ESP32 +#ifdef ARDUINO_ARCH_RP2040 + SPI1.setRX(LORA_SPI_MISO); + SPI1.setTX(LORA_SPI_MOSI); + SPI1.setSCK(LORA_SPI_SCK); + SPI1.begin(false); +#endif //ARDUINO_ARCH_RP2040 +#endif // CUSTOM_SPI + +#ifdef USE_SX126X + int state = radio.begin(FDRS_LORA_FREQUENCY, FDRS_LORA_BANDWIDTH, FDRS_LORA_SF, FDRS_LORA_CR, FDRS_LORA_SYNCWORD, FDRS_LORA_TXPWR); +#else + int state = radio.begin(FDRS_LORA_FREQUENCY, FDRS_LORA_BANDWIDTH, FDRS_LORA_SF, FDRS_LORA_CR, FDRS_LORA_SYNCWORD, FDRS_LORA_TXPWR, 8, 0); +#endif + if (state == RADIOLIB_ERR_NONE) + { + DBG("RadioLib initialization successful!"); + } + else + { + DBG("RadioLib initialization failed, code " + String(state)); + while (true) + ; + } +#ifdef USE_SX126X + radio.setDio1Action(setFlag); +#else + radio.setDio0Action(setFlag, RISING); +#endif + + radio.setCRC(false); + DBG("LoRa Initialized. Frequency: " + String(FDRS_LORA_FREQUENCY) + " Bandwidth: " + String(FDRS_LORA_BANDWIDTH) + " SF: " + String(FDRS_LORA_SF) + " CR: " + String(FDRS_LORA_CR) + " SyncWord: " + String(FDRS_LORA_SYNCWORD) + " Tx Power: " + String(FDRS_LORA_TXPWR) + "dBm"); + state = radio.startReceive(); // start listening for LoRa packets + if (state == RADIOLIB_ERR_NONE) + { + } + else + { + DBG(" failed, code " + String(state)); + while (true) + ; + } +} + +crcResult getLoRa() +{ + + int packetSize = radio.getPacketLength(); + if ((((packetSize - 6) % sizeof(DataReading) == 0) || ((packetSize - 6) % sizeof(SystemPacket) == 0)) && packetSize > 0) + { // packet size should be 6 bytes plus multiple of size of DataReading + uint8_t packet[packetSize]; + uint16_t packetCRC = 0x0000; // CRC Extracted from received LoRa packet + uint16_t calcCRC = 0x0000; // CRC calculated from received LoRa packet + uint16_t sourceMAC = 0x0000; + uint16_t destMAC = 0x0000; + + radio.readData((uint8_t *)&packet, packetSize); + + destMAC = (packet[0] << 8) | packet[1]; + sourceMAC = (packet[2] << 8) | packet[3]; + packetCRC = ((packet[packetSize - 2] << 8) | packet[packetSize - 1]); + // DBG("Packet Address: 0x" + String(packet[0], HEX) + String(packet[1], HEX) + " Self Address: 0x" + String(selfAddress[4], HEX) + String(selfAddress[5], HEX)); + if (destMAC == (selfAddress[4] << 8 | selfAddress[5])) + { // Check if addressed to this device (2 bytes, bytes 1 and 2) + // printLoraPacket(packet,sizeof(packet)); + if (receivedLoRaMsg != 0) + { // Avoid divide by 0 + DBG("Incoming LoRa. Size: " + String(packetSize) + " Bytes, RSSI: " + String(radio.getRSSI()) + "dBm, SNR: " + String(radio.getSNR()) + "dB, PacketCRC: 0x" + String(packetCRC, HEX) + ", Total LoRa received: " + String(receivedLoRaMsg) + ", CRC Ok Pct " + String((float)ackOkLoRaMsg / receivedLoRaMsg * 100) + "%"); + } + else + { + DBG("Incoming LoRa. Size: " + String(packetSize) + " Bytes, RSSI: " + String(radio.getRSSI()) + "dBm, SNR: " + String(radio.getSNR()) + "dB, PacketCRC: 0x" + String(packetCRC, HEX) + ", Total LoRa received: " + String(receivedLoRaMsg)); + } + receivedLoRaMsg++; + // Evaluate CRC + for (int i = 0; i < (packetSize - 2); i++) + { // Last 2 bytes of packet are the CRC so do not include them in calculation + // printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, packet[i]); + } + if ((packetSize - 6) % sizeof(DataReading) == 0) + { // DataReading type packet + if (calcCRC == packetCRC) + { + SystemPacket ACK = {.cmd = cmd_ack, .param = CRC_OK}; + DBG("CRC Match, sending ACK packet to sensor 0x" + String(sourceMAC, HEX) + "(hex)"); + transmitLoRa(&sourceMAC, &ACK, 1); // Send ACK back to source + } + else if (packetCRC == crc16_update(calcCRC, 0xA1)) + { // Sender does not want ACK and CRC is valid + DBG("Sensor address 0x" + String(sourceMAC, 16) + "(hex) does not want ACK"); + } + else + { + SystemPacket NAK = {.cmd = cmd_ack, .param = CRC_BAD}; + // Send NAK packet to sensor + DBG("CRC Mismatch! Packet CRC is 0x" + String(packetCRC, HEX) + ", Calculated CRC is 0x" + String(calcCRC, HEX) + " Sending NAK packet to sensor 0x" + String(sourceMAC, HEX) + "(hex)"); + transmitLoRa(&sourceMAC, &NAK, 1); // CRC did not match so send NAK to source + newData = event_clear; // do not process data as data may be corrupt + return CRC_BAD; // Exit function and do not update newData to send invalid data further on + } + memcpy(&theData, &packet[4], packetSize - 6); // Split off data portion of packet (N - 6 bytes (6 bytes for headers and CRC)) + ln = (packetSize - 6) / sizeof(DataReading); + ackOkLoRaMsg++; + if (memcmp(&sourceMAC, &LoRa1, 2) == 0) + { // Check if it is from a registered sender + newData = event_lora1; + return CRC_OK; + } + if (memcmp(&sourceMAC, &LoRa2, 2) == 0) + { + newData = event_lora2; + return CRC_OK; + } + newData = event_lorag; + return CRC_OK; + } + else if ((packetSize - 6) % sizeof(SystemPacket) == 0) + { + unsigned int ln = (packetSize - 6) / sizeof(SystemPacket); + SystemPacket receiveData[ln]; + + if (calcCRC == packetCRC) + { + memcpy(receiveData, &packet[4], packetSize - 6); // Split off data portion of packet (N bytes) + if (ln == 1 && receiveData[0].cmd == cmd_ack) + { + DBG("ACK Received - CRC Match"); + } + else if (ln == 1 && receiveData[0].cmd == cmd_ping) + { // We have received a ping request or reply?? + if (receiveData[0].param == 1) + { // This is a reply to our ping request + pingFlag = true; + DBG("We have received a ping reply via LoRa from address " + String(sourceMAC, HEX)); + } + else if (receiveData[0].param == 0) + { + DBG("We have received a ping request from 0x" + String(sourceMAC, HEX) + ", Replying."); + SystemPacket pingReply = {.cmd = cmd_ping, .param = 1}; + transmitLoRa(&sourceMAC, &pingReply, 1); + } + } + else + { // data we have received is not yet programmed. How we handle is future enhancement. + DBG("Received some LoRa SystemPacket data that is not yet handled. To be handled in future enhancement."); + DBG("ln: " + String(ln) + "data type: " + String(receiveData[0].cmd)); + } + ackOkLoRaMsg++; + return CRC_OK; + } + else if (packetCRC == crc16_update(calcCRC, 0xA1)) + { // Sender does not want ACK and CRC is valid + memcpy(receiveData, &packet[4], packetSize - 6); // Split off data portion of packet (N bytes) + if (ln == 1 && receiveData[0].cmd == cmd_ack) + { + DBG("ACK Received - CRC Match"); + } + else if (ln == 1 && receiveData[0].cmd == cmd_ping) + { // We have received a ping request or reply?? + if (receiveData[0].param == 1) + { // This is a reply to our ping request + pingFlag = true; + DBG("We have received a ping reply via LoRa from address " + String(sourceMAC, HEX)); + } + else if (receiveData[0].param == 0) + { + DBG("We have received a ping request from 0x" + String(sourceMAC, HEX) + ", Replying."); + SystemPacket pingReply = {.cmd = cmd_ping, .param = 1}; + transmitLoRa(&sourceMAC, &pingReply, 1); + } + } + else + { // data we have received is not yet programmed. How we handle is future enhancement. + DBG("Received some LoRa SystemPacket data that is not yet handled. To be handled in future enhancement."); + DBG("ln: " + String(ln) + "data type: " + String(receiveData[0].cmd)); + } + ackOkLoRaMsg++; + return CRC_OK; + } + else + { + DBG("ACK Received CRC Mismatch! Packet CRC is 0x" + String(packetCRC, HEX) + ", Calculated CRC is 0x" + String(calcCRC, HEX)); + return CRC_BAD; + } + } + } + else + { + // DBG("Incoming LoRa packet of " + String(packetSize) + " bytes received from address 0x" + String(sourceMAC, HEX) + " destined for node address 0x" + String(destMAC, HEX)); + // printLoraPacket(packet,sizeof(packet)); + return CRC_NULL; + } + } + else + { + if (packetSize != 0) + { + // DBG("Incoming LoRa packet of " + String(packetSize) + "bytes not processed."); + // uint8_t packet[packetSize]; + // radio.readData((uint8_t *)&packet, packetSize); + // printLoraPacket(packet,sizeof(packet)); + return CRC_NULL; + } + } + return CRC_NULL; +} + +// Sends packet to any node that is paired to this gateway +void broadcastLoRa() +{ + DBG("Sending to LoRa broadcast buffer"); + + for (int i = 0; i < ln; i++) + { + LORABBuffer.buffer[LORABBuffer.len + i] = theData[i]; + } + LORABBuffer.len += ln; +} + +// Sends packet to neighbor gateways +void sendLoRaNbr(uint8_t interface) +{ + DBG("Sending to LoRa neighbor buffer"); + switch (interface) + { + case 1: + { + for (int i = 0; i < ln; i++) + { + LORA1Buffer.buffer[LORA1Buffer.len + i] = theData[i]; + } + LORA1Buffer.len += ln; + break; + } + case 2: + { + for (int i = 0; i < ln; i++) + { + LORA2Buffer.buffer[LORA2Buffer.len + i] = theData[i]; + } + LORA2Buffer.len += ln; + break; + } + } +} + +void asyncReleaseLoRa(bool first_run) +{ + delay(3); + if (first_run) + { + if (LORA1Buffer.len > 0) { + TxStatus = TxLoRa1; + } else if (LORA2Buffer.len > 0) { + TxStatus = TxLoRa2; + } else if (LORABBuffer.len > 0) { + TxStatus = TxLoRaB; + } else { + goto TxFin; + } + tx_start_time = millis(); + } + switch (TxStatus) + { + case TxLoRa1: + if (LORA1Buffer.len - tx_buffer_position > lora_size) { + transmitLoRa(&LoRa1, &LORA1Buffer.buffer[tx_buffer_position], lora_size); + tx_buffer_position += lora_size; + } else { + transmitLoRa(&LoRa1, &LORA1Buffer.buffer[tx_buffer_position], LORA1Buffer.len - tx_buffer_position); + tx_buffer_position = 0; + if (LORA2Buffer.len > 0) { + TxStatus = TxLoRa2; + } else if ((LORABBuffer.len > 0)) { + TxStatus = TxLoRaB; + } else { + goto TxFin; + } + } + break; + case TxLoRa2: + if (LORA2Buffer.len - tx_buffer_position > lora_size) { + transmitLoRa(&LoRa2, &LORA2Buffer.buffer[tx_buffer_position], lora_size); + tx_buffer_position += lora_size; + } else { + transmitLoRa(&LoRa2, &LORA2Buffer.buffer[tx_buffer_position], LORA2Buffer.len - tx_buffer_position); + tx_buffer_position = 0; + if (LORABBuffer.len > 0) { + TxStatus = TxLoRaB; + } else { + goto TxFin; + } + } + break; + + case TxLoRaB: + if (LORABBuffer.len - tx_buffer_position > lora_size) { + transmitLoRa(&loraBroadcast, &LORABBuffer.buffer[tx_buffer_position], lora_size); + tx_buffer_position += lora_size; + } else { + transmitLoRa(&loraBroadcast, &LORABBuffer.buffer[tx_buffer_position], LORABBuffer.len - tx_buffer_position); +TxFin: + if (LORABBuffer.len + LORA1Buffer.len + LORA2Buffer.len > 0) { + LORABBuffer.len = 0; + LORA1Buffer.len = 0; + LORA2Buffer.len = 0; + tx_buffer_position = 0; + TxStatus = TxIdle; + } + } + break; + } +} + + +void asyncReleaseLoRaFirst() +{ + asyncReleaseLoRa(true); +} + +crcResult handleLoRa() +{ + crcResult crcReturned = CRC_NULL; + if (operationDone) // the interrupt was triggered + { + // DBG("Interrupt triggered"); + // DBG("TxFlag: " + String(transmitFlag)); + // DBG("TxStatus: " + String(TxStatus)); + + enableInterrupt = false; + operationDone = false; + if (transmitFlag) // the previous operation was transmission + { + radio.finishTransmit(); + if (TxStatus != TxIdle) + { + asyncReleaseLoRa(false); + enableInterrupt = true; + } + else + { + DBG("LoRa airtime: " + String(millis() - tx_start_time) + "ms"); + radio.startReceive(); // return to listen mode + enableInterrupt = true; + transmitFlag = false; + } + } + else // the previous operation was reception + { + crcReturned = getLoRa(); + if (!transmitFlag) // return to listen if no transmission was begun + { + radio.startReceive(); + } + enableInterrupt = true; + } + } + return crcReturned; +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_mqtt.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_mqtt.h new file mode 100644 index 0000000..af50e75 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_mqtt.h @@ -0,0 +1,191 @@ +#include +#include + +// select MQTT server address +#if defined(MQTT_ADDR) +#define FDRS_MQTT_ADDR MQTT_ADDR +#elif defined(GLOBAL_MQTT_ADDR) +#define FDRS_MQTT_ADDR GLOBAL_MQTT_ADDR +#else +// ASSERT("NO MQTT address defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); +#endif // MQTT_ADDR + +// select MQTT server port +#if defined(MQTT_PORT) +#define FDRS_MQTT_PORT MQTT_PORT +#elif defined(GLOBAL_MQTT_PORT) +#define FDRS_MQTT_PORT GLOBAL_MQTT_PORT +#else +#define FDRS_MQTT_PORT 1883 +#endif // MQTT_PORT + +// select MQTT user name +#if defined(MQTT_USER) +#define FDRS_MQTT_USER MQTT_USER +#elif defined(GLOBAL_MQTT_USER) +#define FDRS_MQTT_USER GLOBAL_MQTT_USER +#else +// ASSERT("NO MQTT user defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); +#endif // MQTT_USER + +// select MQTT user password +#if defined(MQTT_PASS) +#define FDRS_MQTT_PASS MQTT_PASS +#elif defined(GLOBAL_MQTT_PASS) +#define FDRS_MQTT_PASS GLOBAL_MQTT_PASS +#else +// ASSERT("NO MQTT password defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); +#endif // MQTT_PASS +#if defined(MQTT_AUTH) || defined(GLOBAL_MQTT_AUTH) +#define FDRS_MQTT_AUTH +#endif // MQTT_AUTH + + +WiFiClient espClient; +PubSubClient client(espClient); + +const char *mqtt_server = FDRS_MQTT_ADDR; +const int mqtt_port = FDRS_MQTT_PORT; +#if defined(USE_SD_LOG) || defined(USE_FS_LOG) + extern time_t last_log_write; + extern time_t last_mqtt_success; +#endif + + +#ifdef FDRS_MQTT_AUTH +const char *mqtt_user = FDRS_MQTT_USER; +const char *mqtt_pass = FDRS_MQTT_PASS; +#else +const char *mqtt_user = NULL; +const char *mqtt_pass = NULL; +#endif // FDRS_MQTT_AUTH + +void reconnect_mqtt(short int attempts, bool silent) +{ + if (!silent) + DBG("Connecting MQTT..."); + + for (short int i = 1; i <= attempts; i++) + { + // Attempt to connect + if (client.connect("FDRS_GATEWAY", mqtt_user, mqtt_pass)) + { + // Subscribe + client.subscribe(TOPIC_COMMAND); + if (!silent) + DBG(" MQTT Connected"); + return; + } + else + { + if (!silent) + { + char msg[23]; + sprintf(msg, " Attempt %d/%d", i, attempts); + DBG(msg); + } + if ((attempts != 1)) + { + delay(3000); + } + } + } + + if (!silent) + DBG(" Connecting MQTT failed."); +} + +void reconnect_mqtt(int attempts) +{ + reconnect_mqtt(attempts, false); +} + +// Handles MQTT in loop() +void handleMQTT() +{ + if (!client.connected()) + { + reconnect_mqtt(1, true); + } + client.loop(); // for recieving incoming messages and maintaining connection +} + +void mqtt_callback(char *topic, byte *message, unsigned int length) +{ + String incomingString; + DBG(topic); + for (unsigned int i = 0; i < length; i++) + { + incomingString += (char)message[i]; + } + StaticJsonDocument<2048> doc; + DeserializationError error = deserializeJson(doc, incomingString); + if (error) + { // Test if parsing succeeds. + DBG("json parse err"); + DBG(incomingString); + return; + } + else + { + int s = doc.size(); + // UART_IF.println(s); + for (int i = 0; i < s; i++) + { + theData[i].id = doc[i]["id"]; + theData[i].t = doc[i]["type"]; + theData[i].d = doc[i]["data"]; + } + ln = s; + newData = event_mqtt; + DBG("Incoming MQTT."); + } +} + +void begin_mqtt() +{ + client.setServer(mqtt_server, mqtt_port); + if (!client.connected()) + { + reconnect_mqtt(5); + } + client.setCallback(mqtt_callback); +} + +void mqtt_publish(const char *payload) +{ + if (!client.publish(TOPIC_DATA, payload)) + { + DBG(" Error on sending MQTT"); +#if defined(USE_SD_LOG) || defined(USE_FS_LOG) + sendLog(); +#endif + } + else + { +#if defined(USE_SD_LOG) || defined(USE_FS_LOG) + if (last_log_write >= last_mqtt_success) + { + releaseLogBuffer(); + resendLog(); + } + time(&last_mqtt_success); +#endif + } +} + +void sendMQTT() +{ + DBG("Sending MQTT."); + DynamicJsonDocument doc(24576); + for (int i = 0; i < ln; i++) + { + doc[i]["id"] = theData[i].id; + doc[i]["type"] = theData[i].t; + doc[i]["data"] = theData[i].d; + doc[i]["time"] = time(nullptr); + } + String outgoingString; + serializeJson(doc, outgoingString); + mqtt_publish((char *)outgoingString.c_str()); +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_scheduler.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_scheduler.h new file mode 100644 index 0000000..687ef47 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_scheduler.h @@ -0,0 +1,37 @@ + + +typedef struct ScheduleItem +{ + uint32_t interval; + uint32_t start; + void (*functionName)(); + bool active; +} ScheduleItem; + +ScheduleItem theSchedule[16]; + +bool scheduleFDRS(void (*added_func)(), uint32_t added_interval) +{ + ScheduleItem arg = {.interval = added_interval, .start = millis(), .functionName = added_func, .active = true}; + for (int i = 0; i < 16; i++) + { + if (!theSchedule[i].active) + { + theSchedule[i] = arg; + return true; + } + } + DBG("Schedule is full!"); + return false; +} +void handle_schedule() +{ + for (int i = 0; i < 16; i++) + { + if (theSchedule[i].active && (millis() - theSchedule[i].start > theSchedule[i].interval)) + { + theSchedule[i].start = millis(); + theSchedule[i].functionName(); + } + } +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_serial.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_serial.h new file mode 100644 index 0000000..971947f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_serial.h @@ -0,0 +1,61 @@ +#include + +#if defined (ESP32) +#define UART_IF Serial +#else +#define UART_IF Serial +#endif + + +void getSerial() { + String incomingString; + if (UART_IF.available()){ + incomingString = UART_IF.readStringUntil('\n'); + } + else if (Serial.available()){ + incomingString = Serial.readStringUntil('\n'); + } + DynamicJsonDocument doc(24576); + DeserializationError error = deserializeJson(doc, incomingString); + if (error) { // Test if parsing succeeds. + // DBG("json parse err"); + // DBG(incomingString); + return; + } else { + int s = doc.size(); + //UART_IF.println(s); + for (int i = 0; i < s; i++) { + theData[i].id = doc[i]["id"]; + theData[i].t = doc[i]["type"]; + theData[i].d = doc[i]["data"]; + } + ln = s; + newData = event_serial; + DBG("Incoming Serial."); + + } +} + +void sendSerial() { + DBG("Sending Serial."); + DynamicJsonDocument doc(24576); + for (int i = 0; i < ln; i++) { + doc[i]["id"] = theData[i].id; + doc[i]["type"] = theData[i].t; + doc[i]["data"] = theData[i].d; + } + serializeJson(doc, Serial); + Serial.println(); + +#ifndef ESP8266 + serializeJson(doc, Serial); + Serial.println(); +#endif + +} +void handleSerial(){ + while (UART_IF.available() || Serial.available()) + { + getSerial(); + } +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_wifi.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_wifi.h new file mode 100644 index 0000000..1a124cb --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_gateway_wifi.h @@ -0,0 +1,94 @@ +#include +#ifdef ESP8266 +#include +#elif defined(ESP32) +#include +#include +#elif defined(ARDUINO_ARCH_RP2040) +#include +#endif +#ifdef USE_ETHERNET +#include +#endif + +// select WiFi SSID configuration +#if defined(WIFI_SSID) +#define FDRS_WIFI_SSID WIFI_SSID +#elif defined(GLOBAL_WIFI_SSID) +#define FDRS_WIFI_SSID GLOBAL_WIFI_SSID +#else +// ASSERT("NO WiFi SSID defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); +#endif // WIFI_SSID + +// select WiFi password +#if defined(WIFI_PASS) +#define FDRS_WIFI_PASS WIFI_PASS +#elif defined(GLOBAL_WIFI_PASS) +#define FDRS_WIFI_PASS GLOBAL_WIFI_PASS +#else +// ASSERT("NO WiFi password defined! Please define in fdrs_globals.h (recommended) or in fdrs_node_config.h"); +#endif // WIFI_PASS +#ifdef USE_ETHERNET +static bool eth_connected = false; +void WiFiEvent(WiFiEvent_t event) +{ + switch (event) { + case ARDUINO_EVENT_ETH_START: + Serial.println("ETH Started"); + //set eth hostname here + ETH.setHostname("esp32-ethernet"); + break; + case ARDUINO_EVENT_ETH_CONNECTED: + Serial.println("ETH Connected"); + break; + case ARDUINO_EVENT_ETH_GOT_IP: + Serial.print("ETH MAC: "); + Serial.print(ETH.macAddress()); + Serial.print(", IPv4: "); + Serial.print(ETH.localIP()); + if (ETH.fullDuplex()) { + Serial.print(", FULL_DUPLEX"); + } + Serial.print(", "); + Serial.print(ETH.linkSpeed()); + Serial.println("Mbps"); + eth_connected = true; + break; + case ARDUINO_EVENT_ETH_DISCONNECTED: + Serial.println("ETH Disconnected"); + eth_connected = false; + break; + case ARDUINO_EVENT_ETH_STOP: + Serial.println("ETH Stopped"); + eth_connected = false; + break; + default: + break; + } +} + +#endif // USE_ETHERNET +const char *ssid = FDRS_WIFI_SSID; +const char *password = FDRS_WIFI_PASS; + +void begin_wifi() +{ + delay(10); +#ifdef USE_ETHERNET + WiFi.onEvent(WiFiEvent); + ETH.begin(); + while (!eth_connected) + { + DBG("Connecting ethernet..."); + delay(500); + } +#else + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) + { + DBG("Connecting to WiFi..."); + DBG(FDRS_WIFI_SSID); + delay(500); + } +#endif // USE_ETHERNET +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_globals.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_globals.h new file mode 100644 index 0000000..e07630e --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_globals.h @@ -0,0 +1,33 @@ +// FARM DATA RELAY SYSTEM + +// Global Configuration + +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +#ifndef __FDRS_GLOBALS_h__ +#define __FDRS_GLOBALS_h__ + +#define GLOBAL_WIFI_SSID "Your SSID" +#define GLOBAL_WIFI_PASS "Password" + +#define GLOBAL_MQTT_ADDR "192.168.0.8" +#define GLOBAL_MQTT_PORT 1883 + +//#define GLOBAL_MQTT_AUTH //uncomment to enable MQTT authentication +#define GLOBAL_MQTT_USER "Your MQTT Username" +#define GLOBAL_MQTT_PASS "Your MQTT Password" +// MQTT Topics +#define TOPIC_DATA "fdrs/data" +#define TOPIC_STATUS "fdrs/status" +#define TOPIC_COMMAND "fdrs/command" +#define TOPIC_DATA_BACKLOG "fdrs/databacklog" // Used in filesystem module + +#define GLOBAL_LORA_FREQUENCY 915.0 // Carrier frequency in MHz. Allowed values range from 137.0 MHz to 1020.0 MHz (varies by chip). +#define GLOBAL_LORA_SF 7 // LoRa link spreading factor. Allowed values range from 6 to 12. +#define GLOBAL_LORA_BANDWIDTH 125.0 // LoRa link bandwidth in kHz. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz. +#define GLOBAL_LORA_CR 5 // LoRa link coding rate denominator. Allowed values range from 5 to 8. +#define GLOBAL_LORA_SYNCWORD 0x12 // LoRa sync word. Can be used to distinguish different LoRa networks. Note that 0x34 is reserved for LoRaWAN. +#define GLOBAL_LORA_INTERVAL 5000 // Interval between LoRa buffer releases. Must be longer than transmission time-on-air. + +#define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE // MAC address prefix. Can be used to distinguish different ESP-NOW networks. + +#endif diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node.h new file mode 100644 index 0000000..1168a68 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node.h @@ -0,0 +1,382 @@ +// FARM DATA RELAY SYSTEM +// +// "fdrs_node.h" +// +// Developed by Timm Bogner (timmbogner@gmail.com) in Urbana, Illinois, USA. +// +#include +#include +#define FDRS_NODE + +// CRC16 from https://github.com/4-20ma/ModbusMaster/blob/3a05ff87677a9bdd8e027d6906dc05ca15ca8ade/src/util/crc16.h#L71 + +/** @ingroup util_crc16 + Processor-independent CRC-16 calculation. + Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
+ Initial value: 0xFFFF + This CRC is normally used in disk-drive controllers. + @param uint16_t crc (0x0000..0xFFFF) + @param uint8_t a (0x00..0xFF) + @return calculated CRC (0x0000..0xFFFF) +*/ + +static uint16_t crc16_update(uint16_t crc, uint8_t a) +{ + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} + +SystemPacket theCmd; +DataReading theData[256]; +uint8_t ln; +bool newData; +uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; +const uint16_t espnow_size = 250 / sizeof(DataReading); +crcResult crcReturned = CRC_NULL; + +uint32_t gtwy_timeout = 0; +uint8_t incMAC[6]; +DataReading fdrsData[espnow_size]; +DataReading incData[espnow_size]; + +uint8_t data_count = 0; + +uint32_t last_refresh; +void (*callback_ptr)(DataReading); +uint16_t subscription_list[256] = {}; +bool active_subs[256] = {}; + +#include "fdrs_debug.h" +#ifdef DEBUG_CONFIG +// #include "fdrs_checkConfig.h" +#endif +#ifdef USE_OLED + #include "fdrs_oled.h" +#endif +#ifdef USE_ESPNOW + #include "fdrs_node_espnow.h" +#endif +#ifdef USE_LORA + #include "fdrs_node_lora.h" +#endif + +void beginFDRS() +{ +#ifdef FDRS_DEBUG + Serial.begin(115200); + // // find out the reset reason + // esp_reset_reason_t resetReason; + // resetReason = esp_reset_reason(); +#endif +#ifdef USE_OLED + init_oled(); + DBG("Display initialized!"); + DBG("Hello, World!"); +#endif + DBG("FDRS User Node initializing..."); + DBG(" Reading ID " + String(READING_ID)); + DBG(" Gateway: " + String(GTWY_MAC, HEX)); +#ifdef POWER_CTRL + DBG("Powering up the sensor array!"); + pinMode(POWER_CTRL, OUTPUT); + digitalWrite(POWER_CTRL, 1); + delay(50); +#endif + // Init ESP-NOW for either ESP8266 or ESP32 + +#ifdef USE_ESPNOW + DBG("Initializing ESP-NOW!"); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); +#if defined(ESP8266) +#ifdef USE_LR + DBG(" LR mode is only available on ESP32. ESP-NOW will begin in normal mode."); +#endif + if (esp_now_init() != 0) + { + return; + } + esp_now_set_self_role(ESP_NOW_ROLE_COMBO); + esp_now_register_recv_cb(OnDataRecv); + esp_now_register_send_cb(OnDataSent); + + // Register peers + esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); +#elif defined(ESP32) +#ifdef USE_LR + DBG(" ESP-NOW LR mode is active!"); + esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); +#endif + if (esp_now_init() != ESP_OK) + { + DBG("Error initializing ESP-NOW"); + return; + } + esp_now_register_recv_cb(OnDataRecv); + esp_now_register_send_cb(OnDataSent); + + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, broadcast_mac, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer bcast"); + return; + } + memcpy(peerInfo.peer_addr, gatewayAddress, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) + { + DBG("Failed to add peer"); + return; + } +#endif + DBG(" ESP-NOW Initialized."); +#endif // USE_ESPNOW +#ifdef USE_LORA + begin_lora(); +#endif +#ifdef DEBUG_CONFIG + // if (resetReason != ESP_RST_DEEPSLEEP) { + // checkConfig(); + // } +#endif // DEBUG_CONFIG +} + +void handleIncoming() +{ + if (newData) + { + + newData = false; + for (int i = 0; i < ln; i++) + { // Cycle through array of incoming DataReadings for any we are subbed to + for (int j = 0; j < 255; j++) + { // Cycle through subscriptions for active entries + if (active_subs[j]) + { + + if (theData[i].id == subscription_list[j]) + { + + (*callback_ptr)(theData[i]); + } + } + } + } + } +} + +bool sendFDRS() +{ + if(data_count == 0) { + return false; + } + DBG("Sending FDRS Packet!"); +#ifdef USE_ESPNOW + esp_now_send(gatewayAddress, (uint8_t *)&fdrsData, data_count * sizeof(DataReading)); + esp_now_ack_flag = CRC_NULL; + while (esp_now_ack_flag == CRC_NULL) + { + yield(); + delay(0); + } + if (esp_now_ack_flag == CRC_OK) + { + data_count = 0; + return true; + } + else + { + data_count = 0; + return false; + } +#endif +#ifdef USE_LORA + crcReturned = transmitLoRa(>wyAddress, fdrsData, data_count); + // DBG(" LoRa sent."); +#ifdef LORA_ACK + if(crcReturned == CRC_OK) { + data_count = 0; + return true; + } +#endif +#ifndef LORA_ACK + if(crcReturned == CRC_OK || crcReturned == CRC_NULL) { + data_count = 0; + return true; +} +#endif + else { + data_count = 0; + return false; + } +#endif +} + +void loadFDRS(float d, uint8_t t) +{ + DBG("Id: " + String(READING_ID) + " - Type: " + String(t) + " - Data loaded: " + String(d)); + if (data_count > espnow_size) + sendFDRS(); + DataReading dr; + dr.id = READING_ID; + dr.t = t; + dr.d = d; + fdrsData[data_count] = dr; + data_count++; +} +void loadFDRS(float d, uint8_t t, uint16_t id) +{ + DBG("Id: " + String(id) + " - Type: " + String(t) + " - Data loaded: " + String(d)); + if (data_count > espnow_size) + sendFDRS(); + DataReading dr; + dr.id = id; + dr.t = t; + dr.d = d; + fdrsData[data_count] = dr; + data_count++; +} +void sleepFDRS(int sleep_time) +{ +#ifdef DEEP_SLEEP + DBG(" Deep sleeping."); +#ifdef ESP32 + esp_sleep_enable_timer_wakeup(sleep_time * 1000000); + esp_deep_sleep_start(); +#endif +#ifdef ESP8266 + ESP.deepSleep(sleep_time * 1000000); +#endif +#endif + DBG(" Delaying."); + delay(sleep_time * 1000); +} + +void loopFDRS() +{ +#ifdef USE_LORA + handleLoRa(); +#endif + handleIncoming(); + // // TO-DO: + // if (is_added) + // { + // if ((millis() - last_refresh) >= gtwy_timeout) + // { + // last_refresh = millis(); + // } + // } +} + +bool subscribeFDRS(uint16_t sub_id) +{ + for (int i = 0; i < 255; i++) + { + if ((subscription_list[i] == sub_id) && (active_subs[i])) + { + DBG("You're already subscribed to ID " + String(sub_id)); + return true; + } + } + for (int i = 0; i < 255; i++) + { + if (!active_subs[i]) + { + DBG("Subscribing to DataReading ID " + String(sub_id)); + subscription_list[i] = sub_id; + active_subs[i] = true; + return true; + } + } + DBG("No subscription could be established!"); + return false; +} +bool unsubscribeFDRS(uint16_t sub_id) +{ + for (int i = 0; i < 255; i++) + { + if ((subscription_list[i] == sub_id) && (active_subs[i])) + { + DBG("Removing subscription to ID " + String(sub_id)); + active_subs[i] = false; + return true; + } + } + DBG("No subscription to remove"); + return false; +} + +bool addFDRS(void (*new_cb_ptr)(DataReading)) +{ + callback_ptr = new_cb_ptr; +#ifdef USE_ESPNOW + SystemPacket sys_packet = {.cmd = cmd_add, .param = 0}; + esp_now_send(gatewayAddress, (uint8_t *)&sys_packet, sizeof(SystemPacket)); + DBG("ESP-NOW peer registration request submitted to " + String(gatewayAddress[5])); + uint32_t add_start = millis(); + is_added = false; + while ((millis() - add_start) <= 1000) // 1000ms timeout + { + yield(); + if (is_added) + { + DBG("Registration accepted. Timeout: " + String(gtwy_timeout)); + last_refresh = millis(); + return true; + } + } + DBG("No gateways accepted the request"); + return false; +#endif // USE_ESPNOW + return true; +} + +bool addFDRS(int timeout, void (*new_cb_ptr)(DataReading)) +{ + callback_ptr = new_cb_ptr; +#ifdef USE_ESPNOW + SystemPacket sys_packet = {.cmd = cmd_add, .param = 0}; + esp_now_send(gatewayAddress, (uint8_t *)&sys_packet, sizeof(SystemPacket)); + DBG("ESP-NOW peer registration request submitted to " + String(gatewayAddress[5])); + uint32_t add_start = millis(); + is_added = false; + while ((millis() - add_start) <= timeout) + { + yield(); + if (is_added) + { + DBG("Registration accepted. Timeout: " + String(gtwy_timeout)); + last_refresh = millis(); + return true; + } + } + DBG("No gateways accepted the request"); + return false; +#endif // USE_ESPNOW + return true; +} + +uint32_t pingFDRS(uint32_t timeout) +{ +#ifdef USE_ESPNOW + uint32_t pingResponseMs = pingFDRSEspNow(gatewayAddress, timeout); + return pingResponseMs; +#endif +#ifdef USE_LORA + uint32_t pingResponseMs = pingFDRSLoRa(>wyAddress, timeout); + return pingResponseMs; +#endif +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node_espnow.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node_espnow.h new file mode 100644 index 0000000..5f9004f --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node_espnow.h @@ -0,0 +1,89 @@ +#if defined(ESP8266) + #include + #include +#elif defined(ESP32) + #include + #include + #include +#endif + +uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +crcResult esp_now_ack_flag; +bool is_added = false; +bool pingFlag = false; + +// Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 +#if defined(ESP8266) +void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) +{ + if (sendStatus == 0) + { + esp_now_ack_flag = CRC_OK; + } + else + { + esp_now_ack_flag = CRC_BAD; + } +} +void OnDataRecv(uint8_t *mac, uint8_t *incomingData, uint8_t len) +{ +#elif defined(ESP32) +void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) +{ + if (status == ESP_NOW_SEND_SUCCESS) + { + esp_now_ack_flag = CRC_OK; + } + else + { + esp_now_ack_flag = CRC_BAD; + } +} +void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) +{ +#endif + if (len < sizeof(DataReading)) + { + SystemPacket command; + memcpy(&command, incomingData, sizeof(command)); + switch (command.cmd) + { + case cmd_ping: + pingFlag = true; + break; + case cmd_add: + is_added = true; + gtwy_timeout = command.param; + break; + } + } + else + { + memcpy(&theData, incomingData, len); + ln = len / sizeof(DataReading); + newData = true; + } +} + +// FDRS node pings gateway and listens for a defined amount of time for a reply +// Blocking function for timeout amount of time (up to timeout time waiting for reply)(IE no callback) +// Returns the amount of time in ms that the ping takes or predefined value if ping fails within timeout +uint32_t pingFDRSEspNow(uint8_t *address, uint32_t timeout) { + SystemPacket sys_packet = {.cmd = cmd_ping, .param = 0}; + + esp_now_send(address, (uint8_t *)&sys_packet, sizeof(SystemPacket)); + DBG(" ESP-NOW ping sent."); + uint32_t ping_start = millis(); + pingFlag = false; + while ((millis() - ping_start) <= timeout) + { + yield(); // do I need to yield or does it automatically? + if (pingFlag) + { + DBG("ESP-NOW Ping Reply in " + String(millis() - ping_start) + "ms from " + String(address[0], HEX) + ":" + String(address[1], HEX) + ":" + String(address[2], HEX) + ":" + String(address[3], HEX) + ":" + String(address[4], HEX) + ":" + String(address[5], HEX)); + return (millis() - ping_start); + } + } + DBG("No ESP-NOW ping returned within " + String(timeout) + "ms."); + return UINT32_MAX; +} \ No newline at end of file diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node_lora.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node_lora.h new file mode 100644 index 0000000..519b488 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_node_lora.h @@ -0,0 +1,521 @@ + +#include + +// Internal Globals +// Default values: overridden by settings in config, if present + +#define GLOBAL_ACK_TIMEOUT 400 // LoRa ACK timeout in ms. (Minimum = 200) +#define GLOBAL_LORA_RETRIES 2 // LoRa ACK automatic retries [0 - 3] +#define GLOBAL_LORA_TXPWR 17 // LoRa TX power in dBm (: +2dBm - +17dBm (for SX1276-7) +20dBm (for SX1278)) + +// select LoRa band configuration +#if defined(LORA_FREQUENCY) +#define FDRS_LORA_FREQUENCY LORA_FREQUENCY +#else +#define FDRS_LORA_FREQUENCY GLOBAL_LORA_FREQUENCY +#endif // LORA_FREQUENCY + +// select LoRa SF configuration +#if defined(LORA_SF) +#define FDRS_LORA_SF LORA_SF +#else +#define FDRS_LORA_SF GLOBAL_LORA_SF +#endif // LORA_SF + +// select LoRa ACK configuration +#if defined(LORA_ACK) || defined(GLOBAL_LORA_ACK) +#define FDRS_LORA_ACK +#endif // LORA_ACK + +// select LoRa ACK Timeout configuration +#if defined(LORA_ACK_TIMEOUT) +#define FDRS_ACK_TIMEOUT LORA_ACK_TIMEOUT +#else +#define FDRS_ACK_TIMEOUT GLOBAL_ACK_TIMEOUT +#endif // LORA_ACK_TIMEOUT + +// select LoRa Retry configuration +#if defined(LORA_RETRIES) +#define FDRS_LORA_RETRIES LORA_RETRIES +#else +#define FDRS_LORA_RETRIES GLOBAL_LORA_RETRIES +#endif // LORA_RETRIES + +// select LoRa Tx Power configuration +#if defined(LORA_TXPWR) +#define FDRS_LORA_TXPWR LORA_TXPWR +#else +#define FDRS_LORA_TXPWR GLOBAL_LORA_TXPWR +#endif // LORA_TXPWR + +// select LoRa BANDWIDTH configuration +#if defined(LORA_BANDWIDTH) +#define FDRS_LORA_BANDWIDTH LORA_BANDWIDTH +#else +#define FDRS_LORA_BANDWIDTH GLOBAL_LORA_BANDWIDTH +#endif // LORA_BANDWIDTH + +// select LoRa Coding Rate configuration +#if defined(LORA_CR) +#define FDRS_LORA_CR LORA_CR +#else +#define FDRS_LORA_CR GLOBAL_LORA_CR +#endif // LORA_CR + +// select LoRa SyncWord configuration +#if defined(LORA_SYNCWORD) +#define FDRS_LORA_SYNCWORD LORA_SYNCWORD +#else +#define FDRS_LORA_SYNCWORD GLOBAL_LORA_SYNCWORD +#endif // LORA_SYNCWORD + +#ifdef CUSTOM_SPI +#ifdef ARDUINO_ARCH_RP2040 +RADIOLIB_MODULE radio = new Module(LORA_SS, LORA_DIO, LORA_RST, LORA_BUSY, SPI1); +#endif // RP2040 +RADIOLIB_MODULE radio = new Module(LORA_SS, LORA_DIO, LORA_RST, LORA_BUSY, SPI); +#else +RADIOLIB_MODULE radio = new Module(LORA_SS, LORA_DIO, LORA_RST, LORA_BUSY); +#endif // CUSTOM_SPI + +bool pingFlag = false; +bool transmitFlag = false; // flag to indicate transmission or reception state +volatile bool enableInterrupt = true; // disable interrupt when it's not needed +volatile bool operationDone = false; // flag to indicate that a packet was sent or received + +unsigned long receivedLoRaMsg = 0; // Number of total LoRa packets destined for us and of valid size +unsigned long ackOkLoRaMsg = 0; // Number of total LoRa packets with valid CRC + +uint16_t LoRaAddress; + +unsigned long transmitLoRaMsgwAck = 0; // Number of total LoRa packets destined for us and of valid size +unsigned long msgOkLoRa = 0; // Number of total LoRa packets with valid CRC +void printLoraPacket(uint8_t *p, int size); + +uint16_t gtwyAddress = ((gatewayAddress[4] << 8) | GTWY_MAC); + +// Function prototypes +crcResult getLoRa(); + +#if defined(ESP8266) || defined(ESP32) +ICACHE_RAM_ATTR +#endif +void setFlag(void) +{ + if (!enableInterrupt) + { // check if the interrupt is enabled + return; + } + operationDone = true; // we sent or received packet, set the flag +} + +crcResult handleLoRa() +{ + crcResult crcReturned = CRC_NULL; + if (operationDone) + { // the interrupt was triggered + // DBG("Interrupt Triggered."); + enableInterrupt = false; + operationDone = false; + if (transmitFlag) // the previous operation was transmission, + { + radio.finishTransmit(); + radio.startReceive(); // return to listen mode + enableInterrupt = true; + transmitFlag = false; + } + else + { // the previous operation was reception + crcReturned = getLoRa(); + if (!transmitFlag) // return to listen if no transmission was begun + { + radio.startReceive(); + } + enableInterrupt = true; + } + } + return crcReturned; +} + +void begin_lora() +{ +#ifdef CUSTOM_SPI +#ifdef ESP32 + SPI.begin(LORA_SPI_SCK, LORA_SPI_MISO, LORA_SPI_MOSI); +#endif // ESP32 +#ifdef ARDUINO_ARCH_RP2040 + SPI1.setRX(LORA_SPI_MISO); + SPI1.setTX(LORA_SPI_MOSI); + SPI1.setSCK(LORA_SPI_SCK); + SPI1.begin(false); +#endif //ARDUINO_ARCH_RP2040 +#endif // CUSTOM_SPI + +#ifdef USE_SX126X + int state = radio.begin(FDRS_LORA_FREQUENCY, FDRS_LORA_BANDWIDTH, FDRS_LORA_SF, FDRS_LORA_CR, FDRS_LORA_SYNCWORD, FDRS_LORA_TXPWR, 8, 1.6, false); +#else + int state = radio.begin(FDRS_LORA_FREQUENCY, FDRS_LORA_BANDWIDTH, FDRS_LORA_SF, FDRS_LORA_CR, FDRS_LORA_SYNCWORD, FDRS_LORA_TXPWR, 8, 0); +#endif + + if (state == RADIOLIB_ERR_NONE) + { + DBG("RadioLib initialization successful!"); + } + else + { + DBG("RadioLib initialization failed, code " + String(state)); + while (true) + ; + } + DBG("LoRa Initialized. Frequency: " + String(FDRS_LORA_FREQUENCY) + " Bandwidth: " + String(FDRS_LORA_BANDWIDTH) + " SF: " + String(FDRS_LORA_SF) + " CR: " + String(FDRS_LORA_CR) + " SyncWord: " + String(FDRS_LORA_SYNCWORD) + " Tx Power: " + String(FDRS_LORA_TXPWR) + "dBm"); +#ifdef USE_SX126X + radio.setDio1Action(setFlag); +#else + radio.setDio0Action(setFlag, RISING); +#endif + radio.setCRC(false); + LoRaAddress = ((radio.randomByte() << 8) | radio.randomByte()); + DBG("LoRa node address is " + String(LoRaAddress, HEX) + " (hex)."); + state = radio.startReceive(); // start listening for LoRa packets + if (state == RADIOLIB_ERR_NONE) + { + } + else + { + DBG(" failed, code " + String(state)); + while (true) + ; + } +} + +// Transmits Lora data by calling RadioLib library function +// Returns the CRC result if ACKs are enabled otherwise returns CRC_NULL + +crcResult transmitLoRa(uint16_t *destMAC, DataReading *packet, uint8_t len) +{ + crcResult crcReturned = CRC_NULL; + uint8_t pkt[6 + (len * sizeof(DataReading))]; + uint16_t calcCRC = 0x0000; + + pkt[0] = (*destMAC >> 8); + pkt[1] = (*destMAC & 0x00FF); + pkt[2] = (LoRaAddress >> 8); + pkt[3] = (LoRaAddress & 0x00FF); + memcpy(&pkt[4], packet, len * sizeof(DataReading)); + for (int i = 0; i < (sizeof(pkt) - 2); i++) + { // Last 2 bytes are CRC so do not include them in the calculation itself + // printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, pkt[i]); + } +#ifndef LORA_ACK + calcCRC = crc16_update(calcCRC, 0xA1); // Recalculate CRC for No ACK +#endif // LORA_ACK + pkt[len * sizeof(DataReading) + 4] = (calcCRC >> 8); + pkt[len * sizeof(DataReading) + 5] = (calcCRC & 0x00FF); +#ifdef LORA_ACK // Wait for ACK + int retries = FDRS_LORA_RETRIES + 1; + while (retries != 0) + { + if (transmitLoRaMsgwAck != 0) + { + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, HEX) + " to gateway 0x" + String(*destMAC, HEX) + ". Retries remaining: " + String(retries - 1) + ", Ack Ok " + String((float)msgOkLoRa / transmitLoRaMsgwAck * 100) + "%"); + } + else + { + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, HEX) + " to gateway 0x" + String(*destMAC, HEX) + ". Retries remaining: " + String(retries - 1)); + } + // printLoraPacket(pkt,sizeof(pkt)); + int state = radio.transmit(pkt, sizeof(pkt)); + transmitFlag = true; + if (state == RADIOLIB_ERR_NONE) + { + } + else + { + DBG(" failed, code " + String(state)); + while (true) + ; + } + transmitLoRaMsgwAck++; + unsigned long loraAckTimeout = millis() + FDRS_ACK_TIMEOUT; + retries--; + delay(10); + while (crcReturned == CRC_NULL && (millis() < loraAckTimeout)) + { + crcReturned = handleLoRa(); + } + if (crcReturned == CRC_OK) + { + // DBG("LoRa ACK Received! CRC OK"); + msgOkLoRa++; + return CRC_OK; // we're done + } + else if (crcReturned == CRC_BAD) + { + // DBG("LoRa ACK Received! CRC BAD"); + // Resend original packet again if retries are available + } + else + { + DBG("LoRa Timeout waiting for ACK!"); + // resend original packet again if retries are available + } + } +#else // Send and do not wait for ACK reply + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, HEX) + " to gateway 0x" + String(*destMAC, HEX)); + // printLoraPacket(pkt,sizeof(pkt)); + int state = radio.startTransmit(pkt, sizeof(pkt)); + transmitFlag = true; + if (state == RADIOLIB_ERR_NONE) + { + } + else + { + DBG(" failed, code " + String(state)); + while (true) + ; + } + transmitLoRaMsgwAck++; +#endif // LORA_ACK + return crcReturned; +} + +// For now SystemPackets will not use ACK but will calculate CRC +// Returns CRC_NULL ask SystemPackets do not use ACKS at current time +crcResult transmitLoRa(uint16_t *destMAC, SystemPacket *packet, uint8_t len) +{ + crcResult crcReturned = CRC_NULL; + uint8_t pkt[6 + (len * sizeof(SystemPacket))]; + uint16_t calcCRC = 0x0000; + + // Building packet -- address portion - first 4 bytes + pkt[0] = (*destMAC >> 8); + pkt[1] = (*destMAC & 0x00FF); + pkt[2] = (LoRaAddress >> 8); + pkt[3] = (LoRaAddress & 0x00FF); + // Building packet -- data portion - 5 bytes + memcpy(&pkt[4], packet, len * sizeof(SystemPacket)); + // Calculate CRC of address and data portion of the packet + // Last 2 bytes are CRC so do not include them in the calculation itself + for (int i = 0; i < (sizeof(pkt) - 2); i++) + { + // printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, pkt[i]); + } + calcCRC = crc16_update(calcCRC, 0xA1); // Recalculate CRC for No ACK + // Building packet -- adding CRC - last 2 bytes + pkt[len * sizeof(SystemPacket) + 4] = (calcCRC >> 8); + pkt[len * sizeof(SystemPacket) + 5] = (calcCRC & 0x00FF); + // Packet is constructed now transmit the packet + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, HEX) + " to destination 0x" + String(*destMAC, HEX)); + // printLoraPacket(pkt,sizeof(pkt)); + int state = radio.transmit(pkt, sizeof(pkt)); + transmitFlag = true; + if (state == RADIOLIB_ERR_NONE) + { + } + else + { + DBG(" failed, code " + String(state)); + while (true) + ; + } + return crcReturned; +} + +// ****DO NOT CALL getLoRa() directly! ***** Call handleLoRa() instead! +// getLoRa for Sensors +// USED to get ACKs (SystemPacket type) from LoRa gateway at this point. May be used in the future to get other data +// Return type is crcResult struct - CRC_OK, CRC_BAD, CRC_NULL. CRC_NULL used for non-ack data + +crcResult getLoRa() +{ + int packetSize = radio.getPacketLength(); + if ((((packetSize - 6) % sizeof(DataReading) == 0) || ((packetSize - 6) % sizeof(SystemPacket) == 0)) && packetSize > 0) + { // packet size should be 6 bytes plus multiple of size of DataReading + uint8_t packet[packetSize]; + uint16_t packetCRC = 0x0000; // CRC Extracted from received LoRa packet + uint16_t calcCRC = 0x0000; // CRC calculated from received LoRa packet + uint16_t sourceMAC = 0x0000; + uint16_t destMAC = 0x0000; + + radio.readData((uint8_t *)&packet, packetSize); + + destMAC = (packet[0] << 8) | packet[1]; + sourceMAC = (packet[2] << 8) | packet[3]; + packetCRC = ((packet[packetSize - 2] << 8) | packet[packetSize - 1]); + // DBG("Source Address: 0x" + String(packet[2], HEX) + String(packet[3], HEX) + " Destination Address: 0x" + String(packet[0], HEX) + String(packet[1], HEX)); + if ((destMAC == LoRaAddress) || (destMAC == 0xFFFF)) + { // Check if addressed to this device or broadcast + // printLoraPacket(packet,sizeof(packet)); + if (receivedLoRaMsg != 0) + { // Avoid divide by 0 + DBG("Incoming LoRa. Size: " + String(packetSize) + " Bytes, RSSI: " + String(radio.getRSSI()) + "dBm, SNR: " + String(radio.getSNR()) + "dB, PacketCRC: 0x" + String(packetCRC, HEX) + ", Total LoRa received: " + String(receivedLoRaMsg) + ", CRC Ok Pct " + String((float)ackOkLoRaMsg / receivedLoRaMsg * 100) + "%"); + } + else + { + DBG("Incoming LoRa. Size: " + String(packetSize) + " Bytes, RSSI: " + String(radio.getRSSI()) + "dBm, SNR: " + String(radio.getSNR()) + "dB, PacketCRC: 0x" + String(packetCRC, HEX) + ", Total LoRa received: " + String(receivedLoRaMsg)); + } + receivedLoRaMsg++; + // Evaluate CRC + for (int i = 0; i < (packetSize - 2); i++) + { // Last 2 bytes of packet are the CRC so do not include them in calculation + // printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, packet[i]); + } + if ((packetSize - 6) % sizeof(DataReading) == 0) + { // DataReading type packet + if (calcCRC == packetCRC) + { + SystemPacket ACK = {.cmd = cmd_ack, .param = CRC_OK}; + DBG("CRC Match, sending ACK packet to node 0x" + String(sourceMAC, HEX) + "(hex)"); + transmitLoRa(&sourceMAC, &ACK, 1); // Send ACK back to source + } + else if (packetCRC == crc16_update(calcCRC, 0xA1)) + { // Sender does not want ACK and CRC is valid + DBG("Node address 0x" + String(sourceMAC, 16) + "(hex) does not want ACK"); + } + else + { + SystemPacket NAK = {.cmd = cmd_ack, .param = CRC_BAD}; + // Send NAK packet to sensor + DBG("CRC Mismatch! Packet CRC is 0x" + String(packetCRC, HEX) + ", Calculated CRC is 0x" + String(calcCRC, HEX) + " Sending NAK packet to node 0x" + String(sourceMAC, HEX) + "(hex)"); + transmitLoRa(&sourceMAC, &NAK, 1); // CRC did not match so send NAK to source + return CRC_BAD; // Exit function and do not update newData to send invalid data further on + } + memcpy(&theData, &packet[4], packetSize - 6); // Split off data portion of packet (N - 6 bytes (6 bytes for headers and CRC)) + ln = (packetSize - 6) / sizeof(DataReading); + newData = true; + ackOkLoRaMsg++; + return CRC_OK; + } + else if ((packetSize - 6) == sizeof(SystemPacket)) + { + unsigned int ln = (packetSize - 6) / sizeof(SystemPacket); + SystemPacket receiveData[ln]; + + if (calcCRC == packetCRC) + { + memcpy(receiveData, &packet[4], packetSize - 6); // Split off data portion of packet (N bytes) + if (ln == 1 && receiveData[0].cmd == cmd_ack) + { + DBG("ACK Received - CRC Match"); + } + else if (ln == 1 && receiveData[0].cmd == cmd_ping) + { // We have received a ping request or reply?? + if (receiveData[0].param == 1) + { // This is a reply to our ping request + pingFlag = true; + DBG("We have received a ping reply via LoRa from address " + String(sourceMAC, HEX)); + } + else if (receiveData[0].param == 0) + { + DBG("We have received a ping request from 0x" + String(sourceMAC, HEX) + ", Replying."); + SystemPacket pingReply = {.cmd = cmd_ping, .param = 1}; + transmitLoRa(&sourceMAC, &pingReply, 1); + } + } + else + { // data we have received is not yet programmed. How we handle is future enhancement. + DBG("Received some LoRa SystemPacket data that is not yet handled. To be handled in future enhancement."); + DBG("ln: " + String(ln) + "data type: " + String(receiveData[0].cmd)); + } + ackOkLoRaMsg++; + return CRC_OK; + } + else if (packetCRC == crc16_update(calcCRC, 0xA1)) + { // Sender does not want ACK and CRC is valid + memcpy(receiveData, &packet[4], packetSize - 6); // Split off data portion of packet (N bytes) + if (ln == 1 && receiveData[0].cmd == cmd_ack) + { + DBG("ACK Received - CRC Match"); + } + else if (ln == 1 && receiveData[0].cmd == cmd_ping) + { // We have received a ping request or reply?? + if (receiveData[0].param == 1) + { // This is a reply to our ping request + pingFlag = true; + DBG("We have received a ping reply via LoRa from address " + String(sourceMAC, HEX)); + } + else if (receiveData[0].param == 0) + { + DBG("We have received a ping request from 0x" + String(sourceMAC, HEX) + ", Replying."); + SystemPacket pingReply = {.cmd = cmd_ping, .param = 1}; + transmitLoRa(&sourceMAC, &pingReply, 1); + } + } + else + { // data we have received is not yet programmed. How we handle is future enhancement. + DBG("Received some LoRa SystemPacket data that is not yet handled. To be handled in future enhancement."); + DBG("ln: " + String(ln) + "data type: " + String(receiveData[0].cmd)); + } + ackOkLoRaMsg++; + return CRC_OK; + } + else + { + DBG("ACK Received CRC Mismatch! Packet CRC is 0x" + String(packetCRC, HEX) + ", Calculated CRC is 0x" + String(calcCRC, HEX)); + return CRC_BAD; + } + } + } + else + { + // DBG("Incoming LoRa packet of " + String(packetSize) + " bytes received from address 0x" + String(sourceMAC, HEX) + " destined for node address 0x" + String(destMAC, HEX)); + // printLoraPacket(packet,sizeof(packet)); + return CRC_NULL; + } + } + else + { + if (packetSize != 0) + { + // DBG("Incoming LoRa packet of " + String(packetSize) + "bytes not processed."); + // uint8_t packet[packetSize]; + // radio.readData((uint8_t *)&packet, packetSize); + // printLoraPacket(packet,sizeof(packet)); + return CRC_NULL; + } + } + return CRC_NULL; +} + +// FDRS Sensor pings gateway and listens for a defined amount of time for a reply +// Blocking function for timeout amount of time (up to timeout time waiting for reply)(IE no callback) +// Returns the amount of time in ms that the ping takes or predefined value if ping fails within timeout +uint32_t pingFDRSLoRa(uint16_t *address, uint32_t timeout) +{ + SystemPacket sys_packet = {.cmd = cmd_ping, .param = 0}; + + transmitLoRa(address, &sys_packet, 1); + DBG("LoRa ping sent to address: 0x" + String(*address, HEX)); + uint32_t ping_start = millis(); + pingFlag = false; + while ((millis() - ping_start) <= timeout) + { + handleLoRa(); + #ifdef ESP8266 + yield(); + #endif + if (pingFlag) + { + DBG("LoRa Ping Returned: " + String(millis() - ping_start) + "ms."); + pingFlag = false; + return (millis() - ping_start); + } + } + DBG("No LoRa ping returned within " + String(timeout) + "ms."); + return UINT32_MAX; +} + +void printLoraPacket(uint8_t *p, int size) +{ + printf("Printing packet of size %d.", size); + for (int i = 0; i < size; i++) + { + if (i % 2 == 0) + printf("\n%02d: ", i); + printf("%02X ", p[i]); + } + printf("\n"); +} diff --git a/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_oled.h b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_oled.h new file mode 100644 index 0000000..bd34b01 --- /dev/null +++ b/IOT/receiver LoRa/lib/Farm-Data-Relay-System/src/fdrs_oled.h @@ -0,0 +1,144 @@ + + +#include +#define DISPLAY_PAGES 4 + +String debug_buffer[5] = {"", "", "", "", ""}; +SSD1306Wire display(0x3c, OLED_SDA, OLED_SCL); // ADDRESS, SDA, SCL +unsigned long displayEvent = 0; +uint8_t displayPage = 0; + +void draw_OLED_header() +{ + #ifdef FDRS_GATEWAY + display.setFont(ArialMT_Plain_10); + display.clear(); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 0, "MAC: " + String(UNIT_MAC, HEX)); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(63, 0, OLED_HEADER); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(127, 0, "TBD"); + display.display(); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + #endif + #ifdef FDRS_NODE + display.setFont(ArialMT_Plain_10); + display.clear(); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 0, "ID: " + String(READING_ID)); + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(63, 0, OLED_HEADER); + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(127, 0, "GW: " + String(GTWY_MAC, HEX)); + display.display(); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + #endif + display.drawHorizontalLine(0, 15, 128); + display.drawHorizontalLine(0, 16, 128); +} + +void drawDebugPage() { + draw_OLED_header(); + uint8_t lineNumber = 0; + for (uint8_t i = 0; i < 5; i++) + { + uint8_t ret = display.FDRS_drawStringMaxWidth(0, 17 + (lineNumber * 9), 127, debug_buffer[i]); + lineNumber = ret + lineNumber; + if (lineNumber > 5) + break; + } + display.display(); +} + +void debug_OLED(String debug_text) +{ + displayEvent = millis()/1000; // Display Event is tracked in units of seconds + displayPage = 0; + display.clear(); + + for (uint8_t i = 4; i > 0; i--) + { + + debug_buffer[i] = debug_buffer[i - 1]; + } + debug_buffer[0] = String(millis() / 1000) + " " + debug_text; + drawDebugPage(); +} + +void drawBlankPage() { + display.clear(); + display.display(); +} + +void drawStatusPage() { + // draw_OLED_header(); + // display.FDRS_drawStringMaxWidth(0, 17, 127, "Status Page 1 " + String(millis()/1000)); + // display.display(); +} + +void drawPage2() { + // draw_OLED_header(); + // display.FDRS_drawStringMaxWidth(0, 17, 127, "Page 2 " + String(millis()/1000)); + // display.display(); +} + +void drawPage3() { + // draw_OLED_header(); + // display.FDRS_drawStringMaxWidth(0, 17, 127, "Page 3 " + String(millis()/1000)); + // display.display(); +} + +// write display content to display buffer +// nextpage = true -> flip 1 page +// When debug info comes in then switch to debug page +// after 60 seconds switch to blank page to save screen +void drawPageOLED(bool nextpage) { + + if((millis()/1000 - displayEvent) > OLED_PAGE_SECS && nextpage) { + displayPage = (displayPage >= DISPLAY_PAGES) ? 0 : (displayPage + 1); + displayEvent = millis()/1000; + display.clear(); + + switch(displayPage) { + + // page 0: debug output + // page 1: gateway/node status + // page 2: to be defined + // page 3: to be defined + // page 4: blank (screen saver) + + case 0: // display debug output + drawDebugPage(); + break; + case 1: // gateway/node status + // drawStatusPage(); + // break; + case 2: // to be defined later + // drawPage2(); + // break; + case 3: // to be defined later + // drawPage3(); + // break; + case 4: // Blank page + drawBlankPage(); + break; + default: // Blank page + drawBlankPage(); + break; + } + } +} + +void init_oled(){ + pinMode(OLED_RST, OUTPUT); + digitalWrite(OLED_RST, LOW); + delay(30); + digitalWrite(OLED_RST, HIGH); + Wire.begin(OLED_SDA, OLED_SCL); + display.init(); + display.flipScreenVertically(); + draw_OLED_header(); + } \ No newline at end of file