mirror of
https://github.com/markqvist/RNode_Firmware.git
synced 2025-12-16 08:54:25 -05:00
Add E-Ink display driver and chip detection for VME213
- Implemented LCMEN2R13EFC1 display driver in src/LCMEN2R13EFC1.h, supporting full and fast refresh modes. - Added E-Ink chip detection functionality in src/einkDetect_VME213.h to differentiate between LCMEN213EFC1 and E0213A367. - Included GNU General Public License text in tools/LICENSE.txt. - Added arduino-cli executable to tools directory.
This commit is contained in:
parent
5b7b0d3afe
commit
31e12f3bd4
41 changed files with 14273 additions and 3 deletions
63
Boards.h
63
Boards.h
|
|
@ -104,6 +104,11 @@
|
|||
#define MODEL_C6 0xC6 // Heltec Mesh Node T114, 470-510 MHz
|
||||
#define MODEL_C7 0xC7 // Heltec Mesh Node T114, 863-928 MHz
|
||||
|
||||
#define PRODUCT_VME213 0xC9 // Heltec VisionMaster E213
|
||||
#define BOARD_VME213 0x40
|
||||
#define MODEL_CD 0xCD // VisionMaster E213, 470-510 MHz
|
||||
#define MODEL_CE 0xCE // VisionMaster E213, 863-928 MHz
|
||||
|
||||
#define PRODUCT_TECHO 0x15 // LilyGO T-Echo devices
|
||||
#define BOARD_TECHO 0x44
|
||||
#define MODEL_16 0x16 // T-Echo 433 MHz
|
||||
|
|
@ -432,6 +437,64 @@
|
|||
const int pin_miso = 11;
|
||||
const int pin_sclk = 9;
|
||||
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// Heltec VisionMaster E213 with ESP32-S3R8 + SX1262 + 2.13" E-Ink
|
||||
#define IS_ESP32S3 true
|
||||
#define HAS_DISPLAY true // 2.13" E-Ink LCMEN2R13EFC1, 250x122 pixels
|
||||
#define HAS_EINK true // Enable E-Ink specific display code
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_PMU false
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define PIN_WAKEUP GPIO_NUM_0
|
||||
#define WAKEUP_LEVEL 0
|
||||
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
// No physical LEDs on VME213, display only
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 35;
|
||||
const int pin_led_tx = 35;
|
||||
#else
|
||||
const int pin_led_rx = -1;
|
||||
const int pin_led_tx = -1;
|
||||
#endif
|
||||
|
||||
// SX1262 LoRa radio configuration
|
||||
#define MODEM SX1262
|
||||
#define HAS_TCXO true
|
||||
const int pin_tcxo_enable = -1;
|
||||
#define HAS_BUSY true
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
|
||||
// SX1262 pins (from Heltec board-config.h)
|
||||
const int pin_cs = 8;
|
||||
const int pin_busy = 13;
|
||||
const int pin_dio = 14;
|
||||
const int pin_reset = 12;
|
||||
const int pin_mosi = 10;
|
||||
const int pin_miso = 11;
|
||||
const int pin_sclk = 9;
|
||||
|
||||
// E-Ink display pins (HT_ICMEN2R13EFC1 controller)
|
||||
const int pin_disp_reset = 3;
|
||||
const int pin_disp_dc = 2;
|
||||
const int pin_disp_cs = 5;
|
||||
const int pin_disp_busy = 1;
|
||||
const int pin_disp_sck = 4;
|
||||
const int pin_disp_mosi = 6;
|
||||
const int pin_disp_miso = -1;
|
||||
|
||||
// Vext power control (HIGH=ON, LOW=OFF)
|
||||
#define Vext GPIO_NUM_18
|
||||
|
||||
// I2C for sensors (QuickLink)
|
||||
#define I2C_SDA 39
|
||||
#define I2C_SCL 38
|
||||
|
||||
#elif BOARD_MODEL == BOARD_RNODE_NG_20
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH true
|
||||
|
|
|
|||
144
Display.h
144
Display.h
|
|
@ -84,6 +84,14 @@
|
|||
#define DISP_W 128
|
||||
#define DISP_H 64
|
||||
#define DISP_ADDR -1
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
#include "src/LCMEN2R13EFC1.h"
|
||||
#include "src/einkDetect_VME213.h"
|
||||
// VME213 E-Ink display: 250x122 pixels
|
||||
#define DISP_W 250
|
||||
#define DISP_H 122
|
||||
#define DISP_ADDR -1
|
||||
#define VME213_REFRESH_RATIO 10 // 10 FAST : 1 FULL refresh
|
||||
#elif BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
#define DISP_RST -1
|
||||
#define DISP_ADDR 0x3C
|
||||
|
|
@ -121,6 +129,18 @@
|
|||
uint32_t last_epd_refresh = 0;
|
||||
uint32_t last_epd_full_refresh = 0;
|
||||
#define REFRESH_PERIOD 300000
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
LCMEN2R13EFC1 vme213_display;
|
||||
EInkChipType vme213_chip_type = EINK_LCMEN213EFC1;
|
||||
uint8_t vme213_displayBuffer[4000]; // 250x122 pixels = 4000 bytes
|
||||
uint32_t last_epd_refresh = 0;
|
||||
uint32_t last_epd_full_refresh = 0;
|
||||
uint8_t vme213_fast_refresh_count = 0;
|
||||
#define REFRESH_PERIOD 300000
|
||||
// Compatibility macro
|
||||
#define display vme213_display
|
||||
#define SSD1306_BLACK 0
|
||||
#define SSD1306_WHITE 1
|
||||
#else
|
||||
Adafruit_SSD1306 display(DISP_W, DISP_H, &Wire, DISP_RST);
|
||||
#endif
|
||||
|
|
@ -189,6 +209,19 @@ void update_area_positions() {
|
|||
p_as_x = 64;
|
||||
p_as_y = 0;
|
||||
}
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// VME213: 250x122 landscape - split into two 125x122 areas
|
||||
if (disp_mode == DISP_MODE_LANDSCAPE) {
|
||||
p_ad_x = 0;
|
||||
p_ad_y = 0;
|
||||
p_as_x = 125;
|
||||
p_as_y = 0;
|
||||
} else {
|
||||
p_ad_x = 0;
|
||||
p_ad_y = 0;
|
||||
p_as_x = 0;
|
||||
p_as_y = 61;
|
||||
}
|
||||
#else
|
||||
if (disp_mode == DISP_MODE_PORTRAIT) {
|
||||
p_ad_x = 0 * DISPLAY_SCALE;
|
||||
|
|
@ -247,6 +280,58 @@ uint8_t display_contrast = 0x00;
|
|||
}
|
||||
#endif
|
||||
|
||||
#if BOARD_MODEL == BOARD_VME213
|
||||
// VME213 E-Ink display rendering helpers
|
||||
void vme213_drawPixel(uint16_t x, uint16_t y, uint8_t color) {
|
||||
if (x >= DISP_W || y >= DISP_H) return;
|
||||
uint16_t byteIndex = y * ((DISP_W + 7) / 8) + (x / 8);
|
||||
uint8_t bitMask = 0x80 >> (x % 8);
|
||||
|
||||
if (color == SSD1306_WHITE) {
|
||||
vme213_displayBuffer[byteIndex] |= bitMask; // White pixel
|
||||
} else {
|
||||
vme213_displayBuffer[byteIndex] &= ~bitMask; // Black pixel
|
||||
}
|
||||
}
|
||||
|
||||
void vme213_fillScreen(uint8_t color) {
|
||||
memset(vme213_displayBuffer, (color == SSD1306_WHITE) ? 0xFF : 0x00, sizeof(vme213_displayBuffer));
|
||||
}
|
||||
|
||||
void vme213_drawText(uint16_t x, uint16_t y, const char* text, uint8_t size) {
|
||||
// Simple text rendering using 5x7 font
|
||||
// This is a placeholder - full implementation would use Adafruit_GFX font rendering
|
||||
// For now, just mark text position
|
||||
for (int i = 0; i < 10; i++) {
|
||||
vme213_drawPixel(x + i, y, SSD1306_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
void vme213_renderStatus() {
|
||||
vme213_fillScreen(SSD1306_WHITE);
|
||||
|
||||
// Header area (top 20 pixels)
|
||||
for (int y = 0; y < 20; y++) {
|
||||
for (int x = 0; x < DISP_W; x++) {
|
||||
vme213_drawPixel(x, y, (y % 2 == 0) ? SSD1306_BLACK : SSD1306_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
// Status text placeholder
|
||||
vme213_drawText(10, 30, "RNode", 2);
|
||||
|
||||
// Signal indicator (simple bars)
|
||||
if (radio_online) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int barHeight = 10 + i * 5;
|
||||
for (int y = 0; y < barHeight; y++) {
|
||||
vme213_drawPixel(10 + i * 8, 100 - y, SSD1306_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool display_init() {
|
||||
#if HAS_DISPLAY
|
||||
#if BOARD_MODEL == BOARD_RNODE_NG_20 || BOARD_MODEL == BOARD_LORA32_V2_0
|
||||
|
|
@ -299,6 +384,27 @@ bool display_init() {
|
|||
pinMode(pin_backlight, OUTPUT);
|
||||
analogWrite(pin_backlight, 0);
|
||||
#endif
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// Enable Vext power (GPIO 18) for peripherals
|
||||
pinMode(Vext, OUTPUT);
|
||||
digitalWrite(Vext, HIGH);
|
||||
delay(100);
|
||||
|
||||
// Detect E-Ink chip type
|
||||
vme213_chip_type = detectEInkChip(pin_disp_reset, pin_disp_busy);
|
||||
|
||||
// Initialize SPI for E-Ink display
|
||||
SPI.begin(pin_disp_sck, pin_disp_miso, pin_disp_mosi, pin_disp_cs);
|
||||
|
||||
// Initialize E-Ink driver
|
||||
vme213_display.begin(&SPI, pin_disp_dc, pin_disp_cs, pin_disp_busy, pin_disp_reset);
|
||||
|
||||
// Clear buffer
|
||||
memset(vme213_displayBuffer, 0xFF, sizeof(vme213_displayBuffer));
|
||||
|
||||
// Perform initial FULL refresh to clear display
|
||||
vme213_display.update(vme213_displayBuffer, LCMEN2R13EFC1::UPDATE_FULL);
|
||||
vme213_fast_refresh_count = 0;
|
||||
#elif BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
Wire.begin(SDA_OLED, SCL_OLED);
|
||||
#elif BOARD_MODEL == BOARD_XIAO_S3
|
||||
|
|
@ -348,6 +454,9 @@ bool display_init() {
|
|||
#if BOARD_MODEL == BOARD_TECHO
|
||||
// Don't check if display is actually connected
|
||||
if(false) {
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// VME213 E-Ink display initialization already done, skip connection check
|
||||
if(false) {
|
||||
#elif BOARD_MODEL == BOARD_TDECK
|
||||
display.init(240, 320);
|
||||
display.setSPISpeed(80e6);
|
||||
|
|
@ -414,6 +523,10 @@ bool display_init() {
|
|||
#elif BOARD_MODEL == BOARD_TECHO
|
||||
disp_mode = DISP_MODE_PORTRAIT;
|
||||
display.setRotation(3);
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// VME213 E-Ink: 250x122 landscape by default
|
||||
disp_mode = DISP_MODE_LANDSCAPE;
|
||||
// No setRotation for VME213 (driver handles orientation)
|
||||
#else
|
||||
disp_mode = DISP_MODE_PORTRAIT;
|
||||
display.setRotation(3);
|
||||
|
|
@ -429,7 +542,7 @@ bool display_init() {
|
|||
stat_area.cp437(true);
|
||||
disp_area.cp437(true);
|
||||
|
||||
#if BOARD_MODEL != BOARD_HELTEC_T114
|
||||
#if BOARD_MODEL != BOARD_HELTEC_T114 && BOARD_MODEL != BOARD_VME213
|
||||
display.cp437(true);
|
||||
#endif
|
||||
|
||||
|
|
@ -990,6 +1103,12 @@ void update_display(bool blank = false) {
|
|||
epd_blank();
|
||||
epd_blanked = true;
|
||||
}
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
if (!epd_blanked) {
|
||||
vme213_fillScreen(SSD1306_WHITE);
|
||||
vme213_display.update(vme213_displayBuffer, LCMEN2R13EFC1::UPDATE_FULL);
|
||||
epd_blanked = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BOARD_MODEL == BOARD_HELTEC_T114
|
||||
|
|
@ -1015,6 +1134,8 @@ void update_display(bool blank = false) {
|
|||
|
||||
#if BOARD_MODEL == BOARD_HELTEC_T114
|
||||
display.clear();
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// E-Ink buffer will be redrawn completely
|
||||
#elif BOARD_MODEL != BOARD_TDECK && BOARD_MODEL != BOARD_TECHO
|
||||
display.clearDisplay();
|
||||
#endif
|
||||
|
|
@ -1027,6 +1148,9 @@ void update_display(bool blank = false) {
|
|||
#if BOARD_MODEL == BOARD_TECHO
|
||||
display.setFullWindow();
|
||||
display.fillScreen(SSD1306_WHITE);
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// Render RNode UI to E-Ink buffer
|
||||
vme213_renderStatus();
|
||||
#endif
|
||||
|
||||
update_stat_area();
|
||||
|
|
@ -1040,6 +1164,24 @@ void update_display(bool blank = false) {
|
|||
last_epd_refresh = millis();
|
||||
epd_blanked = false;
|
||||
}
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
if (current-last_epd_refresh >= epd_update_interval) {
|
||||
// Decide refresh type: 10 FAST : 1 FULL
|
||||
LCMEN2R13EFC1::UpdateType refreshType;
|
||||
if (vme213_fast_refresh_count >= VME213_REFRESH_RATIO || current-last_epd_full_refresh >= REFRESH_PERIOD) {
|
||||
refreshType = LCMEN2R13EFC1::UPDATE_FULL;
|
||||
vme213_fast_refresh_count = 0;
|
||||
last_epd_full_refresh = millis();
|
||||
} else {
|
||||
refreshType = LCMEN2R13EFC1::UPDATE_FAST;
|
||||
vme213_fast_refresh_count++;
|
||||
}
|
||||
|
||||
// Perform E-Ink update
|
||||
vme213_display.update(vme213_displayBuffer, refreshType);
|
||||
last_epd_refresh = millis();
|
||||
epd_blanked = false;
|
||||
}
|
||||
#elif BOARD_MODEL != BOARD_TDECK
|
||||
display.display();
|
||||
#endif
|
||||
|
|
|
|||
12
Makefile
12
Makefile
|
|
@ -128,6 +128,9 @@ firmware-heltec32_v3:
|
|||
firmware-heltec32_v4:
|
||||
arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x3F\""
|
||||
|
||||
firmware-vme213:
|
||||
arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
|
||||
|
||||
firmware-rnode_ng_20: check_bt_buffers
|
||||
arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
|
||||
|
||||
|
|
@ -400,6 +403,15 @@ release-heltec32_v4: check_bt_buffers
|
|||
zip --junk-paths ./Release/rnode_firmware_heltec32v4pa.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_heltec32v4pa.boot_app0 build/rnode_firmware_heltec32v4pa.bin build/rnode_firmware_heltec32v4pa.bootloader build/rnode_firmware_heltec32v4pa.partitions
|
||||
rm -r build
|
||||
|
||||
release-vme213:
|
||||
arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x40\""
|
||||
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_vme213.boot_app0
|
||||
cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin build/rnode_firmware_vme213.bin
|
||||
cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_vme213.bootloader
|
||||
cp build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin build/rnode_firmware_vme213.partitions
|
||||
zip --junk-paths ./Release/rnode_firmware_vme213.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_vme213.boot_app0 build/rnode_firmware_vme213.bin build/rnode_firmware_vme213.bootloader build/rnode_firmware_vme213.partitions
|
||||
rm -r build
|
||||
|
||||
release-heltec32_v2_extled: check_bt_buffers
|
||||
arduino-cli compile --fqbn esp32:esp32:heltec_wifi_lora_32_V2 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x38\" \"-DEXTERNAL_LEDS=true\""
|
||||
cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_heltec32v2.boot_app0
|
||||
|
|
|
|||
171
RNode_Firmware/BLESerial.cpp
Normal file
171
RNode_Firmware/BLESerial.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Boards.h"
|
||||
|
||||
#if PLATFORM != PLATFORM_NRF52
|
||||
#if HAS_BLE
|
||||
|
||||
#include "BLESerial.h"
|
||||
|
||||
uint32_t bt_passkey_callback();
|
||||
void bt_passkey_notify_callback(uint32_t passkey);
|
||||
bool bt_security_request_callback();
|
||||
void bt_authentication_complete_callback(esp_ble_auth_cmpl_t auth_result);
|
||||
bool bt_confirm_pin_callback(uint32_t pin);
|
||||
void bt_connect_callback(BLEServer *server);
|
||||
void bt_disconnect_callback(BLEServer *server);
|
||||
bool bt_client_authenticated();
|
||||
|
||||
uint32_t BLESerial::onPassKeyRequest() { return bt_passkey_callback(); }
|
||||
void BLESerial::onPassKeyNotify(uint32_t passkey) { bt_passkey_notify_callback(passkey); }
|
||||
bool BLESerial::onSecurityRequest() { return bt_security_request_callback(); }
|
||||
void BLESerial::onAuthenticationComplete(esp_ble_auth_cmpl_t auth_result) { bt_authentication_complete_callback(auth_result); }
|
||||
void BLESerial::onConnect(BLEServer *server) { bt_connect_callback(server); }
|
||||
void BLESerial::onDisconnect(BLEServer *server) { bt_disconnect_callback(server); ble_server->startAdvertising(); }
|
||||
bool BLESerial::onConfirmPIN(uint32_t pin) { return bt_confirm_pin_callback(pin); };
|
||||
bool BLESerial::connected() { return ble_server->getConnectedCount() > 0; }
|
||||
|
||||
int BLESerial::read() {
|
||||
int result = this->rx_buffer.pop();
|
||||
if (result == '\n') { this->numAvailableLines--; }
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t BLESerial::readBytes(uint8_t *buffer, size_t bufferSize) {
|
||||
int i = 0;
|
||||
while (i < bufferSize && available()) { buffer[i] = (uint8_t)this->rx_buffer.pop(); i++; }
|
||||
return i;
|
||||
}
|
||||
|
||||
int BLESerial::peek() {
|
||||
if (this->rx_buffer.getLength() == 0) return -1;
|
||||
return this->rx_buffer.get(0);
|
||||
}
|
||||
|
||||
int BLESerial::available() { return this->rx_buffer.getLength(); }
|
||||
|
||||
size_t BLESerial::print(const char *str) {
|
||||
if (ble_server->getConnectedCount() <= 0) return 0;
|
||||
size_t written = 0; for (size_t i = 0; str[i] != '\0'; i++) { written += this->write(str[i]); }
|
||||
flush();
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t BLESerial::write(const uint8_t *buffer, size_t bufferSize) {
|
||||
if (ble_server->getConnectedCount() <= 0) { return 0; } else {
|
||||
size_t written = 0; for (int i = 0; i < bufferSize; i++) { written += this->write(buffer[i]); }
|
||||
flush();
|
||||
|
||||
return written;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BLESerial::write(uint8_t byte) {
|
||||
if (bt_client_authenticated()) {
|
||||
if (ble_server->getConnectedCount() <= 0) { return 0; } else {
|
||||
this->transmitBuffer[this->transmitBufferLength] = byte;
|
||||
this->transmitBufferLength++;
|
||||
if (this->transmitBufferLength == maxTransferSize) { flush(); }
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BLESerial::flush() {
|
||||
if (this->transmitBufferLength > 0) {
|
||||
TxCharacteristic->setValue(this->transmitBuffer, this->transmitBufferLength);
|
||||
this->transmitBufferLength = 0;
|
||||
this->lastFlushTime = millis();
|
||||
TxCharacteristic->notify(true);
|
||||
}
|
||||
}
|
||||
|
||||
void BLESerial::disconnect() {
|
||||
if (ble_server->getConnectedCount() > 0) {
|
||||
uint16_t conn_id = ble_server->getConnId();
|
||||
// Serial.printf("Have connected: %d\n", conn_id);
|
||||
ble_server->disconnect(conn_id);
|
||||
// Serial.println("Disconnected");
|
||||
} else {
|
||||
// Serial.println("No connected");
|
||||
}
|
||||
}
|
||||
|
||||
void BLESerial::begin(const char *name) {
|
||||
ConnectedDeviceCount = 0;
|
||||
BLEDevice::init(name);
|
||||
|
||||
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
|
||||
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
|
||||
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN ,ESP_PWR_LVL_P9);
|
||||
|
||||
ble_server = BLEDevice::createServer();
|
||||
ble_server->setCallbacks(this);
|
||||
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_MITM);
|
||||
BLEDevice::setSecurityCallbacks(this);
|
||||
|
||||
SetupSerialService();
|
||||
this->startAdvertising();
|
||||
}
|
||||
|
||||
void BLESerial::startAdvertising() {
|
||||
ble_adv = BLEDevice::getAdvertising();
|
||||
ble_adv->addServiceUUID(BLE_SERIAL_SERVICE_UUID);
|
||||
ble_adv->setMinPreferred(0x20);
|
||||
ble_adv->setMaxPreferred(0x40);
|
||||
ble_adv->setScanResponse(true);
|
||||
ble_adv->start();
|
||||
}
|
||||
|
||||
void BLESerial::stopAdvertising() {
|
||||
ble_adv = BLEDevice::getAdvertising();
|
||||
ble_adv->stop();
|
||||
}
|
||||
|
||||
void BLESerial::end() { BLEDevice::deinit(); }
|
||||
|
||||
void BLESerial::onWrite(BLECharacteristic *characteristic) {
|
||||
if (characteristic->getUUID().toString() == BLE_RX_UUID) {
|
||||
auto value = characteristic->getValue();
|
||||
for (int i = 0; i < value.length(); i++) { rx_buffer.push(value[i]); }
|
||||
}
|
||||
}
|
||||
|
||||
void BLESerial::SetupSerialService() {
|
||||
SerialService = ble_server->createService(BLE_SERIAL_SERVICE_UUID);
|
||||
|
||||
RxCharacteristic = SerialService->createCharacteristic(BLE_RX_UUID, BLECharacteristic::PROPERTY_WRITE);
|
||||
RxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENC_MITM);
|
||||
RxCharacteristic->addDescriptor(new BLE2902());
|
||||
RxCharacteristic->setWriteProperty(true);
|
||||
RxCharacteristic->setCallbacks(this);
|
||||
|
||||
TxCharacteristic = SerialService->createCharacteristic(BLE_TX_UUID, BLECharacteristic::PROPERTY_NOTIFY);
|
||||
TxCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM);
|
||||
TxCharacteristic->addDescriptor(new BLE2902());
|
||||
TxCharacteristic->setNotifyProperty(true);
|
||||
TxCharacteristic->setReadProperty(true);
|
||||
|
||||
SerialService->start();
|
||||
}
|
||||
|
||||
BLESerial::BLESerial() { }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
136
RNode_Firmware/BLESerial.h
Normal file
136
RNode_Firmware/BLESerial.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Boards.h"
|
||||
|
||||
#if PLATFORM != PLATFORM_NRF52
|
||||
#if HAS_BLE
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
template <size_t n>
|
||||
class BLEFIFO {
|
||||
private:
|
||||
uint8_t buffer[n];
|
||||
int head = 0;
|
||||
int tail = 0;
|
||||
|
||||
public:
|
||||
void push(uint8_t value) {
|
||||
buffer[head] = value;
|
||||
head = (head + 1) % n;
|
||||
if (head == tail) { tail = (tail + 1) % n; }
|
||||
}
|
||||
|
||||
int pop() {
|
||||
if (head == tail) {
|
||||
return -1;
|
||||
} else {
|
||||
uint8_t value = buffer[tail];
|
||||
tail = (tail + 1) % n;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() { head = 0; tail = 0; }
|
||||
|
||||
int get(size_t index) {
|
||||
if (index >= this->getLength()) {
|
||||
return -1;
|
||||
} else {
|
||||
return buffer[(tail + index) % n];
|
||||
}
|
||||
}
|
||||
|
||||
size_t getLength() {
|
||||
if (head >= tail) {
|
||||
return head - tail;
|
||||
} else {
|
||||
return n - tail + head;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define RX_BUFFER_SIZE 6144
|
||||
#define BLE_BUFFER_SIZE 512 // Must fit in max GATT attribute length
|
||||
#define MIN_MTU 50
|
||||
|
||||
class BLESerial : public BLECharacteristicCallbacks, public BLEServerCallbacks, public BLESecurityCallbacks, public Stream {
|
||||
public:
|
||||
BLESerial();
|
||||
|
||||
void begin(const char *name);
|
||||
void end();
|
||||
void disconnect();
|
||||
void startAdvertising();
|
||||
void stopAdvertising();
|
||||
void onWrite(BLECharacteristic *characteristic);
|
||||
int available();
|
||||
int peek();
|
||||
int read();
|
||||
size_t readBytes(uint8_t *buffer, size_t bufferSize);
|
||||
size_t write(uint8_t byte);
|
||||
size_t write(const uint8_t *buffer, size_t bufferSize);
|
||||
size_t print(const char *value);
|
||||
void flush();
|
||||
void onConnect(BLEServer *server);
|
||||
void onDisconnect(BLEServer *server);
|
||||
|
||||
uint32_t onPassKeyRequest();
|
||||
void onPassKeyNotify(uint32_t passkey);
|
||||
bool onSecurityRequest();
|
||||
void onAuthenticationComplete(esp_ble_auth_cmpl_t);
|
||||
bool onConfirmPIN(uint32_t pin);
|
||||
|
||||
bool connected();
|
||||
|
||||
BLEServer *ble_server;
|
||||
BLEAdvertising *ble_adv;
|
||||
BLEService *SerialService;
|
||||
BLECharacteristic *TxCharacteristic;
|
||||
BLECharacteristic *RxCharacteristic;
|
||||
size_t transmitBufferLength;
|
||||
unsigned long long lastFlushTime;
|
||||
|
||||
private:
|
||||
BLESerial(BLESerial const &other) = delete;
|
||||
void operator=(BLESerial const &other) = delete;
|
||||
|
||||
BLEFIFO<RX_BUFFER_SIZE> rx_buffer;
|
||||
size_t numAvailableLines;
|
||||
uint8_t transmitBuffer[BLE_BUFFER_SIZE];
|
||||
|
||||
int ConnectedDeviceCount;
|
||||
void SetupSerialService();
|
||||
|
||||
uint16_t peerMTU;
|
||||
uint16_t maxTransferSize = BLE_BUFFER_SIZE;
|
||||
|
||||
bool checkMTU();
|
||||
|
||||
const char *BLE_SERIAL_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
|
||||
const char *BLE_RX_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
|
||||
const char *BLE_TX_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
|
||||
|
||||
bool started = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
615
RNode_Firmware/Bluetooth.h
Normal file
615
RNode_Firmware/Bluetooth.h
Normal file
|
|
@ -0,0 +1,615 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
#endif
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if HAS_BLUETOOTH == true
|
||||
#include "BluetoothSerial.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
BluetoothSerial SerialBT;
|
||||
#elif HAS_BLE == true
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "BLESerial.h"
|
||||
BLESerial SerialBT;
|
||||
#endif
|
||||
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
#include <bluefruit.h>
|
||||
#include <math.h>
|
||||
#define BLE_RX_BUF 6144
|
||||
BLEUart SerialBT(BLE_RX_BUF);
|
||||
BLEDis bledis;
|
||||
BLEBas blebas;
|
||||
bool SerialBT_init = false;
|
||||
#endif
|
||||
|
||||
#define BT_PAIRING_TIMEOUT 35000
|
||||
#define BLE_FLUSH_TIMEOUT 20
|
||||
uint32_t bt_pairing_started = 0;
|
||||
|
||||
#define BT_DEV_ADDR_LEN 6
|
||||
#define BT_DEV_HASH_LEN 16
|
||||
uint8_t dev_bt_mac[BT_DEV_ADDR_LEN];
|
||||
char bt_da[BT_DEV_ADDR_LEN];
|
||||
char bt_dh[BT_DEV_HASH_LEN];
|
||||
char bt_devname[11];
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if HAS_BLUETOOTH == true
|
||||
|
||||
void bt_confirm_pairing(uint32_t numVal) {
|
||||
bt_ssp_pin = numVal;
|
||||
kiss_indicate_btpin();
|
||||
if (bt_allow_pairing) {
|
||||
SerialBT.confirmReply(true);
|
||||
} else {
|
||||
SerialBT.confirmReply(false);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_stop() {
|
||||
display_unblank();
|
||||
if (bt_state != BT_STATE_OFF) {
|
||||
SerialBT.end();
|
||||
bt_allow_pairing = false;
|
||||
bt_state = BT_STATE_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_start() {
|
||||
display_unblank();
|
||||
if (bt_state == BT_STATE_OFF) {
|
||||
SerialBT.begin(bt_devname);
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_enable_pairing() {
|
||||
display_unblank();
|
||||
if (bt_state == BT_STATE_OFF) bt_start();
|
||||
bt_allow_pairing = true;
|
||||
bt_pairing_started = millis();
|
||||
bt_state = BT_STATE_PAIRING;
|
||||
}
|
||||
|
||||
void bt_disable_pairing() {
|
||||
display_unblank();
|
||||
bt_allow_pairing = false;
|
||||
bt_ssp_pin = 0;
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
|
||||
void bt_pairing_complete(boolean success) {
|
||||
display_unblank();
|
||||
if (success) {
|
||||
bt_disable_pairing();
|
||||
} else {
|
||||
bt_ssp_pin = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_connection_callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
|
||||
display_unblank();
|
||||
if(event == ESP_SPP_SRV_OPEN_EVT) {
|
||||
bt_state = BT_STATE_CONNECTED;
|
||||
cable_state = CABLE_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
if(event == ESP_SPP_CLOSE_EVT ){
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
}
|
||||
|
||||
bool bt_setup_hw() {
|
||||
if (!bt_ready) {
|
||||
if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
|
||||
bt_enabled = true;
|
||||
} else {
|
||||
bt_enabled = false;
|
||||
}
|
||||
if (btStart()) {
|
||||
if (esp_bluedroid_init() == ESP_OK) {
|
||||
if (esp_bluedroid_enable() == ESP_OK) {
|
||||
const uint8_t* bda_ptr = esp_bt_dev_get_address();
|
||||
char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
|
||||
for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
|
||||
data[i] = bda_ptr[i];
|
||||
}
|
||||
data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
|
||||
unsigned char *hash = MD5::make_hash(data, BT_DEV_ADDR_LEN);
|
||||
memcpy(bt_dh, hash, BT_DEV_HASH_LEN);
|
||||
sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
|
||||
free(data);
|
||||
|
||||
SerialBT.enableSSP();
|
||||
SerialBT.onConfirmRequest(bt_confirm_pairing);
|
||||
SerialBT.onAuthComplete(bt_pairing_complete);
|
||||
SerialBT.register_callback(bt_connection_callback);
|
||||
|
||||
bt_ready = true;
|
||||
return true;
|
||||
|
||||
} else { return false; }
|
||||
} else { return false; }
|
||||
} else { return false; }
|
||||
} else { return false; }
|
||||
}
|
||||
|
||||
bool bt_init() {
|
||||
bt_state = BT_STATE_OFF;
|
||||
if (bt_setup_hw()) {
|
||||
if (bt_enabled && !console_active) bt_start();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void update_bt() {
|
||||
if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
|
||||
bt_disable_pairing();
|
||||
}
|
||||
}
|
||||
|
||||
#elif HAS_BLE == true
|
||||
bool bt_setup_hw(); void bt_security_setup();
|
||||
BLESecurity *ble_security = new BLESecurity();
|
||||
bool ble_authenticated = false;
|
||||
uint32_t pairing_pin = 0;
|
||||
|
||||
void bt_flush() { if (bt_state == BT_STATE_CONNECTED) { SerialBT.flush(); } }
|
||||
|
||||
void bt_start() {
|
||||
// Serial.println("BT start");
|
||||
display_unblank();
|
||||
if (bt_state == BT_STATE_OFF) {
|
||||
bt_state = BT_STATE_ON;
|
||||
SerialBT.begin(bt_devname);
|
||||
SerialBT.setTimeout(10);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_stop() {
|
||||
// Serial.println("BT stop");
|
||||
display_unblank();
|
||||
if (bt_state != BT_STATE_OFF) {
|
||||
bt_allow_pairing = false;
|
||||
bt_state = BT_STATE_OFF;
|
||||
SerialBT.end();
|
||||
}
|
||||
}
|
||||
|
||||
bool bt_init() {
|
||||
// Serial.println("BT init");
|
||||
bt_state = BT_STATE_OFF;
|
||||
if (bt_setup_hw()) {
|
||||
if (bt_enabled && !console_active) bt_start();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_debond_all() {
|
||||
// Serial.println("Debonding all");
|
||||
int dev_num = esp_ble_get_bond_device_num();
|
||||
esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
|
||||
esp_ble_get_bond_device_list(&dev_num, dev_list);
|
||||
for (int i = 0; i < dev_num; i++) { esp_ble_remove_bond_device(dev_list[i].bd_addr); }
|
||||
free(dev_list);
|
||||
}
|
||||
|
||||
void bt_enable_pairing() {
|
||||
// Serial.println("BT enable pairing");
|
||||
display_unblank();
|
||||
if (bt_state == BT_STATE_OFF) bt_start();
|
||||
|
||||
bt_security_setup();
|
||||
|
||||
bt_allow_pairing = true;
|
||||
bt_pairing_started = millis();
|
||||
bt_state = BT_STATE_PAIRING;
|
||||
bt_ssp_pin = pairing_pin;
|
||||
}
|
||||
|
||||
void bt_disable_pairing() {
|
||||
// Serial.println("BT disable pairing");
|
||||
display_unblank();
|
||||
bt_allow_pairing = false;
|
||||
bt_ssp_pin = 0;
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
|
||||
void bt_passkey_notify_callback(uint32_t passkey) {
|
||||
// Serial.printf("Got passkey notification: %d\n", passkey);
|
||||
if (bt_allow_pairing) {
|
||||
bt_ssp_pin = passkey;
|
||||
bt_pairing_started = millis();
|
||||
kiss_indicate_btpin();
|
||||
} else {
|
||||
// Serial.println("Pairing not allowed, re-init");
|
||||
SerialBT.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
bool bt_confirm_pin_callback(uint32_t pin) {
|
||||
// Serial.printf("Confirm PIN callback: %d\n", pin);
|
||||
return true;
|
||||
}
|
||||
|
||||
void bt_update_passkey() {
|
||||
// Serial.println("Updating passkey");
|
||||
pairing_pin = random(899999)+100000;
|
||||
bt_ssp_pin = pairing_pin;
|
||||
}
|
||||
|
||||
uint32_t bt_passkey_callback() {
|
||||
// Serial.println("API passkey request");
|
||||
if (pairing_pin == 0) { bt_update_passkey(); }
|
||||
return pairing_pin;
|
||||
}
|
||||
|
||||
bool bt_client_authenticated() {
|
||||
return ble_authenticated;
|
||||
}
|
||||
|
||||
bool bt_security_request_callback() {
|
||||
if (bt_allow_pairing) {
|
||||
// Serial.println("Accepting security request");
|
||||
return true;
|
||||
} else {
|
||||
// Serial.println("Rejecting security request");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_authentication_complete_callback(esp_ble_auth_cmpl_t auth_result) {
|
||||
if (auth_result.success == true) {
|
||||
// Serial.println("Authentication success");
|
||||
ble_authenticated = true;
|
||||
if (bt_state == BT_STATE_PAIRING) {
|
||||
// Serial.println("Pairing complete, disconnecting");
|
||||
delay(2000); SerialBT.disconnect();
|
||||
} else { bt_state = BT_STATE_CONNECTED; }
|
||||
} else {
|
||||
// Serial.println("Authentication fail");
|
||||
ble_authenticated = false;
|
||||
bt_state = BT_STATE_ON;
|
||||
bt_update_passkey();
|
||||
bt_security_setup();
|
||||
}
|
||||
bt_allow_pairing = false;
|
||||
bt_ssp_pin = 0;
|
||||
}
|
||||
|
||||
void bt_connect_callback(BLEServer *server) {
|
||||
uint16_t conn_id = server->getConnId();
|
||||
// Serial.printf("Connected: %d\n", conn_id);
|
||||
display_unblank();
|
||||
ble_authenticated = false;
|
||||
if (bt_state != BT_STATE_PAIRING) { bt_state = BT_STATE_CONNECTED; }
|
||||
cable_state = CABLE_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
void bt_disconnect_callback(BLEServer *server) {
|
||||
uint16_t conn_id = server->getConnId();
|
||||
// Serial.printf("Disconnected: %d\n", conn_id);
|
||||
display_unblank();
|
||||
ble_authenticated = false;
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
|
||||
bool bt_setup_hw() {
|
||||
// Serial.println("BT setup hw");
|
||||
if (!bt_ready) {
|
||||
if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
|
||||
bt_enabled = true;
|
||||
} else {
|
||||
bt_enabled = false;
|
||||
}
|
||||
if (btStart()) {
|
||||
if (esp_bluedroid_init() == ESP_OK) {
|
||||
if (esp_bluedroid_enable() == ESP_OK) {
|
||||
const uint8_t* bda_ptr = esp_bt_dev_get_address();
|
||||
char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
|
||||
for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
|
||||
data[i] = bda_ptr[i];
|
||||
}
|
||||
data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
|
||||
unsigned char *hash = MD5::make_hash(data, BT_DEV_ADDR_LEN);
|
||||
memcpy(bt_dh, hash, BT_DEV_HASH_LEN);
|
||||
sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
|
||||
free(data);
|
||||
|
||||
bt_security_setup();
|
||||
|
||||
bt_ready = true;
|
||||
return true;
|
||||
|
||||
} else { return false; }
|
||||
} else { return false; }
|
||||
} else { return false; }
|
||||
} else { return false; }
|
||||
}
|
||||
|
||||
void bt_security_setup() {
|
||||
// Serial.println("Executing BT security setup");
|
||||
if (pairing_pin == 0) { bt_update_passkey(); }
|
||||
uint32_t passkey = pairing_pin;
|
||||
// Serial.printf("Passkey is %d\n", passkey);
|
||||
|
||||
uint8_t key_size = 16;
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
|
||||
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;
|
||||
uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_ENABLE;
|
||||
uint8_t oob_support = ESP_BLE_OOB_DISABLE;
|
||||
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT;
|
||||
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void update_bt() {
|
||||
if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
|
||||
bt_disable_pairing();
|
||||
}
|
||||
if (bt_state == BT_STATE_CONNECTED && millis()-SerialBT.lastFlushTime >= BLE_FLUSH_TIMEOUT) {
|
||||
if (SerialBT.transmitBufferLength > 0) {
|
||||
bt_flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
uint32_t pairing_pin = 0;
|
||||
|
||||
uint8_t eeprom_read(uint32_t mapped_addr);
|
||||
|
||||
void bt_stop() {
|
||||
// Serial.println("BT Stop");
|
||||
if (bt_state != BT_STATE_OFF) {
|
||||
bt_allow_pairing = false;
|
||||
bt_state = BT_STATE_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_flush() { if (bt_state == BT_STATE_CONNECTED) { SerialBT.flushTXD(); } }
|
||||
|
||||
void bt_disable_pairing() {
|
||||
// Serial.println("BT Disable pairing");
|
||||
bt_allow_pairing = false;
|
||||
pairing_pin = 0;
|
||||
bt_ssp_pin = 0;
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
|
||||
void bt_pairing_complete(uint16_t conn_handle, uint8_t auth_status) {
|
||||
// Serial.println("BT pairing complete");
|
||||
BLEConnection* connection = Bluefruit.Connection(conn_handle);
|
||||
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
|
||||
ble_gap_conn_sec_mode_t security = connection->getSecureMode();
|
||||
// Serial.println("Bonding success");
|
||||
|
||||
// On the NRF52 it is not possible with the Arduino library to reject
|
||||
// requests from devices with no IO capabilities, which would allow
|
||||
// bypassing pin entry through pairing using the "just works" mode.
|
||||
// Therefore, we must check the security level of the connection after
|
||||
// pairing to ensure "just works" has not been used. If it has, we need
|
||||
// to disconnect, unpair and delete any bonding information immediately.
|
||||
// Settings on the SerialBT service should prevent unauthorised access to
|
||||
// the serial port anyway, but this is still wise to do regardless.
|
||||
//
|
||||
// Note: It may be nice to have this done in the BLESecurity class in the
|
||||
// future, but as it stands right now I'd have to fork the BSP to do
|
||||
// that, which I don't fancy doing. Impact on security is likely minimal.
|
||||
// Requires investigation.
|
||||
|
||||
if (security.sm == 1 && security.lv >= 3) {
|
||||
// Serial.println("Auth level success");
|
||||
bt_state = BT_STATE_CONNECTED;
|
||||
cable_state = CABLE_STATE_DISCONNECTED;
|
||||
connection->disconnect();
|
||||
bt_disable_pairing();
|
||||
} else {
|
||||
// Serial.println("Auth level failure, debonding");
|
||||
if (connection->bonded()) { connection->removeBondKey(); }
|
||||
connection->disconnect();
|
||||
bt_disable_pairing();
|
||||
}
|
||||
} else {
|
||||
// Serial.println("Bonding failure");
|
||||
connection->disconnect();
|
||||
bt_disable_pairing();
|
||||
}
|
||||
}
|
||||
|
||||
bool bt_passkey_callback(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) {
|
||||
// Serial.println("Passkey callback");
|
||||
if (bt_allow_pairing) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void bt_connect_callback(uint16_t conn_handle) {
|
||||
// Serial.println("Connect callback");
|
||||
bt_state = BT_STATE_CONNECTED;
|
||||
cable_state = CABLE_STATE_DISCONNECTED;
|
||||
|
||||
BLEConnection* conn = Bluefruit.Connection(conn_handle);
|
||||
conn->requestPHY(BLE_GAP_PHY_2MBPS);
|
||||
conn->requestMtuExchange(512+3);
|
||||
conn->requestDataLengthUpdate();
|
||||
}
|
||||
|
||||
void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason) {
|
||||
// Serial.println("Disconnect callback");
|
||||
if (reason != BLE_GAP_SEC_STATUS_SUCCESS) {
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_update_passkey() {
|
||||
// Serial.println("Update passkey");
|
||||
pairing_pin = random(899999)+100000;
|
||||
bt_ssp_pin = pairing_pin;
|
||||
}
|
||||
|
||||
uint32_t bt_get_passkey() {
|
||||
// Serial.println("API passkey request");
|
||||
if (pairing_pin == 0) { bt_update_passkey(); }
|
||||
return pairing_pin;
|
||||
}
|
||||
|
||||
bool bt_setup_hw() {
|
||||
// Serial.println("Setup HW");
|
||||
if (!bt_ready) {
|
||||
#if HAS_EEPROM
|
||||
if (EEPROM.read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
|
||||
#else
|
||||
if (eeprom_read(eeprom_addr(ADDR_CONF_BT)) == BT_ENABLE_BYTE) {
|
||||
#endif
|
||||
bt_enabled = true;
|
||||
} else {
|
||||
bt_enabled = false;
|
||||
}
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.autoConnLed(false);
|
||||
if (Bluefruit.begin()) {
|
||||
uint32_t pin = bt_get_passkey();
|
||||
char pin_char[6];
|
||||
sprintf(pin_char,"%lu", pin);
|
||||
|
||||
Bluefruit.setTxPower(8); // Check bluefruit.h for supported values
|
||||
Bluefruit.Security.setIOCaps(true, false, false); // display, yes; yes / no, no; keyboard, no
|
||||
// This device is indeed capable of yes / no through the pairing mode
|
||||
// being set, but I have chosen to set it thus to force the input of the
|
||||
// pin on the device initiating the pairing.
|
||||
|
||||
Bluefruit.Security.setMITM(true);
|
||||
Bluefruit.Security.setPairPasskeyCallback(bt_passkey_callback);
|
||||
Bluefruit.Security.setSecuredCallback(bt_connect_callback);
|
||||
Bluefruit.Security.setPIN(pin_char);
|
||||
Bluefruit.Periph.setDisconnectCallback(bt_disconnect_callback);
|
||||
Bluefruit.Security.setPairCompleteCallback(bt_pairing_complete);
|
||||
Bluefruit.Periph.setConnInterval(6, 12); // 7.5 - 15 ms
|
||||
|
||||
const ble_gap_addr_t gap_addr = Bluefruit.getAddr();
|
||||
char *data = (char*)malloc(BT_DEV_ADDR_LEN+1);
|
||||
for (int i = 0; i < BT_DEV_ADDR_LEN; i++) {
|
||||
data[i] = gap_addr.addr[i];
|
||||
}
|
||||
#if HAS_EEPROM
|
||||
data[BT_DEV_ADDR_LEN] = EEPROM.read(eeprom_addr(ADDR_SIGNATURE));
|
||||
#else
|
||||
data[BT_DEV_ADDR_LEN] = eeprom_read(eeprom_addr(ADDR_SIGNATURE));
|
||||
#endif
|
||||
unsigned char *hash = MD5::make_hash(data, BT_DEV_ADDR_LEN);
|
||||
memcpy(bt_dh, hash, BT_DEV_HASH_LEN);
|
||||
sprintf(bt_devname, "RNode %02X%02X", bt_dh[14], bt_dh[15]);
|
||||
free(data);
|
||||
|
||||
bt_ready = true;
|
||||
return true;
|
||||
|
||||
} else { return false; }
|
||||
} else { return false; }
|
||||
}
|
||||
|
||||
void bt_start() {
|
||||
// Serial.println("BT Start");
|
||||
if (bt_state == BT_STATE_OFF) {
|
||||
Bluefruit.setName(bt_devname);
|
||||
bledis.setManufacturer(BLE_MANUFACTURER);
|
||||
bledis.setModel(BLE_MODEL);
|
||||
// start device information service
|
||||
bledis.begin();
|
||||
blebas.begin();
|
||||
|
||||
// Guard to ensure SerialBT service is not duplicated through BT being power cycled
|
||||
if (!SerialBT_init) {
|
||||
SerialBT.bufferTXD(true); // enable buffering
|
||||
|
||||
SerialBT.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // enable encryption for BLE serial
|
||||
SerialBT.begin();
|
||||
SerialBT_init = true;
|
||||
}
|
||||
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
Bluefruit.Advertising.addTxPower();
|
||||
|
||||
// Include bleuart 128-bit uuid
|
||||
Bluefruit.Advertising.addService(SerialBT);
|
||||
|
||||
// There is no room for Name in Advertising packet
|
||||
// Use Scan response for Name
|
||||
Bluefruit.ScanResponse.addName();
|
||||
|
||||
Bluefruit.Advertising.start(0);
|
||||
|
||||
bt_state = BT_STATE_ON;
|
||||
}
|
||||
}
|
||||
|
||||
bool bt_init() {
|
||||
// Serial.println("BT init");
|
||||
bt_state = BT_STATE_OFF;
|
||||
if (bt_setup_hw()) {
|
||||
if (bt_enabled && !console_active) bt_start();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_enable_pairing() {
|
||||
// Serial.println("BT enable pairing");
|
||||
if (bt_state == BT_STATE_OFF) bt_start();
|
||||
|
||||
uint32_t pin = bt_get_passkey();
|
||||
char pin_char[6];
|
||||
sprintf(pin_char,"%lu", pin);
|
||||
Bluefruit.Security.setPIN(pin_char);
|
||||
|
||||
bt_allow_pairing = true;
|
||||
bt_pairing_started = millis();
|
||||
bt_state = BT_STATE_PAIRING;
|
||||
kiss_indicate_btpin();
|
||||
}
|
||||
|
||||
void bt_debond_all() { }
|
||||
|
||||
void update_bt() {
|
||||
if (bt_allow_pairing && millis()-bt_pairing_started >= BT_PAIRING_TIMEOUT) {
|
||||
bt_disable_pairing();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
983
RNode_Firmware/Boards.h
Normal file
983
RNode_Firmware/Boards.h
Normal file
|
|
@ -0,0 +1,983 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Modem.h"
|
||||
|
||||
#ifndef BOARDS_H
|
||||
#define BOARDS_H
|
||||
|
||||
#define PLATFORM_AVR 0x90
|
||||
#define PLATFORM_ESP32 0x80
|
||||
#define PLATFORM_NRF52 0x70
|
||||
|
||||
#define MCU_1284P 0x91
|
||||
#define MCU_2560 0x92
|
||||
#define MCU_ESP32 0x81
|
||||
#define MCU_NRF52 0x71
|
||||
|
||||
// Products, boards and models ////
|
||||
#define PRODUCT_RNODE 0x03 // RNode devices
|
||||
#define BOARD_RNODE 0x31 // Original v1.0 RNode
|
||||
#define MODEL_A4 0xA4 // RNode v1.0, 433 MHz
|
||||
#define MODEL_A9 0xA9 // RNode v1.0, 868 MHz
|
||||
|
||||
#define BOARD_RNODE_NG_20 0x40 // RNode hardware revision v2.0
|
||||
#define MODEL_A3 0xA3 // RNode v2.0, 433 MHz
|
||||
#define MODEL_A8 0xA8 // RNode v2.0, 868 MHz
|
||||
|
||||
#define BOARD_RNODE_NG_21 0x41 // RNode hardware revision v2.1
|
||||
#define MODEL_A2 0xA2 // RNode v2.1, 433 MHz
|
||||
#define MODEL_A7 0xA7 // RNode v2.1, 868 MHz
|
||||
|
||||
#define BOARD_T3S3 0x42 // T3S3 devices
|
||||
#define MODEL_A1 0xA1 // T3S3, 433 MHz with SX1268
|
||||
#define MODEL_A5 0xA5 // T3S3, 433 MHz with SX1278
|
||||
#define MODEL_A6 0xA6 // T3S3, 868 MHz with SX1262
|
||||
#define MODEL_AA 0xAA // T3S3, 868 MHz with SX1276
|
||||
#define MODEL_AC 0xAC // T3S3, 2.4 GHz with SX1280 and PA
|
||||
|
||||
#define PRODUCT_TBEAM 0xE0 // T-Beam devices
|
||||
#define BOARD_TBEAM 0x33
|
||||
#define MODEL_E4 0xE4 // T-Beam SX1278, 433 Mhz
|
||||
#define MODEL_E9 0xE9 // T-Beam SX1276, 868 Mhz
|
||||
#define MODEL_E3 0xE3 // T-Beam SX1268, 433 Mhz
|
||||
#define MODEL_E8 0xE8 // T-Beam SX1262, 868 Mhz
|
||||
|
||||
#define PRODUCT_TDECK_V1 0xD0
|
||||
#define BOARD_TDECK 0x3B
|
||||
#define MODEL_D4 0xD4 // LilyGO T-Deck, 433 MHz
|
||||
#define MODEL_D9 0xD9 // LilyGO T-Deck, 868 MHz
|
||||
|
||||
#define PRODUCT_TBEAM_S_V1 0xEA
|
||||
#define BOARD_TBEAM_S_V1 0x3D
|
||||
#define MODEL_DB 0xDB // LilyGO T-Beam Supreme, 433 MHz
|
||||
#define MODEL_DC 0xDC // LilyGO T-Beam Supreme, 868 MHz
|
||||
|
||||
#define PRODUCT_XIAO_S3 0xEB
|
||||
#define BOARD_XIAO_S3 0x3E
|
||||
#define MODEL_DE 0xDE // Xiao ESP32S3 with Wio-SX1262 module, 433 MHz
|
||||
#define MODEL_DD 0xDD // Xiao ESP32S3 with Wio-SX1262 module, 868 MHz
|
||||
|
||||
#define PRODUCT_T32_10 0xB2
|
||||
#define BOARD_LORA32_V1_0 0x39
|
||||
#define MODEL_BA 0xBA // LilyGO T3 v1.0, 433 MHz
|
||||
#define MODEL_BB 0xBB // LilyGO T3 v1.0, 868 MHz
|
||||
|
||||
#define PRODUCT_T32_20 0xB0
|
||||
#define BOARD_LORA32_V2_0 0x36
|
||||
#define MODEL_B3 0xB3 // LilyGO T3 v2.0, 433 MHz
|
||||
#define MODEL_B8 0xB8 // LilyGO T3 v2.0, 868 MHz
|
||||
|
||||
#define PRODUCT_T32_21 0xB1
|
||||
#define BOARD_LORA32_V2_1 0x37
|
||||
#define MODEL_B4 0xB4 // LilyGO T3 v2.1, 433 MHz
|
||||
#define MODEL_B9 0xB9 // LilyGO T3 v2.1, 868 MHz
|
||||
|
||||
#define PRODUCT_H32_V2 0xC0 // Board code 0x38
|
||||
#define BOARD_HELTEC32_V2 0x38
|
||||
#define MODEL_C4 0xC4 // Heltec Lora32 v2, 433 MHz
|
||||
#define MODEL_C9 0xC9 // Heltec Lora32 v2, 868 MHz
|
||||
|
||||
#define PRODUCT_H32_V3 0xC1
|
||||
#define BOARD_HELTEC32_V3 0x3A
|
||||
#define MODEL_C5 0xC5 // Heltec Lora32 v3, 433 MHz
|
||||
#define MODEL_CA 0xCA // Heltec Lora32 v3, 868 MHz
|
||||
|
||||
#define PRODUCT_H32_V4 0xC3
|
||||
#define BOARD_HELTEC32_V4 0x3F
|
||||
#define MODEL_C8 0xC8 // Heltec Lora32 v3, 850-950 MHz, 28dBm
|
||||
|
||||
#define PRODUCT_HELTEC_T114 0xC2 // Heltec Mesh Node T114
|
||||
#define BOARD_HELTEC_T114 0x3C
|
||||
#define MODEL_C6 0xC6 // Heltec Mesh Node T114, 470-510 MHz
|
||||
#define MODEL_C7 0xC7 // Heltec Mesh Node T114, 863-928 MHz
|
||||
|
||||
#define PRODUCT_VME213 0xC9 // Heltec VisionMaster E213
|
||||
#define BOARD_VME213 0x40
|
||||
#define MODEL_CD 0xCD // VisionMaster E213, 470-510 MHz
|
||||
#define MODEL_CE 0xCE // VisionMaster E213, 863-928 MHz
|
||||
|
||||
#define PRODUCT_TECHO 0x15 // LilyGO T-Echo devices
|
||||
#define BOARD_TECHO 0x44
|
||||
#define MODEL_16 0x16 // T-Echo 433 MHz
|
||||
#define MODEL_17 0x17 // T-Echo 868/915 MHz
|
||||
|
||||
#define PRODUCT_RAK4631 0x10
|
||||
#define BOARD_RAK4631 0x51
|
||||
#define MODEL_11 0x11 // RAK4631, 433 Mhz
|
||||
#define MODEL_12 0x12 // RAK4631, 868 Mhz
|
||||
|
||||
#define PRODUCT_HMBRW 0xF0
|
||||
#define BOARD_HMBRW 0x32
|
||||
#define BOARD_HUZZAH32 0x34
|
||||
#define BOARD_GENERIC_ESP32 0x35
|
||||
#define BOARD_GENERIC_NRF52 0x50
|
||||
#define MODEL_FE 0xFE // Homebrew board, max 17dBm output power
|
||||
#define MODEL_FF 0xFF // Homebrew board, max 14dBm output power
|
||||
|
||||
#if defined(__AVR_ATmega1284P__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_1284P
|
||||
#elif defined(__AVR_ATmega2560__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_2560
|
||||
#elif defined(ESP32)
|
||||
#define PLATFORM PLATFORM_ESP32
|
||||
#define MCU_VARIANT MCU_ESP32
|
||||
#elif defined(NRF52840_XXAA)
|
||||
#include <variant.h>
|
||||
#define PLATFORM PLATFORM_NRF52
|
||||
#define MCU_VARIANT MCU_NRF52
|
||||
#else
|
||||
#error "The firmware cannot be compiled for the selected MCU variant"
|
||||
#endif
|
||||
|
||||
#ifndef MODEM
|
||||
#if BOARD_MODEL == BOARD_RAK4631
|
||||
#define MODEM SX1262
|
||||
#elif BOARD_MODEL == BOARD_GENERIC_NRF52
|
||||
#define MODEM SX1262
|
||||
#else
|
||||
#define MODEM SX1276
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define HAS_DISPLAY false
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE false
|
||||
#define HAS_TCXO false
|
||||
#define HAS_PMU false
|
||||
#define HAS_NP false
|
||||
#define HAS_EEPROM false
|
||||
#define HAS_INPUT false
|
||||
#define HAS_SLEEP false
|
||||
#define HAS_LORA_PA false
|
||||
#define HAS_LORA_LNA false
|
||||
#define PIN_DISP_SLEEP -1
|
||||
#define VALIDATE_FIRMWARE true
|
||||
|
||||
#if defined(ENABLE_TCXO)
|
||||
#define HAS_TCXO true
|
||||
#endif
|
||||
|
||||
#if MCU_VARIANT == MCU_1284P
|
||||
const int pin_cs = 4;
|
||||
const int pin_reset = 3;
|
||||
const int pin_dio = 2;
|
||||
const int pin_led_rx = 12;
|
||||
const int pin_led_tx = 13;
|
||||
|
||||
#define BOARD_MODEL BOARD_RNODE
|
||||
#define HAS_EEPROM true
|
||||
#define CONFIG_UART_BUFFER_SIZE 6144
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 200
|
||||
#define EEPROM_SIZE 4096
|
||||
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
|
||||
|
||||
#elif MCU_VARIANT == MCU_2560
|
||||
const int pin_cs = 5;
|
||||
const int pin_reset = 4;
|
||||
const int pin_dio = 2;
|
||||
const int pin_led_rx = 12;
|
||||
const int pin_led_tx = 13;
|
||||
|
||||
#define BOARD_MODEL BOARD_HMBRW
|
||||
#define HAS_EEPROM true
|
||||
#define CONFIG_UART_BUFFER_SIZE 768
|
||||
#define CONFIG_QUEUE_SIZE 5120
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 24
|
||||
#define EEPROM_SIZE 4096
|
||||
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
|
||||
|
||||
#elif MCU_VARIANT == MCU_ESP32
|
||||
|
||||
// Board models for ESP32 based builds are
|
||||
// defined by the build target in the makefile.
|
||||
// If you are not using make to compile this
|
||||
// firmware, you can manually define model here.
|
||||
//
|
||||
// #define BOARD_MODEL BOARD_GENERIC_ESP32
|
||||
#define CONFIG_UART_BUFFER_SIZE 6144
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 200
|
||||
|
||||
#define EEPROM_SIZE 1024
|
||||
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
|
||||
|
||||
#define GPS_BAUD_RATE 9600
|
||||
#define PIN_GPS_TX 12
|
||||
#define PIN_GPS_RX 34
|
||||
|
||||
#if BOARD_MODEL == BOARD_GENERIC_ESP32
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
const int pin_cs = 4;
|
||||
const int pin_reset = 33;
|
||||
const int pin_dio = 39;
|
||||
const int pin_led_rx = 14;
|
||||
const int pin_led_tx = 32;
|
||||
|
||||
#elif BOARD_MODEL == BOARD_TBEAM
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_PMU true
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_SD false
|
||||
#define HAS_EEPROM true
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
#define PMU_IRQ 35
|
||||
|
||||
#define HAS_INPUT true
|
||||
const int pin_btn_usr1 = 38;
|
||||
|
||||
const int pin_cs = 18;
|
||||
const int pin_reset = 23;
|
||||
const int pin_led_rx = 2;
|
||||
const int pin_led_tx = 4;
|
||||
|
||||
#if MODEM == SX1262
|
||||
#define HAS_TCXO true
|
||||
#define HAS_BUSY true
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
#define OCP_TUNED 0x18
|
||||
const int pin_busy = 32;
|
||||
const int pin_dio = 33;
|
||||
const int pin_tcxo_enable = -1;
|
||||
#else
|
||||
const int pin_dio = 26;
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_HUZZAH32
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
const int pin_cs = 4;
|
||||
const int pin_reset = 33;
|
||||
const int pin_dio = 39;
|
||||
const int pin_led_rx = 14;
|
||||
const int pin_led_tx = 32;
|
||||
|
||||
#elif BOARD_MODEL == BOARD_LORA32_V1_0
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
const int pin_cs = 18;
|
||||
const int pin_reset = 14;
|
||||
const int pin_dio = 26;
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 25;
|
||||
const int pin_led_tx = 2;
|
||||
#else
|
||||
const int pin_led_rx = 2;
|
||||
const int pin_led_tx = 2;
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_LORA32_V2_0
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
const int pin_cs = 18;
|
||||
const int pin_reset = 12;
|
||||
const int pin_dio = 26;
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 2;
|
||||
const int pin_led_tx = 0;
|
||||
#else
|
||||
const int pin_led_rx = 22;
|
||||
const int pin_led_tx = 22;
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_LORA32_V2_1
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_PMU true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
const int pin_cs = 18;
|
||||
const int pin_reset = 23;
|
||||
const int pin_dio = 26;
|
||||
#if HAS_TCXO == true
|
||||
const int pin_tcxo_enable = 33;
|
||||
#endif
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 15;
|
||||
const int pin_led_tx = 4;
|
||||
#else
|
||||
const int pin_led_rx = 25;
|
||||
const int pin_led_tx = 25;
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V2
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define PIN_WAKEUP GPIO_NUM_0
|
||||
#define WAKEUP_LEVEL 0
|
||||
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
const int pin_cs = 18;
|
||||
const int pin_reset = 14;
|
||||
const int pin_dio = 26;
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 36;
|
||||
const int pin_led_tx = 37;
|
||||
#else
|
||||
const int pin_led_rx = 25;
|
||||
const int pin_led_tx = 25;
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V3
|
||||
#define IS_ESP32S3 true
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_PMU true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define PIN_WAKEUP GPIO_NUM_0
|
||||
#define WAKEUP_LEVEL 0
|
||||
#define OCP_TUNED 0x18
|
||||
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 13;
|
||||
const int pin_led_tx = 14;
|
||||
#else
|
||||
const int pin_led_rx = 35;
|
||||
const int pin_led_tx = 35;
|
||||
#endif
|
||||
|
||||
#define MODEM SX1262
|
||||
#define HAS_TCXO true
|
||||
const int pin_tcxo_enable = -1;
|
||||
#define HAS_BUSY true
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
|
||||
// Following pins are for the SX1262
|
||||
const int pin_cs = 8;
|
||||
const int pin_busy = 13;
|
||||
const int pin_dio = 14;
|
||||
const int pin_reset = 12;
|
||||
const int pin_mosi = 10;
|
||||
const int pin_miso = 11;
|
||||
const int pin_sclk = 9;
|
||||
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V4
|
||||
#define IS_ESP32S3 true
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_PMU true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define HAS_LORA_PA true
|
||||
#define HAS_LORA_LNA true
|
||||
#define PIN_WAKEUP GPIO_NUM_0
|
||||
#define WAKEUP_LEVEL 0
|
||||
#define OCP_TUNED 0x18
|
||||
#define Vext GPIO_NUM_36
|
||||
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 13;
|
||||
const int pin_led_tx = 14;
|
||||
#else
|
||||
const int pin_led_rx = 35;
|
||||
const int pin_led_tx = 35;
|
||||
#endif
|
||||
|
||||
#define MODEM SX1262
|
||||
#define HAS_TCXO true
|
||||
const int pin_tcxo_enable = -1;
|
||||
#define HAS_BUSY true
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
|
||||
#define LORA_LNA_GAIN 17
|
||||
#define LORA_PA_GC1109 true
|
||||
#define LORA_PA_PWR_EN 7
|
||||
#define LORA_PA_CSD 2
|
||||
#define LORA_PA_CPS 46
|
||||
|
||||
#define PA_MAX_OUTPUT 28
|
||||
#define PA_GAIN_POINTS 22
|
||||
#define PA_GAIN_VALUES 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7
|
||||
|
||||
const int pin_cs = 8;
|
||||
const int pin_busy = 13;
|
||||
const int pin_dio = 14;
|
||||
const int pin_reset = 12;
|
||||
const int pin_mosi = 10;
|
||||
const int pin_miso = 11;
|
||||
const int pin_sclk = 9;
|
||||
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// Heltec VisionMaster E213 with ESP32-S3R8 + SX1262 + 2.13" E-Ink
|
||||
#define IS_ESP32S3 true
|
||||
#define HAS_DISPLAY true // 2.13" E-Ink LCMEN2R13EFC1, 250x122 pixels
|
||||
#define HAS_EINK true // Enable E-Ink specific display code
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_PMU false
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define PIN_WAKEUP GPIO_NUM_0
|
||||
#define WAKEUP_LEVEL 0
|
||||
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
// No physical LEDs on VME213, display only
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 35;
|
||||
const int pin_led_tx = 35;
|
||||
#else
|
||||
const int pin_led_rx = -1;
|
||||
const int pin_led_tx = -1;
|
||||
#endif
|
||||
|
||||
// SX1262 LoRa radio configuration
|
||||
#define MODEM SX1262
|
||||
#define HAS_TCXO true
|
||||
const int pin_tcxo_enable = -1;
|
||||
#define HAS_BUSY true
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
|
||||
// SX1262 pins (from Heltec board-config.h)
|
||||
const int pin_cs = 8;
|
||||
const int pin_busy = 13;
|
||||
const int pin_dio = 14;
|
||||
const int pin_reset = 12;
|
||||
const int pin_mosi = 10;
|
||||
const int pin_miso = 11;
|
||||
const int pin_sclk = 9;
|
||||
|
||||
// E-Ink display pins (HT_ICMEN2R13EFC1 controller)
|
||||
const int pin_disp_reset = 3;
|
||||
const int pin_disp_dc = 2;
|
||||
const int pin_disp_cs = 5;
|
||||
const int pin_disp_busy = 1;
|
||||
const int pin_disp_sck = 4;
|
||||
const int pin_disp_mosi = 6;
|
||||
const int pin_disp_miso = -1;
|
||||
|
||||
// Vext power control (HIGH=ON, LOW=OFF)
|
||||
#define Vext GPIO_NUM_18
|
||||
|
||||
// I2C for sensors (QuickLink)
|
||||
#define I2C_SDA 39
|
||||
#define I2C_SCL 38
|
||||
|
||||
#elif BOARD_MODEL == BOARD_RNODE_NG_20
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_NP true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_EEPROM true
|
||||
const int pin_cs = 18;
|
||||
const int pin_reset = 12;
|
||||
const int pin_dio = 26;
|
||||
const int pin_np = 4;
|
||||
#if HAS_NP == false
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 2;
|
||||
const int pin_led_tx = 0;
|
||||
#else
|
||||
const int pin_led_rx = 22;
|
||||
const int pin_led_tx = 22;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_RNODE_NG_21
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_PMU true
|
||||
#define HAS_NP true
|
||||
#define HAS_SD false
|
||||
#define HAS_EEPROM true
|
||||
const int pin_cs = 18;
|
||||
const int pin_reset = 23;
|
||||
const int pin_dio = 26;
|
||||
const int pin_np = 12;
|
||||
const int pin_dac = 25;
|
||||
const int pin_adc = 34;
|
||||
const int SD_MISO = 2;
|
||||
const int SD_MOSI = 15;
|
||||
const int SD_CLK = 14;
|
||||
const int SD_CS = 13;
|
||||
#if HAS_NP == false
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 12;
|
||||
const int pin_led_tx = 4;
|
||||
#else
|
||||
const int pin_led_rx = 25;
|
||||
const int pin_led_tx = 25;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_T3S3
|
||||
#define IS_ESP32S3 true
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_PMU true
|
||||
#define HAS_NP false
|
||||
#define HAS_SD false
|
||||
#define HAS_EEPROM true
|
||||
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define PIN_WAKEUP GPIO_NUM_0
|
||||
#define WAKEUP_LEVEL 0
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
const int pin_cs = 7;
|
||||
const int pin_reset = 8;
|
||||
const int pin_sclk = 5;
|
||||
const int pin_mosi = 6;
|
||||
const int pin_miso = 3;
|
||||
|
||||
#if MODEM == SX1262
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
#define HAS_BUSY true
|
||||
#define HAS_TCXO true
|
||||
const int pin_busy = 34;
|
||||
const int pin_dio = 33;
|
||||
const int pin_tcxo_enable = -1;
|
||||
#elif MODEM == SX1280
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define DIO2_AS_RF_SWITCH false
|
||||
#define HAS_BUSY true
|
||||
#define HAS_TCXO true
|
||||
#define HAS_PA true
|
||||
const int pa_max_input = 3;
|
||||
|
||||
#define HAS_RF_SWITCH_RX_TX true
|
||||
const int pin_rxen = 21;
|
||||
const int pin_txen = 10;
|
||||
|
||||
const int pin_busy = 36;
|
||||
const int pin_dio = 9;
|
||||
const int pin_tcxo_enable = -1;
|
||||
#else
|
||||
const int pin_dio = 9;
|
||||
#endif
|
||||
|
||||
const int pin_np = 38;
|
||||
const int pin_dac = 25;
|
||||
const int pin_adc = 1;
|
||||
|
||||
const int SD_MISO = 2;
|
||||
const int SD_MOSI = 11;
|
||||
const int SD_CLK = 14;
|
||||
const int SD_CS = 13;
|
||||
|
||||
#if HAS_NP == false
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 37;
|
||||
const int pin_led_tx = 37;
|
||||
#else
|
||||
const int pin_led_rx = 37;
|
||||
const int pin_led_tx = 37;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_TDECK
|
||||
#define IS_ESP32S3 true
|
||||
#define MODEM SX1262
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
#define HAS_BUSY true
|
||||
#define HAS_TCXO true
|
||||
|
||||
#define HAS_DISPLAY false
|
||||
#define HAS_CONSOLE false
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_PMU true
|
||||
#define HAS_NP false
|
||||
#define HAS_SD false
|
||||
#define HAS_EEPROM true
|
||||
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define PIN_WAKEUP GPIO_NUM_0
|
||||
#define WAKEUP_LEVEL 0
|
||||
|
||||
const int pin_poweron = 10;
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
const int pin_cs = 9;
|
||||
const int pin_reset = 17;
|
||||
const int pin_sclk = 40;
|
||||
const int pin_mosi = 41;
|
||||
const int pin_miso = 38;
|
||||
const int pin_tcxo_enable = -1;
|
||||
const int pin_dio = 45;
|
||||
const int pin_busy = 13;
|
||||
|
||||
const int SD_MISO = 38;
|
||||
const int SD_MOSI = 41;
|
||||
const int SD_CLK = 40;
|
||||
const int SD_CS = 39;
|
||||
|
||||
const int DISPLAY_DC = 11;
|
||||
const int DISPLAY_CS = 12;
|
||||
const int DISPLAY_MISO = 38;
|
||||
const int DISPLAY_MOSI = 41;
|
||||
const int DISPLAY_CLK = 40;
|
||||
const int DISPLAY_BL_PIN = 42;
|
||||
|
||||
#if HAS_NP == false
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 43;
|
||||
const int pin_led_tx = 43;
|
||||
#else
|
||||
const int pin_led_rx = 43;
|
||||
const int pin_led_tx = 43;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
#define IS_ESP32S3 true
|
||||
#define MODEM SX1262
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
#define HAS_BUSY true
|
||||
#define HAS_TCXO true
|
||||
#define OCP_TUNED 0x18
|
||||
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_PMU true
|
||||
#define HAS_NP false
|
||||
#define HAS_SD false
|
||||
#define HAS_EEPROM true
|
||||
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP false
|
||||
|
||||
#define PMU_IRQ 40
|
||||
#define I2C_SCL 41
|
||||
#define I2C_SDA 42
|
||||
|
||||
const int pin_btn_usr1 = 0;
|
||||
|
||||
const int pin_cs = 10;
|
||||
const int pin_reset = 5;
|
||||
const int pin_sclk = 12;
|
||||
const int pin_mosi = 11;
|
||||
const int pin_miso = 13;
|
||||
const int pin_tcxo_enable = -1;
|
||||
const int pin_dio = 1;
|
||||
const int pin_busy = 4;
|
||||
|
||||
const int SD_MISO = 37;
|
||||
const int SD_MOSI = 35;
|
||||
const int SD_CLK = 36;
|
||||
const int SD_CS = 47;
|
||||
|
||||
const int IMU_CS = 34;
|
||||
|
||||
#if HAS_NP == false
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 43;
|
||||
const int pin_led_tx = 43;
|
||||
#else
|
||||
const int pin_led_rx = 43;
|
||||
const int pin_led_tx = 43;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif BOARD_MODEL == BOARD_XIAO_S3
|
||||
#define IS_ESP32S3 true
|
||||
#define MODEM SX1262
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
#define HAS_BUSY true
|
||||
#define HAS_TCXO true
|
||||
|
||||
#define HAS_DISPLAY false
|
||||
#define HAS_CONSOLE true
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_NP false
|
||||
#define HAS_SD false
|
||||
#define HAS_EEPROM true
|
||||
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define PIN_WAKEUP GPIO_NUM_21
|
||||
#define WAKEUP_LEVEL 0
|
||||
|
||||
const int pin_btn_usr1 = 21;
|
||||
const int pin_cs = 41;
|
||||
const int pin_reset = 42;
|
||||
const int pin_sclk = 7;
|
||||
const int pin_mosi = 9;
|
||||
const int pin_miso = 8;
|
||||
const int pin_tcxo_enable = -1;
|
||||
const int pin_dio = 39;
|
||||
const int pin_busy = 40;
|
||||
|
||||
#if HAS_NP == false
|
||||
#if defined(EXTERNAL_LEDS)
|
||||
const int pin_led_rx = 48;
|
||||
const int pin_led_tx = 48;
|
||||
#else
|
||||
const int pin_led_rx = 48;
|
||||
const int pin_led_tx = 48;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error An unsupported ESP32 board was selected. Cannot compile RNode firmware.
|
||||
#endif
|
||||
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
#if BOARD_MODEL == BOARD_RAK4631
|
||||
#define HAS_EEPROM false
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_CONSOLE false
|
||||
#define HAS_PMU false
|
||||
#define HAS_NP false
|
||||
#define HAS_SD false
|
||||
#define HAS_TCXO true
|
||||
#define HAS_RF_SWITCH_RX_TX true
|
||||
#define HAS_BUSY true
|
||||
#define HAS_INPUT true
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
#define CONFIG_UART_BUFFER_SIZE 6144
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 200
|
||||
#define EEPROM_SIZE 296
|
||||
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
|
||||
#define BLE_MANUFACTURER "RAK Wireless"
|
||||
#define BLE_MODEL "RAK4640"
|
||||
|
||||
const int pin_btn_usr1 = 9;
|
||||
|
||||
// Following pins are for the sx1262
|
||||
const int pin_rxen = 37;
|
||||
const int pin_txen = -1;
|
||||
const int pin_reset = 38;
|
||||
const int pin_cs = 42;
|
||||
const int pin_sclk = 43;
|
||||
const int pin_mosi = 44;
|
||||
const int pin_miso = 45;
|
||||
const int pin_busy = 46;
|
||||
const int pin_dio = 47;
|
||||
const int pin_led_rx = LED_BLUE;
|
||||
const int pin_led_tx = LED_GREEN;
|
||||
const int pin_tcxo_enable = -1;
|
||||
|
||||
#elif BOARD_MODEL == BOARD_TECHO
|
||||
#define _PINNUM(port, pin) ((port) * 32 + (pin))
|
||||
#define MODEM SX1262
|
||||
#define HAS_EEPROM false
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_CONSOLE false
|
||||
#define HAS_PMU true
|
||||
#define HAS_NP false
|
||||
#define HAS_SD false
|
||||
#define HAS_TCXO true
|
||||
#define HAS_BUSY true
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define BLE_MANUFACTURER "LilyGO"
|
||||
#define BLE_MODEL "T-Echo"
|
||||
|
||||
#define HAS_INPUT true
|
||||
#define EEPROM_SIZE 296
|
||||
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
|
||||
|
||||
#define CONFIG_UART_BUFFER_SIZE 32768
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 200
|
||||
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BACKLIGHT true
|
||||
#define DISPLAY_SCALE 1
|
||||
|
||||
#define LED_ON LOW
|
||||
#define LED_OFF HIGH
|
||||
#define PIN_LED_GREEN _PINNUM(1, 1)
|
||||
#define PIN_LED_RED _PINNUM(1, 3)
|
||||
#define PIN_LED_BLUE _PINNUM(0, 14)
|
||||
#define PIN_VEXT_EN _PINNUM(0, 12)
|
||||
|
||||
const int pin_disp_cs = 30;
|
||||
const int pin_disp_dc = 28;
|
||||
const int pin_disp_reset = 2;
|
||||
const int pin_disp_busy = 3;
|
||||
const int pin_disp_en = -1;
|
||||
const int pin_disp_sck = 31;
|
||||
const int pin_disp_mosi = 29;
|
||||
const int pin_disp_miso = -1;
|
||||
const int pin_backlight = 43;
|
||||
|
||||
const int pin_btn_usr1 = _PINNUM(1, 10);
|
||||
const int pin_btn_touch = _PINNUM(0, 11);
|
||||
|
||||
const int pin_reset = 25;
|
||||
const int pin_cs = 24;
|
||||
const int pin_sclk = 19;
|
||||
const int pin_mosi = 22;
|
||||
const int pin_miso = 23;
|
||||
const int pin_busy = 17;
|
||||
const int pin_dio = 20;
|
||||
const int pin_tcxo_enable = 21;
|
||||
const int pin_led_rx = PIN_LED_BLUE;
|
||||
const int pin_led_tx = PIN_LED_RED;
|
||||
|
||||
#elif BOARD_MODEL == BOARD_HELTEC_T114
|
||||
#define MODEM SX1262
|
||||
#define HAS_EEPROM false
|
||||
#define HAS_DISPLAY true
|
||||
#define HAS_BLUETOOTH false
|
||||
#define HAS_BLE true
|
||||
#define HAS_CONSOLE false
|
||||
#define HAS_PMU true
|
||||
#define HAS_NP true
|
||||
#define HAS_SD false
|
||||
#define HAS_TCXO true
|
||||
#define HAS_BUSY true
|
||||
#define HAS_INPUT true
|
||||
#define HAS_SLEEP true
|
||||
#define DIO2_AS_RF_SWITCH true
|
||||
#define CONFIG_UART_BUFFER_SIZE 6144
|
||||
#define CONFIG_QUEUE_SIZE 6144
|
||||
#define CONFIG_QUEUE_MAX_LENGTH 200
|
||||
#define EEPROM_SIZE 296
|
||||
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
|
||||
#define BLE_MANUFACTURER "Heltec"
|
||||
#define BLE_MODEL "T114"
|
||||
|
||||
#define PIN_T114_ADC_EN 6
|
||||
#define PIN_VEXT_EN 21
|
||||
|
||||
// LED
|
||||
#define LED_T114_GREEN 3
|
||||
#define PIN_T114_LED 14
|
||||
#define NP_M 1
|
||||
const int pin_np = PIN_T114_LED;
|
||||
|
||||
// SPI
|
||||
#define PIN_T114_MOSI 22
|
||||
#define PIN_T114_MISO 23
|
||||
#define PIN_T114_SCK 19
|
||||
#define PIN_T114_SS 24
|
||||
|
||||
// SX1262
|
||||
#define PIN_T114_RST 25
|
||||
#define PIN_T114_DIO1 20
|
||||
#define PIN_T114_BUSY 17
|
||||
|
||||
// TFT
|
||||
#define DISPLAY_SCALE 2
|
||||
#define PIN_T114_TFT_MOSI 9
|
||||
#define PIN_T114_TFT_MISO 11 // not connected
|
||||
#define PIN_T114_TFT_SCK 8
|
||||
#define PIN_T114_TFT_SS 11
|
||||
#define PIN_T114_TFT_DC 12
|
||||
#define PIN_T114_TFT_RST 2
|
||||
#define PIN_T114_TFT_EN 3
|
||||
#define PIN_T114_TFT_BLGT 15
|
||||
|
||||
// pins for buttons on Heltec T114
|
||||
const int pin_btn_usr1 = 42;
|
||||
|
||||
// pins for sx1262 on Heltec T114
|
||||
const int pin_reset = PIN_T114_RST;
|
||||
const int pin_cs = PIN_T114_SS;
|
||||
const int pin_sclk = PIN_T114_SCK;
|
||||
const int pin_mosi = PIN_T114_MOSI;
|
||||
const int pin_miso = PIN_T114_MISO;
|
||||
const int pin_busy = PIN_T114_BUSY;
|
||||
const int pin_dio = PIN_T114_DIO1;
|
||||
const int pin_led_rx = 35;
|
||||
const int pin_led_tx = 35;
|
||||
const int pin_tcxo_enable = -1;
|
||||
|
||||
// pins for ST7789 display on Heltec T114
|
||||
const int DISPLAY_DC = PIN_T114_TFT_DC;
|
||||
const int DISPLAY_CS = PIN_T114_TFT_SS;
|
||||
const int DISPLAY_MISO = PIN_T114_TFT_MISO;
|
||||
const int DISPLAY_MOSI = PIN_T114_TFT_MOSI;
|
||||
const int DISPLAY_CLK = PIN_T114_TFT_SCK;
|
||||
const int DISPLAY_BL_PIN = PIN_T114_TFT_BLGT;
|
||||
const int DISPLAY_RST = PIN_T114_TFT_RST;
|
||||
|
||||
#else
|
||||
#error An unsupported nRF board was selected. Cannot compile RNode firmware.
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_SCALE
|
||||
#define DISPLAY_SCALE 1
|
||||
#endif
|
||||
|
||||
#ifndef HAS_RF_SWITCH_RX_TX
|
||||
const int pin_rxen = -1;
|
||||
const int pin_txen = -1;
|
||||
#endif
|
||||
|
||||
#ifndef HAS_BUSY
|
||||
const int pin_busy = -1;
|
||||
#endif
|
||||
|
||||
#ifndef LED_ON
|
||||
#define LED_ON HIGH
|
||||
#endif
|
||||
|
||||
#ifndef LED_OFF
|
||||
#define LED_OFF LOW
|
||||
#endif
|
||||
|
||||
#ifndef DIO2_AS_RF_SWITCH
|
||||
#define DIO2_AS_RF_SWITCH false
|
||||
#endif
|
||||
|
||||
// Default OCP value if not specified
|
||||
// in board configuration
|
||||
#ifndef OCP_TUNED
|
||||
#define OCP_TUNED 0x18
|
||||
#endif
|
||||
|
||||
#ifndef NP_M
|
||||
#define NP_M 0.15
|
||||
#endif
|
||||
|
||||
#endif
|
||||
224
RNode_Firmware/Config.h
Normal file
224
RNode_Firmware/Config.h
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "ROM.h"
|
||||
#include "Boards.h"
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#define MAJ_VERS 0x01
|
||||
#define MIN_VERS 0x54
|
||||
|
||||
#define MODE_HOST 0x11
|
||||
#define MODE_TNC 0x12
|
||||
|
||||
#define CABLE_STATE_DISCONNECTED 0x00
|
||||
#define CABLE_STATE_CONNECTED 0x01
|
||||
uint8_t cable_state = CABLE_STATE_DISCONNECTED;
|
||||
|
||||
#define BT_STATE_NA 0xff
|
||||
#define BT_STATE_OFF 0x00
|
||||
#define BT_STATE_ON 0x01
|
||||
#define BT_STATE_PAIRING 0x02
|
||||
#define BT_STATE_CONNECTED 0x03
|
||||
uint8_t bt_state = BT_STATE_NA;
|
||||
uint32_t bt_ssp_pin = 0;
|
||||
bool bt_ready = false;
|
||||
bool bt_enabled = false;
|
||||
bool bt_allow_pairing = false;
|
||||
|
||||
#define M_FRQ_S 27388122
|
||||
#define M_FRQ_R 27388061
|
||||
bool console_active = false;
|
||||
bool modem_installed = false;
|
||||
|
||||
#define MTU 508
|
||||
#define SINGLE_MTU 255
|
||||
#define HEADER_L 1
|
||||
#define MIN_L 1
|
||||
#define CMD_L 64
|
||||
|
||||
bool mw_radio_online = false;
|
||||
|
||||
#define eeprom_addr(a) (a+EEPROM_OFFSET)
|
||||
|
||||
#if (MODEM == SX1262 || MODEM == SX1280) && defined(NRF52840_XXAA)
|
||||
SPIClass spiModem(NRF_SPIM2, pin_miso, pin_sclk, pin_mosi);
|
||||
#endif
|
||||
|
||||
// MCU independent configuration parameters
|
||||
const long serial_baudrate = 115200;
|
||||
|
||||
// SX1276 RSSI offset to get dBm value from
|
||||
// packet RSSI register
|
||||
const int rssi_offset = 157;
|
||||
|
||||
// Default LoRa settings
|
||||
#define PHY_HEADER_LORA_SYMBOLS 20
|
||||
#define PHY_CRC_LORA_BITS 16
|
||||
#define LORA_PREAMBLE_SYMBOLS_MIN 18
|
||||
#define LORA_PREAMBLE_TARGET_MS 24
|
||||
#define LORA_PREAMBLE_FAST_DELTA 18
|
||||
#define LORA_FAST_THRESHOLD_BPS 30E3
|
||||
#define LORA_LIMIT_THRESHOLD_BPS 60E3
|
||||
long lora_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
|
||||
long lora_preamble_time_ms = 0;
|
||||
long lora_header_time_ms = 0;
|
||||
float lora_symbol_time_ms = 0.0;
|
||||
float lora_symbol_rate = 0.0;
|
||||
float lora_us_per_byte = 0.0;
|
||||
bool lora_low_datarate = false;
|
||||
bool lora_limit_rate = false;
|
||||
|
||||
// CSMA Parameters
|
||||
#define CSMA_SIFS_MS 0
|
||||
#define CSMA_POST_TX_YIELD_SLOTS 3
|
||||
#define CSMA_SLOT_MAX_MS 100
|
||||
#define CSMA_SLOT_MIN_MS 24
|
||||
#define CSMA_SLOT_MIN_FAST_DELTA 18
|
||||
#define CSMA_SLOT_SYMBOLS 12
|
||||
#define CSMA_CW_BANDS 4
|
||||
#define CSMA_CW_MIN 0
|
||||
#define CSMA_CW_PER_BAND_WINDOWS 15
|
||||
#define CSMA_BAND_1_MAX_AIRTIME 7
|
||||
#define CSMA_BAND_N_MIN_AIRTIME 85
|
||||
#define CSMA_INFR_THRESHOLD_DB 12
|
||||
bool interference_detected = false;
|
||||
bool avoid_interference = true;
|
||||
int csma_slot_ms = CSMA_SLOT_MIN_MS;
|
||||
unsigned long difs_ms = CSMA_SIFS_MS + 2*csma_slot_ms;
|
||||
unsigned long difs_wait_start = -1;
|
||||
unsigned long cw_wait_start = -1;
|
||||
unsigned long cw_wait_target = -1;
|
||||
unsigned long cw_wait_passed = 0;
|
||||
int csma_cw = -1;
|
||||
uint8_t cw_band = 1;
|
||||
uint8_t cw_min = 0;
|
||||
uint8_t cw_max = CSMA_CW_PER_BAND_WINDOWS;
|
||||
|
||||
// LoRa settings
|
||||
int lora_sf = 0;
|
||||
int lora_cr = 5;
|
||||
int lora_txp = 0xFF;
|
||||
uint32_t lora_bw = 0;
|
||||
uint32_t lora_freq = 0;
|
||||
uint32_t lora_bitrate = 0;
|
||||
|
||||
// Operational variables
|
||||
bool radio_locked = true;
|
||||
bool radio_online = false;
|
||||
bool community_fw = true;
|
||||
bool hw_ready = false;
|
||||
bool radio_error = false;
|
||||
bool disp_ready = false;
|
||||
bool pmu_ready = false;
|
||||
bool promisc = false;
|
||||
bool implicit = false;
|
||||
bool memory_low = false;
|
||||
uint8_t implicit_l = 0;
|
||||
|
||||
uint8_t op_mode = MODE_HOST;
|
||||
uint8_t model = 0x00;
|
||||
uint8_t hwrev = 0x00;
|
||||
|
||||
#define NOISE_FLOOR_SAMPLES 64
|
||||
int noise_floor = -292;
|
||||
int current_rssi = -292;
|
||||
int last_rssi = -292;
|
||||
uint8_t last_rssi_raw = 0x00;
|
||||
uint8_t last_snr_raw = 0x80;
|
||||
uint8_t seq = 0xFF;
|
||||
uint16_t read_len = 0;
|
||||
|
||||
// Incoming packet buffer
|
||||
uint8_t pbuf[MTU];
|
||||
|
||||
// KISS command buffer
|
||||
uint8_t cmdbuf[CMD_L];
|
||||
|
||||
// LoRa transmit buffer
|
||||
uint8_t tbuf[MTU];
|
||||
|
||||
uint32_t stat_rx = 0;
|
||||
uint32_t stat_tx = 0;
|
||||
|
||||
#define STATUS_INTERVAL_MS 3
|
||||
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
|
||||
#define DCD_SAMPLES 2500
|
||||
#define UTIL_UPDATE_INTERVAL_MS 1000
|
||||
#define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS)
|
||||
#define AIRTIME_LONGTERM 3600
|
||||
#define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000)
|
||||
#define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES)
|
||||
#define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS)
|
||||
bool util_samples[DCD_SAMPLES];
|
||||
uint16_t airtime_bins[AIRTIME_BINS];
|
||||
float longterm_bins[AIRTIME_BINS];
|
||||
int dcd_sample = 0;
|
||||
float local_channel_util = 0.0;
|
||||
float total_channel_util = 0.0;
|
||||
float longterm_channel_util = 0.0;
|
||||
float airtime = 0.0;
|
||||
float longterm_airtime = 0.0;
|
||||
#define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS
|
||||
#endif
|
||||
float st_airtime_limit = 0.0;
|
||||
float lt_airtime_limit = 0.0;
|
||||
bool airtime_lock = false;
|
||||
|
||||
bool stat_signal_detected = false;
|
||||
bool stat_signal_synced = false;
|
||||
bool stat_rx_ongoing = false;
|
||||
bool dcd = false;
|
||||
bool dcd_led = false;
|
||||
bool dcd_waiting = false;
|
||||
long dcd_wait_until = 0;
|
||||
uint16_t dcd_count = 0;
|
||||
uint16_t dcd_threshold = 2;
|
||||
|
||||
uint32_t status_interval_ms = STATUS_INTERVAL_MS;
|
||||
uint32_t last_status_update = 0;
|
||||
uint32_t last_dcd = 0;
|
||||
|
||||
// Power management
|
||||
#define BATTERY_STATE_UNKNOWN 0x00
|
||||
#define BATTERY_STATE_DISCHARGING 0x01
|
||||
#define BATTERY_STATE_CHARGING 0x02
|
||||
#define BATTERY_STATE_CHARGED 0x03
|
||||
bool battery_installed = false;
|
||||
bool battery_indeterminate = false;
|
||||
bool external_power = false;
|
||||
bool battery_ready = false;
|
||||
float battery_voltage = 0.0;
|
||||
float battery_percent = 0.0;
|
||||
uint8_t battery_state = 0x00;
|
||||
uint8_t display_intensity = 0xFF;
|
||||
uint8_t display_addr = 0xFF;
|
||||
volatile bool display_updating = false;
|
||||
bool display_blanking_enabled = false;
|
||||
bool display_diagnostics = true;
|
||||
bool device_init_done = false;
|
||||
bool eeprom_ok = false;
|
||||
bool firmware_update_mode = false;
|
||||
bool serial_in_frame = false;
|
||||
|
||||
// Boot flags
|
||||
#define START_FROM_BOOTLOADER 0x01
|
||||
#define START_FROM_POWERON 0x02
|
||||
#define START_FROM_BROWNOUT 0x03
|
||||
#define START_FROM_JTAG 0x04
|
||||
|
||||
#endif
|
||||
203
RNode_Firmware/Console.h
Normal file
203
RNode_Firmware/Console.h
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <FS.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
#include "SD.h"
|
||||
#include "SPI.h"
|
||||
|
||||
#if HAS_SD
|
||||
SPIClass *spi = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/rtc.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
void console_dbg(String msg) {
|
||||
Serial.print("[Webserver] ");
|
||||
Serial.println(msg);
|
||||
}
|
||||
|
||||
bool exists(String path){
|
||||
bool yes = false;
|
||||
File file = SPIFFS.open(path, "r");
|
||||
if(!file.isDirectory()){
|
||||
yes = true;
|
||||
}
|
||||
file.close();
|
||||
return yes;
|
||||
}
|
||||
|
||||
String console_get_content_type(String filename) {
|
||||
if (server.hasArg("download")) {
|
||||
return "application/octet-stream";
|
||||
} else if (filename.endsWith(".htm")) {
|
||||
return "text/html";
|
||||
} else if (filename.endsWith(".html")) {
|
||||
return "text/html";
|
||||
} else if (filename.endsWith(".css")) {
|
||||
return "text/css";
|
||||
} else if (filename.endsWith(".js")) {
|
||||
return "application/javascript";
|
||||
} else if (filename.endsWith(".png")) {
|
||||
return "image/png";
|
||||
} else if (filename.endsWith(".gif")) {
|
||||
return "image/gif";
|
||||
} else if (filename.endsWith(".jpg")) {
|
||||
return "image/jpeg";
|
||||
} else if (filename.endsWith(".ico")) {
|
||||
return "image/x-icon";
|
||||
} else if (filename.endsWith(".xml")) {
|
||||
return "text/xml";
|
||||
} else if (filename.endsWith(".pdf")) {
|
||||
return "application/x-pdf";
|
||||
} else if (filename.endsWith(".zip")) {
|
||||
return "application/x-zip";
|
||||
} else if (filename.endsWith(".gz")) {
|
||||
return "application/x-gzip";
|
||||
} else if (filename.endsWith(".whl")) {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
bool console_serve_file(String path) {
|
||||
console_dbg("Request for: "+path);
|
||||
if (path.endsWith("/")) {
|
||||
path += "index.html";
|
||||
}
|
||||
|
||||
if (path == "/r/manual/index.html") {
|
||||
path = "/m.html";
|
||||
}
|
||||
if (path == "/r/manual/Reticulum Manual.pdf") {
|
||||
path = "/h.html";
|
||||
}
|
||||
|
||||
|
||||
String content_type = console_get_content_type(path);
|
||||
String pathWithGz = path + ".gz";
|
||||
if (exists(pathWithGz) || exists(path)) {
|
||||
if (exists(pathWithGz)) {
|
||||
path += ".gz";
|
||||
}
|
||||
|
||||
File file = SPIFFS.open(path, "r");
|
||||
console_dbg("Serving file to client");
|
||||
server.streamFile(file, content_type);
|
||||
file.close();
|
||||
|
||||
console_dbg("File serving done\n");
|
||||
return true;
|
||||
} else {
|
||||
int spos = pathWithGz.lastIndexOf('/');
|
||||
if (spos > 0) {
|
||||
String remap_path = "/d";
|
||||
remap_path.concat(pathWithGz.substring(spos));
|
||||
Serial.println(remap_path);
|
||||
|
||||
if (exists(remap_path)) {
|
||||
File file = SPIFFS.open(remap_path, "r");
|
||||
console_dbg("Serving remapped file to client");
|
||||
server.streamFile(file, content_type);
|
||||
console_dbg("Closing file");
|
||||
file.close();
|
||||
|
||||
console_dbg("File serving done\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console_dbg("Error: Could not open file for serving\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void console_register_pages() {
|
||||
server.onNotFound([]() {
|
||||
if (!console_serve_file(server.uri())) {
|
||||
server.send(404, "text/plain", "Not Found");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void console_start() {
|
||||
Serial.println("");
|
||||
console_dbg("Starting Access Point...");
|
||||
WiFi.softAP(bt_devname);
|
||||
delay(150);
|
||||
IPAddress ip(10, 0, 0, 1);
|
||||
IPAddress nm(255, 255, 255, 0);
|
||||
WiFi.softAPConfig(ip, ip, nm);
|
||||
|
||||
if(!SPIFFS.begin(true)){
|
||||
console_dbg("Error: Could not mount SPIFFS");
|
||||
return;
|
||||
} else {
|
||||
console_dbg("SPIFFS Ready");
|
||||
}
|
||||
|
||||
#if HAS_SD
|
||||
spi = new SPIClass(HSPI);
|
||||
spi->begin(SD_CLK, SD_MISO, SD_MOSI, SD_CS);
|
||||
if(!SD.begin(SD_CS, *spi)){
|
||||
console_dbg("No SD card inserted");
|
||||
} else {
|
||||
uint8_t cardType = SD.cardType();
|
||||
if(cardType == CARD_NONE){
|
||||
console_dbg("No SD card type");
|
||||
} else {
|
||||
console_dbg("SD Card Type: ");
|
||||
if(cardType == CARD_MMC){
|
||||
console_dbg("MMC");
|
||||
} else if(cardType == CARD_SD){
|
||||
console_dbg("SDSC");
|
||||
} else if(cardType == CARD_SDHC){
|
||||
console_dbg("SDHC");
|
||||
} else {
|
||||
console_dbg("UNKNOWN");
|
||||
}
|
||||
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
|
||||
Serial.printf("SD Card Size: %lluMB\n", cardSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
console_register_pages();
|
||||
server.begin();
|
||||
led_indicate_console();
|
||||
}
|
||||
|
||||
void console_loop(){
|
||||
server.handleClient();
|
||||
// Internally, this yields the thread and allows
|
||||
// other tasks to run.
|
||||
delay(2);
|
||||
}
|
||||
267
RNode_Firmware/Device.h
Normal file
267
RNode_Firmware/Device.h
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Ed25519.h>
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#include "mbedtls/md.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
#include "Adafruit_nRFCrypto.h"
|
||||
|
||||
// size of chunk to retrieve from flash sector
|
||||
#define CHUNK_SIZE 128
|
||||
|
||||
#define END_SECTION_SIZE 256
|
||||
|
||||
#if defined(NRF52840_XXAA)
|
||||
// https://learn.adafruit.com/introducing-the-adafruit-nrf52840-feather/hathach-memory-map
|
||||
// each section follows along from one another, in this order
|
||||
// this is always at the start of the memory map
|
||||
#define APPLICATION_START 0x26000
|
||||
|
||||
#define USER_DATA_START 0xED000
|
||||
|
||||
#define IMG_SIZE_START 0xFF008
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Forward declaration from Utilities.h
|
||||
void eeprom_update(int mapped_addr, uint8_t byte);
|
||||
uint8_t eeprom_read(uint32_t addr);
|
||||
void hard_reset(void);
|
||||
|
||||
#if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
|
||||
void eeprom_flush();
|
||||
#endif
|
||||
|
||||
const uint8_t dev_keys [] PROGMEM = {
|
||||
0x0f, 0x15, 0x86, 0x74, 0xa0, 0x7d, 0xf2, 0xde, 0x32, 0x11, 0x29, 0xc1, 0x0d, 0xda, 0xcc, 0xc3,
|
||||
0xe1, 0x9b, 0xac, 0xf2, 0x27, 0x06, 0xee, 0x89, 0x1f, 0x7a, 0xfc, 0xc3, 0x6a, 0xf5, 0x38, 0x08
|
||||
};
|
||||
|
||||
#define DEV_SIG_LEN 64
|
||||
uint8_t dev_sig[DEV_SIG_LEN];
|
||||
|
||||
#define DEV_KEY_LEN 32
|
||||
uint8_t dev_k_prv[DEV_KEY_LEN];
|
||||
uint8_t dev_k_pub[DEV_KEY_LEN];
|
||||
|
||||
#define DEV_HASH_LEN 32
|
||||
uint8_t dev_hash[DEV_HASH_LEN];
|
||||
uint8_t dev_partition_table_hash[DEV_HASH_LEN];
|
||||
uint8_t dev_bootloader_hash[DEV_HASH_LEN];
|
||||
uint8_t dev_firmware_hash[DEV_HASH_LEN];
|
||||
uint8_t dev_firmware_hash_target[DEV_HASH_LEN];
|
||||
|
||||
#define EEPROM_SIG_LEN 128
|
||||
uint8_t dev_eeprom_signature[EEPROM_SIG_LEN];
|
||||
|
||||
bool dev_signature_validated = false;
|
||||
bool fw_signature_validated = true;
|
||||
|
||||
#define DEV_SIG_OFFSET EEPROM_SIZE-EEPROM_RESERVED-DEV_SIG_LEN
|
||||
#define dev_sig_addr(a) (a+DEV_SIG_OFFSET)
|
||||
|
||||
#define DEV_FWHASH_OFFSET EEPROM_SIZE-EEPROM_RESERVED-DEV_SIG_LEN-DEV_HASH_LEN
|
||||
#define dev_fwhash_addr(a) (a+DEV_FWHASH_OFFSET)
|
||||
|
||||
bool device_signatures_ok() {
|
||||
return dev_signature_validated && fw_signature_validated;
|
||||
}
|
||||
|
||||
void device_validate_signature() {
|
||||
int n_keys = sizeof(dev_keys)/DEV_KEY_LEN;
|
||||
bool valid_signature_found = false;
|
||||
for (int i = 0; i < n_keys; i++) {
|
||||
memcpy(dev_k_pub, dev_keys+DEV_KEY_LEN*i, DEV_KEY_LEN);
|
||||
if (Ed25519::verify(dev_sig, dev_k_pub, dev_hash, DEV_HASH_LEN)) {
|
||||
valid_signature_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_signature_found) {
|
||||
dev_signature_validated = true;
|
||||
} else {
|
||||
dev_signature_validated = false;
|
||||
}
|
||||
}
|
||||
|
||||
void device_save_signature() {
|
||||
device_validate_signature();
|
||||
if (dev_signature_validated) {
|
||||
for (uint8_t i = 0; i < DEV_SIG_LEN; i++) {
|
||||
eeprom_update(dev_sig_addr(i), dev_sig[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void device_load_signature() {
|
||||
for (uint8_t i = 0; i < DEV_SIG_LEN; i++) {
|
||||
#if HAS_EEPROM
|
||||
dev_sig[i] = EEPROM.read(dev_sig_addr(i));
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
dev_sig[i] = eeprom_read(dev_sig_addr(i));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void device_load_firmware_hash() {
|
||||
for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
|
||||
#if HAS_EEPROM
|
||||
dev_firmware_hash_target[i] = EEPROM.read(dev_fwhash_addr(i));
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
dev_firmware_hash_target[i] = eeprom_read(dev_fwhash_addr(i));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void device_save_firmware_hash() {
|
||||
for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
|
||||
eeprom_update(dev_fwhash_addr(i), dev_firmware_hash_target[i]);
|
||||
}
|
||||
#if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
|
||||
eeprom_flush();
|
||||
#endif
|
||||
if (!fw_signature_validated) hard_reset();
|
||||
}
|
||||
|
||||
#if MCU_VARIANT == MCU_NRF52
|
||||
uint32_t retrieve_application_size() {
|
||||
uint8_t bytes[4];
|
||||
memcpy(bytes, (const void*)IMG_SIZE_START, 4);
|
||||
uint32_t fw_len = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24;
|
||||
return fw_len;
|
||||
}
|
||||
|
||||
void calculate_region_hash(unsigned long long start, unsigned long long end, uint8_t* return_hash) {
|
||||
// this function calculates the hash digest of a region of memory,
|
||||
// currently it is only designed to work for the application region
|
||||
uint8_t chunk[CHUNK_SIZE] = {0};
|
||||
|
||||
// to store potential last chunk of program
|
||||
uint8_t chunk_next[CHUNK_SIZE] = {0};
|
||||
nRFCrypto_Hash hash;
|
||||
|
||||
hash.begin(CRYS_HASH_SHA256_mode);
|
||||
|
||||
uint8_t size;
|
||||
|
||||
while (start < end ) {
|
||||
const void* src = (const void*)start;
|
||||
if (start + CHUNK_SIZE >= end) {
|
||||
size = end - start;
|
||||
}
|
||||
else {
|
||||
size = CHUNK_SIZE;
|
||||
}
|
||||
|
||||
memcpy(chunk, src, CHUNK_SIZE);
|
||||
|
||||
hash.update(chunk, size);
|
||||
|
||||
start += CHUNK_SIZE;
|
||||
}
|
||||
hash.end(return_hash);
|
||||
}
|
||||
#endif
|
||||
|
||||
void device_validate_partitions() {
|
||||
device_load_firmware_hash();
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
esp_partition_t partition;
|
||||
partition.address = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_MAX_LEN;
|
||||
partition.type = ESP_PARTITION_TYPE_DATA;
|
||||
esp_partition_get_sha256(&partition, dev_partition_table_hash);
|
||||
partition.address = ESP_BOOTLOADER_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.type = ESP_PARTITION_TYPE_APP;
|
||||
esp_partition_get_sha256(&partition, dev_bootloader_hash);
|
||||
esp_partition_get_sha256(esp_ota_get_running_partition(), dev_firmware_hash);
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
// todo, add bootloader, partition table, or softdevice?
|
||||
calculate_region_hash(APPLICATION_START, APPLICATION_START+retrieve_application_size(), dev_firmware_hash);
|
||||
#endif
|
||||
#if VALIDATE_FIRMWARE
|
||||
for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
|
||||
if (dev_firmware_hash_target[i] != dev_firmware_hash[i]) {
|
||||
fw_signature_validated = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool device_firmware_ok() {
|
||||
return fw_signature_validated;
|
||||
}
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
|
||||
bool device_init() {
|
||||
if (bt_ready) {
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
for (uint8_t i=0; i<EEPROM_SIG_LEN; i++){dev_eeprom_signature[i]=EEPROM.read(eeprom_addr(ADDR_SIGNATURE+i));}
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
|
||||
mbedtls_md_starts(&ctx);
|
||||
#if HAS_BLUETOOTH == true || HAS_BLE == true
|
||||
mbedtls_md_update(&ctx, dev_bt_mac, BT_DEV_ADDR_LEN);
|
||||
#else
|
||||
// TODO: Get from BLE stack instead
|
||||
// mbedtls_md_update(&ctx, dev_bt_mac, BT_DEV_ADDR_LEN);
|
||||
#endif
|
||||
mbedtls_md_update(&ctx, dev_eeprom_signature, EEPROM_SIG_LEN);
|
||||
mbedtls_md_finish(&ctx, dev_hash);
|
||||
mbedtls_md_free(&ctx);
|
||||
#elif MCU_VARIANT == MCU_NRF52
|
||||
for (uint8_t i=0; i<EEPROM_SIG_LEN; i++){dev_eeprom_signature[i]=eeprom_read(eeprom_addr(ADDR_SIGNATURE+i));}
|
||||
nRFCrypto.begin();
|
||||
|
||||
nRFCrypto_Hash hash;
|
||||
|
||||
hash.begin(CRYS_HASH_SHA256_mode);
|
||||
|
||||
#if HAS_BLUETOOTH == true || HAS_BLE == true
|
||||
hash.update(dev_bt_mac, BT_DEV_ADDR_LEN);
|
||||
#else
|
||||
// TODO: Get from BLE stack instead
|
||||
// hash.update(dev_bt_mac, BT_DEV_ADDR_LEN);
|
||||
#endif
|
||||
hash.update(dev_eeprom_signature, EEPROM_SIG_LEN);
|
||||
|
||||
hash.end(dev_hash);
|
||||
#endif
|
||||
device_load_signature();
|
||||
device_validate_signature();
|
||||
|
||||
device_validate_partitions();
|
||||
|
||||
#if MCU_VARIANT == MCU_NRF52
|
||||
nRFCrypto.end();
|
||||
#endif
|
||||
device_init_done = true;
|
||||
return device_init_done && fw_signature_validated;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
1205
RNode_Firmware/Display.h
Normal file
1205
RNode_Firmware/Display.h
Normal file
File diff suppressed because it is too large
Load diff
131
RNode_Firmware/Fonts/Org_01.h
Normal file
131
RNode_Firmware/Fonts/Org_01.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
#include <Adafruit_GFX.h>
|
||||
|
||||
// Org_v01 by Orgdot (www.orgdot.com/aliasfonts). A tiny,
|
||||
// stylized font with all characters within a 6 pixel height.
|
||||
|
||||
const uint8_t Org_01Bitmaps[] PROGMEM = {
|
||||
0xE8, 0xA0, 0x57, 0xD5, 0xF5, 0x00, 0xFD, 0x3E, 0x5F, 0x80, 0x88, 0x88,
|
||||
0x88, 0x80, 0xF4, 0xBF, 0x2E, 0x80, 0x80, 0x6A, 0x40, 0x95, 0x80, 0xAA,
|
||||
0x80, 0x5D, 0x00, 0xC0, 0xF0, 0x80, 0x08, 0x88, 0x88, 0x00, 0xFC, 0x63,
|
||||
0x1F, 0x80, 0xF8, 0xF8, 0x7F, 0x0F, 0x80, 0xF8, 0x7E, 0x1F, 0x80, 0x8C,
|
||||
0x7E, 0x10, 0x80, 0xFC, 0x3E, 0x1F, 0x80, 0xFC, 0x3F, 0x1F, 0x80, 0xF8,
|
||||
0x42, 0x10, 0x80, 0xFC, 0x7F, 0x1F, 0x80, 0xFC, 0x7E, 0x1F, 0x80, 0x90,
|
||||
0xB0, 0x2A, 0x22, 0xF0, 0xF0, 0x88, 0xA8, 0xF8, 0x4E, 0x02, 0x00, 0xFD,
|
||||
0x6F, 0x0F, 0x80, 0xFC, 0x7F, 0x18, 0x80, 0xF4, 0x7D, 0x1F, 0x00, 0xFC,
|
||||
0x21, 0x0F, 0x80, 0xF4, 0x63, 0x1F, 0x00, 0xFC, 0x3F, 0x0F, 0x80, 0xFC,
|
||||
0x3F, 0x08, 0x00, 0xFC, 0x2F, 0x1F, 0x80, 0x8C, 0x7F, 0x18, 0x80, 0xF9,
|
||||
0x08, 0x4F, 0x80, 0x78, 0x85, 0x2F, 0x80, 0x8D, 0xB1, 0x68, 0x80, 0x84,
|
||||
0x21, 0x0F, 0x80, 0xFD, 0x6B, 0x5A, 0x80, 0xFC, 0x63, 0x18, 0x80, 0xFC,
|
||||
0x63, 0x1F, 0x80, 0xFC, 0x7F, 0x08, 0x00, 0xFC, 0x63, 0x3F, 0x80, 0xFC,
|
||||
0x7F, 0x29, 0x00, 0xFC, 0x3E, 0x1F, 0x80, 0xF9, 0x08, 0x42, 0x00, 0x8C,
|
||||
0x63, 0x1F, 0x80, 0x8C, 0x62, 0xA2, 0x00, 0xAD, 0x6B, 0x5F, 0x80, 0x8A,
|
||||
0x88, 0xA8, 0x80, 0x8C, 0x54, 0x42, 0x00, 0xF8, 0x7F, 0x0F, 0x80, 0xEA,
|
||||
0xC0, 0x82, 0x08, 0x20, 0x80, 0xD5, 0xC0, 0x54, 0xF8, 0x80, 0xF1, 0xFF,
|
||||
0x8F, 0x99, 0xF0, 0xF8, 0x8F, 0x1F, 0x99, 0xF0, 0xFF, 0x8F, 0x6B, 0xA4,
|
||||
0xF9, 0x9F, 0x10, 0x8F, 0x99, 0x90, 0xF0, 0x55, 0xC0, 0x8A, 0xF9, 0x90,
|
||||
0xF8, 0xFD, 0x63, 0x10, 0xF9, 0x99, 0xF9, 0x9F, 0xF9, 0x9F, 0x80, 0xF9,
|
||||
0x9F, 0x20, 0xF8, 0x88, 0x47, 0x1F, 0x27, 0xC8, 0x42, 0x00, 0x99, 0x9F,
|
||||
0x99, 0x97, 0x8C, 0x6B, 0xF0, 0x96, 0x69, 0x99, 0x9F, 0x10, 0x2E, 0x8F,
|
||||
0x2B, 0x22, 0xF8, 0x89, 0xA8, 0x0F, 0xE0};
|
||||
|
||||
const GFXglyph Org_01Glyphs[] PROGMEM = {{0, 0, 0, 6, 0, 1}, // 0x20 ' '
|
||||
{0, 1, 5, 2, 0, -4}, // 0x21 '!'
|
||||
{1, 3, 1, 4, 0, -4}, // 0x22 '"'
|
||||
{2, 5, 5, 6, 0, -4}, // 0x23 '#'
|
||||
{6, 5, 5, 6, 0, -4}, // 0x24 '$'
|
||||
{10, 5, 5, 6, 0, -4}, // 0x25 '%'
|
||||
{14, 5, 5, 6, 0, -4}, // 0x26 '&'
|
||||
{18, 1, 1, 2, 0, -4}, // 0x27 '''
|
||||
{19, 2, 5, 3, 0, -4}, // 0x28 '('
|
||||
{21, 2, 5, 3, 0, -4}, // 0x29 ')'
|
||||
{23, 3, 3, 4, 0, -3}, // 0x2A '*'
|
||||
{25, 3, 3, 4, 0, -3}, // 0x2B '+'
|
||||
{27, 1, 2, 2, 0, 0}, // 0x2C ','
|
||||
{28, 4, 1, 5, 0, -2}, // 0x2D '-'
|
||||
{29, 1, 1, 2, 0, 0}, // 0x2E '.'
|
||||
{30, 5, 5, 6, 0, -4}, // 0x2F '/'
|
||||
{34, 5, 5, 6, 0, -4}, // 0x30 '0'
|
||||
{38, 1, 5, 2, 0, -4}, // 0x31 '1'
|
||||
{39, 5, 5, 6, 0, -4}, // 0x32 '2'
|
||||
{43, 5, 5, 6, 0, -4}, // 0x33 '3'
|
||||
{47, 5, 5, 6, 0, -4}, // 0x34 '4'
|
||||
{51, 5, 5, 6, 0, -4}, // 0x35 '5'
|
||||
{55, 5, 5, 6, 0, -4}, // 0x36 '6'
|
||||
{59, 5, 5, 6, 0, -4}, // 0x37 '7'
|
||||
{63, 5, 5, 6, 0, -4}, // 0x38 '8'
|
||||
{67, 5, 5, 6, 0, -4}, // 0x39 '9'
|
||||
{71, 1, 4, 2, 0, -3}, // 0x3A ':'
|
||||
{72, 1, 4, 2, 0, -3}, // 0x3B ';'
|
||||
{73, 3, 5, 4, 0, -4}, // 0x3C '<'
|
||||
{75, 4, 3, 5, 0, -3}, // 0x3D '='
|
||||
{77, 3, 5, 4, 0, -4}, // 0x3E '>'
|
||||
{79, 5, 5, 6, 0, -4}, // 0x3F '?'
|
||||
{83, 5, 5, 6, 0, -4}, // 0x40 '@'
|
||||
{87, 5, 5, 6, 0, -4}, // 0x41 'A'
|
||||
{91, 5, 5, 6, 0, -4}, // 0x42 'B'
|
||||
{95, 5, 5, 6, 0, -4}, // 0x43 'C'
|
||||
{99, 5, 5, 6, 0, -4}, // 0x44 'D'
|
||||
{103, 5, 5, 6, 0, -4}, // 0x45 'E'
|
||||
{107, 5, 5, 6, 0, -4}, // 0x46 'F'
|
||||
{111, 5, 5, 6, 0, -4}, // 0x47 'G'
|
||||
{115, 5, 5, 6, 0, -4}, // 0x48 'H'
|
||||
{119, 5, 5, 6, 0, -4}, // 0x49 'I'
|
||||
{123, 5, 5, 6, 0, -4}, // 0x4A 'J'
|
||||
{127, 5, 5, 6, 0, -4}, // 0x4B 'K'
|
||||
{131, 5, 5, 6, 0, -4}, // 0x4C 'L'
|
||||
{135, 5, 5, 6, 0, -4}, // 0x4D 'M'
|
||||
{139, 5, 5, 6, 0, -4}, // 0x4E 'N'
|
||||
{143, 5, 5, 6, 0, -4}, // 0x4F 'O'
|
||||
{147, 5, 5, 6, 0, -4}, // 0x50 'P'
|
||||
{151, 5, 5, 6, 0, -4}, // 0x51 'Q'
|
||||
{155, 5, 5, 6, 0, -4}, // 0x52 'R'
|
||||
{159, 5, 5, 6, 0, -4}, // 0x53 'S'
|
||||
{163, 5, 5, 6, 0, -4}, // 0x54 'T'
|
||||
{167, 5, 5, 6, 0, -4}, // 0x55 'U'
|
||||
{171, 5, 5, 6, 0, -4}, // 0x56 'V'
|
||||
{175, 5, 5, 6, 0, -4}, // 0x57 'W'
|
||||
{179, 5, 5, 6, 0, -4}, // 0x58 'X'
|
||||
{183, 5, 5, 6, 0, -4}, // 0x59 'Y'
|
||||
{187, 5, 5, 6, 0, -4}, // 0x5A 'Z'
|
||||
{191, 2, 5, 3, 0, -4}, // 0x5B '['
|
||||
{193, 5, 5, 6, 0, -4}, // 0x5C '\'
|
||||
{197, 2, 5, 3, 0, -4}, // 0x5D ']'
|
||||
{199, 3, 2, 4, 0, -4}, // 0x5E '^'
|
||||
{200, 5, 1, 6, 0, 1}, // 0x5F '_'
|
||||
{201, 1, 1, 2, 0, -4}, // 0x60 '`'
|
||||
{202, 4, 4, 5, 0, -3}, // 0x61 'a'
|
||||
{204, 4, 5, 5, 0, -4}, // 0x62 'b'
|
||||
{207, 4, 4, 5, 0, -3}, // 0x63 'c'
|
||||
{209, 4, 5, 5, 0, -4}, // 0x64 'd'
|
||||
{212, 4, 4, 5, 0, -3}, // 0x65 'e'
|
||||
{214, 3, 5, 4, 0, -4}, // 0x66 'f'
|
||||
{216, 4, 5, 5, 0, -3}, // 0x67 'g'
|
||||
{219, 4, 5, 5, 0, -4}, // 0x68 'h'
|
||||
{222, 1, 4, 2, 0, -3}, // 0x69 'i'
|
||||
{223, 2, 5, 3, 0, -3}, // 0x6A 'j'
|
||||
{225, 4, 5, 5, 0, -4}, // 0x6B 'k'
|
||||
{228, 1, 5, 2, 0, -4}, // 0x6C 'l'
|
||||
{229, 5, 4, 6, 0, -3}, // 0x6D 'm'
|
||||
{232, 4, 4, 5, 0, -3}, // 0x6E 'n'
|
||||
{234, 4, 4, 5, 0, -3}, // 0x6F 'o'
|
||||
{236, 4, 5, 5, 0, -3}, // 0x70 'p'
|
||||
{239, 4, 5, 5, 0, -3}, // 0x71 'q'
|
||||
{242, 4, 4, 5, 0, -3}, // 0x72 'r'
|
||||
{244, 4, 4, 5, 0, -3}, // 0x73 's'
|
||||
{246, 5, 5, 6, 0, -4}, // 0x74 't'
|
||||
{250, 4, 4, 5, 0, -3}, // 0x75 'u'
|
||||
{252, 4, 4, 5, 0, -3}, // 0x76 'v'
|
||||
{254, 5, 4, 6, 0, -3}, // 0x77 'w'
|
||||
{257, 4, 4, 5, 0, -3}, // 0x78 'x'
|
||||
{259, 4, 5, 5, 0, -3}, // 0x79 'y'
|
||||
{262, 4, 4, 5, 0, -3}, // 0x7A 'z'
|
||||
{264, 3, 5, 4, 0, -4}, // 0x7B '{'
|
||||
{266, 1, 5, 2, 0, -4}, // 0x7C '|'
|
||||
{267, 3, 5, 4, 0, -4}, // 0x7D '}'
|
||||
{269, 5, 3, 6, 0, -3}}; // 0x7E '~'
|
||||
|
||||
const GFXfont Org_01 PROGMEM = {(uint8_t *)Org_01Bitmaps,
|
||||
(GFXglyph *)Org_01Glyphs, 0x20, 0x7E, 7};
|
||||
|
||||
// Approx. 943 bytes
|
||||
123
RNode_Firmware/Fonts/PicoPixel.h
Normal file
123
RNode_Firmware/Fonts/PicoPixel.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#pragma once
|
||||
#include <Adafruit_GFX.h>
|
||||
|
||||
// Picopixel by Sebastian Weber. A tiny font
|
||||
// with all characters within a 6 pixel height.
|
||||
|
||||
const uint8_t PicopixelBitmaps[] PROGMEM = {
|
||||
0xE8, 0xB4, 0x57, 0xD5, 0xF5, 0x00, 0x4E, 0x3E, 0x80, 0xA5, 0x4A, 0x4A,
|
||||
0x5A, 0x50, 0xC0, 0x6A, 0x40, 0x95, 0x80, 0xAA, 0x80, 0x5D, 0x00, 0x60,
|
||||
0xE0, 0x80, 0x25, 0x48, 0x56, 0xD4, 0x75, 0x40, 0xC5, 0x4E, 0xC5, 0x1C,
|
||||
0x97, 0x92, 0xF3, 0x1C, 0x53, 0x54, 0xE5, 0x48, 0x55, 0x54, 0x55, 0x94,
|
||||
0xA0, 0x46, 0x64, 0xE3, 0x80, 0x98, 0xC5, 0x04, 0x56, 0xC6, 0x57, 0xDA,
|
||||
0xD7, 0x5C, 0x72, 0x46, 0xD6, 0xDC, 0xF3, 0xCE, 0xF3, 0x48, 0x72, 0xD4,
|
||||
0xB7, 0xDA, 0xF8, 0x24, 0xD4, 0xBB, 0x5A, 0x92, 0x4E, 0x8E, 0xEB, 0x58,
|
||||
0x80, 0x9D, 0xB9, 0x90, 0x56, 0xD4, 0xD7, 0x48, 0x56, 0xD4, 0x40, 0xD7,
|
||||
0x5A, 0x71, 0x1C, 0xE9, 0x24, 0xB6, 0xD4, 0xB6, 0xA4, 0x8C, 0x6B, 0x55,
|
||||
0x00, 0xB5, 0x5A, 0xB5, 0x24, 0xE5, 0x4E, 0xEA, 0xC0, 0x91, 0x12, 0xD5,
|
||||
0xC0, 0x54, 0xF0, 0x90, 0xC7, 0xF0, 0x93, 0x5E, 0x71, 0x80, 0x25, 0xDE,
|
||||
0x5E, 0x30, 0x6E, 0x80, 0x77, 0x9C, 0x93, 0x5A, 0xB8, 0x45, 0x60, 0x92,
|
||||
0xEA, 0xAA, 0x40, 0xD5, 0x6A, 0xD6, 0x80, 0x55, 0x00, 0xD7, 0x40, 0x75,
|
||||
0x90, 0xE8, 0x71, 0xE0, 0xBA, 0x40, 0xB5, 0x80, 0xB5, 0x00, 0x8D, 0x54,
|
||||
0xAA, 0x80, 0xAC, 0xE0, 0xE5, 0x70, 0x6A, 0x26, 0xFC, 0xC8, 0xAC, 0x5A};
|
||||
|
||||
const GFXglyph PicopixelGlyphs[] PROGMEM = {{0, 0, 0, 2, 0, 1}, // 0x20 ' '
|
||||
{0, 1, 5, 2, 0, -4}, // 0x21 '!'
|
||||
{1, 3, 2, 4, 0, -4}, // 0x22 '"'
|
||||
{2, 5, 5, 6, 0, -4}, // 0x23 '#'
|
||||
{6, 3, 6, 4, 0, -4}, // 0x24 '$'
|
||||
{9, 3, 5, 4, 0, -4}, // 0x25 '%'
|
||||
{11, 4, 5, 5, 0, -4}, // 0x26 '&'
|
||||
{14, 1, 2, 2, 0, -4}, // 0x27 '''
|
||||
{15, 2, 5, 3, 0, -4}, // 0x28 '('
|
||||
{17, 2, 5, 3, 0, -4}, // 0x29 ')'
|
||||
{19, 3, 3, 4, 0, -3}, // 0x2A '*'
|
||||
{21, 3, 3, 4, 0, -3}, // 0x2B '+'
|
||||
{23, 2, 2, 3, 0, 0}, // 0x2C ','
|
||||
{24, 3, 1, 4, 0, -2}, // 0x2D '-'
|
||||
{25, 1, 1, 2, 0, 0}, // 0x2E '.'
|
||||
{26, 3, 5, 4, 0, -4}, // 0x2F '/'
|
||||
{28, 3, 5, 4, 0, -4}, // 0x30 '0'
|
||||
{30, 2, 5, 3, 0, -4}, // 0x31 '1'
|
||||
{32, 3, 5, 4, 0, -4}, // 0x32 '2'
|
||||
{34, 3, 5, 4, 0, -4}, // 0x33 '3'
|
||||
{36, 3, 5, 4, 0, -4}, // 0x34 '4'
|
||||
{38, 3, 5, 4, 0, -4}, // 0x35 '5'
|
||||
{40, 3, 5, 4, 0, -4}, // 0x36 '6'
|
||||
{42, 3, 5, 4, 0, -4}, // 0x37 '7'
|
||||
{44, 3, 5, 4, 0, -4}, // 0x38 '8'
|
||||
{46, 3, 5, 4, 0, -4}, // 0x39 '9'
|
||||
{48, 1, 3, 2, 0, -3}, // 0x3A ':'
|
||||
{49, 2, 4, 3, 0, -3}, // 0x3B ';'
|
||||
{50, 2, 3, 3, 0, -3}, // 0x3C '<'
|
||||
{51, 3, 3, 4, 0, -3}, // 0x3D '='
|
||||
{53, 2, 3, 3, 0, -3}, // 0x3E '>'
|
||||
{54, 3, 5, 4, 0, -4}, // 0x3F '?'
|
||||
{56, 3, 5, 4, 0, -4}, // 0x40 '@'
|
||||
{58, 3, 5, 4, 0, -4}, // 0x41 'A'
|
||||
{60, 3, 5, 4, 0, -4}, // 0x42 'B'
|
||||
{62, 3, 5, 4, 0, -4}, // 0x43 'C'
|
||||
{64, 3, 5, 4, 0, -4}, // 0x44 'D'
|
||||
{66, 3, 5, 4, 0, -4}, // 0x45 'E'
|
||||
{68, 3, 5, 4, 0, -4}, // 0x46 'F'
|
||||
{70, 3, 5, 4, 0, -4}, // 0x47 'G'
|
||||
{72, 3, 5, 4, 0, -4}, // 0x48 'H'
|
||||
{74, 1, 5, 2, 0, -4}, // 0x49 'I'
|
||||
{75, 3, 5, 4, 0, -4}, // 0x4A 'J'
|
||||
{77, 3, 5, 4, 0, -4}, // 0x4B 'K'
|
||||
{79, 3, 5, 4, 0, -4}, // 0x4C 'L'
|
||||
{81, 5, 5, 6, 0, -4}, // 0x4D 'M'
|
||||
{85, 4, 5, 5, 0, -4}, // 0x4E 'N'
|
||||
{88, 3, 5, 4, 0, -4}, // 0x4F 'O'
|
||||
{90, 3, 5, 4, 0, -4}, // 0x50 'P'
|
||||
{92, 3, 6, 4, 0, -4}, // 0x51 'Q'
|
||||
{95, 3, 5, 4, 0, -4}, // 0x52 'R'
|
||||
{97, 3, 5, 4, 0, -4}, // 0x53 'S'
|
||||
{99, 3, 5, 4, 0, -4}, // 0x54 'T'
|
||||
{101, 3, 5, 4, 0, -4}, // 0x55 'U'
|
||||
{103, 3, 5, 4, 0, -4}, // 0x56 'V'
|
||||
{105, 5, 5, 6, 0, -4}, // 0x57 'W'
|
||||
{109, 3, 5, 4, 0, -4}, // 0x58 'X'
|
||||
{111, 3, 5, 4, 0, -4}, // 0x59 'Y'
|
||||
{113, 3, 5, 4, 0, -4}, // 0x5A 'Z'
|
||||
{115, 2, 5, 3, 0, -4}, // 0x5B '['
|
||||
{117, 3, 5, 4, 0, -4}, // 0x5C '\'
|
||||
{119, 2, 5, 3, 0, -4}, // 0x5D ']'
|
||||
{121, 3, 2, 4, 0, -4}, // 0x5E '^'
|
||||
{122, 4, 1, 4, 0, 1}, // 0x5F '_'
|
||||
{123, 2, 2, 3, 0, -4}, // 0x60 '`'
|
||||
{124, 3, 4, 4, 0, -3}, // 0x61 'a'
|
||||
{126, 3, 5, 4, 0, -4}, // 0x62 'b'
|
||||
{128, 3, 3, 4, 0, -2}, // 0x63 'c'
|
||||
{130, 3, 5, 4, 0, -4}, // 0x64 'd'
|
||||
{132, 3, 4, 4, 0, -3}, // 0x65 'e'
|
||||
{134, 2, 5, 3, 0, -4}, // 0x66 'f'
|
||||
{136, 3, 5, 4, 0, -3}, // 0x67 'g'
|
||||
{138, 3, 5, 4, 0, -4}, // 0x68 'h'
|
||||
{140, 1, 5, 2, 0, -4}, // 0x69 'i'
|
||||
{141, 2, 6, 3, 0, -4}, // 0x6A 'j'
|
||||
{143, 3, 5, 4, 0, -4}, // 0x6B 'k'
|
||||
{145, 2, 5, 3, 0, -4}, // 0x6C 'l'
|
||||
{147, 5, 3, 6, 0, -2}, // 0x6D 'm'
|
||||
{149, 3, 3, 4, 0, -2}, // 0x6E 'n'
|
||||
{151, 3, 3, 4, 0, -2}, // 0x6F 'o'
|
||||
{153, 3, 4, 4, 0, -2}, // 0x70 'p'
|
||||
{155, 3, 4, 4, 0, -2}, // 0x71 'q'
|
||||
{157, 2, 3, 3, 0, -2}, // 0x72 'r'
|
||||
{158, 3, 4, 4, 0, -3}, // 0x73 's'
|
||||
{160, 2, 5, 3, 0, -4}, // 0x74 't'
|
||||
{162, 3, 3, 4, 0, -2}, // 0x75 'u'
|
||||
{164, 3, 3, 4, 0, -2}, // 0x76 'v'
|
||||
{166, 5, 3, 6, 0, -2}, // 0x77 'w'
|
||||
{168, 3, 3, 4, 0, -2}, // 0x78 'x'
|
||||
{170, 3, 4, 4, 0, -2}, // 0x79 'y'
|
||||
{172, 3, 4, 4, 0, -3}, // 0x7A 'z'
|
||||
{174, 3, 5, 4, 0, -4}, // 0x7B '{'
|
||||
{176, 1, 6, 2, 0, -4}, // 0x7C '|'
|
||||
{177, 3, 5, 4, 0, -4}, // 0x7D '}'
|
||||
{179, 4, 2, 5, 0, -3}}; // 0x7E '~'
|
||||
|
||||
const GFXfont Picopixel PROGMEM = {(uint8_t *)PicopixelBitmaps,
|
||||
(GFXglyph *)PicopixelGlyphs, 0x20, 0x7E, 7};
|
||||
|
||||
// Approx. 852 bytes
|
||||
117
RNode_Firmware/Framing.h
Normal file
117
RNode_Firmware/Framing.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef FRAMING_H
|
||||
#define FRAMING_H
|
||||
|
||||
#define FEND 0xC0
|
||||
#define FESC 0xDB
|
||||
#define TFEND 0xDC
|
||||
#define TFESC 0xDD
|
||||
|
||||
#define CMD_UNKNOWN 0xFE
|
||||
#define CMD_DATA 0x00
|
||||
#define CMD_FREQUENCY 0x01
|
||||
#define CMD_BANDWIDTH 0x02
|
||||
#define CMD_TXPOWER 0x03
|
||||
#define CMD_SF 0x04
|
||||
#define CMD_CR 0x05
|
||||
#define CMD_RADIO_STATE 0x06
|
||||
#define CMD_RADIO_LOCK 0x07
|
||||
#define CMD_DETECT 0x08
|
||||
#define CMD_IMPLICIT 0x09
|
||||
#define CMD_LEAVE 0x0A
|
||||
#define CMD_ST_ALOCK 0x0B
|
||||
#define CMD_LT_ALOCK 0x0C
|
||||
#define CMD_PROMISC 0x0E
|
||||
#define CMD_READY 0x0F
|
||||
|
||||
#define CMD_STAT_RX 0x21
|
||||
#define CMD_STAT_TX 0x22
|
||||
#define CMD_STAT_RSSI 0x23
|
||||
#define CMD_STAT_SNR 0x24
|
||||
#define CMD_STAT_CHTM 0x25
|
||||
#define CMD_STAT_PHYPRM 0x26
|
||||
#define CMD_STAT_BAT 0x27
|
||||
#define CMD_STAT_CSMA 0x28
|
||||
#define CMD_STAT_TEMP 0x29
|
||||
#define CMD_BLINK 0x30
|
||||
#define CMD_RANDOM 0x40
|
||||
|
||||
#define CMD_FB_EXT 0x41
|
||||
#define CMD_FB_READ 0x42
|
||||
#define CMD_FB_WRITE 0x43
|
||||
#define CMD_FB_READL 0x44
|
||||
#define CMD_DISP_READ 0x66
|
||||
#define CMD_DISP_INT 0x45
|
||||
#define CMD_DISP_ADDR 0x63
|
||||
#define CMD_DISP_BLNK 0x64
|
||||
#define CMD_DISP_ROT 0x67
|
||||
#define CMD_DISP_RCND 0x68
|
||||
#define CMD_NP_INT 0x65
|
||||
#define CMD_BT_CTRL 0x46
|
||||
#define CMD_BT_UNPAIR 0x70
|
||||
#define CMD_BT_PIN 0x62
|
||||
#define CMD_DIS_IA 0x69
|
||||
|
||||
#define CMD_BOARD 0x47
|
||||
#define CMD_PLATFORM 0x48
|
||||
#define CMD_MCU 0x49
|
||||
#define CMD_FW_VERSION 0x50
|
||||
#define CMD_ROM_READ 0x51
|
||||
#define CMD_ROM_WRITE 0x52
|
||||
#define CMD_CONF_SAVE 0x53
|
||||
#define CMD_CONF_DELETE 0x54
|
||||
#define CMD_DEV_HASH 0x56
|
||||
#define CMD_DEV_SIG 0x57
|
||||
#define CMD_FW_HASH 0x58
|
||||
#define CMD_HASHES 0x60
|
||||
#define CMD_FW_UPD 0x61
|
||||
#define CMD_UNLOCK_ROM 0x59
|
||||
#define ROM_UNLOCK_BYTE 0xF8
|
||||
#define CMD_RESET 0x55
|
||||
#define CMD_RESET_BYTE 0xF8
|
||||
|
||||
#define CMD_LOG 0x80
|
||||
#define CMD_TIME 0x81
|
||||
#define CMD_MUX_CHAIN 0x82
|
||||
#define CMD_MUX_DSCVR 0x83
|
||||
|
||||
#define DETECT_REQ 0x73
|
||||
#define DETECT_RESP 0x46
|
||||
|
||||
#define RADIO_STATE_OFF 0x00
|
||||
#define RADIO_STATE_ON 0x01
|
||||
|
||||
#define NIBBLE_SEQ 0xF0
|
||||
#define NIBBLE_FLAGS 0x0F
|
||||
#define FLAG_SPLIT 0x01
|
||||
#define SEQ_UNSET 0xFF
|
||||
|
||||
#define CMD_ERROR 0x90
|
||||
#define ERROR_INITRADIO 0x01
|
||||
#define ERROR_TXFAILED 0x02
|
||||
#define ERROR_EEPROM_LOCKED 0x03
|
||||
#define ERROR_QUEUE_FULL 0x04
|
||||
#define ERROR_MEMORY_LOW 0x05
|
||||
#define ERROR_MODEM_TIMEOUT 0x06
|
||||
|
||||
// Serial framing variables
|
||||
size_t frame_len;
|
||||
bool IN_FRAME = false;
|
||||
bool ESCAPE = false;
|
||||
uint8_t command = CMD_UNKNOWN;
|
||||
|
||||
#endif
|
||||
434
RNode_Firmware/Graphics.h
Normal file
434
RNode_Firmware/Graphics.h
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const unsigned char bm_cable [] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x07, 0xfc, 0x08, 0x38, 0x10, 0x1c, 0x10, 0x00, 0x08, 0x00,
|
||||
0x07, 0xc0, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x20, 0x07, 0xc0, 0x08, 0x00, 0x10, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x80, 0x04, 0x43, 0x08, 0x46,
|
||||
0xf1, 0x8f, 0x02, 0x16, 0x02, 0x23, 0x01, 0x20, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char bm_rf [] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xc4,
|
||||
0x4a, 0xaa, 0x4a, 0xce, 0x6e, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x07, 0xe0, 0x08, 0x10, 0x13, 0xc8, 0x04, 0x20, 0x01, 0x80, 0x00, 0x00, 0x4e, 0xc4,
|
||||
0x4a, 0xaa, 0x4a, 0xce, 0x6e, 0xaa, 0x00, 0x00, 0x01, 0x80, 0x04, 0x20, 0x03, 0xc0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x4e,
|
||||
0x31, 0x48, 0x61, 0xca, 0x74, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x07, 0xe0, 0x08, 0x10, 0x13, 0xc8, 0x04, 0x20, 0x01, 0x80, 0x00, 0x00, 0x71, 0x4e,
|
||||
0x31, 0x48, 0x61, 0xca, 0x74, 0x4e, 0x00, 0x00, 0x01, 0x80, 0x04, 0x20, 0x03, 0xc0, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char bm_bt [] PROGMEM = {
|
||||
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x40, 0x00, 0x00, 0x05, 0x10, 0x00, 0x00, 0x01, 0x40,
|
||||
0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x05, 0x10, 0x00, 0x00, 0x11, 0x40, 0x00, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x01, 0x80, 0x01, 0x40, 0x09, 0x20, 0x05, 0x10, 0x03, 0x20, 0x01, 0x40,
|
||||
0x01, 0x80, 0x01, 0x40, 0x03, 0x20, 0x05, 0x10, 0x09, 0x20, 0x01, 0x40, 0x01, 0x80, 0x01, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x01, 0x80, 0x01, 0x40, 0x09, 0x20, 0x05, 0x10, 0x03, 0x20, 0x01, 0x40,
|
||||
0x29, 0x94, 0x01, 0x40, 0x03, 0x20, 0x05, 0x10, 0x09, 0x20, 0x01, 0x40, 0x01, 0x80, 0x01, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x01, 0x80, 0x01, 0x40, 0x09, 0x20, 0x05, 0x10, 0x03, 0x20, 0x11, 0x48,
|
||||
0x29, 0x94, 0x11, 0x48, 0x03, 0x20, 0x05, 0x10, 0x09, 0x20, 0x01, 0x40, 0x01, 0x80, 0x01, 0x00
|
||||
};
|
||||
|
||||
const unsigned char bm_boot [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xfc, 0x38, 0x66, 0x67, 0x1c, 0x3f, 0xff, 0xff, 0xfc, 0x99, 0xe6, 0x66, 0x4c, 0xff, 0xff,
|
||||
0xff, 0xfc, 0x98, 0x70, 0xe6, 0x7c, 0x3f, 0xff, 0xff, 0xfc, 0x99, 0xf0, 0xe6, 0x4c, 0xff, 0xff,
|
||||
0xff, 0xfc, 0x38, 0x79, 0xe7, 0x1c, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x0c, 0x38, 0xe1, 0xc3, 0x33, 0x38, 0x7f, 0xfe, 0x7e, 0x72, 0x64, 0xe7, 0x31, 0x33, 0xff,
|
||||
0xff, 0x1e, 0x70, 0x61, 0xe7, 0x30, 0x32, 0x7f, 0xff, 0xce, 0x72, 0x61, 0xe7, 0x32, 0x32, 0x7f,
|
||||
0xfe, 0x1e, 0x72, 0x64, 0xe7, 0x33, 0x38, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_fw_update [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xfc, 0x98, 0x70, 0xf1, 0xc3, 0x33, 0x38, 0x7f, 0xfc, 0x99, 0x32, 0x64, 0xe7, 0x31, 0x33, 0xff,
|
||||
0xfc, 0x98, 0x72, 0x60, 0xe7, 0x30, 0x32, 0x7f, 0xfc, 0x99, 0xf2, 0x64, 0xe7, 0x32, 0x32, 0x7f,
|
||||
0xfe, 0x39, 0xf0, 0xe4, 0xe7, 0x33, 0x38, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xf8, 0x66, 0x1c, 0xe6, 0x73, 0x8e, 0x1c, 0x3f, 0xf9, 0xe6, 0x4c, 0x46, 0x53, 0x26, 0x4c, 0xff,
|
||||
0xf8, 0x66, 0x1c, 0x06, 0x53, 0x06, 0x1c, 0x3f, 0xf9, 0xe6, 0x1c, 0xa6, 0x03, 0x26, 0x1c, 0xff,
|
||||
0xf9, 0xe6, 0x4c, 0xe7, 0x27, 0x26, 0x4c, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_console_active [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0x8e, 0x67, 0x0e, 0x39, 0xe1, 0xff,
|
||||
0xff, 0x93, 0x26, 0x26, 0x7c, 0x99, 0xe7, 0xff, 0xff, 0x9f, 0x26, 0x07, 0x1c, 0x99, 0xe1, 0xff,
|
||||
0xff, 0x93, 0x26, 0x47, 0xcc, 0x99, 0xe7, 0xff, 0xff, 0xc7, 0x8e, 0x66, 0x1e, 0x38, 0x61, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3c, 0x70, 0xcc, 0xcc, 0x3f, 0xff,
|
||||
0xff, 0xfc, 0x99, 0x39, 0xcc, 0xcc, 0xff, 0xff, 0xff, 0xfc, 0x19, 0xf9, 0xce, 0x1c, 0x3f, 0xff,
|
||||
0xff, 0xfc, 0x99, 0x39, 0xce, 0x1c, 0xff, 0xff, 0xff, 0xfc, 0x9c, 0x79, 0xcf, 0x3c, 0x3f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_updating [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf1, 0xff, 0x71, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x7f, 0xf5, 0xff, 0x75, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xf1, 0xff, 0x71, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x17, 0xd4, 0x7f, 0x44, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x57, 0xd5, 0x7f, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x17, 0xd4, 0x7f, 0x44, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x44, 0x71, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x57, 0x55, 0x75, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x47, 0x44, 0x71, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x44, 0x51, 0x51, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x55, 0x55, 0x55, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x44, 0x51, 0x51, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x14, 0x54, 0x45, 0x44, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x55, 0x55, 0x55, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x14, 0x54, 0x45, 0x44, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0x44, 0x51, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x55, 0x55, 0x55, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x45, 0x44, 0x51, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xff, 0xff,
|
||||
0xff, 0xff, 0x60, 0x00, 0x00, 0x03, 0x7f, 0xff, 0xff, 0xff, 0x30, 0x00, 0x00, 0x07, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xf8, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x1c, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x56, 0x00, 0x00, 0x35, 0x7f, 0xff, 0xff, 0xff, 0x57, 0x00, 0x00, 0x74, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x44, 0xc0, 0x01, 0xd1, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x55, 0x60, 0x03, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x44, 0x70, 0x07, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x14, 0x5c, 0x1d, 0x44, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x55, 0x56, 0x35, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x14, 0x57, 0xe5, 0x44, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0x44, 0x51, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x55, 0x55, 0x55, 0x55, 0x7f, 0xff, 0xff, 0xff, 0x45, 0x44, 0x51, 0x51, 0x7f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_version [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x99, 0x86, 0x1e, 0x19, 0xc7, 0x33, 0xff, 0xff, 0x99, 0x9e, 0x4c, 0xf9, 0x93, 0x13, 0xff,
|
||||
0xff, 0xc3, 0x86, 0x1e, 0x39, 0x93, 0x03, 0xff, 0xff, 0xc3, 0x9e, 0x1f, 0x99, 0x93, 0x23, 0xff,
|
||||
0xff, 0xe7, 0x86, 0x4c, 0x39, 0xc7, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_fw_corrupt [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x30, 0xe7, 0x33, 0x9c, 0x70, 0xe1, 0xff,
|
||||
0xcf, 0x32, 0x62, 0x32, 0x99, 0x32, 0x67, 0xff, 0xc3, 0x30, 0xe0, 0x32, 0x98, 0x30, 0xe1, 0xff,
|
||||
0xcf, 0x30, 0xe5, 0x30, 0x19, 0x30, 0xe7, 0xff, 0xcf, 0x32, 0x67, 0x39, 0x39, 0x32, 0x61, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xc7, 0x0e, 0x1c, 0x98, 0x70, 0xfc, 0xff,
|
||||
0xc9, 0x93, 0x26, 0x4c, 0x99, 0x39, 0xfb, 0x7f, 0xcf, 0x93, 0x0e, 0x1c, 0x98, 0x79, 0xfb, 0x7f,
|
||||
0xc9, 0x93, 0x0e, 0x1c, 0x99, 0xf9, 0xf7, 0xbf, 0xe3, 0xc7, 0x26, 0x4e, 0x39, 0xf9, 0xf4, 0xbf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xdf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xef,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xf7,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7c, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfb,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
static unsigned char bm_def[] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, 0x61, 0x10, 0x8c, 0x23, 0xc4, 0x3f, 0xff,
|
||||
0xb5, 0xa7, 0xb7, 0xb5, 0xed, 0xed, 0xbf, 0xff, 0xb5, 0xb9, 0xb4, 0xb4, 0x6d, 0xed, 0xbf, 0xff,
|
||||
0x85, 0xa1, 0x10, 0xb4, 0x21, 0x44, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe7, 0x1c, 0xfe, 0x7f, 0x8f, 0xf0, 0x00,
|
||||
0x1f, 0xf7, 0x9d, 0xff, 0x7f, 0x9f, 0xf0, 0x00, 0x1c, 0x77, 0xfd, 0xc7, 0x73, 0xdc, 0x00, 0x00,
|
||||
0x1f, 0xe7, 0xfd, 0xc7, 0x71, 0xdf, 0x00, 0x00, 0x1f, 0xe7, 0x7d, 0xc7, 0x71, 0xdf, 0x00, 0x00,
|
||||
0x1c, 0x77, 0x3d, 0xc7, 0x73, 0xdc, 0x00, 0x00, 0x1c, 0x77, 0x1d, 0xff, 0x7f, 0x9f, 0xf0, 0x00,
|
||||
0x1c, 0x77, 0x1c, 0xfe, 0x7f, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54,
|
||||
0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x62, 0x24, 0x49, 0x22, 0x4e, 0x44,
|
||||
0x00, 0x24, 0x93, 0x66, 0xc9, 0x32, 0x44, 0x28, 0x00, 0x20, 0x92, 0xa5, 0x49, 0x2a, 0x44, 0x10,
|
||||
0x00, 0x24, 0x92, 0x24, 0x49, 0x26, 0x44, 0x10, 0x00, 0x18, 0x62, 0x24, 0x46, 0x22, 0x44, 0x10,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1c, 0x9c, 0x44, 0x88, 0xc7, 0x1c, 0x00, 0x00, 0x10, 0x92, 0x6c, 0xa9, 0x24, 0x90,
|
||||
0x00, 0x00, 0x1c, 0x9c, 0x54, 0xa9, 0xe7, 0x1c, 0x00, 0x00, 0x10, 0x94, 0x44, 0xa9, 0x25, 0x10,
|
||||
0x00, 0x00, 0x10, 0x92, 0x44, 0x51, 0x24, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char bm_def_lc [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, 0x61, 0x10, 0x8c, 0x23, 0xc4, 0x3f, 0xff,
|
||||
0xb5, 0xa7, 0xb7, 0xb5, 0xed, 0xed, 0xbf, 0xff, 0xb5, 0xb9, 0xb4, 0xb4, 0x6d, 0xed, 0xbf, 0xff,
|
||||
0x85, 0xa1, 0x10, 0xb4, 0x21, 0x44, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe7, 0x1c, 0xfe, 0x7f, 0x8f, 0xf0, 0x00,
|
||||
0x1f, 0xf7, 0x9d, 0xff, 0x7f, 0x9f, 0xf0, 0x00, 0x1c, 0x77, 0xfd, 0xc7, 0x73, 0xdc, 0x00, 0x00,
|
||||
0x1f, 0xe7, 0xfd, 0xc7, 0x71, 0xdf, 0x00, 0x00, 0x1f, 0xe7, 0x7d, 0xc7, 0x71, 0xdf, 0x00, 0x00,
|
||||
0x1c, 0x77, 0x3d, 0xc7, 0x73, 0xdc, 0x00, 0x00, 0x1c, 0x77, 0x1d, 0xff, 0x7f, 0x9f, 0xf0, 0x00,
|
||||
0x1c, 0x77, 0x1c, 0xfe, 0x7f, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54,
|
||||
0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8e, 0x39, 0x10, 0x61, 0x88, 0x91, 0x1c,
|
||||
0x02, 0x49, 0x21, 0x90, 0x92, 0x4d, 0x9b, 0x20, 0x02, 0x4e, 0x39, 0x50, 0x82, 0x4a, 0x95, 0x18,
|
||||
0x02, 0x48, 0x21, 0x30, 0x92, 0x48, 0x91, 0x04, 0x01, 0x88, 0x39, 0x10, 0x61, 0x88, 0x91, 0x38,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0xc8, 0x8e, 0x73, 0x91, 0x1c, 0x00, 0x00, 0x02, 0x05, 0x10, 0x22, 0x1b, 0x20,
|
||||
0x00, 0x00, 0x01, 0x82, 0x0c, 0x23, 0x95, 0x18, 0x00, 0x00, 0x00, 0x42, 0x02, 0x22, 0x11, 0x04,
|
||||
0x00, 0x00, 0x03, 0x82, 0x1c, 0x23, 0x91, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char bm_frame [] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xf0, 0x40, 0x02, 0x0f, 0xff, 0xfc,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x1e, 0x40, 0x02, 0x78, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x3f, 0xff, 0xf2, 0x40, 0x02, 0x4f, 0xff, 0xfc,
|
||||
0x00, 0x00, 0x02, 0x40, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x03, 0xc0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x40, 0x02, 0x40, 0x00, 0x00, 0x3f, 0xff, 0xf2, 0x40, 0x02, 0x4f, 0xff, 0xfc,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04, 0x20, 0x00, 0x12, 0x40, 0x02, 0x48, 0x00, 0x04,
|
||||
0x20, 0x00, 0x1e, 0x40, 0x02, 0x78, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04,
|
||||
0x20, 0x00, 0x10, 0x40, 0x02, 0x08, 0x00, 0x04, 0x3f, 0xff, 0xf0, 0x40, 0x02, 0x0f, 0xff, 0xfc,
|
||||
0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x1c,
|
||||
0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x18,
|
||||
0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x38,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xaa, 0x8a, 0xaa, 0x80
|
||||
};
|
||||
|
||||
const unsigned char bm_console [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff,
|
||||
0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x1f, 0xcf, 0xff, 0xff, 0xf3, 0xf8, 0xff,
|
||||
0xfe, 0x3f, 0x9f, 0xff, 0xff, 0xf9, 0xfc, 0x7f, 0xfc, 0x7f, 0x99, 0xe6, 0x61, 0x99, 0xfe, 0x3f,
|
||||
0xf8, 0xe7, 0x99, 0x26, 0x67, 0x99, 0xe7, 0x1f, 0xf9, 0xc7, 0x99, 0x26, 0x61, 0x99, 0xe3, 0x9f,
|
||||
0xf1, 0x8f, 0x98, 0x06, 0x67, 0x99, 0xf1, 0x8f, 0xf3, 0x9f, 0x9c, 0xce, 0x67, 0x99, 0xf9, 0xcf,
|
||||
0xf3, 0x99, 0x9f, 0xff, 0xff, 0xf9, 0x99, 0xcf, 0xf3, 0x99, 0x9f, 0xff, 0xff, 0xf9, 0x99, 0xcf,
|
||||
0xf3, 0x9f, 0x9f, 0xe3, 0x83, 0xf9, 0xf9, 0xcf, 0xf1, 0x8f, 0x9f, 0xc9, 0x93, 0xf9, 0xf1, 0x8f,
|
||||
0xf9, 0xc7, 0x9f, 0xc1, 0x83, 0xf9, 0xe3, 0x9f, 0xf8, 0xe7, 0x9f, 0xc9, 0x9f, 0xf9, 0xe7, 0x1f,
|
||||
0xfc, 0x7f, 0x9f, 0xc9, 0x9f, 0xf9, 0xfe, 0x3f, 0xfe, 0x3f, 0x9f, 0xff, 0xff, 0xf9, 0xfc, 0x7f,
|
||||
0xff, 0x1f, 0xcf, 0xff, 0xff, 0xf3, 0xf8, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff,
|
||||
0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff,
|
||||
0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xff, 0x7f, 0xdf, 0xf7, 0xfd, 0xff, 0x7f,
|
||||
0xfd, 0xff, 0x7f, 0xdf, 0xf7, 0xfd, 0xff, 0x7f, 0xfd, 0xff, 0x7f, 0xdf, 0xf7, 0xfd, 0xff, 0x7f,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x78, 0x1e, 0x07, 0x81, 0xe0, 0x78, 0x1f,
|
||||
0xef, 0xbb, 0xee, 0xfb, 0xbe, 0xef, 0xbb, 0xef, 0xe8, 0xda, 0xb6, 0x9d, 0xb3, 0x6d, 0xda, 0x37,
|
||||
0xef, 0xda, 0xf6, 0xb5, 0xad, 0x6c, 0xdb, 0xf7, 0xe8, 0x5a, 0x36, 0x95, 0xad, 0x6c, 0xda, 0x97,
|
||||
0xef, 0xdb, 0xf6, 0x85, 0xb3, 0x6c, 0xda, 0x97, 0xea, 0x5a, 0x36, 0xb5, 0xb3, 0x6c, 0xdb, 0xf7,
|
||||
0xef, 0xda, 0xf6, 0xa5, 0xad, 0x6f, 0xda, 0x57, 0xe8, 0x5a, 0xb6, 0x85, 0xad, 0x6c, 0xda, 0x57,
|
||||
0xef, 0xdb, 0xf6, 0xfd, 0xbf, 0x6f, 0xdb, 0xf7, 0xe0, 0x18, 0x06, 0x01, 0x80, 0x60, 0x18, 0x07,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xfe, 0x42, 0x7c, 0x60, 0xf0, 0x78, 0x3c, 0x7f, 0xfe, 0x4a, 0x7c, 0x64, 0xf2, 0x79, 0x3c, 0x7f,
|
||||
0xfe, 0x43, 0xfe, 0x64, 0xf2, 0x79, 0x3e, 0x7f, 0xfe, 0x4e, 0x7e, 0x64, 0x92, 0x49, 0x26, 0x7f,
|
||||
0xfe, 0x4e, 0x7e, 0x60, 0x90, 0x48, 0x26, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
const unsigned char bm_checks [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0x0c, 0x99, 0xc7, 0x0f,
|
||||
0xe6, 0x00, 0x7f, 0x93, 0x3c, 0x99, 0x93, 0x3f, 0xe6, 0x00, 0x7f, 0x93, 0x0e, 0x39, 0x9f, 0x0f,
|
||||
0xff, 0xff, 0xff, 0x93, 0x3e, 0x39, 0x93, 0x3f, 0xff, 0xff, 0xff, 0x87, 0x0f, 0x79, 0xc7, 0x0f,
|
||||
0xe6, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x39, 0x30, 0xe3, 0x93, 0x87,
|
||||
0xe6, 0x00, 0x7c, 0x99, 0x33, 0xc9, 0x87, 0x3f, 0xe6, 0x00, 0x7c, 0xf8, 0x30, 0xcf, 0x8f, 0x8f,
|
||||
0xff, 0xff, 0xfc, 0x99, 0x33, 0xc9, 0x87, 0xe7, 0xff, 0xff, 0xfe, 0x39, 0x30, 0xe3, 0x93, 0x0f,
|
||||
0xe6, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9c, 0x3c, 0x78, 0x70, 0xc3, 0x0f,
|
||||
0xe6, 0x0d, 0x3c, 0x99, 0x33, 0xe7, 0xcf, 0x27, 0xe6, 0x04, 0x7c, 0x38, 0x38, 0xf1, 0xc3, 0x27,
|
||||
0xff, 0xfe, 0xfc, 0xf9, 0x3e, 0x7c, 0xcf, 0x27, 0xff, 0xff, 0xfc, 0xf9, 0x30, 0xe1, 0xc3, 0x0f,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_hwfail [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0xe3, 0x87, 0x0e, 0x73, 0x8e, 0x1c, 0x3f,
|
||||
0xe4, 0xc9, 0x93, 0x26, 0x53, 0x26, 0x4c, 0xff, 0xe0, 0xc1, 0x87, 0x26, 0x53, 0x06, 0x1c, 0x3f,
|
||||
0xe4, 0xc9, 0x87, 0x26, 0x03, 0x26, 0x1c, 0xff, 0xe4, 0xc9, 0x93, 0x0f, 0x27, 0x26, 0x4c, 0x3f,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xe1, 0xc7, 0x33, 0xc9, 0x87, 0x0f, 0xf9, 0xff, 0xe7, 0x93, 0x33, 0xc9, 0x93, 0x3f, 0xf6, 0xff,
|
||||
0xe1, 0x83, 0x33, 0xc9, 0x87, 0x0f, 0xf6, 0xff, 0xe7, 0x93, 0x33, 0xc9, 0x87, 0x3f, 0xef, 0x7f,
|
||||
0xe7, 0x93, 0x30, 0xe3, 0x93, 0x0f, 0xe9, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xbf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xdf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xef,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xf7,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_conf_missing [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x33, 0x87, 0x0c, 0xcc, 0xe1, 0xff, 0xff,
|
||||
0xe2, 0x33, 0x3e, 0x7c, 0xc4, 0xcf, 0xff, 0xff, 0xe0, 0x33, 0x8f, 0x1c, 0xc0, 0xc9, 0xff, 0xff,
|
||||
0xe5, 0x33, 0xe7, 0xcc, 0xc8, 0xc9, 0xff, 0xff, 0xe7, 0x33, 0x0e, 0x1c, 0xcc, 0xe3, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xf1, 0xe3, 0x99, 0x86, 0x70, 0xff, 0xf6, 0xff,
|
||||
0xe4, 0xc9, 0x89, 0x9e, 0x67, 0xff, 0xf6, 0xff, 0xe7, 0xc9, 0x81, 0x86, 0x64, 0xff, 0xef, 0x7f,
|
||||
0xe4, 0xc9, 0x91, 0x9e, 0x64, 0xff, 0xe9, 0x7f, 0xf1, 0xe3, 0x99, 0x9e, 0x71, 0xff, 0xd9, 0xbf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xdf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xef,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xf7,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_no_radio [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc7, 0x0e, 0x71, 0xfc, 0xce, 0x38, 0x7f,
|
||||
0xc9, 0x93, 0x26, 0x64, 0xfc, 0x4c, 0x9c, 0xff, 0xc3, 0x83, 0x26, 0x64, 0xfc, 0x0c, 0x9c, 0xff,
|
||||
0xc3, 0x93, 0x26, 0x64, 0xfc, 0x8c, 0x9c, 0xff, 0xc9, 0x93, 0x0e, 0x71, 0xfc, 0xce, 0x3c, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x8e, 0x4c, 0xcc, 0x3f, 0xff, 0xfc, 0xff,
|
||||
0xcf, 0x26, 0x4c, 0x4c, 0x9f, 0xff, 0xfb, 0x7f, 0xc3, 0x26, 0x4c, 0x0c, 0x9f, 0xff, 0xfb, 0x7f,
|
||||
0xcf, 0x26, 0x4c, 0x8c, 0x9f, 0xff, 0xf7, 0xbf, 0xcf, 0x8f, 0x1c, 0xcc, 0x3f, 0xff, 0xf4, 0xbf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xdf,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xef,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xf7,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7c, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfb,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_hwok [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xf2, 0x71, 0xc3, 0x87, 0x39, 0xc7, 0x0e, 0x1f, 0xf2, 0x64, 0xc9, 0x93, 0x29, 0x93, 0x26, 0x7f,
|
||||
0xf0, 0x60, 0xc3, 0x93, 0x29, 0x83, 0x0e, 0x1f, 0xf2, 0x64, 0xc3, 0x93, 0x01, 0x93, 0x0e, 0x7f,
|
||||
0xf2, 0x64, 0xc9, 0x87, 0x93, 0x93, 0x26, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x33, 0x30, 0xff, 0x8e, 0x4f, 0xff,
|
||||
0xff, 0xf3, 0x13, 0x39, 0xff, 0x26, 0x1f, 0xff, 0xff, 0xf3, 0x03, 0x39, 0xff, 0x26, 0x3f, 0xff,
|
||||
0xff, 0xf3, 0x23, 0x39, 0xff, 0x26, 0x1f, 0xff, 0xff, 0xf3, 0x33, 0x39, 0xff, 0x8e, 0x4f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xc3, 0x0e, 0x67, 0xf8, 0x70, 0xe3, 0x87, 0x33, 0xe7, 0x27, 0x0f, 0xf9, 0x33, 0xc9, 0x93, 0x87,
|
||||
0xe7, 0x0f, 0x9f, 0xf8, 0x70, 0xc1, 0x93, 0xcf, 0xe7, 0x0f, 0x0f, 0xf8, 0x73, 0xc9, 0x93, 0xcf,
|
||||
0xe7, 0x26, 0x67, 0xf9, 0x30, 0xc9, 0x87, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_nfr [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xfc, 0x38, 0x66, 0x67, 0x1c, 0x3f, 0xff, 0xff, 0xfc, 0x99, 0xe6, 0x66, 0x4c, 0xff,
|
||||
0xff, 0x9f, 0xfc, 0x98, 0x70, 0xe6, 0x7c, 0x3f, 0xff, 0x6f, 0xfc, 0x99, 0xf0, 0xe6, 0x4c, 0xff,
|
||||
0xff, 0x6f, 0xfc, 0x38, 0x79, 0xe7, 0x1c, 0x3f, 0xfe, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xfe, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x9b, 0xe6, 0x71, 0xc3, 0xe1, 0xc7, 0x0f,
|
||||
0xfd, 0x9b, 0xe2, 0x64, 0xe7, 0xe7, 0x93, 0x27, 0xfb, 0x9d, 0xe0, 0x64, 0xe7, 0xe1, 0x93, 0x0f,
|
||||
0xfb, 0x9d, 0xe4, 0x64, 0xe7, 0xe7, 0x93, 0x0f, 0xf7, 0xfe, 0xe6, 0x71, 0xe7, 0xe7, 0xc7, 0x27,
|
||||
0xf7, 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x9f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xef, 0xff, 0x7f, 0xf8, 0x71, 0xcf, 0x0f, 0xff, 0xf0, 0x00, 0xff, 0xf3, 0xe4, 0xcf, 0x3f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xf8, 0xe0, 0xcf, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x64, 0xcf, 0x3f, 0xff,
|
||||
0xff, 0xff, 0xff, 0xf0, 0xe4, 0xc3, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_online [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x1c, 0x66, 0x61, 0x8c, 0x24, 0x90, 0x87,
|
||||
0xe6, 0x49, 0x22, 0x4f, 0x24, 0xe4, 0x93, 0x93, 0xe6, 0x18, 0x20, 0x63, 0x3c, 0x26, 0x30, 0x87,
|
||||
0xe6, 0x19, 0x24, 0x79, 0x24, 0xe6, 0x33, 0x87, 0xe6, 0x49, 0x26, 0x43, 0x8c, 0x27, 0x70, 0x93,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xe6, 0x73, 0xe7, 0x33, 0x87, 0xff,
|
||||
0xff, 0xe4, 0xe2, 0x73, 0xe7, 0x13, 0x9f, 0xff, 0xff, 0xe4, 0xe0, 0x73, 0xe7, 0x03, 0x87, 0xff,
|
||||
0xff, 0xe4, 0xe4, 0x73, 0xe7, 0x23, 0x9f, 0xff, 0xff, 0xf1, 0xe6, 0x70, 0xe7, 0x33, 0x87, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_pairing [] PROGMEM = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xf0, 0xe3, 0x98, 0x73, 0x33, 0x87, 0xff, 0xff, 0xf2, 0xc9, 0x99, 0x33, 0x13, 0x3f, 0xff,
|
||||
0xff, 0xf0, 0xc1, 0x98, 0x73, 0x03, 0x27, 0xff, 0xff, 0xf3, 0xc9, 0x98, 0x73, 0x23, 0x27, 0xff,
|
||||
0xff, 0xf3, 0xc9, 0x99, 0x33, 0x33, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned char bm_n_uh [] PROGMEM = {
|
||||
0x07, 0x27, 0x27, 0x27, 0x07, 0x8f, 0x8f, 0xcf, 0xcf, 0xcf, 0x07, 0xe7, 0x07, 0x3f, 0x07, 0x07,
|
||||
0xe7, 0xc7, 0xe7, 0x07, 0x27, 0x27, 0x07, 0xe7, 0xe7, 0x07, 0x3f, 0x07, 0xe7, 0x07, 0x07, 0x3f,
|
||||
0x07, 0x27, 0x07, 0x07, 0xc7, 0xcf, 0x9f, 0x1f, 0x07, 0x27, 0x07, 0x27, 0x07, 0x07, 0x27, 0x07,
|
||||
0xe7, 0xe7
|
||||
};
|
||||
|
||||
const unsigned char bm_plug [] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x7f, 0x80, 0x55, 0xfc, 0x00, 0xaa, 0xfc, 0x00, 0x00,
|
||||
0x7f, 0x80, 0x00, 0x1c, 0x00
|
||||
};
|
||||
|
||||
const unsigned char bm_hg_low [] PROGMEM = {
|
||||
0xf8, 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0xf8, 0xf8
|
||||
};
|
||||
|
||||
const unsigned char bm_hg_high [] PROGMEM = {
|
||||
0xf8, 0x88, 0xf8, 0x70, 0x20, 0x70, 0xf8, 0xf8, 0xf8
|
||||
};
|
||||
95
RNode_Firmware/Input.h
Normal file
95
RNode_Firmware/Input.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
|
||||
#define PIN_BUTTON pin_btn_usr1
|
||||
|
||||
#define PRESSED LOW
|
||||
#define RELEASED HIGH
|
||||
|
||||
#define EVENT_ALL 0x00
|
||||
#define EVENT_CLICKS 0x01
|
||||
#define EVENT_BUTTON_DOWN 0x11
|
||||
#define EVENT_BUTTON_UP 0x12
|
||||
#define EVENT_BUTTON_CLICK 0x13
|
||||
#define EVENT_BUTTON_DOUBLE_CLICK 0x14
|
||||
#define EVENT_BUTTON_TRIPLE_CLICK 0x15
|
||||
|
||||
int button_events = EVENT_CLICKS;
|
||||
int button_state = RELEASED;
|
||||
int debounce_state = button_state;
|
||||
unsigned long button_debounce_last = 0;
|
||||
unsigned long button_debounce_delay = 25;
|
||||
unsigned long button_down_last = 0;
|
||||
unsigned long button_up_last = 0;
|
||||
|
||||
// Forward declaration
|
||||
void button_event(uint8_t event, unsigned long duration);
|
||||
|
||||
void input_init() {
|
||||
pinMode(PIN_BUTTON, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
void input_get_all_events() {
|
||||
button_events = EVENT_ALL;
|
||||
}
|
||||
|
||||
void input_get_click_events() {
|
||||
button_events = EVENT_CLICKS;
|
||||
}
|
||||
|
||||
void input_read() {
|
||||
int button_reading = digitalRead(PIN_BUTTON);
|
||||
if (button_reading != debounce_state) {
|
||||
button_debounce_last = millis();
|
||||
debounce_state = button_reading;
|
||||
}
|
||||
|
||||
if ((millis() - button_debounce_last) > button_debounce_delay) {
|
||||
if (button_reading != button_state) {
|
||||
// State changed
|
||||
int previous_state = button_state;
|
||||
button_state = button_reading;
|
||||
|
||||
if (button_events == EVENT_ALL) {
|
||||
if (button_state == PRESSED) {
|
||||
button_event(EVENT_BUTTON_DOWN, 0);
|
||||
} else if (button_state == RELEASED) {
|
||||
button_event(EVENT_BUTTON_UP, 0);
|
||||
}
|
||||
} else if (button_events == EVENT_CLICKS) {
|
||||
if (previous_state == PRESSED && button_state == RELEASED) {
|
||||
button_up_last = millis();
|
||||
button_event(EVENT_BUTTON_CLICK, button_up_last-button_down_last);
|
||||
} else if (previous_state == RELEASED && button_state == PRESSED) {
|
||||
button_down_last = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool button_pressed() {
|
||||
if (button_state == PRESSED) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
301
RNode_Firmware/MD5.cpp
Normal file
301
RNode_Firmware/MD5.cpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
#include "MD5.h"
|
||||
|
||||
MD5::MD5()
|
||||
{
|
||||
//nothing
|
||||
return;
|
||||
}
|
||||
|
||||
char* MD5::make_digest(const unsigned char *digest, int len) /* {{{ */
|
||||
{
|
||||
char * md5str = (char*) malloc(sizeof(char)*(len*2+1));
|
||||
static const char hexits[17] = "0123456789abcdef";
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
md5str[i * 2] = hexits[digest[i] >> 4];
|
||||
md5str[(i * 2) + 1] = hexits[digest[i] & 0x0F];
|
||||
}
|
||||
md5str[len * 2] = '\0';
|
||||
return md5str;
|
||||
}
|
||||
|
||||
/*
|
||||
* The basic MD5 functions.
|
||||
*
|
||||
* E and G are optimized compared to their RFC 1321 definitions for
|
||||
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
|
||||
* implementation.
|
||||
* E() has been used instead of F() because F() is already defined in the Arduino core
|
||||
*/
|
||||
#define E(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
|
||||
/*
|
||||
* The MD5 transformation for all four rounds.
|
||||
*/
|
||||
#define STEP(f, a, b, c, d, x, t, s) \
|
||||
(a) += f((b), (c), (d)) + (x) + (t); \
|
||||
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
|
||||
(a) += (b);
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them
|
||||
* in a properly aligned word in host byte order.
|
||||
*
|
||||
* The check for little-endian architectures that tolerate unaligned
|
||||
* memory accesses is just an optimization. Nothing will break if it
|
||||
* doesn't work.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
|
||||
# define SET(n) \
|
||||
(*(MD5_u32plus *)&ptr[(n) * 4])
|
||||
# define GET(n) \
|
||||
SET(n)
|
||||
#else
|
||||
# define SET(n) \
|
||||
(ctx->block[(n)] = \
|
||||
(MD5_u32plus)ptr[(n) * 4] | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
|
||||
# define GET(n) \
|
||||
(ctx->block[(n)])
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update
|
||||
* the bit counters. There are no alignment requirements.
|
||||
*/
|
||||
const void *MD5::body(void *ctxBuf, const void *data, size_t size)
|
||||
{
|
||||
MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
|
||||
const unsigned char *ptr;
|
||||
MD5_u32plus a, b, c, d;
|
||||
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
|
||||
|
||||
ptr = (unsigned char*)data;
|
||||
|
||||
a = ctx->a;
|
||||
b = ctx->b;
|
||||
c = ctx->c;
|
||||
d = ctx->d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1
|
||||
* E() has been used instead of F() because F() is already defined in the Arduino core
|
||||
*/
|
||||
STEP(E, a, b, c, d, SET(0), 0xd76aa478, 7)
|
||||
STEP(E, d, a, b, c, SET(1), 0xe8c7b756, 12)
|
||||
STEP(E, c, d, a, b, SET(2), 0x242070db, 17)
|
||||
STEP(E, b, c, d, a, SET(3), 0xc1bdceee, 22)
|
||||
STEP(E, a, b, c, d, SET(4), 0xf57c0faf, 7)
|
||||
STEP(E, d, a, b, c, SET(5), 0x4787c62a, 12)
|
||||
STEP(E, c, d, a, b, SET(6), 0xa8304613, 17)
|
||||
STEP(E, b, c, d, a, SET(7), 0xfd469501, 22)
|
||||
STEP(E, a, b, c, d, SET(8), 0x698098d8, 7)
|
||||
STEP(E, d, a, b, c, SET(9), 0x8b44f7af, 12)
|
||||
STEP(E, c, d, a, b, SET(10), 0xffff5bb1, 17)
|
||||
STEP(E, b, c, d, a, SET(11), 0x895cd7be, 22)
|
||||
STEP(E, a, b, c, d, SET(12), 0x6b901122, 7)
|
||||
STEP(E, d, a, b, c, SET(13), 0xfd987193, 12)
|
||||
STEP(E, c, d, a, b, SET(14), 0xa679438e, 17)
|
||||
STEP(E, b, c, d, a, SET(15), 0x49b40821, 22)
|
||||
|
||||
/* Round 2 */
|
||||
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
|
||||
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
|
||||
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
|
||||
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
|
||||
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
|
||||
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
|
||||
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
|
||||
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
|
||||
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
|
||||
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
|
||||
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
|
||||
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
|
||||
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
|
||||
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
|
||||
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
|
||||
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
|
||||
|
||||
/* Round 3 */
|
||||
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
|
||||
STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
|
||||
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
|
||||
STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
|
||||
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
|
||||
STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
|
||||
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
|
||||
STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
|
||||
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
|
||||
STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
|
||||
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
|
||||
STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
|
||||
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
|
||||
STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
|
||||
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
|
||||
STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
|
||||
|
||||
/* Round 4 */
|
||||
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
|
||||
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
|
||||
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
|
||||
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
|
||||
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
|
||||
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
|
||||
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
|
||||
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
|
||||
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
|
||||
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
|
||||
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
|
||||
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
|
||||
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
|
||||
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
|
||||
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
|
||||
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx->a = a;
|
||||
ctx->b = b;
|
||||
ctx->c = c;
|
||||
ctx->d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void MD5::MD5Init(void *ctxBuf)
|
||||
{
|
||||
MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
|
||||
ctx->a = 0x67452301;
|
||||
ctx->b = 0xefcdab89;
|
||||
ctx->c = 0x98badcfe;
|
||||
ctx->d = 0x10325476;
|
||||
|
||||
ctx->lo = 0;
|
||||
ctx->hi = 0;
|
||||
|
||||
memset(ctx->block, 0, sizeof(ctx->block));
|
||||
memset(ctx->buffer, 0, sizeof(ctx->buffer));
|
||||
}
|
||||
|
||||
void MD5::MD5Update(void *ctxBuf, const void *data, size_t size)
|
||||
{
|
||||
MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
|
||||
MD5_u32plus saved_lo;
|
||||
MD5_u32plus used, free;
|
||||
|
||||
saved_lo = ctx->lo;
|
||||
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
|
||||
ctx->hi++;
|
||||
}
|
||||
ctx->hi += size >> 29;
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
free = 64 - used;
|
||||
|
||||
if (size < free) {
|
||||
memcpy(&ctx->buffer[used], data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ctx->buffer[used], data, free);
|
||||
data = (unsigned char *)data + free;
|
||||
size -= free;
|
||||
body(ctx, ctx->buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = body(ctx, data, size & ~(size_t)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
memcpy(ctx->buffer, data, size);
|
||||
}
|
||||
|
||||
void MD5::MD5Final(unsigned char *result, void *ctxBuf)
|
||||
{
|
||||
MD5_CTX *ctx = (MD5_CTX*)ctxBuf;
|
||||
MD5_u32plus used, free;
|
||||
|
||||
used = ctx->lo & 0x3f;
|
||||
|
||||
ctx->buffer[used++] = 0x80;
|
||||
|
||||
free = 64 - used;
|
||||
|
||||
if (free < 8) {
|
||||
memset(&ctx->buffer[used], 0, free);
|
||||
body(ctx, ctx->buffer, 64);
|
||||
used = 0;
|
||||
free = 64;
|
||||
}
|
||||
|
||||
memset(&ctx->buffer[used], 0, free - 8);
|
||||
|
||||
ctx->lo <<= 3;
|
||||
ctx->buffer[56] = ctx->lo;
|
||||
ctx->buffer[57] = ctx->lo >> 8;
|
||||
ctx->buffer[58] = ctx->lo >> 16;
|
||||
ctx->buffer[59] = ctx->lo >> 24;
|
||||
ctx->buffer[60] = ctx->hi;
|
||||
ctx->buffer[61] = ctx->hi >> 8;
|
||||
ctx->buffer[62] = ctx->hi >> 16;
|
||||
ctx->buffer[63] = ctx->hi >> 24;
|
||||
|
||||
body(ctx, ctx->buffer, 64);
|
||||
|
||||
result[0] = ctx->a;
|
||||
result[1] = ctx->a >> 8;
|
||||
result[2] = ctx->a >> 16;
|
||||
result[3] = ctx->a >> 24;
|
||||
result[4] = ctx->b;
|
||||
result[5] = ctx->b >> 8;
|
||||
result[6] = ctx->b >> 16;
|
||||
result[7] = ctx->b >> 24;
|
||||
result[8] = ctx->c;
|
||||
result[9] = ctx->c >> 8;
|
||||
result[10] = ctx->c >> 16;
|
||||
result[11] = ctx->c >> 24;
|
||||
result[12] = ctx->d;
|
||||
result[13] = ctx->d >> 8;
|
||||
result[14] = ctx->d >> 16;
|
||||
result[15] = ctx->d >> 24;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
unsigned char* MD5::make_hash(char *arg)
|
||||
{
|
||||
MD5_CTX context;
|
||||
unsigned char * hash = (unsigned char *) malloc(16);
|
||||
MD5Init(&context);
|
||||
MD5Update(&context, arg, strlen(arg));
|
||||
MD5Final(hash, &context);
|
||||
return hash;
|
||||
}
|
||||
unsigned char* MD5::make_hash(char *arg,size_t size)
|
||||
{
|
||||
MD5_CTX context;
|
||||
unsigned char * hash = (unsigned char *) malloc(16);
|
||||
MD5Init(&context);
|
||||
MD5Update(&context, arg, size);
|
||||
MD5Final(hash, &context);
|
||||
return hash;
|
||||
}
|
||||
52
RNode_Firmware/MD5.h
Normal file
52
RNode_Firmware/MD5.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef MD5_h
|
||||
#define MD5_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security,
|
||||
* Inc. MD5 Message-Digest Algorithm (RFC 1321).
|
||||
*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2001, and placed
|
||||
* in the public domain. There's absolutely no warranty.
|
||||
*
|
||||
* This differs from Colin Plumb's older public domain implementation in
|
||||
* that no 32-bit integer data type is required, there's no compile-time
|
||||
* endianness configuration, and the function prototypes match OpenSSL's.
|
||||
* The primary goals are portability and ease of use.
|
||||
*
|
||||
* This implementation is meant to be fast, but not as fast as possible.
|
||||
* Some known optimizations are not included to reduce source code size
|
||||
* and avoid compile-time configuration.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Updated by Scott MacVicar for arduino
|
||||
* <scott@macvicar.net>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef unsigned long MD5_u32plus;
|
||||
|
||||
typedef struct {
|
||||
MD5_u32plus lo, hi;
|
||||
MD5_u32plus a, b, c, d;
|
||||
unsigned char buffer[64];
|
||||
MD5_u32plus block[16];
|
||||
} MD5_CTX;
|
||||
|
||||
class MD5
|
||||
{
|
||||
public:
|
||||
MD5();
|
||||
static unsigned char* make_hash(char *arg);
|
||||
static unsigned char* make_hash(char *arg,size_t size);
|
||||
static char* make_digest(const unsigned char *digest, int len);
|
||||
static const void *body(void *ctxBuf, const void *data, size_t size);
|
||||
static void MD5Init(void *ctxBuf);
|
||||
static void MD5Final(unsigned char *result, void *ctxBuf);
|
||||
static void MD5Update(void *ctxBuf, const void *data, size_t size);
|
||||
};
|
||||
|
||||
#endif
|
||||
4
RNode_Firmware/Modem.h
Normal file
4
RNode_Firmware/Modem.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#define SX1276 0x01
|
||||
#define SX1278 0x02
|
||||
#define SX1262 0x03
|
||||
#define SX1280 0x04
|
||||
650
RNode_Firmware/Power.h
Normal file
650
RNode_Firmware/Power.h
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#define PMU_TEMP_MIN -30
|
||||
#define PMU_TEMP_MAX 90
|
||||
#define PMU_TEMP_OFFSET 120
|
||||
bool pmu_temp_sensor_ready = false;
|
||||
float pmu_temperature = PMU_TEMP_MIN-1;
|
||||
|
||||
#if BOARD_MODEL == BOARD_TBEAM || BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
#include <XPowersLib.h>
|
||||
XPowersLibInterface* PMU = NULL;
|
||||
|
||||
#ifndef PMU_WIRE_PORT
|
||||
#if BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
#define PMU_WIRE_PORT Wire1
|
||||
#else
|
||||
#define PMU_WIRE_PORT Wire
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define BAT_V_MIN 3.15
|
||||
#define BAT_V_MAX 4.14
|
||||
|
||||
void disablePeripherals() {
|
||||
if (PMU) {
|
||||
// GNSS RTC PowerVDD
|
||||
PMU->enablePowerOutput(XPOWERS_VBACKUP);
|
||||
|
||||
// LoRa VDD
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO2);
|
||||
|
||||
// GNSS VDD
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO3);
|
||||
}
|
||||
}
|
||||
|
||||
bool pmuInterrupt;
|
||||
void setPmuFlag()
|
||||
{
|
||||
pmuInterrupt = true;
|
||||
}
|
||||
#elif BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1
|
||||
#define BAT_V_MIN 3.15
|
||||
#define BAT_V_MAX 4.3
|
||||
#define BAT_V_CHG 4.48
|
||||
#define BAT_V_FLOAT 4.33
|
||||
#define BAT_SAMPLES 5
|
||||
const uint8_t pin_vbat = 35;
|
||||
float bat_p_samples[BAT_SAMPLES];
|
||||
float bat_v_samples[BAT_SAMPLES];
|
||||
uint8_t bat_samples_count = 0;
|
||||
int bat_discharging_samples = 0;
|
||||
int bat_charging_samples = 0;
|
||||
int bat_charged_samples = 0;
|
||||
bool bat_voltage_dropping = false;
|
||||
float bat_delay_v = 0;
|
||||
float bat_state_change_v = 0;
|
||||
#elif BOARD_MODEL == BOARD_T3S3
|
||||
#define BAT_V_MIN 3.15
|
||||
#define BAT_V_MAX 4.217
|
||||
#define BAT_V_CHG 4.48
|
||||
#define BAT_V_FLOAT 4.33
|
||||
#define BAT_SAMPLES 5
|
||||
const uint8_t pin_vbat = 1;
|
||||
float bat_p_samples[BAT_SAMPLES];
|
||||
float bat_v_samples[BAT_SAMPLES];
|
||||
uint8_t bat_samples_count = 0;
|
||||
int bat_discharging_samples = 0;
|
||||
int bat_charging_samples = 0;
|
||||
int bat_charged_samples = 0;
|
||||
bool bat_voltage_dropping = false;
|
||||
float bat_delay_v = 0;
|
||||
float bat_state_change_v = 0;
|
||||
#elif BOARD_MODEL == BOARD_TDECK
|
||||
#define BAT_V_MIN 3.15
|
||||
#define BAT_V_MAX 4.3
|
||||
#define BAT_V_CHG 4.48
|
||||
#define BAT_V_FLOAT 4.33
|
||||
#define BAT_SAMPLES 5
|
||||
const uint8_t pin_vbat = 4;
|
||||
float bat_p_samples[BAT_SAMPLES];
|
||||
float bat_v_samples[BAT_SAMPLES];
|
||||
uint8_t bat_samples_count = 0;
|
||||
int bat_discharging_samples = 0;
|
||||
int bat_charging_samples = 0;
|
||||
int bat_charged_samples = 0;
|
||||
bool bat_voltage_dropping = false;
|
||||
float bat_delay_v = 0;
|
||||
float bat_state_change_v = 0;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V3
|
||||
// Unless we implement some real voodoo
|
||||
// on these boards, we can't say with
|
||||
// any certainty whether we are actually
|
||||
// charging and have reached a charge
|
||||
// complete state. The *only* data point
|
||||
// we have to go from is the bus voltage.
|
||||
// The BAT_V_CHG and BAT_V_FLOAT values
|
||||
// are set high here to avoid the display
|
||||
// indication confusingly flapping
|
||||
// between charge completed, charging and
|
||||
// discharging states.
|
||||
// Update: Vodoo implemented. Hopefully
|
||||
// it will work accross different boards.
|
||||
#define BAT_V_MIN 3.05
|
||||
#define BAT_V_MAX 4.0
|
||||
#define BAT_V_CHG 4.48
|
||||
#define BAT_V_FLOAT 4.33
|
||||
#define BAT_SAMPLES 7
|
||||
const uint8_t pin_vbat = 1;
|
||||
const uint8_t pin_ctrl = 37;
|
||||
float bat_p_samples[BAT_SAMPLES];
|
||||
float bat_v_samples[BAT_SAMPLES];
|
||||
uint8_t bat_samples_count = 0;
|
||||
int bat_discharging_samples = 0;
|
||||
int bat_charging_samples = 0;
|
||||
int bat_charged_samples = 0;
|
||||
bool bat_voltage_dropping = false;
|
||||
float bat_delay_v = 0;
|
||||
float bat_state_change_v = 0;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V4
|
||||
#define BAT_V_MIN 3.05
|
||||
#define BAT_V_MAX 4.0
|
||||
#define BAT_V_CHG 4.48
|
||||
#define BAT_V_FLOAT 4.33
|
||||
#define BAT_SAMPLES 7
|
||||
const uint8_t pin_vbat = 1;
|
||||
const uint8_t pin_ctrl = 37;
|
||||
float bat_p_samples[BAT_SAMPLES];
|
||||
float bat_v_samples[BAT_SAMPLES];
|
||||
uint8_t bat_samples_count = 0;
|
||||
int bat_discharging_samples = 0;
|
||||
int bat_charging_samples = 0;
|
||||
int bat_charged_samples = 0;
|
||||
bool bat_voltage_dropping = false;
|
||||
float bat_delay_v = 0;
|
||||
float bat_state_change_v = 0;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC_T114
|
||||
#define BAT_V_MIN 3.15
|
||||
#define BAT_V_MAX 4.165
|
||||
#define BAT_V_CHG 4.48
|
||||
#define BAT_V_FLOAT 4.33
|
||||
#define BAT_SAMPLES 7
|
||||
const uint8_t pin_vbat = 4;
|
||||
const uint8_t pin_ctrl = 6;
|
||||
float bat_p_samples[BAT_SAMPLES];
|
||||
float bat_v_samples[BAT_SAMPLES];
|
||||
uint8_t bat_samples_count = 0;
|
||||
int bat_discharging_samples = 0;
|
||||
int bat_charging_samples = 0;
|
||||
int bat_charged_samples = 0;
|
||||
bool bat_voltage_dropping = false;
|
||||
float bat_delay_v = 0;
|
||||
float bat_state_change_v = 0;
|
||||
#elif BOARD_MODEL == BOARD_TECHO
|
||||
#define BAT_V_MIN 3.15
|
||||
#define BAT_V_MAX 4.16
|
||||
#define BAT_V_CHG 4.48
|
||||
#define BAT_V_FLOAT 4.33
|
||||
#define BAT_SAMPLES 7
|
||||
const uint8_t pin_vbat = 4;
|
||||
float bat_p_samples[BAT_SAMPLES];
|
||||
float bat_v_samples[BAT_SAMPLES];
|
||||
uint8_t bat_samples_count = 0;
|
||||
int bat_discharging_samples = 0;
|
||||
int bat_charging_samples = 0;
|
||||
int bat_charged_samples = 0;
|
||||
bool bat_voltage_dropping = false;
|
||||
float bat_delay_v = 0;
|
||||
float bat_state_change_v = 0;
|
||||
#endif
|
||||
|
||||
uint32_t last_pmu_update = 0;
|
||||
uint8_t pmu_target_pps = 1;
|
||||
int pmu_update_interval = 1000/pmu_target_pps;
|
||||
uint8_t pmu_charged_ascertain = 0;
|
||||
uint8_t pmu_rc = 0;
|
||||
uint8_t pmu_sc = 0;
|
||||
float bat_delay_diff = 0;
|
||||
bool bat_diff_positive = false;
|
||||
#define PMU_R_INTERVAL 5
|
||||
#define PMU_SCV_RESET_INTERVAL 3
|
||||
void kiss_indicate_battery();
|
||||
void kiss_indicate_temperature();
|
||||
|
||||
void measure_temperature() {
|
||||
#if PLATFORM == PLATFORM_ESP32
|
||||
if (pmu_temp_sensor_ready) { pmu_temperature = temperatureRead(); } else { pmu_temperature = PMU_TEMP_MIN-1; }
|
||||
#endif
|
||||
}
|
||||
|
||||
void measure_battery() {
|
||||
#if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC_T114 || BOARD_MODEL == BOARD_TECHO
|
||||
battery_installed = true;
|
||||
#if BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4
|
||||
battery_indeterminate = false;
|
||||
#else
|
||||
battery_indeterminate = true;
|
||||
#endif
|
||||
|
||||
#if BOARD_MODEL == BOARD_HELTEC32_V3
|
||||
float battery_measurement = (float)(analogRead(pin_vbat)) * 0.0041;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V4
|
||||
float battery_measurement = (float)(analogRead(pin_vbat)) * 0.00418;
|
||||
#elif BOARD_MODEL == BOARD_T3S3
|
||||
float battery_measurement = (float)(analogRead(pin_vbat)) / 4095.0*6.7828;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC_T114
|
||||
float battery_measurement = (float)(analogRead(pin_vbat)) * 0.017165;
|
||||
#elif BOARD_MODEL == BOARD_TECHO
|
||||
float battery_measurement = (float)(analogRead(pin_vbat)) * 0.007067;
|
||||
#else
|
||||
float battery_measurement = (float)(analogRead(pin_vbat)) / 4095.0*7.26;
|
||||
#endif
|
||||
|
||||
bat_v_samples[bat_samples_count%BAT_SAMPLES] = battery_measurement;
|
||||
bat_p_samples[bat_samples_count%BAT_SAMPLES] = ((battery_voltage-BAT_V_MIN) / (BAT_V_MAX-BAT_V_MIN))*100.0;
|
||||
|
||||
bat_samples_count++;
|
||||
if (!battery_ready && bat_samples_count >= BAT_SAMPLES) {
|
||||
battery_ready = true;
|
||||
}
|
||||
|
||||
if (battery_ready) {
|
||||
|
||||
battery_percent = 0;
|
||||
for (uint8_t bi = 0; bi < BAT_SAMPLES; bi++) {
|
||||
battery_percent += bat_p_samples[bi];
|
||||
}
|
||||
battery_percent = battery_percent/BAT_SAMPLES;
|
||||
|
||||
battery_voltage = 0;
|
||||
for (uint8_t bi = 0; bi < BAT_SAMPLES; bi++) {
|
||||
battery_voltage += bat_v_samples[bi];
|
||||
}
|
||||
battery_voltage = battery_voltage/BAT_SAMPLES;
|
||||
|
||||
if (bat_delay_v == 0) bat_delay_v = battery_voltage;
|
||||
if (bat_state_change_v == 0) bat_state_change_v = battery_voltage;
|
||||
if (battery_percent > 100.0) battery_percent = 100.0;
|
||||
if (battery_percent < 0.0) battery_percent = 0.0;
|
||||
|
||||
if (bat_samples_count%BAT_SAMPLES == 0) {
|
||||
pmu_sc++;
|
||||
bat_delay_diff = battery_voltage-bat_state_change_v;
|
||||
|
||||
if (battery_voltage < bat_delay_v && battery_voltage < BAT_V_FLOAT) {
|
||||
if (bat_voltage_dropping == false) {
|
||||
if (bat_delay_diff < -0.008) {
|
||||
bat_voltage_dropping = true;
|
||||
bat_state_change_v = battery_voltage;
|
||||
}
|
||||
} else {
|
||||
if (pmu_sc%PMU_SCV_RESET_INTERVAL == 0) { bat_state_change_v = battery_voltage; }
|
||||
}
|
||||
} else {
|
||||
if (bat_voltage_dropping == true) {
|
||||
if (bat_delay_diff > 0.01) {
|
||||
bat_voltage_dropping = false;
|
||||
bat_state_change_v = battery_voltage;
|
||||
}
|
||||
}
|
||||
}
|
||||
bat_samples_count = 0;
|
||||
bat_delay_v = battery_voltage;
|
||||
}
|
||||
|
||||
if (bat_voltage_dropping && battery_voltage < BAT_V_FLOAT) {
|
||||
// if (battery_state != BATTERY_STATE_DISCHARGING) { SerialBT.printf("STATE CHANGE to DISCHARGING at delta=%.3fv. State change v is now %.3fv.\n", bat_delay_diff, bat_state_change_v); }
|
||||
battery_state = BATTERY_STATE_DISCHARGING;
|
||||
pmu_charged_ascertain = 0;
|
||||
} else {
|
||||
if (pmu_charged_ascertain < 8) { pmu_charged_ascertain++; }
|
||||
else {
|
||||
if (battery_percent < 100.0) {
|
||||
// if (battery_state != BATTERY_STATE_CHARGING) { SerialBT.printf("STATE CHANGE to CHARGING at delta=%.3fv. State change v is now %.3fv.\n", bat_delay_diff, bat_state_change_v); }
|
||||
battery_state = BATTERY_STATE_CHARGING;
|
||||
} else {
|
||||
// if (battery_state != BATTERY_STATE_CHARGED) { SerialBT.printf("STATE CHANGE to CHARGED at delta=%.3fv. State change v is now %.3fv.\n", bat_delay_diff, bat_state_change_v); }
|
||||
battery_state = BATTERY_STATE_CHARGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if MCU_VARIANT == MCU_NRF52
|
||||
if (bt_state != BT_STATE_OFF) { blebas.write(battery_percent); }
|
||||
#endif
|
||||
|
||||
// if (bt_state == BT_STATE_CONNECTED) {
|
||||
// SerialBT.printf("\nBus voltage %.3fv. Unfiltered %.3fv. Diff %.3f", battery_voltage, bat_v_samples[BAT_SAMPLES-1], bat_delay_diff);
|
||||
// if (bat_voltage_dropping) { SerialBT.printf("\n Voltage is dropping. Percentage %.1f%%.", battery_percent); }
|
||||
// else { SerialBT.printf("\n Voltage is not dropping. Percentage %.1f%%.", battery_percent); }
|
||||
// if (battery_state == BATTERY_STATE_DISCHARGING) { SerialBT.printf("\n Battery discharging. delay_v %.3fv\nState change at %.3fv", bat_delay_v, bat_state_change_v); }
|
||||
// if (battery_state == BATTERY_STATE_CHARGING) { SerialBT.printf("\n Battery charging. delay_v %.3fv\nState change at %.3fv", bat_delay_v, bat_state_change_v); }
|
||||
// if (battery_state == BATTERY_STATE_CHARGED) { SerialBT.print("\n Battery is charged."); }
|
||||
// SerialBT.print("\n");
|
||||
// }
|
||||
}
|
||||
|
||||
#elif BOARD_MODEL == BOARD_TBEAM || BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
if (PMU) {
|
||||
float discharge_current = 0;
|
||||
float charge_current = 0;
|
||||
float ext_voltage = 0;
|
||||
float ext_current = 0;
|
||||
if (PMU->getChipModel() == XPOWERS_AXP192) {
|
||||
discharge_current = ((XPowersAXP192*)PMU)->getBattDischargeCurrent();
|
||||
charge_current = ((XPowersAXP192*)PMU)->getBatteryChargeCurrent();
|
||||
battery_voltage = PMU->getBattVoltage()/1000.0;
|
||||
// battery_percent = PMU->getBattPercentage()*1.0;
|
||||
battery_installed = PMU->isBatteryConnect();
|
||||
external_power = PMU->isVbusIn();
|
||||
ext_voltage = PMU->getVbusVoltage()/1000.0;
|
||||
ext_current = ((XPowersAXP192*)PMU)->getVbusCurrent();
|
||||
}
|
||||
else if (PMU->getChipModel() == XPOWERS_AXP2101) {
|
||||
battery_voltage = PMU->getBattVoltage()/1000.0;
|
||||
// battery_percent = PMU->getBattPercentage()*1.0;
|
||||
battery_installed = PMU->isBatteryConnect();
|
||||
external_power = PMU->isVbusIn();
|
||||
ext_voltage = PMU->getVbusVoltage()/1000.0;
|
||||
}
|
||||
|
||||
if (battery_installed) {
|
||||
if (PMU->isCharging()) {
|
||||
battery_state = BATTERY_STATE_CHARGING;
|
||||
battery_percent = ((battery_voltage-BAT_V_MIN) / (BAT_V_MAX-BAT_V_MIN))*100.0;
|
||||
} else {
|
||||
if (PMU->isDischarge()) {
|
||||
battery_state = BATTERY_STATE_DISCHARGING;
|
||||
battery_percent = ((battery_voltage-BAT_V_MIN) / (BAT_V_MAX-BAT_V_MIN))*100.0;
|
||||
} else {
|
||||
battery_state = BATTERY_STATE_CHARGED;
|
||||
battery_percent = 100.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
battery_state = BATTERY_STATE_UNKNOWN;
|
||||
battery_percent = 0.0;
|
||||
battery_voltage = 0.0;
|
||||
}
|
||||
|
||||
if (battery_percent > 100.0) battery_percent = 100.0;
|
||||
if (battery_percent < 0.0) battery_percent = 0.0;
|
||||
|
||||
float charge_watts = battery_voltage*(charge_current/1000.0);
|
||||
float discharge_watts = battery_voltage*(discharge_current/1000.0);
|
||||
float ext_watts = ext_voltage*(ext_current/1000.0);
|
||||
|
||||
battery_ready = true;
|
||||
|
||||
// if (bt_state == BT_STATE_CONNECTED) {
|
||||
// if (battery_installed) {
|
||||
// if (external_power) {
|
||||
// SerialBT.printf("External power connected, drawing %.2fw, %.1fmA at %.1fV\n", ext_watts, ext_current, ext_voltage);
|
||||
// } else {
|
||||
// SerialBT.println("Running on battery");
|
||||
// }
|
||||
// SerialBT.printf("Battery percentage %.1f%%\n", battery_percent);
|
||||
// SerialBT.printf("Battery voltage %.2fv\n", battery_voltage);
|
||||
// // SerialBT.printf("Temperature %.1f%\n", auxillary_temperature);
|
||||
|
||||
// if (battery_state == BATTERY_STATE_CHARGING) {
|
||||
// SerialBT.printf("Charging with %.2fw, %.1fmA at %.1fV\n", charge_watts, charge_current, battery_voltage);
|
||||
// } else if (battery_state == BATTERY_STATE_DISCHARGING) {
|
||||
// SerialBT.printf("Discharging at %.2fw, %.1fmA at %.1fV\n", discharge_watts, discharge_current, battery_voltage);
|
||||
// } else if (battery_state == BATTERY_STATE_CHARGED) {
|
||||
// SerialBT.printf("Battery charged\n");
|
||||
// }
|
||||
// } else {
|
||||
// SerialBT.println("No battery installed");
|
||||
// }
|
||||
// SerialBT.println("");
|
||||
// }
|
||||
}
|
||||
else {
|
||||
battery_ready = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (battery_ready) {
|
||||
pmu_rc++;
|
||||
if (pmu_rc%PMU_R_INTERVAL == 0) {
|
||||
kiss_indicate_battery();
|
||||
if (pmu_temp_sensor_ready) { kiss_indicate_temperature(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_pmu() {
|
||||
if (millis()-last_pmu_update >= pmu_update_interval) {
|
||||
measure_battery();
|
||||
measure_temperature();
|
||||
last_pmu_update = millis();
|
||||
}
|
||||
}
|
||||
|
||||
bool init_pmu() {
|
||||
#if IS_ESP32S3
|
||||
pmu_temp_sensor_ready = true;
|
||||
#endif
|
||||
|
||||
#if BOARD_MODEL == BOARD_RNODE_NG_21 || BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_TECHO
|
||||
pinMode(pin_vbat, INPUT);
|
||||
return true;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V3
|
||||
// there are three version of V3: V3, V3.1, and V3.2
|
||||
// V3 and V3.1 have a pull up on pin_ctrl and are active low
|
||||
// V3.2 has a transistor and active high
|
||||
// put the pin input mode and read it. if it's high, we have V3 or V3.1
|
||||
// other wise, it's a V3.2
|
||||
uint16_t pin_ctrl_value;
|
||||
uint8_t pin_ctrl_active = LOW;
|
||||
pinMode(pin_ctrl, INPUT);
|
||||
pin_ctrl_value = digitalRead(pin_ctrl);
|
||||
if(pin_ctrl_value == HIGH) {
|
||||
// We have either a V3 or V3.1
|
||||
pin_ctrl_active = LOW;
|
||||
}
|
||||
else {
|
||||
// We have a V3.2
|
||||
pin_ctrl_active = HIGH;
|
||||
}
|
||||
pinMode(pin_ctrl,OUTPUT);
|
||||
digitalWrite(pin_ctrl, pin_ctrl_active);
|
||||
return true;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V4
|
||||
pinMode(pin_ctrl,OUTPUT);
|
||||
digitalWrite(pin_ctrl, HIGH);
|
||||
return true;
|
||||
#elif BOARD_MODEL == BOARD_HELTEC_T114
|
||||
pinMode(pin_ctrl,OUTPUT);
|
||||
digitalWrite(pin_ctrl, HIGH);
|
||||
return true;
|
||||
#elif BOARD_MODEL == BOARD_TBEAM
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
|
||||
if (!PMU) {
|
||||
PMU = new XPowersAXP2101(PMU_WIRE_PORT);
|
||||
if (!PMU->init()) {
|
||||
delete PMU;
|
||||
PMU = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PMU) {
|
||||
PMU = new XPowersAXP192(PMU_WIRE_PORT);
|
||||
if (!PMU->init()) {
|
||||
delete PMU;
|
||||
PMU = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PMU) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure charging indicator
|
||||
PMU->setChargingLedMode(XPOWERS_CHG_LED_OFF);
|
||||
|
||||
pinMode(PMU_IRQ, INPUT_PULLUP);
|
||||
attachInterrupt(PMU_IRQ, setPmuFlag, FALLING);
|
||||
|
||||
if (PMU->getChipModel() == XPOWERS_AXP192) {
|
||||
|
||||
// Turn off unused power sources to save power
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC1);
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC2);
|
||||
PMU->disablePowerOutput(XPOWERS_LDO2);
|
||||
PMU->disablePowerOutput(XPOWERS_LDO3);
|
||||
|
||||
// Set the power of LoRa and GPS module to 3.3V
|
||||
// LoRa
|
||||
PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300);
|
||||
// GPS
|
||||
PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300);
|
||||
// OLED
|
||||
PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
|
||||
|
||||
// Turn on LoRa
|
||||
PMU->enablePowerOutput(XPOWERS_LDO2);
|
||||
|
||||
// Turn on GPS
|
||||
//PMU->enablePowerOutput(XPOWERS_LDO3);
|
||||
|
||||
// protected oled power source
|
||||
PMU->setProtectedChannel(XPOWERS_DCDC1);
|
||||
// protected esp32 power source
|
||||
PMU->setProtectedChannel(XPOWERS_DCDC3);
|
||||
// enable oled power
|
||||
PMU->enablePowerOutput(XPOWERS_DCDC1);
|
||||
|
||||
PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ);
|
||||
|
||||
PMU->enableIRQ(XPOWERS_AXP192_VBUS_REMOVE_IRQ |
|
||||
XPOWERS_AXP192_VBUS_INSERT_IRQ |
|
||||
XPOWERS_AXP192_BAT_CHG_DONE_IRQ |
|
||||
XPOWERS_AXP192_BAT_CHG_START_IRQ |
|
||||
XPOWERS_AXP192_BAT_REMOVE_IRQ |
|
||||
XPOWERS_AXP192_BAT_INSERT_IRQ |
|
||||
XPOWERS_AXP192_PKEY_SHORT_IRQ
|
||||
);
|
||||
|
||||
}
|
||||
else if (PMU->getChipModel() == XPOWERS_AXP2101) {
|
||||
|
||||
// Turn off unused power sources to save power
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC2);
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC3);
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC4);
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC5);
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO1);
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO2);
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO3);
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO4);
|
||||
PMU->disablePowerOutput(XPOWERS_BLDO1);
|
||||
PMU->disablePowerOutput(XPOWERS_BLDO2);
|
||||
PMU->disablePowerOutput(XPOWERS_DLDO1);
|
||||
PMU->disablePowerOutput(XPOWERS_DLDO2);
|
||||
PMU->disablePowerOutput(XPOWERS_VBACKUP);
|
||||
|
||||
// Set the power of LoRa and GPS module to 3.3V
|
||||
// LoRa
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
|
||||
// GPS
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
|
||||
PMU->setPowerChannelVoltage(XPOWERS_VBACKUP, 3300);
|
||||
|
||||
// ESP32 VDD
|
||||
// ! No need to set, automatically open , Don't close it
|
||||
// PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
|
||||
// PMU->setProtectedChannel(XPOWERS_DCDC1);
|
||||
PMU->setProtectedChannel(XPOWERS_DCDC1);
|
||||
|
||||
// LoRa VDD
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
|
||||
// GNSS VDD
|
||||
//PMU->enablePowerOutput(XPOWERS_ALDO3);
|
||||
|
||||
// GNSS RTC PowerVDD
|
||||
//PMU->enablePowerOutput(XPOWERS_VBACKUP);
|
||||
}
|
||||
|
||||
PMU->enableSystemVoltageMeasure();
|
||||
PMU->enableVbusVoltageMeasure();
|
||||
PMU->enableBattVoltageMeasure();
|
||||
// It is necessary to disable the detection function of the TS pin on the board
|
||||
// without the battery temperature detection function, otherwise it will cause abnormal charging
|
||||
PMU->disableTSPinMeasure();
|
||||
|
||||
// Set the time of pressing the button to turn off
|
||||
PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S);
|
||||
|
||||
return true;
|
||||
#elif BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
Wire1.begin(I2C_SDA, I2C_SCL);
|
||||
|
||||
if (!PMU) {
|
||||
PMU = new XPowersAXP2101(PMU_WIRE_PORT);
|
||||
if (!PMU->init()) {
|
||||
delete PMU;
|
||||
PMU = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PMU) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnss module power channel
|
||||
* The default ALDO4 is off, you need to turn on the GNSS power first, otherwise it will be invalid during
|
||||
* initialization
|
||||
*/
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO4);
|
||||
|
||||
// lora radio power channel
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO3);
|
||||
|
||||
// m.2 interface
|
||||
PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_DCDC3);
|
||||
|
||||
/**
|
||||
* ALDO2 cannot be turned off.
|
||||
* It is a necessary condition for sensor communication.
|
||||
* It must be turned on to properly access the sensor and screen
|
||||
* It is also responsible for the power supply of PCF8563
|
||||
*/
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
|
||||
// 6-axis , magnetometer ,bme280 , oled screen power channel
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO1);
|
||||
|
||||
// sdcard power channle
|
||||
PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_BLDO1);
|
||||
|
||||
// PMU->setPowerChannelVoltage(XPOWERS_DCDC4, 3300);
|
||||
// PMU->enablePowerOutput(XPOWERS_DCDC4);
|
||||
|
||||
// not use channel
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC2); // not elicited
|
||||
PMU->disablePowerOutput(XPOWERS_DCDC5); // not elicited
|
||||
PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist
|
||||
PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist
|
||||
PMU->disablePowerOutput(XPOWERS_VBACKUP);
|
||||
|
||||
// Configure charging
|
||||
PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2);
|
||||
PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
|
||||
// TODO: Reset
|
||||
PMU->setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG);
|
||||
|
||||
// Set the time of pressing the button to turn off
|
||||
PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S);
|
||||
PMU->setPowerKeyPressOnTime(XPOWERS_POWERON_128MS);
|
||||
|
||||
// disable all axp chip interrupt
|
||||
PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
||||
PMU->clearIrqStatus();
|
||||
|
||||
// It is necessary to disable the detection function of the TS pin on the board
|
||||
// without the battery temperature detection function, otherwise it will cause abnormal charging
|
||||
PMU->disableTSPinMeasure();
|
||||
PMU->enableVbusVoltageMeasure();
|
||||
PMU->enableBattVoltageMeasure();
|
||||
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
1822
RNode_Firmware/RNode_Firmware.ino
Normal file
1822
RNode_Firmware/RNode_Firmware.ino
Normal file
File diff suppressed because it is too large
Load diff
55
RNode_Firmware/ROM.h
Normal file
55
RNode_Firmware/ROM.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (C) 2024, Mark Qvist
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef ROM_H
|
||||
#define ROM_H
|
||||
#define CHECKSUMMED_SIZE 0x0B
|
||||
|
||||
// ROM address map ///////////////
|
||||
#define ADDR_PRODUCT 0x00
|
||||
#define ADDR_MODEL 0x01
|
||||
#define ADDR_HW_REV 0x02
|
||||
#define ADDR_SERIAL 0x03
|
||||
#define ADDR_MADE 0x07
|
||||
#define ADDR_CHKSUM 0x0B
|
||||
#define ADDR_SIGNATURE 0x1B
|
||||
#define ADDR_INFO_LOCK 0x9B
|
||||
|
||||
#define ADDR_CONF_SF 0x9C
|
||||
#define ADDR_CONF_CR 0x9D
|
||||
#define ADDR_CONF_TXP 0x9E
|
||||
#define ADDR_CONF_BW 0x9F
|
||||
#define ADDR_CONF_FREQ 0xA3
|
||||
#define ADDR_CONF_OK 0xA7
|
||||
|
||||
#define ADDR_CONF_BT 0xB0
|
||||
#define ADDR_CONF_DSET 0xB1
|
||||
#define ADDR_CONF_DINT 0xB2
|
||||
#define ADDR_CONF_DADR 0xB3
|
||||
#define ADDR_CONF_DBLK 0xB4
|
||||
#define ADDR_CONF_DROT 0xB8
|
||||
#define ADDR_CONF_PSET 0xB5
|
||||
#define ADDR_CONF_PINT 0xB6
|
||||
#define ADDR_CONF_BSET 0xB7
|
||||
#define ADDR_CONF_DIA 0xB9
|
||||
|
||||
#define INFO_LOCK_BYTE 0x73
|
||||
#define CONF_OK_BYTE 0x73
|
||||
#define BT_ENABLE_BYTE 0x73
|
||||
|
||||
#define EEPROM_RESERVED 200
|
||||
//////////////////////////////////
|
||||
|
||||
#endif
|
||||
440
RNode_Firmware/ST7789.h
Normal file
440
RNode_Firmware/ST7789.h
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
|
||||
* Copyright (c) 2018 by Fabrice Weinberg
|
||||
* Copyright (c) 2024 by Heltec AutoMation
|
||||
* 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 ST7789Spi_h
|
||||
#define ST7789Spi_h
|
||||
|
||||
#include "OLEDDisplay.h"
|
||||
#include <SPI.h>
|
||||
|
||||
|
||||
#define ST_CMD_DELAY 0x80 // special signifier for command lists
|
||||
|
||||
#define ST77XX_NOP 0x00
|
||||
#define ST77XX_SWRESET 0x01
|
||||
#define ST77XX_RDDID 0x04
|
||||
#define ST77XX_RDDST 0x09
|
||||
|
||||
#define ST77XX_SLPIN 0x10
|
||||
#define ST77XX_SLPOUT 0x11
|
||||
#define ST77XX_PTLON 0x12
|
||||
#define ST77XX_NORON 0x13
|
||||
|
||||
#define ST77XX_INVOFF 0x20
|
||||
#define ST77XX_INVON 0x21
|
||||
#define ST77XX_DISPOFF 0x28
|
||||
#define ST77XX_DISPON 0x29
|
||||
#define ST77XX_CASET 0x2A
|
||||
#define ST77XX_RASET 0x2B
|
||||
#define ST77XX_RAMWR 0x2C
|
||||
#define ST77XX_RAMRD 0x2E
|
||||
|
||||
#define ST77XX_PTLAR 0x30
|
||||
#define ST77XX_TEOFF 0x34
|
||||
#define ST77XX_TEON 0x35
|
||||
#define ST77XX_MADCTL 0x36
|
||||
#define ST77XX_COLMOD 0x3A
|
||||
|
||||
#define ST77XX_MADCTL_MY 0x80
|
||||
#define ST77XX_MADCTL_MX 0x40
|
||||
#define ST77XX_MADCTL_MV 0x20
|
||||
#define ST77XX_MADCTL_ML 0x10
|
||||
#define ST77XX_MADCTL_RGB 0x00
|
||||
|
||||
#define ST77XX_RDID1 0xDA
|
||||
#define ST77XX_RDID2 0xDB
|
||||
#define ST77XX_RDID3 0xDC
|
||||
#define ST77XX_RDID4 0xDD
|
||||
|
||||
// Some ready-made 16-bit ('565') color settings:
|
||||
#define ST77XX_BLACK 0x0000
|
||||
#define ST77XX_WHITE 0xFFFF
|
||||
#define ST77XX_RED 0xF800
|
||||
#define ST77XX_GREEN 0x07E0
|
||||
#define ST77XX_BLUE 0x001F
|
||||
#define ST77XX_CYAN 0x07FF
|
||||
#define ST77XX_MAGENTA 0xF81F
|
||||
#define ST77XX_YELLOW 0xFFE0
|
||||
#define ST77XX_ORANGE 0xFC00
|
||||
|
||||
#define LED_A_ON LOW
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#undef LED_A_ON
|
||||
#define LED_A_ON HIGH
|
||||
#define rtos_free free
|
||||
#define rtos_malloc malloc
|
||||
//SPIClass SPI1(HSPI);
|
||||
#endif
|
||||
class ST7789Spi : public OLEDDisplay {
|
||||
private:
|
||||
uint8_t _rst;
|
||||
uint8_t _dc;
|
||||
uint8_t _cs;
|
||||
uint8_t _ledA;
|
||||
int _miso;
|
||||
int _mosi;
|
||||
int _clk;
|
||||
SPIClass * _spi;
|
||||
SPISettings _spiSettings;
|
||||
uint16_t _RGB=0xFFFF;
|
||||
uint8_t _buffheight;
|
||||
public:
|
||||
/* pass _cs as -1 to indicate "do not use CS pin", for cases where it is hard wired low */
|
||||
ST7789Spi(SPIClass *spiClass,uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_RAWMODE,uint16_t width=240,uint16_t height=320,int mosi=-1,int miso=-1,int clk=-1) {
|
||||
this->_spi = spiClass;
|
||||
this->_rst = _rst;
|
||||
this->_dc = _dc;
|
||||
this->_cs = _cs;
|
||||
this->_mosi=mosi;
|
||||
this->_miso=miso;
|
||||
this->_clk=clk;
|
||||
//this->_ledA = _ledA;
|
||||
_spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0);
|
||||
setGeometry(g,width,height);
|
||||
}
|
||||
|
||||
bool connect(){
|
||||
this->_buffheight=displayHeight / 8;
|
||||
this->_buffheight+=displayHeight % 8 ? 1:0;
|
||||
pinMode(_cs, OUTPUT);
|
||||
pinMode(_dc, OUTPUT);
|
||||
//pinMode(_ledA, OUTPUT);
|
||||
if (_cs != (uint8_t) -1) {
|
||||
pinMode(_cs, OUTPUT);
|
||||
}
|
||||
pinMode(_rst, OUTPUT);
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
_spi->begin(_clk,_miso,_mosi,-1);
|
||||
#else
|
||||
_spi->begin();
|
||||
#endif
|
||||
_spi->setClockDivider (SPI_CLOCK_DIV2);
|
||||
|
||||
// Pulse Reset low for 10ms
|
||||
digitalWrite(_rst, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(_rst, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_rst, HIGH);
|
||||
_spi->begin ();
|
||||
//digitalWrite(_ledA, LED_A_ON);
|
||||
return true;
|
||||
}
|
||||
|
||||
void display(void) {
|
||||
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
|
||||
|
||||
uint16_t minBoundY = UINT16_MAX;
|
||||
uint16_t maxBoundY = 0;
|
||||
|
||||
uint16_t minBoundX = UINT16_MAX;
|
||||
uint16_t maxBoundX = 0;
|
||||
|
||||
uint16_t x, y;
|
||||
|
||||
// Calculate the Y bounding box of changes
|
||||
// and copy buffer[pos] to buffer_back[pos];
|
||||
for (y = 0; y < _buffheight; y++) {
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
//Serial.printf("x %d y %d\r\n",x,y);
|
||||
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 == UINT16_MAX) return;
|
||||
|
||||
set_CS(LOW);
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
|
||||
for (y = minBoundY; y <= maxBoundY; y++)
|
||||
{
|
||||
for(int temp = 0; temp<8;temp++)
|
||||
{
|
||||
//setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
|
||||
setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
|
||||
//setAddrWindow(y*8+temp,minBoundX,1,maxBoundX-minBoundX+1);
|
||||
uint32_t const pixbufcount = maxBoundX-minBoundX+1;
|
||||
uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount);
|
||||
for (x = minBoundX; x <= maxBoundX; x++)
|
||||
{
|
||||
pixbuf[x-minBoundX] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0;
|
||||
}
|
||||
#ifdef ESP_PLATFORM
|
||||
_spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount);
|
||||
#else
|
||||
_spi->transfer(pixbuf, NULL, 2 * pixbufcount);
|
||||
#endif
|
||||
rtos_free(pixbuf);
|
||||
}
|
||||
}
|
||||
_spi->endTransaction();
|
||||
set_CS(HIGH);
|
||||
|
||||
#else
|
||||
set_CS(LOW);
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
uint8_t x, y;
|
||||
for (y = 0; y < _buffheight; y++)
|
||||
{
|
||||
for(int temp = 0; temp<8;temp++)
|
||||
{
|
||||
//setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
|
||||
//setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
|
||||
setAddrWindow(y*8+temp,0,1,displayWidth);
|
||||
uint32_t const pixbufcount = displayWidth;
|
||||
uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount);
|
||||
for (x = 0; x < displayWidth; x++)
|
||||
{
|
||||
pixbuf[x] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0;
|
||||
}
|
||||
#ifdef ESP_PLATFORM
|
||||
_spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount);
|
||||
#else
|
||||
_spi->transfer(pixbuf, NULL, 2 * pixbufcount);
|
||||
#endif
|
||||
rtos_free(pixbuf);
|
||||
}
|
||||
}
|
||||
_spi->endTransaction();
|
||||
set_CS(HIGH);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void resetOrientation() {
|
||||
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX;
|
||||
sendCommand(ST77XX_MADCTL);
|
||||
WriteData(madctl);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
virtual void flipScreenVertically() {
|
||||
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY;
|
||||
sendCommand(ST77XX_MADCTL);
|
||||
WriteData(madctl);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
virtual void mirrorScreen() {
|
||||
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX|ST77XX_MADCTL_MY;
|
||||
sendCommand(ST77XX_MADCTL);
|
||||
WriteData(madctl);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
virtual void setRotation(uint8_t r) {
|
||||
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX;
|
||||
if (r == 1) { madctl = 0xC0; }
|
||||
if (r == 2) { madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; }
|
||||
if (r == 3) { madctl = 0x00; }
|
||||
sendCommand(ST77XX_MADCTL);
|
||||
WriteData(madctl);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
void setRGB(uint16_t c)
|
||||
{
|
||||
|
||||
this->_RGB=0x00|c>>8|c<<8&0xFF00;
|
||||
}
|
||||
|
||||
void displayOn(void) {
|
||||
//sendCommand(DISPLAYON);
|
||||
}
|
||||
|
||||
void displayOff(void) {
|
||||
//sendCommand(DISPLAYOFF);
|
||||
}
|
||||
|
||||
//#define ST77XX_MADCTL_MY 0x80
|
||||
//#define ST77XX_MADCTL_MX 0x40
|
||||
//#define ST77XX_MADCTL_MV 0x20
|
||||
//#define ST77XX_MADCTL_ML 0x10
|
||||
protected:
|
||||
// Send all the init commands
|
||||
virtual void sendInitCommands()
|
||||
{
|
||||
sendCommand(ST77XX_SWRESET); // 1: Software reset, no args, w/delay
|
||||
delay(150);
|
||||
|
||||
sendCommand(ST77XX_SLPOUT); // 2: Out of sleep mode, no args, w/delay
|
||||
delay(10);
|
||||
|
||||
sendCommand(ST77XX_COLMOD); // 3: Set color mode, 16-bit color
|
||||
WriteData(0x55);
|
||||
delay(10);
|
||||
|
||||
sendCommand(ST77XX_MADCTL); // 4: Mem access ctrl (directions), Row/col addr, bottom-top refresh
|
||||
WriteData(0x08);
|
||||
|
||||
sendCommand(ST77XX_CASET); // 5: Column addr set,
|
||||
WriteData(0x00);
|
||||
WriteData(0x00); // XSTART = 0
|
||||
WriteData(0x00);
|
||||
WriteData(240); // XEND = 240
|
||||
|
||||
sendCommand(ST77XX_RASET); // 6: Row addr set,
|
||||
WriteData(0x00);
|
||||
WriteData(0x00); // YSTART = 0
|
||||
WriteData(320>>8);
|
||||
WriteData(320&0xFF); // YSTART = 320
|
||||
|
||||
sendCommand(ST77XX_SLPOUT); // 7: hack
|
||||
delay(10);
|
||||
|
||||
sendCommand(ST77XX_NORON); // 8: Normal display on, no args, w/delay
|
||||
delay(10);
|
||||
|
||||
sendCommand(ST77XX_DISPON); // 9: Main screen turn on, no args, delay
|
||||
delay(10);
|
||||
|
||||
sendCommand(ST77XX_INVON); // 10: invert
|
||||
delay(10);
|
||||
|
||||
//uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MX;
|
||||
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX;
|
||||
sendCommand(ST77XX_MADCTL);
|
||||
WriteData(madctl);
|
||||
delay(10);
|
||||
setRGB(ST77XX_GREEN);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||
x += (320-displayWidth)/2;
|
||||
y += (240-displayHeight)/2;
|
||||
uint32_t xa = ((uint32_t)x << 16) | (x + w - 1);
|
||||
uint32_t ya = ((uint32_t)y << 16) | (y + h - 1);
|
||||
|
||||
writeCommand(ST77XX_CASET); // Column addr set
|
||||
SPI_WRITE32(xa);
|
||||
|
||||
writeCommand(ST77XX_RASET); // Row addr set
|
||||
SPI_WRITE32(ya);
|
||||
|
||||
writeCommand(ST77XX_RAMWR); // write to RAM
|
||||
}
|
||||
int getBufferOffset(void) {
|
||||
return 0;
|
||||
}
|
||||
inline void set_CS(bool level) {
|
||||
if (_cs != (uint8_t) -1) {
|
||||
digitalWrite(_cs, level);
|
||||
}
|
||||
};
|
||||
inline void sendCommand(uint8_t com) __attribute__((always_inline)){
|
||||
set_CS(HIGH);
|
||||
digitalWrite(_dc, LOW);
|
||||
set_CS(LOW);
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
_spi->transfer(com);
|
||||
_spi->endTransaction();
|
||||
set_CS(HIGH);
|
||||
digitalWrite(_dc, HIGH);
|
||||
}
|
||||
|
||||
inline void WriteData(uint8_t data) __attribute__((always_inline)){
|
||||
digitalWrite(_cs, LOW);
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
_spi->transfer(data);
|
||||
_spi->endTransaction();
|
||||
digitalWrite(_cs, HIGH);
|
||||
}
|
||||
void SPI_WRITE32(uint32_t l)
|
||||
{
|
||||
_spi->transfer(l >> 24);
|
||||
_spi->transfer(l >> 16);
|
||||
_spi->transfer(l >> 8);
|
||||
_spi->transfer(l);
|
||||
}
|
||||
void writeCommand(uint8_t cmd) {
|
||||
digitalWrite(_dc, LOW);
|
||||
_spi->transfer(cmd);
|
||||
digitalWrite(_dc, HIGH);
|
||||
}
|
||||
|
||||
// Private functions
|
||||
void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) {
|
||||
this->geometry = g;
|
||||
|
||||
switch (g) {
|
||||
case GEOMETRY_128_128:
|
||||
this->displayWidth = 128;
|
||||
this->displayHeight = 128;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
uint8_t tmp=displayHeight % 8;
|
||||
uint8_t _buffheight=displayHeight / 8;
|
||||
|
||||
if(tmp!=0)
|
||||
_buffheight++;
|
||||
this->displayBufferSize = displayWidth * _buffheight ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
1987
RNode_Firmware/Utilities.h
Normal file
1987
RNode_Firmware/Utilities.h
Normal file
File diff suppressed because it is too large
Load diff
242
RNode_Firmware/src/LCMEN2R13EFC1.cpp
Normal file
242
RNode_Firmware/src/LCMEN2R13EFC1.cpp
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// E-Ink display driver for LCMEN2R13EFC1
|
||||
// Adapted from Meshtastic NicheGraphics driver
|
||||
|
||||
#include "LCMEN2R13EFC1.h"
|
||||
|
||||
// Look-up tables for FAST refresh
|
||||
static const uint8_t LUT_FAST_VCOMDC[] = {
|
||||
0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01,
|
||||
0x01, 0x06, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_WW[] = {
|
||||
0x01, 0x06, 0x03, 0x02, 0x81, 0x01, 0x01,
|
||||
0x01, 0x06, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_BW[] = {
|
||||
0x01, 0x86, 0x83, 0x82, 0x81, 0x01, 0x01,
|
||||
0x01, 0x86, 0x82, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_WB[] = {
|
||||
0x01, 0x46, 0x43, 0x02, 0x01, 0x01, 0x01,
|
||||
0x01, 0x46, 0x42, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_BB[] = {
|
||||
0x01, 0x06, 0x03, 0x42, 0x41, 0x01, 0x01,
|
||||
0x01, 0x06, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
LCMEN2R13EFC1::LCMEN2R13EFC1()
|
||||
: _spiSettings(6000000, MSBFIRST, SPI_MODE0)
|
||||
{
|
||||
// Calculate buffer size (8 pixels per byte)
|
||||
_bufferRowSize = ((DISPLAY_WIDTH - 1) / 8) + 1; // 122px -> 16 bytes
|
||||
_bufferSize = _bufferRowSize * DISPLAY_HEIGHT; // 16 * 250 = 4000 bytes
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst) {
|
||||
_spi = spi;
|
||||
_pin_dc = pin_dc;
|
||||
_pin_cs = pin_cs;
|
||||
_pin_busy = pin_busy;
|
||||
_pin_rst = pin_rst;
|
||||
|
||||
pinMode(_pin_dc, OUTPUT);
|
||||
pinMode(_pin_cs, OUTPUT);
|
||||
pinMode(_pin_busy, INPUT);
|
||||
pinMode(_pin_rst, INPUT_PULLUP); // Active low, hold high
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::reset() {
|
||||
pinMode(_pin_rst, OUTPUT);
|
||||
digitalWrite(_pin_rst, LOW);
|
||||
delay(10);
|
||||
pinMode(_pin_rst, INPUT_PULLUP);
|
||||
wait();
|
||||
|
||||
sendCommand(0x12); // Software reset
|
||||
wait();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::wait() {
|
||||
// Busy when LOW
|
||||
while (digitalRead(_pin_busy) == LOW) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
bool LCMEN2R13EFC1::isBusy() {
|
||||
return (digitalRead(_pin_busy) == LOW);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::sendCommand(const uint8_t command) {
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
digitalWrite(_pin_dc, LOW); // DC low = command
|
||||
digitalWrite(_pin_cs, LOW);
|
||||
_spi->transfer(command);
|
||||
digitalWrite(_pin_cs, HIGH);
|
||||
digitalWrite(_pin_dc, HIGH);
|
||||
_spi->endTransaction();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::sendData(uint8_t data) {
|
||||
sendData(&data, 1);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::sendData(const uint8_t *data, uint32_t size) {
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
digitalWrite(_pin_dc, HIGH); // DC high = data
|
||||
digitalWrite(_pin_cs, LOW);
|
||||
|
||||
#if defined(ARCH_ESP32) || defined(ESP32)
|
||||
_spi->transferBytes(data, NULL, size);
|
||||
#else
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
_spi->transfer(data[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
digitalWrite(_pin_cs, HIGH);
|
||||
digitalWrite(_pin_dc, HIGH);
|
||||
_spi->endTransaction();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::configFull() {
|
||||
sendCommand(0x00); // Panel setting register
|
||||
sendData(0b11 << 6 // Display resolution
|
||||
| 1 << 4 // B&W only
|
||||
| 1 << 3 // Vertical scan direction
|
||||
| 1 << 2 // Horizontal scan direction
|
||||
| 1 << 1 // Shutdown: no
|
||||
| 1 << 0 // Reset: no
|
||||
);
|
||||
|
||||
sendCommand(0x50); // VCOM and data interval setting
|
||||
sendData(0b10 << 6 // Border driven white
|
||||
| 0b11 << 4 // Invert image colors: no
|
||||
| 0b0111 // Interval between VCOM on and image data
|
||||
);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::configFast() {
|
||||
sendCommand(0x00); // Panel setting register
|
||||
sendData(0b11 << 6 // Display resolution
|
||||
| 1 << 5 // LUT from registers (set below)
|
||||
| 1 << 4 // B&W only
|
||||
| 1 << 3 // Vertical scan direction
|
||||
| 1 << 2 // Horizontal scan direction
|
||||
| 1 << 1 // Shutdown: no
|
||||
| 1 << 0 // Reset: no
|
||||
);
|
||||
|
||||
sendCommand(0x50); // VCOM and data interval setting
|
||||
sendData(0b11 << 6 // Border floating
|
||||
| 0b01 << 4 // Invert image colors: no
|
||||
| 0b0111 // Interval between VCOM on and image data
|
||||
);
|
||||
|
||||
// Load LUT tables
|
||||
sendCommand(0x20); // VCOM
|
||||
sendData(LUT_FAST_VCOMDC, sizeof(LUT_FAST_VCOMDC));
|
||||
|
||||
sendCommand(0x21); // White -> White
|
||||
sendData(LUT_FAST_WW, sizeof(LUT_FAST_WW));
|
||||
|
||||
sendCommand(0x22); // Black -> White
|
||||
sendData(LUT_FAST_BW, sizeof(LUT_FAST_BW));
|
||||
|
||||
sendCommand(0x23); // White -> Black
|
||||
sendData(LUT_FAST_WB, sizeof(LUT_FAST_WB));
|
||||
|
||||
sendCommand(0x24); // Black -> Black
|
||||
sendData(LUT_FAST_BB, sizeof(LUT_FAST_BB));
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::writeNewImage() {
|
||||
sendCommand(0x13);
|
||||
sendData(_buffer, _bufferSize);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::writeOldImage() {
|
||||
sendCommand(0x10);
|
||||
sendData(_buffer, _bufferSize);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::update(uint8_t *imageData, UpdateType type) {
|
||||
_updateType = type;
|
||||
_buffer = imageData;
|
||||
|
||||
reset();
|
||||
|
||||
// Configure display
|
||||
if (_updateType == UPDATE_FULL) {
|
||||
configFull();
|
||||
} else {
|
||||
configFast();
|
||||
}
|
||||
|
||||
// Transfer image data
|
||||
if (_updateType == UPDATE_FULL) {
|
||||
writeNewImage();
|
||||
writeOldImage();
|
||||
} else {
|
||||
writeNewImage();
|
||||
}
|
||||
|
||||
// Power on and start refresh
|
||||
sendCommand(0x04); // Power on panel voltage
|
||||
wait();
|
||||
|
||||
sendCommand(0x12); // Begin executing update
|
||||
wait();
|
||||
|
||||
// Power off
|
||||
sendCommand(0x02);
|
||||
wait();
|
||||
|
||||
// Update "old memory" for next differential refresh
|
||||
if (_updateType != UPDATE_FULL) {
|
||||
writeOldImage();
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::powerOn() {
|
||||
sendCommand(0x04);
|
||||
wait();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::powerOff() {
|
||||
sendCommand(0x02);
|
||||
wait();
|
||||
}
|
||||
59
RNode_Firmware/src/LCMEN2R13EFC1.h
Normal file
59
RNode_Firmware/src/LCMEN2R13EFC1.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// E-Ink display driver for LCMEN2R13EFC1 (Heltec VisionMaster E213)
|
||||
// Adapted from Meshtastic firmware for RNode use
|
||||
// Controller IC: Fitipower JD79656
|
||||
// Resolution: 122x250 pixels (width x height)
|
||||
// Supports FAST (partial) and FULL refresh
|
||||
|
||||
#ifndef LCMEN2R13EFC1_H
|
||||
#define LCMEN2R13EFC1_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
|
||||
class LCMEN2R13EFC1 {
|
||||
public:
|
||||
// Display properties
|
||||
static constexpr uint16_t DISPLAY_WIDTH = 122;
|
||||
static constexpr uint16_t DISPLAY_HEIGHT = 250;
|
||||
|
||||
// Update types
|
||||
enum UpdateType {
|
||||
UPDATE_FULL = 0, // Full refresh (slower, ~3.6s)
|
||||
UPDATE_FAST = 1 // Partial refresh (faster, ~720ms)
|
||||
};
|
||||
|
||||
LCMEN2R13EFC1();
|
||||
|
||||
void begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst);
|
||||
void update(uint8_t *imageData, UpdateType type);
|
||||
bool isBusy();
|
||||
void powerOn();
|
||||
void powerOff();
|
||||
|
||||
private:
|
||||
SPIClass *_spi;
|
||||
uint8_t _pin_dc;
|
||||
uint8_t _pin_cs;
|
||||
uint8_t _pin_busy;
|
||||
uint8_t _pin_rst;
|
||||
|
||||
SPISettings _spiSettings;
|
||||
|
||||
uint8_t *_buffer;
|
||||
uint16_t _bufferRowSize;
|
||||
uint32_t _bufferSize;
|
||||
UpdateType _updateType;
|
||||
|
||||
void reset();
|
||||
void wait();
|
||||
void sendCommand(const uint8_t command);
|
||||
void sendData(uint8_t data);
|
||||
void sendData(const uint8_t *data, uint32_t size);
|
||||
|
||||
void configFull();
|
||||
void configFast();
|
||||
void writeNewImage();
|
||||
void writeOldImage();
|
||||
};
|
||||
|
||||
#endif // LCMEN2R13EFC1_H
|
||||
41
RNode_Firmware/src/einkDetect_VME213.h
Normal file
41
RNode_Firmware/src/einkDetect_VME213.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// E-Ink display chip detection for VME213
|
||||
// Detects between LCMEN213EFC1 (V1) and E0213A367 (V1.1)
|
||||
// Based on Meshtastic einkDetect.h
|
||||
|
||||
#ifndef EINK_DETECT_VME213_H
|
||||
#define EINK_DETECT_VME213_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
enum EInkChipType {
|
||||
EINK_LCMEN213EFC1 = 0, // Initial version (Fitipower JD79656)
|
||||
EINK_E0213A367 = 1 // V1.1+ (Solomon Systech SSD1682)
|
||||
};
|
||||
|
||||
// Detect E-Ink controller IC type by BUSY pin logic
|
||||
// Fitipower: BUSY=LOW when busy
|
||||
// Solomon Systech: BUSY=HIGH when busy
|
||||
inline EInkChipType detectEInkChip(uint8_t pin_reset, uint8_t pin_busy) {
|
||||
// Force display BUSY by holding reset pin active
|
||||
pinMode(pin_reset, OUTPUT);
|
||||
digitalWrite(pin_reset, LOW);
|
||||
|
||||
delay(10);
|
||||
|
||||
// Read BUSY pin logic while display is busy
|
||||
pinMode(pin_busy, INPUT);
|
||||
bool busyLogic = digitalRead(pin_busy);
|
||||
|
||||
// Release reset pin
|
||||
pinMode(pin_reset, INPUT);
|
||||
|
||||
// Fitipower = LOW when busy
|
||||
// Solomon = HIGH when busy
|
||||
if (busyLogic == LOW) {
|
||||
return EINK_LCMEN213EFC1;
|
||||
} else {
|
||||
return EINK_E0213A367;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // EINK_DETECT_VME213_H
|
||||
803
RNode_Firmware/sx126x.cpp
Normal file
803
RNode_Firmware/sx126x.cpp
Normal file
|
|
@ -0,0 +1,803 @@
|
|||
// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "Boards.h"
|
||||
|
||||
#if MODEM == SX1262
|
||||
#include "sx126x.h"
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#include "soc/rtc_wdt.h"
|
||||
#endif
|
||||
#define ISR_VECT IRAM_ATTR
|
||||
#else
|
||||
#define ISR_VECT
|
||||
#endif
|
||||
|
||||
#define OP_RF_FREQ_6X 0x86
|
||||
#define OP_SLEEP_6X 0x84
|
||||
#define OP_STANDBY_6X 0x80
|
||||
#define OP_TX_6X 0x83
|
||||
#define OP_RX_6X 0x82
|
||||
#define OP_PA_CONFIG_6X 0x95
|
||||
#define OP_SET_IRQ_FLAGS_6X 0x08 // Also provides info such as
|
||||
// preamble detection, etc for
|
||||
// knowing when it's safe to switch
|
||||
// antenna modes
|
||||
#define OP_CLEAR_IRQ_STATUS_6X 0x02
|
||||
#define OP_GET_IRQ_STATUS_6X 0x12
|
||||
#define OP_RX_BUFFER_STATUS_6X 0x13
|
||||
#define OP_PACKET_STATUS_6X 0x14 // Get snr & rssi of last packet
|
||||
#define OP_CURRENT_RSSI_6X 0x15
|
||||
#define OP_MODULATION_PARAMS_6X 0x8B // BW, SF, CR, etc.
|
||||
#define OP_PACKET_PARAMS_6X 0x8C // CRC, preamble, payload length, etc.
|
||||
#define OP_STATUS_6X 0xC0
|
||||
#define OP_TX_PARAMS_6X 0x8E // Set dbm, etc
|
||||
#define OP_PACKET_TYPE_6X 0x8A
|
||||
#define OP_BUFFER_BASE_ADDR_6X 0x8F
|
||||
#define OP_READ_REGISTER_6X 0x1D
|
||||
#define OP_WRITE_REGISTER_6X 0x0D
|
||||
#define OP_DIO3_TCXO_CTRL_6X 0x97
|
||||
#define OP_DIO2_RF_CTRL_6X 0x9D
|
||||
#define OP_CAD_PARAMS 0x88
|
||||
#define OP_CALIBRATE_6X 0x89
|
||||
#define OP_RX_TX_FALLBACK_MODE_6X 0x93
|
||||
#define OP_REGULATOR_MODE_6X 0x96
|
||||
#define OP_CALIBRATE_IMAGE_6X 0x98
|
||||
|
||||
#define MASK_CALIBRATE_ALL 0x7f
|
||||
|
||||
#define IRQ_TX_DONE_MASK_6X 0x01
|
||||
#define IRQ_RX_DONE_MASK_6X 0x02
|
||||
#define IRQ_HEADER_DET_MASK_6X 0x10
|
||||
#define IRQ_PREAMBLE_DET_MASK_6X 0x04
|
||||
#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40
|
||||
#define IRQ_ALL_MASK_6X 0b0100001111111111
|
||||
|
||||
#define MODE_LONG_RANGE_MODE_6X 0x01
|
||||
|
||||
#define OP_FIFO_WRITE_6X 0x0E
|
||||
#define OP_FIFO_READ_6X 0x1E
|
||||
#define REG_OCP_6X 0x08E7
|
||||
#define REG_LNA_6X 0x08AC // No agc in sx1262
|
||||
#define REG_SYNC_WORD_MSB_6X 0x0740
|
||||
#define REG_SYNC_WORD_LSB_6X 0x0741
|
||||
#define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
|
||||
#define REG_RANDOM_GEN_6X 0x0819
|
||||
|
||||
#define MODE_TCXO_3_3V_6X 0x07
|
||||
#define MODE_TCXO_3_0V_6X 0x06
|
||||
#define MODE_TCXO_2_7V_6X 0x06
|
||||
#define MODE_TCXO_2_4V_6X 0x06
|
||||
#define MODE_TCXO_2_2V_6X 0x03
|
||||
#define MODE_TCXO_1_8V_6X 0x02
|
||||
#define MODE_TCXO_1_7V_6X 0x01
|
||||
#define MODE_TCXO_1_6V_6X 0x00
|
||||
|
||||
#define MODE_STDBY_RC_6X 0x00
|
||||
#define MODE_STDBY_XOSC_6X 0x01
|
||||
#define MODE_FALLBACK_STDBY_RC_6X 0x20
|
||||
#define MODE_IMPLICIT_HEADER 0x01
|
||||
#define MODE_EXPLICIT_HEADER 0x00
|
||||
|
||||
#define SYNC_WORD_6X 0x1424
|
||||
|
||||
#define XTAL_FREQ_6X (double)32000000
|
||||
#define FREQ_DIV_6X (double)pow(2.0, 25.0)
|
||||
#define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X)
|
||||
|
||||
#if BOARD_MODEL == BOARD_TECHO
|
||||
SPIClass spim3 = SPIClass(NRF_SPIM3, pin_miso, pin_sclk, pin_mosi) ;
|
||||
#define SPI spim3
|
||||
|
||||
#elif defined(NRF52840_XXAA)
|
||||
extern SPIClass spiModem;
|
||||
#define SPI spiModem
|
||||
#endif
|
||||
|
||||
extern SPIClass SPI;
|
||||
|
||||
#define MAX_PKT_LENGTH 255
|
||||
|
||||
sx126x::sx126x() :
|
||||
_spiSettings(16E6, MSBFIRST, SPI_MODE0),
|
||||
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN),
|
||||
_frequency(0),
|
||||
_txp(0),
|
||||
_sf(0x07),
|
||||
_bw(0x04),
|
||||
_cr(0x01),
|
||||
_ldro(0x00),
|
||||
_packetIndex(0),
|
||||
_preambleLength(18),
|
||||
_implicitHeaderMode(0),
|
||||
_payloadLength(255),
|
||||
_crcMode(1),
|
||||
_fifo_tx_addr_ptr(0),
|
||||
_fifo_rx_addr_ptr(0),
|
||||
_packet({0}),
|
||||
_preinit_done(false),
|
||||
_onReceive(NULL)
|
||||
{ setTimeout(0); }
|
||||
|
||||
bool sx126x::preInit() {
|
||||
pinMode(_ss, OUTPUT);
|
||||
digitalWrite(_ss, HIGH);
|
||||
|
||||
#if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_XIAO_S3 || BOARD_MODEL == BOARD_VME213
|
||||
SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs);
|
||||
#elif BOARD_MODEL == BOARD_TECHO
|
||||
SPI.setPins(pin_miso, pin_sclk, pin_mosi);
|
||||
SPI.begin();
|
||||
#else
|
||||
SPI.begin();
|
||||
#endif
|
||||
|
||||
// Check version (retry for up to 2 seconds)
|
||||
// TODO: Actually read version registers, not syncwords
|
||||
long start = millis();
|
||||
uint8_t syncmsb;
|
||||
uint8_t synclsb;
|
||||
while (((millis() - start) < 2000) && (millis() >= start)) {
|
||||
syncmsb = readRegister(REG_SYNC_WORD_MSB_6X);
|
||||
synclsb = readRegister(REG_SYNC_WORD_LSB_6X);
|
||||
if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) {
|
||||
break;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_preinit_done = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT sx126x::readRegister(uint16_t address) {
|
||||
return singleTransfer(OP_READ_REGISTER_6X, address, 0x00);
|
||||
}
|
||||
|
||||
void sx126x::writeRegister(uint16_t address, uint8_t value) {
|
||||
singleTransfer(OP_WRITE_REGISTER_6X, address, value);
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) {
|
||||
waitOnBusy();
|
||||
|
||||
uint8_t response;
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(opcode);
|
||||
SPI.transfer((address & 0xFF00) >> 8);
|
||||
SPI.transfer(address & 0x00FF);
|
||||
if (opcode == OP_READ_REGISTER_6X) { SPI.transfer(0x00); }
|
||||
response = SPI.transfer(value);
|
||||
SPI.endTransaction();
|
||||
|
||||
digitalWrite(_ss, HIGH);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void sx126x::rxAntEnable() {
|
||||
if (_rxen != -1) { digitalWrite(_rxen, HIGH); }
|
||||
}
|
||||
|
||||
void sx126x::loraMode() {
|
||||
// Enable lora mode on the SX1262 chip
|
||||
uint8_t mode = MODE_LONG_RANGE_MODE_6X;
|
||||
executeOpcode(OP_PACKET_TYPE_6X, &mode, 1);
|
||||
}
|
||||
|
||||
void sx126x::waitOnBusy() {
|
||||
unsigned long time = millis();
|
||||
if (_busy != -1) {
|
||||
while (digitalRead(_busy) == HIGH) {
|
||||
if (millis() >= (time + 100)) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(opcode);
|
||||
for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(opcode);
|
||||
SPI.transfer(0x00);
|
||||
for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx126x::writeBuffer(const uint8_t* buffer, size_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(OP_FIFO_WRITE_6X);
|
||||
SPI.transfer(_fifo_tx_addr_ptr);
|
||||
for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); _fifo_tx_addr_ptr++; }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx126x::readBuffer(uint8_t* buffer, size_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(OP_FIFO_READ_6X);
|
||||
SPI.transfer(_fifo_rx_addr_ptr);
|
||||
SPI.transfer(0x00);
|
||||
for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
|
||||
// Because there is no access to these registers on the sx1262, we have
|
||||
// to set all these parameters at once or not at all.
|
||||
uint8_t buf[8];
|
||||
buf[0] = sf;
|
||||
buf[1] = bw;
|
||||
buf[2] = cr;
|
||||
buf[3] = ldro; // Low data rate toggle
|
||||
buf[4] = 0x00; // Unused params in LoRa mode
|
||||
buf[5] = 0x00;
|
||||
buf[6] = 0x00;
|
||||
buf[7] = 0x00;
|
||||
executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8);
|
||||
}
|
||||
|
||||
void sx126x::setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc) {
|
||||
// Because there is no access to these registers on the sx1262, we have
|
||||
// to set all these parameters at once or not at all.
|
||||
uint8_t buf[9];
|
||||
buf[0] = uint8_t((preamble_symbols & 0xFF00) >> 8);
|
||||
buf[1] = uint8_t((preamble_symbols & 0x00FF));
|
||||
buf[2] = headermode;
|
||||
buf[3] = payload_length;
|
||||
buf[4] = crc;
|
||||
buf[5] = 0x00; // standard IQ setting (no inversion)
|
||||
buf[6] = 0x00; // unused params
|
||||
buf[7] = 0x00;
|
||||
buf[8] = 0x00;
|
||||
executeOpcode(OP_PACKET_PARAMS_6X, buf, 9);
|
||||
}
|
||||
|
||||
void sx126x::reset(void) {
|
||||
if (_reset != -1) {
|
||||
pinMode(_reset, OUTPUT);
|
||||
digitalWrite(_reset, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_reset, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void sx126x::calibrate(void) {
|
||||
// Put in STDBY_RC mode before calibration
|
||||
uint8_t mode_byte = MODE_STDBY_RC_6X;
|
||||
executeOpcode(OP_STANDBY_6X, &mode_byte, 1);
|
||||
|
||||
// Calibrate RC64k, RC13M, PLL, ADC and image
|
||||
uint8_t calibrate = MASK_CALIBRATE_ALL;
|
||||
executeOpcode(OP_CALIBRATE_6X, &calibrate, 1);
|
||||
|
||||
delay(5);
|
||||
waitOnBusy();
|
||||
}
|
||||
|
||||
void sx126x::calibrate_image(long frequency) {
|
||||
uint8_t image_freq[2] = {0};
|
||||
if (frequency >= 430E6 && frequency <= 440E6) { image_freq[0] = 0x6B; image_freq[1] = 0x6F; }
|
||||
else if (frequency >= 470E6 && frequency <= 510E6) { image_freq[0] = 0x75; image_freq[1] = 0x81; }
|
||||
else if (frequency >= 779E6 && frequency <= 787E6) { image_freq[0] = 0xC1; image_freq[1] = 0xC5; }
|
||||
else if (frequency >= 863E6 && frequency <= 870E6) { image_freq[0] = 0xD7; image_freq[1] = 0xDB; }
|
||||
else if (frequency >= 902E6 && frequency <= 928E6) { image_freq[0] = 0xE1; image_freq[1] = 0xE9; } // TODO: Allow higher freq calibration
|
||||
executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2);
|
||||
waitOnBusy();
|
||||
}
|
||||
|
||||
int sx126x::begin(long frequency) {
|
||||
reset();
|
||||
|
||||
if (_busy != -1) { pinMode(_busy, INPUT); }
|
||||
if (!_preinit_done) { if (!preInit()) { return false; } }
|
||||
if (_rxen != -1) { pinMode(_rxen, OUTPUT); }
|
||||
|
||||
calibrate();
|
||||
calibrate_image(frequency);
|
||||
enableTCXO();
|
||||
loraMode();
|
||||
standby();
|
||||
|
||||
// Set sync word
|
||||
setSyncWord(SYNC_WORD_6X);
|
||||
|
||||
#if DIO2_AS_RF_SWITCH
|
||||
// enable dio2 rf switch
|
||||
uint8_t byte = 0x01;
|
||||
executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1);
|
||||
#endif
|
||||
|
||||
rxAntEnable();
|
||||
setFrequency(frequency);
|
||||
setTxPower(2);
|
||||
enableCrc();
|
||||
writeRegister(REG_LNA_6X, 0x96); // Set LNA boost
|
||||
uint8_t basebuf[2] = {0}; // Set base addresses
|
||||
executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2);
|
||||
|
||||
setModulationParams(_sf, _bw, _cr, _ldro);
|
||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
|
||||
#if HAS_LORA_PA
|
||||
#if LORA_PA_GC1109
|
||||
// Enable Vfem_ctl for supply to
|
||||
// PA power net.
|
||||
pinMode(LORA_PA_PWR_EN, OUTPUT);
|
||||
digitalWrite(LORA_PA_PWR_EN, HIGH);
|
||||
|
||||
// Enable PA LNA and TX standby
|
||||
pinMode(LORA_PA_CSD, OUTPUT);
|
||||
digitalWrite(LORA_PA_CSD, HIGH);
|
||||
|
||||
// Keep PA CPS low until actual
|
||||
// transmit. Does it save power?
|
||||
// Who knows? Will have to measure.
|
||||
pinMode(LORA_PA_CPS, OUTPUT);
|
||||
digitalWrite(LORA_PA_CPS, LOW);
|
||||
|
||||
// On Heltec V4, the PA CTX pin
|
||||
// is driven by the SX1262 DIO2
|
||||
// pin directly, so we do not
|
||||
// need to manually raise this.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void sx126x::end() { sleep(); SPI.end(); _preinit_done = false; }
|
||||
|
||||
int sx126x::beginPacket(int implicitHeader) {
|
||||
#if HAS_LORA_PA
|
||||
#if LORA_PA_GC1109
|
||||
// Enable PA CPS for transmit
|
||||
digitalWrite(LORA_PA_CPS, HIGH);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
standby();
|
||||
if (implicitHeader) { implicitHeaderMode(); }
|
||||
else { explicitHeaderMode(); }
|
||||
|
||||
_payloadLength = 0;
|
||||
_fifo_tx_addr_ptr = 0;
|
||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sx126x::endPacket() {
|
||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
uint8_t timeout[3] = {0}; // Put in single TX mode
|
||||
executeOpcode(OP_TX_6X, timeout, 3);
|
||||
|
||||
uint8_t buf[2];
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
|
||||
|
||||
// Wait for TX done
|
||||
bool timed_out = false;
|
||||
uint32_t w_timeout = millis()+LORA_MODEM_TIMEOUT_MS;
|
||||
while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0)) {
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
|
||||
yield();
|
||||
}
|
||||
|
||||
if (!(millis() < w_timeout)) { timed_out = true; }
|
||||
|
||||
// Clear IRQs
|
||||
uint8_t mask[2];
|
||||
mask[0] = 0x00;
|
||||
mask[1] = IRQ_TX_DONE_MASK_6X;
|
||||
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2);
|
||||
if (timed_out) { return 0; } else { return 1; }
|
||||
}
|
||||
|
||||
unsigned long preamble_detected_at = 0;
|
||||
extern long lora_preamble_time_ms;
|
||||
extern long lora_header_time_ms;
|
||||
bool false_preamble_detected = false;
|
||||
|
||||
bool sx126x::dcd() {
|
||||
uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
|
||||
uint32_t now = millis();
|
||||
|
||||
bool header_detected = false;
|
||||
bool carrier_detected = false;
|
||||
|
||||
if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { header_detected = true; carrier_detected = true; }
|
||||
else { header_detected = false; }
|
||||
|
||||
if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) {
|
||||
carrier_detected = true;
|
||||
if (preamble_detected_at == 0) { preamble_detected_at = now; }
|
||||
if (now - preamble_detected_at > lora_preamble_time_ms + lora_header_time_ms) {
|
||||
preamble_detected_at = 0;
|
||||
if (!header_detected) { false_preamble_detected = true; }
|
||||
uint8_t clearbuf[2] = {0};
|
||||
clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X;
|
||||
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Maybe there's a way of unlatching the RSSI
|
||||
// status without re-activating receive mode?
|
||||
if (false_preamble_detected) { sx126x_modem.receive(); false_preamble_detected = false; }
|
||||
return carrier_detected;
|
||||
}
|
||||
|
||||
uint8_t sx126x::currentRssiRaw() {
|
||||
uint8_t byte = 0;
|
||||
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
|
||||
return byte;
|
||||
}
|
||||
|
||||
int ISR_VECT sx126x::currentRssi() {
|
||||
uint8_t byte = 0;
|
||||
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
|
||||
int rssi = -(int(byte)) / 2;
|
||||
#if HAS_LORA_LNA
|
||||
rssi -= LORA_LNA_GAIN;
|
||||
#endif
|
||||
return rssi;
|
||||
}
|
||||
|
||||
uint8_t sx126x::packetRssiRaw() {
|
||||
uint8_t buf[3] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
|
||||
return buf[2];
|
||||
}
|
||||
|
||||
int ISR_VECT sx126x::packetRssi() {
|
||||
// TODO: May need more calculations here
|
||||
uint8_t buf[3] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
|
||||
int pkt_rssi = -buf[0] / 2;
|
||||
#if HAS_LORA_LNA
|
||||
pkt_rssi -= LORA_LNA_GAIN;
|
||||
#endif
|
||||
return pkt_rssi;
|
||||
}
|
||||
|
||||
int ISR_VECT sx126x::packetRssi(uint8_t pkt_snr_raw) {
|
||||
// TODO: May need more calculations here
|
||||
uint8_t buf[3] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
|
||||
int pkt_rssi = -buf[0] / 2;
|
||||
return pkt_rssi;
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT sx126x::packetSnrRaw() {
|
||||
uint8_t buf[3] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
|
||||
return buf[1];
|
||||
}
|
||||
|
||||
float ISR_VECT sx126x::packetSnr() {
|
||||
uint8_t buf[3] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
|
||||
return float(buf[1]) * 0.25;
|
||||
}
|
||||
|
||||
long sx126x::packetFrequencyError() {
|
||||
// TODO: Implement this, no idea how to check it on the sx1262
|
||||
const float fError = 0.0;
|
||||
return static_cast<long>(fError);
|
||||
}
|
||||
|
||||
size_t sx126x::write(uint8_t byte) { return write(&byte, sizeof(byte)); }
|
||||
size_t sx126x::write(const uint8_t *buffer, size_t size) {
|
||||
if ((_payloadLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - _payloadLength; }
|
||||
writeBuffer(buffer, size);
|
||||
_payloadLength = _payloadLength + size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int ISR_VECT sx126x::available() {
|
||||
uint8_t buf[2] = {0};
|
||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2);
|
||||
return buf[0] - _packetIndex;
|
||||
}
|
||||
|
||||
int ISR_VECT sx126x::read(){
|
||||
if (!available()) { return -1; }
|
||||
if (_packetIndex == 0) {
|
||||
uint8_t rxbuf[2] = {0};
|
||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
|
||||
int size = rxbuf[0];
|
||||
_fifo_rx_addr_ptr = rxbuf[1];
|
||||
readBuffer(_packet, size);
|
||||
}
|
||||
|
||||
uint8_t byte = _packet[_packetIndex];
|
||||
_packetIndex++;
|
||||
return byte;
|
||||
}
|
||||
|
||||
int sx126x::peek() {
|
||||
if (!available()) { return -1; }
|
||||
if (_packetIndex == 0) {
|
||||
uint8_t rxbuf[2] = {0};
|
||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
|
||||
int size = rxbuf[0];
|
||||
_fifo_rx_addr_ptr = rxbuf[1];
|
||||
readBuffer(_packet, size);
|
||||
}
|
||||
|
||||
uint8_t b = _packet[_packetIndex];
|
||||
return b;
|
||||
}
|
||||
|
||||
void sx126x::flush() { }
|
||||
|
||||
void sx126x::onReceive(void(*callback)(int)){
|
||||
_onReceive = callback;
|
||||
|
||||
if (callback) {
|
||||
pinMode(_dio0, INPUT);
|
||||
uint8_t buf[8]; // Set preamble and header detection irqs, plus dio0 mask
|
||||
buf[0] = 0xFF; // Set irq masks, enable all
|
||||
buf[1] = 0xFF;
|
||||
buf[2] = 0x00; // Set dio0 masks
|
||||
buf[3] = IRQ_RX_DONE_MASK_6X;
|
||||
buf[4] = 0x00; // Set dio1 masks
|
||||
buf[5] = 0x00;
|
||||
buf[6] = 0x00; // Set dio2 masks
|
||||
buf[7] = 0x00;
|
||||
executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8);
|
||||
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING);
|
||||
|
||||
} else {
|
||||
detachInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void sx126x::receive(int size) {
|
||||
#if HAS_LORA_PA
|
||||
#if LORA_PA_GC1109
|
||||
// Disable PA CPS for receive
|
||||
digitalWrite(LORA_PA_CPS, LOW);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (size > 0) {
|
||||
implicitHeaderMode();
|
||||
_payloadLength = size;
|
||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
} else { explicitHeaderMode(); }
|
||||
|
||||
if (_rxen != -1) { rxAntEnable(); }
|
||||
uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // Continuous mode
|
||||
executeOpcode(OP_RX_6X, mode, 3);
|
||||
}
|
||||
|
||||
void sx126x::standby() {
|
||||
uint8_t byte = MODE_STDBY_XOSC_6X; // STDBY_XOSC
|
||||
executeOpcode(OP_STANDBY_6X, &byte, 1);
|
||||
}
|
||||
|
||||
void sx126x::sleep() { uint8_t byte = 0x00; executeOpcode(OP_SLEEP_6X, &byte, 1); }
|
||||
|
||||
void sx126x::enableTCXO() {
|
||||
#if HAS_TCXO
|
||||
#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_XIAO_S3 || BOARD_MODEL == BOARD_VME213
|
||||
uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_TBEAM
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_TDECK
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_TBEAM_S_V1
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_T3S3
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_HELTEC_T114
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_TECHO
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_HELTEC32_V4
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
#endif
|
||||
executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO: Once enabled, SX1262 needs a complete reset to disable TCXO
|
||||
void sx126x::disableTCXO() { }
|
||||
|
||||
void sx126x::setTxPower(int level, int outputPin) {
|
||||
// Currently no low power mode for SX1262 implemented, assuming PA boost
|
||||
|
||||
// WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
|
||||
// RegTxClampConfig = @address 0x08D8
|
||||
writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1));
|
||||
|
||||
uint8_t pa_buf[4];
|
||||
pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs
|
||||
pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262
|
||||
pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261)
|
||||
pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet)
|
||||
executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power
|
||||
|
||||
if (level > 22) { level = 22; }
|
||||
else if (level < -9) { level = -9; }
|
||||
writeRegister(REG_OCP_6X, OCP_TUNED); // Use board-specific tuned OCP
|
||||
|
||||
uint8_t tx_buf[2];
|
||||
tx_buf[0] = level;
|
||||
tx_buf[1] = 0x02; // PA ramping time - 40 microseconds
|
||||
executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2);
|
||||
|
||||
_txp = level;
|
||||
}
|
||||
|
||||
uint8_t sx126x::getTxPower() { return _txp; }
|
||||
|
||||
void sx126x::setFrequency(long frequency) {
|
||||
_frequency = frequency;
|
||||
uint8_t buf[4];
|
||||
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X);
|
||||
buf[0] = ((freq >> 24) & 0xFF);
|
||||
buf[1] = ((freq >> 16) & 0xFF);
|
||||
buf[2] = ((freq >> 8) & 0xFF);
|
||||
buf[3] = (freq & 0xFF);
|
||||
executeOpcode(OP_RF_FREQ_6X, buf, 4);
|
||||
}
|
||||
|
||||
uint32_t sx126x::getFrequency() {
|
||||
// We can't read the frequency on the sx1262 / 80
|
||||
uint32_t frequency = _frequency;
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void sx126x::setSpreadingFactor(int sf) {
|
||||
if (sf < 5) { sf = 5; }
|
||||
else if (sf > 12) { sf = 12; }
|
||||
_sf = sf;
|
||||
|
||||
handleLowDataRate();
|
||||
setModulationParams(sf, _bw, _cr, _ldro);
|
||||
}
|
||||
|
||||
long sx126x::getSignalBandwidth() {
|
||||
int bw = _bw;
|
||||
switch (bw) {
|
||||
case 0x00: return 7.8E3;
|
||||
case 0x01: return 15.6E3;
|
||||
case 0x02: return 31.25E3;
|
||||
case 0x03: return 62.5E3;
|
||||
case 0x04: return 125E3;
|
||||
case 0x05: return 250E3;
|
||||
case 0x06: return 500E3;
|
||||
case 0x08: return 10.4E3;
|
||||
case 0x09: return 20.8E3;
|
||||
case 0x0A: return 41.7E3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern bool lora_low_datarate;
|
||||
void sx126x::handleLowDataRate() {
|
||||
if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16)
|
||||
{ _ldro = 0x01; lora_low_datarate = true; }
|
||||
else { _ldro = 0x00; lora_low_datarate = false; }
|
||||
}
|
||||
|
||||
// TODO: Check if there's anything the sx1262 can do here
|
||||
void sx126x::optimizeModemSensitivity(){ }
|
||||
|
||||
void sx126x::setSignalBandwidth(long sbw) {
|
||||
if (sbw <= 7.8E3) { _bw = 0x00; }
|
||||
else if (sbw <= 10.4E3) { _bw = 0x08; }
|
||||
else if (sbw <= 15.6E3) { _bw = 0x01; }
|
||||
else if (sbw <= 20.8E3) { _bw = 0x09; }
|
||||
else if (sbw <= 31.25E3) { _bw = 0x02; }
|
||||
else if (sbw <= 41.7E3) { _bw = 0x0A; }
|
||||
else if (sbw <= 62.5E3) { _bw = 0x03; }
|
||||
else if (sbw <= 125E3) { _bw = 0x04; }
|
||||
else if (sbw <= 250E3) { _bw = 0x05; }
|
||||
else { _bw = 0x06; }
|
||||
|
||||
handleLowDataRate();
|
||||
setModulationParams(_sf, _bw, _cr, _ldro);
|
||||
optimizeModemSensitivity();
|
||||
}
|
||||
|
||||
void sx126x::setCodingRate4(int denominator) {
|
||||
if (denominator < 5) { denominator = 5; }
|
||||
else if (denominator > 8) { denominator = 8; }
|
||||
int cr = denominator - 4;
|
||||
_cr = cr;
|
||||
setModulationParams(_sf, _bw, cr, _ldro);
|
||||
}
|
||||
|
||||
void sx126x::setPreambleLength(long preamble_symbols) {
|
||||
_preambleLength = preamble_symbols;
|
||||
setPacketParams(preamble_symbols, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
}
|
||||
|
||||
void sx126x::setSyncWord(uint16_t sw) {
|
||||
// TODO: Why was this hardcoded instead of using the config value?
|
||||
// writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8);
|
||||
// writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
|
||||
writeRegister(REG_SYNC_WORD_MSB_6X, 0x14);
|
||||
writeRegister(REG_SYNC_WORD_LSB_6X, 0x24);
|
||||
}
|
||||
|
||||
void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) {
|
||||
_ss = ss;
|
||||
_reset = reset;
|
||||
_dio0 = dio0;
|
||||
_busy = busy;
|
||||
_rxen = rxen;
|
||||
}
|
||||
|
||||
void sx126x::dumpRegisters(Stream& out) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
out.print("0x");
|
||||
out.print(i, HEX);
|
||||
out.print(": 0x");
|
||||
out.println(readRegister(i), HEX);
|
||||
}
|
||||
}
|
||||
|
||||
void ISR_VECT sx126x::handleDio0Rise() {
|
||||
uint8_t buf[2];
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
|
||||
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2);
|
||||
|
||||
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) {
|
||||
_packetIndex = 0;
|
||||
uint8_t rxbuf[2] = {0}; // Read packet length
|
||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
|
||||
int packetLength = rxbuf[0];
|
||||
if (_onReceive) { _onReceive(packetLength); }
|
||||
}
|
||||
}
|
||||
|
||||
void ISR_VECT sx126x::onDio0Rise() { sx126x_modem.handleDio0Rise(); }
|
||||
void sx126x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
|
||||
void sx126x::enableCrc() { _crcMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx126x::disableCrc() { _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx126x::explicitHeaderMode() { _implicitHeaderMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx126x::implicitHeaderMode() { _implicitHeaderMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
byte sx126x::random() { return readRegister(REG_RANDOM_GEN_6X); }
|
||||
|
||||
sx126x sx126x_modem;
|
||||
|
||||
#endif
|
||||
145
RNode_Firmware/sx126x.h
Normal file
145
RNode_Firmware/sx126x.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#ifndef SX126X_H
|
||||
#define SX126X_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "Modem.h"
|
||||
|
||||
#define LORA_DEFAULT_SS_PIN 10
|
||||
#define LORA_DEFAULT_RESET_PIN 9
|
||||
#define LORA_DEFAULT_DIO0_PIN 2
|
||||
#define LORA_DEFAULT_RXEN_PIN -1
|
||||
#define LORA_DEFAULT_TXEN_PIN -1
|
||||
#define LORA_DEFAULT_BUSY_PIN -1
|
||||
#define LORA_MODEM_TIMEOUT_MS 20E3
|
||||
|
||||
#define PA_OUTPUT_RFO_PIN 0
|
||||
#define PA_OUTPUT_PA_BOOST_PIN 1
|
||||
|
||||
#define RSSI_OFFSET 157
|
||||
|
||||
class sx126x : public Stream {
|
||||
public:
|
||||
sx126x();
|
||||
|
||||
int begin(long frequency);
|
||||
void end();
|
||||
|
||||
int beginPacket(int implicitHeader = false);
|
||||
int endPacket();
|
||||
|
||||
int parsePacket(int size = 0);
|
||||
int packetRssi();
|
||||
int packetRssi(uint8_t pkt_snr_raw);
|
||||
int currentRssi();
|
||||
uint8_t packetRssiRaw();
|
||||
uint8_t currentRssiRaw();
|
||||
uint8_t packetSnrRaw();
|
||||
float packetSnr();
|
||||
long packetFrequencyError();
|
||||
|
||||
// from Print
|
||||
virtual size_t write(uint8_t byte);
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
// from Stream
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
|
||||
void onReceive(void(*callback)(int));
|
||||
|
||||
void receive(int size = 0);
|
||||
void standby();
|
||||
void sleep();
|
||||
void reset(void);
|
||||
|
||||
bool preInit();
|
||||
uint8_t getTxPower();
|
||||
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
|
||||
uint32_t getFrequency();
|
||||
void setFrequency(long frequency);
|
||||
void setSpreadingFactor(int sf);
|
||||
long getSignalBandwidth();
|
||||
void setSignalBandwidth(long sbw);
|
||||
void setCodingRate4(int denominator);
|
||||
void setPreambleLength(long preamble_symbols);
|
||||
void setSyncWord(uint16_t sw);
|
||||
bool dcd();
|
||||
void enableCrc();
|
||||
void disableCrc();
|
||||
void enableTCXO();
|
||||
void disableTCXO();
|
||||
|
||||
void rxAntEnable();
|
||||
void loraMode();
|
||||
void waitOnBusy();
|
||||
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
|
||||
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
|
||||
void writeBuffer(const uint8_t* buffer, size_t size);
|
||||
void readBuffer(uint8_t* buffer, size_t size);
|
||||
void setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc);
|
||||
|
||||
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
|
||||
|
||||
// deprecated
|
||||
void crc() { enableCrc(); }
|
||||
void noCrc() { disableCrc(); }
|
||||
|
||||
byte random();
|
||||
|
||||
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN);
|
||||
void setSPIFrequency(uint32_t frequency);
|
||||
|
||||
void dumpRegisters(Stream& out);
|
||||
|
||||
private:
|
||||
void explicitHeaderMode();
|
||||
void implicitHeaderMode();
|
||||
|
||||
void handleDio0Rise();
|
||||
|
||||
uint8_t readRegister(uint16_t address);
|
||||
void writeRegister(uint16_t address, uint8_t value);
|
||||
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
|
||||
|
||||
static void onDio0Rise();
|
||||
|
||||
void handleLowDataRate();
|
||||
void optimizeModemSensitivity();
|
||||
|
||||
void calibrate(void);
|
||||
void calibrate_image(long frequency);
|
||||
|
||||
private:
|
||||
SPISettings _spiSettings;
|
||||
int _ss;
|
||||
int _reset;
|
||||
int _dio0;
|
||||
int _rxen;
|
||||
int _busy;
|
||||
long _frequency;
|
||||
int _txp;
|
||||
uint8_t _sf;
|
||||
uint8_t _bw;
|
||||
uint8_t _cr;
|
||||
uint8_t _ldro;
|
||||
int _packetIndex;
|
||||
int _preambleLength;
|
||||
int _implicitHeaderMode;
|
||||
int _payloadLength;
|
||||
int _crcMode;
|
||||
int _fifo_tx_addr_ptr;
|
||||
int _fifo_rx_addr_ptr;
|
||||
uint8_t _packet[255];
|
||||
bool _preinit_done;
|
||||
void (*_onReceive)(int);
|
||||
};
|
||||
|
||||
extern sx126x sx126x_modem;
|
||||
|
||||
#endif
|
||||
503
RNode_Firmware/sx127x.cpp
Normal file
503
RNode_Firmware/sx127x.cpp
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "Boards.h"
|
||||
|
||||
#if MODEM == SX1276
|
||||
#include "sx127x.h"
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#include "hal/wdt_hal.h"
|
||||
#endif
|
||||
#define ISR_VECT IRAM_ATTR
|
||||
#else
|
||||
#define ISR_VECT
|
||||
#endif
|
||||
|
||||
// Registers
|
||||
#define REG_FIFO_7X 0x00
|
||||
#define REG_OP_MODE_7X 0x01
|
||||
#define REG_FRF_MSB_7X 0x06
|
||||
#define REG_FRF_MID_7X 0x07
|
||||
#define REG_FRF_LSB_7X 0x08
|
||||
#define REG_PA_CONFIG_7X 0x09
|
||||
#define REG_OCP_7X 0x0b
|
||||
#define REG_LNA_7X 0x0c
|
||||
#define REG_FIFO_ADDR_PTR_7X 0x0d
|
||||
#define REG_FIFO_TX_BASE_ADDR_7X 0x0e
|
||||
#define REG_FIFO_RX_BASE_ADDR_7X 0x0f
|
||||
#define REG_FIFO_RX_CURRENT_ADDR_7X 0x10
|
||||
#define REG_IRQ_FLAGS_7X 0x12
|
||||
#define REG_RX_NB_BYTES_7X 0x13
|
||||
#define REG_MODEM_STAT_7X 0x18
|
||||
#define REG_PKT_SNR_VALUE_7X 0x19
|
||||
#define REG_PKT_RSSI_VALUE_7X 0x1a
|
||||
#define REG_RSSI_VALUE_7X 0x1b
|
||||
#define REG_MODEM_CONFIG_1_7X 0x1d
|
||||
#define REG_MODEM_CONFIG_2_7X 0x1e
|
||||
#define REG_PREAMBLE_MSB_7X 0x20
|
||||
#define REG_PREAMBLE_LSB_7X 0x21
|
||||
#define REG_PAYLOAD_LENGTH_7X 0x22
|
||||
#define REG_MODEM_CONFIG_3_7X 0x26
|
||||
#define REG_FREQ_ERROR_MSB_7X 0x28
|
||||
#define REG_FREQ_ERROR_MID_7X 0x29
|
||||
#define REG_FREQ_ERROR_LSB_7X 0x2a
|
||||
#define REG_RSSI_WIDEBAND_7X 0x2c
|
||||
#define REG_DETECTION_OPTIMIZE_7X 0x31
|
||||
#define REG_HIGH_BW_OPTIMIZE_1_7X 0x36
|
||||
#define REG_DETECTION_THRESHOLD_7X 0x37
|
||||
#define REG_SYNC_WORD_7X 0x39
|
||||
#define REG_HIGH_BW_OPTIMIZE_2_7X 0x3a
|
||||
#define REG_DIO_MAPPING_1_7X 0x40
|
||||
#define REG_VERSION_7X 0x42
|
||||
#define REG_TCXO_7X 0x4b
|
||||
#define REG_PA_DAC_7X 0x4d
|
||||
|
||||
// Modes
|
||||
#define MODE_LONG_RANGE_MODE_7X 0x80
|
||||
#define MODE_SLEEP_7X 0x00
|
||||
#define MODE_STDBY_7X 0x01
|
||||
#define MODE_TX_7X 0x03
|
||||
#define MODE_RX_CONTINUOUS_7X 0x05
|
||||
#define MODE_RX_SINGLE_7X 0x06
|
||||
|
||||
// PA config
|
||||
#define PA_BOOST_7X 0x80
|
||||
|
||||
// IRQ masks
|
||||
#define IRQ_TX_DONE_MASK_7X 0x08
|
||||
#define IRQ_RX_DONE_MASK_7X 0x40
|
||||
#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20
|
||||
|
||||
#define SYNC_WORD_7X 0x12
|
||||
#define MAX_PKT_LENGTH 255
|
||||
|
||||
extern SPIClass SPI;
|
||||
|
||||
sx127x::sx127x() :
|
||||
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
|
||||
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
|
||||
_frequency(0), _packetIndex(0), _preinit_done(false), _onReceive(NULL) { setTimeout(0); }
|
||||
|
||||
void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
|
||||
void sx127x::setPins(int ss, int reset, int dio0, int busy) { _ss = ss; _reset = reset; _dio0 = dio0; _busy = busy; }
|
||||
uint8_t ISR_VECT sx127x::readRegister(uint8_t address) { return singleTransfer(address & 0x7f, 0x00); }
|
||||
void sx127x::writeRegister(uint8_t address, uint8_t value) { singleTransfer(address | 0x80, value); }
|
||||
void sx127x::standby() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X); }
|
||||
void sx127x::sleep() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X); }
|
||||
void sx127x::setSyncWord(uint8_t sw) { writeRegister(REG_SYNC_WORD_7X, sw); }
|
||||
void sx127x::enableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04); }
|
||||
void sx127x::disableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb); }
|
||||
void sx127x::enableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg | 0x10); }
|
||||
void sx127x::disableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg & 0xEF); }
|
||||
void sx127x::explicitHeaderMode() { _implicitHeaderMode = 0; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) & 0xfe); }
|
||||
void sx127x::implicitHeaderMode() { _implicitHeaderMode = 1; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) | 0x01); }
|
||||
byte sx127x::random() { return readRegister(REG_RSSI_WIDEBAND_7X); }
|
||||
void sx127x::flush() { }
|
||||
|
||||
bool sx127x::preInit() {
|
||||
pinMode(_ss, OUTPUT);
|
||||
digitalWrite(_ss, HIGH);
|
||||
|
||||
#if BOARD_MODEL == BOARD_T3S3
|
||||
SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs);
|
||||
#else
|
||||
SPI.begin();
|
||||
#endif
|
||||
|
||||
// Check modem version
|
||||
uint8_t version;
|
||||
long start = millis();
|
||||
while (((millis() - start) < 500) && (millis() >= start)) {
|
||||
version = readRegister(REG_VERSION_7X);
|
||||
if (version == 0x12) { break; }
|
||||
delay(100);
|
||||
}
|
||||
|
||||
if (version != 0x12) { return false; }
|
||||
_preinit_done = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) {
|
||||
uint8_t response;
|
||||
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(address);
|
||||
response = SPI.transfer(value);
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
int sx127x::begin(long frequency) {
|
||||
if (_reset != -1) {
|
||||
pinMode(_reset, OUTPUT);
|
||||
digitalWrite(_reset, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_reset, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if (_busy != -1) { pinMode(_busy, INPUT); }
|
||||
if (!_preinit_done) { if (!preInit()) { return false; } }
|
||||
|
||||
sleep();
|
||||
setFrequency(frequency);
|
||||
|
||||
// Set base addresses
|
||||
writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0);
|
||||
writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0);
|
||||
|
||||
// Set LNA boost and auto AGC
|
||||
writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03);
|
||||
writeRegister(REG_MODEM_CONFIG_3_7X, 0x04);
|
||||
|
||||
setSyncWord(SYNC_WORD_7X);
|
||||
enableCrc();
|
||||
setTxPower(2);
|
||||
|
||||
standby();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void sx127x::end() { sleep(); SPI.end(); _preinit_done = false; }
|
||||
|
||||
int sx127x::beginPacket(int implicitHeader) {
|
||||
standby();
|
||||
|
||||
if (implicitHeader) { implicitHeaderMode(); }
|
||||
else { explicitHeaderMode(); }
|
||||
|
||||
// Reset FIFO address and payload length
|
||||
writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
|
||||
writeRegister(REG_PAYLOAD_LENGTH_7X, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sx127x::endPacket() {
|
||||
// Enter TX mode
|
||||
writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X);
|
||||
|
||||
// Wait for TX completion
|
||||
while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Clear TX complete IRQ
|
||||
writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool sx127x::dcd() {
|
||||
bool carrier_detected = false;
|
||||
uint8_t status = readRegister(REG_MODEM_STAT_7X);
|
||||
if ((status & SIG_DETECT) == SIG_DETECT) { carrier_detected = true; }
|
||||
if ((status & SIG_SYNCED) == SIG_SYNCED) { carrier_detected = true; }
|
||||
return carrier_detected;
|
||||
}
|
||||
|
||||
uint8_t sx127x::currentRssiRaw() {
|
||||
uint8_t rssi = readRegister(REG_RSSI_VALUE_7X);
|
||||
return rssi;
|
||||
}
|
||||
|
||||
int ISR_VECT sx127x::currentRssi() {
|
||||
int rssi = (int)readRegister(REG_RSSI_VALUE_7X) - RSSI_OFFSET;
|
||||
if (_frequency < 820E6) rssi -= 7;
|
||||
return rssi;
|
||||
}
|
||||
|
||||
uint8_t sx127x::packetRssiRaw() {
|
||||
uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE_7X);
|
||||
return pkt_rssi_value;
|
||||
}
|
||||
|
||||
int ISR_VECT sx127x::packetRssi(uint8_t pkt_snr_raw) {
|
||||
int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
|
||||
int pkt_snr = ((int8_t)pkt_snr_raw)*0.25;
|
||||
|
||||
if (_frequency < 820E6) pkt_rssi -= 7;
|
||||
|
||||
if (pkt_snr < 0) {
|
||||
pkt_rssi += pkt_snr;
|
||||
} else {
|
||||
// Slope correction is (16/15)*pkt_rssi,
|
||||
// this estimation looses one floating point
|
||||
// operation, and should be precise enough.
|
||||
pkt_rssi = (int)(1.066 * pkt_rssi);
|
||||
}
|
||||
return pkt_rssi;
|
||||
}
|
||||
|
||||
int ISR_VECT sx127x::packetRssi() {
|
||||
int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
|
||||
int pkt_snr = packetSnr();
|
||||
|
||||
if (_frequency < 820E6) pkt_rssi -= 7;
|
||||
|
||||
if (pkt_snr < 0) { pkt_rssi += pkt_snr; }
|
||||
else {
|
||||
// Slope correction is (16/15)*pkt_rssi,
|
||||
// this estimation looses one floating point
|
||||
// operation, and should be precise enough.
|
||||
pkt_rssi = (int)(1.066 * pkt_rssi);
|
||||
}
|
||||
return pkt_rssi;
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT sx127x::packetSnrRaw() { return readRegister(REG_PKT_SNR_VALUE_7X); }
|
||||
|
||||
float ISR_VECT sx127x::packetSnr() { return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25; }
|
||||
|
||||
long sx127x::packetFrequencyError() {
|
||||
int32_t freqError = 0;
|
||||
freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB_7X) & B111);
|
||||
freqError <<= 8L;
|
||||
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID_7X));
|
||||
freqError <<= 8L;
|
||||
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB_7X));
|
||||
|
||||
if (readRegister(REG_FREQ_ERROR_MSB_7X) & B1000) { // Sign bit is on
|
||||
freqError -= 524288; // B1000'0000'0000'0000'0000
|
||||
}
|
||||
|
||||
const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
|
||||
const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f);
|
||||
|
||||
return static_cast<long>(fError);
|
||||
}
|
||||
|
||||
size_t sx127x::write(uint8_t byte) { return write(&byte, sizeof(byte)); }
|
||||
|
||||
size_t sx127x::write(const uint8_t *buffer, size_t size) {
|
||||
int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X);
|
||||
if ((currentLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - currentLength; }
|
||||
|
||||
for (size_t i = 0; i < size; i++) { writeRegister(REG_FIFO_7X, buffer[i]); }
|
||||
writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int ISR_VECT sx127x::available() { return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex); }
|
||||
|
||||
int ISR_VECT sx127x::read() {
|
||||
if (!available()) { return -1; }
|
||||
_packetIndex++;
|
||||
return readRegister(REG_FIFO_7X);
|
||||
}
|
||||
|
||||
int sx127x::peek() {
|
||||
if (!available()) { return -1; }
|
||||
|
||||
// Remember current FIFO address, read, and then reset address
|
||||
int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X);
|
||||
uint8_t b = readRegister(REG_FIFO_7X);
|
||||
writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void sx127x::onReceive(void(*callback)(int)) {
|
||||
_onReceive = callback;
|
||||
|
||||
if (callback) {
|
||||
pinMode(_dio0, INPUT);
|
||||
writeRegister(REG_DIO_MAPPING_1_7X, 0x00);
|
||||
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
|
||||
attachInterrupt(digitalPinToInterrupt(_dio0), sx127x::onDio0Rise, RISING);
|
||||
|
||||
} else {
|
||||
detachInterrupt(digitalPinToInterrupt(_dio0));
|
||||
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void sx127x::receive(int size) {
|
||||
if (size > 0) {
|
||||
implicitHeaderMode();
|
||||
writeRegister(REG_PAYLOAD_LENGTH_7X, size & 0xff);
|
||||
} else { explicitHeaderMode(); }
|
||||
|
||||
writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_RX_CONTINUOUS_7X);
|
||||
}
|
||||
|
||||
void sx127x::setTxPower(int level, int outputPin) {
|
||||
// Setup according to RFO or PA_BOOST output pin
|
||||
if (PA_OUTPUT_RFO_PIN == outputPin) {
|
||||
if (level < 0) { level = 0; }
|
||||
else if (level > 14) { level = 14; }
|
||||
|
||||
writeRegister(REG_PA_DAC_7X, 0x84);
|
||||
writeRegister(REG_PA_CONFIG_7X, 0x70 | level);
|
||||
|
||||
} else {
|
||||
if (level < 2) { level = 2; }
|
||||
else if (level > 17) { level = 17; }
|
||||
|
||||
writeRegister(REG_PA_DAC_7X, 0x84);
|
||||
writeRegister(REG_PA_CONFIG_7X, PA_BOOST_7X | (level - 2));
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t sx127x::getTxPower() { byte txp = readRegister(REG_PA_CONFIG_7X); return txp; }
|
||||
|
||||
void sx127x::setFrequency(unsigned long frequency) {
|
||||
_frequency = frequency;
|
||||
uint32_t frf = ((uint64_t)frequency << 19) / 32000000;
|
||||
|
||||
writeRegister(REG_FRF_MSB_7X, (uint8_t)(frf >> 16));
|
||||
writeRegister(REG_FRF_MID_7X, (uint8_t)(frf >> 8));
|
||||
writeRegister(REG_FRF_LSB_7X, (uint8_t)(frf >> 0));
|
||||
|
||||
optimizeModemSensitivity();
|
||||
}
|
||||
|
||||
uint32_t sx127x::getFrequency() {
|
||||
uint8_t msb = readRegister(REG_FRF_MSB_7X);
|
||||
uint8_t mid = readRegister(REG_FRF_MID_7X);
|
||||
uint8_t lsb = readRegister(REG_FRF_LSB_7X);
|
||||
|
||||
uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb;
|
||||
uint64_t frm = (uint64_t)frf*32000000;
|
||||
uint32_t frequency = (frm >> 19);
|
||||
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void sx127x::setSpreadingFactor(int sf) {
|
||||
if (sf < 6) { sf = 6; }
|
||||
else if (sf > 12) { sf = 12; }
|
||||
|
||||
if (sf == 6) {
|
||||
writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc5);
|
||||
writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0c);
|
||||
} else {
|
||||
writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc3);
|
||||
writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0a);
|
||||
}
|
||||
|
||||
writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0));
|
||||
handleLowDataRate();
|
||||
}
|
||||
|
||||
long sx127x::getSignalBandwidth() {
|
||||
byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4);
|
||||
switch (bw) {
|
||||
case 0: return 7.8E3;
|
||||
case 1: return 10.4E3;
|
||||
case 2: return 15.6E3;
|
||||
case 3: return 20.8E3;
|
||||
case 4: return 31.25E3;
|
||||
case 5: return 41.7E3;
|
||||
case 6: return 62.5E3;
|
||||
case 7: return 125E3;
|
||||
case 8: return 250E3;
|
||||
case 9: return 500E3; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sx127x::setSignalBandwidth(long sbw) {
|
||||
int bw;
|
||||
if (sbw <= 7.8E3) {
|
||||
bw = 0;
|
||||
} else if (sbw <= 10.4E3) {
|
||||
bw = 1;
|
||||
} else if (sbw <= 15.6E3) {
|
||||
bw = 2;
|
||||
} else if (sbw <= 20.8E3) {
|
||||
bw = 3;
|
||||
} else if (sbw <= 31.25E3) {
|
||||
bw = 4;
|
||||
} else if (sbw <= 41.7E3) {
|
||||
bw = 5;
|
||||
} else if (sbw <= 62.5E3) {
|
||||
bw = 6;
|
||||
} else if (sbw <= 125E3) {
|
||||
bw = 7;
|
||||
} else if (sbw <= 250E3) {
|
||||
bw = 8;
|
||||
} else /*if (sbw <= 250E3)*/ {
|
||||
bw = 9;
|
||||
}
|
||||
|
||||
writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0x0f) | (bw << 4));
|
||||
handleLowDataRate();
|
||||
optimizeModemSensitivity();
|
||||
}
|
||||
|
||||
void sx127x::setCodingRate4(int denominator) {
|
||||
if (denominator < 5) { denominator = 5; }
|
||||
else if (denominator > 8) { denominator = 8; }
|
||||
int cr = denominator - 4;
|
||||
writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1));
|
||||
}
|
||||
|
||||
void sx127x::setPreambleLength(long preamble_symbols) {
|
||||
long length = preamble_symbols - 4;
|
||||
writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8));
|
||||
writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0));
|
||||
}
|
||||
|
||||
extern bool lora_low_datarate;
|
||||
void sx127x::handleLowDataRate() {
|
||||
int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4);
|
||||
if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) {
|
||||
// Set auto AGC and LowDataRateOptimize
|
||||
writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2));
|
||||
lora_low_datarate = true;
|
||||
} else {
|
||||
// Only set auto AGC
|
||||
writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2));
|
||||
lora_low_datarate = false;
|
||||
}
|
||||
}
|
||||
|
||||
void sx127x::optimizeModemSensitivity() {
|
||||
byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4);
|
||||
uint32_t freq = getFrequency();
|
||||
|
||||
if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) {
|
||||
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
|
||||
writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x7f);
|
||||
} else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) {
|
||||
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
|
||||
writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x64);
|
||||
} else {
|
||||
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x03);
|
||||
}
|
||||
}
|
||||
|
||||
void ISR_VECT sx127x::handleDio0Rise() {
|
||||
int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
|
||||
|
||||
// Clear IRQs
|
||||
writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
|
||||
if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
|
||||
_packetIndex = 0;
|
||||
int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X);
|
||||
writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X));
|
||||
if (_onReceive) { _onReceive(packetLength); }
|
||||
writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ISR_VECT sx127x::onDio0Rise() { sx127x_modem.handleDio0Rise(); }
|
||||
|
||||
sx127x sx127x_modem;
|
||||
|
||||
#endif
|
||||
114
RNode_Firmware/sx127x.h
Normal file
114
RNode_Firmware/sx127x.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#ifndef SX1276_H
|
||||
#define SX1276_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "Modem.h"
|
||||
|
||||
#define LORA_DEFAULT_SS_PIN 10
|
||||
#define LORA_DEFAULT_RESET_PIN 9
|
||||
#define LORA_DEFAULT_DIO0_PIN 2
|
||||
#define LORA_DEFAULT_BUSY_PIN -1
|
||||
|
||||
#define PA_OUTPUT_RFO_PIN 0
|
||||
#define PA_OUTPUT_PA_BOOST_PIN 1
|
||||
|
||||
#define RSSI_OFFSET 157
|
||||
|
||||
// Modem status flags
|
||||
#define SIG_DETECT 0x01
|
||||
#define SIG_SYNCED 0x02
|
||||
#define RX_ONGOING 0x04
|
||||
|
||||
class sx127x : public Stream {
|
||||
public:
|
||||
sx127x();
|
||||
|
||||
int begin(long frequency);
|
||||
void end();
|
||||
|
||||
int beginPacket(int implicitHeader = false);
|
||||
int endPacket();
|
||||
|
||||
int parsePacket(int size = 0);
|
||||
int packetRssi();
|
||||
int packetRssi(uint8_t pkt_snr_raw);
|
||||
int currentRssi();
|
||||
uint8_t packetRssiRaw();
|
||||
uint8_t currentRssiRaw();
|
||||
uint8_t packetSnrRaw();
|
||||
float packetSnr();
|
||||
long packetFrequencyError();
|
||||
|
||||
// from Print
|
||||
virtual size_t write(uint8_t byte);
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
// from Stream
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
|
||||
void onReceive(void(*callback)(int));
|
||||
|
||||
void receive(int size = 0);
|
||||
void standby();
|
||||
void sleep();
|
||||
|
||||
bool preInit();
|
||||
uint8_t getTxPower();
|
||||
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
|
||||
uint32_t getFrequency();
|
||||
void setFrequency(unsigned long frequency);
|
||||
void setSpreadingFactor(int sf);
|
||||
long getSignalBandwidth();
|
||||
void setSignalBandwidth(long sbw);
|
||||
void setCodingRate4(int denominator);
|
||||
void setPreambleLength(long preamble_symbols);
|
||||
void setSyncWord(uint8_t sw);
|
||||
bool dcd();
|
||||
void enableCrc();
|
||||
void disableCrc();
|
||||
void enableTCXO();
|
||||
void disableTCXO();
|
||||
|
||||
byte random();
|
||||
|
||||
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN);
|
||||
void setSPIFrequency(uint32_t frequency);
|
||||
|
||||
private:
|
||||
void explicitHeaderMode();
|
||||
void implicitHeaderMode();
|
||||
|
||||
void handleDio0Rise();
|
||||
|
||||
uint8_t readRegister(uint8_t address);
|
||||
void writeRegister(uint8_t address, uint8_t value);
|
||||
uint8_t singleTransfer(uint8_t address, uint8_t value);
|
||||
|
||||
static void onDio0Rise();
|
||||
|
||||
void handleLowDataRate();
|
||||
void optimizeModemSensitivity();
|
||||
|
||||
private:
|
||||
SPISettings _spiSettings;
|
||||
int _ss;
|
||||
int _reset;
|
||||
int _dio0;
|
||||
int _busy;
|
||||
long _frequency;
|
||||
int _packetIndex;
|
||||
int _implicitHeaderMode;
|
||||
bool _preinit_done;
|
||||
void (*_onReceive)(int);
|
||||
};
|
||||
|
||||
extern sx127x sx127x_modem;
|
||||
|
||||
#endif
|
||||
887
RNode_Firmware/sx128x.cpp
Normal file
887
RNode_Firmware/sx128x.cpp
Normal file
|
|
@ -0,0 +1,887 @@
|
|||
// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "Boards.h"
|
||||
|
||||
#if MODEM == SX1280
|
||||
#include "sx128x.h"
|
||||
|
||||
#define MCU_1284P 0x91
|
||||
#define MCU_2560 0x92
|
||||
#define MCU_ESP32 0x81
|
||||
#define MCU_NRF52 0x71
|
||||
#if defined(__AVR_ATmega1284P__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_1284P
|
||||
#elif defined(__AVR_ATmega2560__)
|
||||
#define PLATFORM PLATFORM_AVR
|
||||
#define MCU_VARIANT MCU_2560
|
||||
#elif defined(ESP32)
|
||||
#define PLATFORM PLATFORM_ESP32
|
||||
#define MCU_VARIANT MCU_ESP32
|
||||
#elif defined(NRF52840_XXAA)
|
||||
#define PLATFORM PLATFORM_NRF52
|
||||
#define MCU_VARIANT MCU_NRF52
|
||||
#endif
|
||||
|
||||
#ifndef MCU_VARIANT
|
||||
#error No MCU variant defined, cannot compile
|
||||
#endif
|
||||
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#include "hal/wdt_hal.h"
|
||||
#endif
|
||||
#define ISR_VECT IRAM_ATTR
|
||||
#else
|
||||
#define ISR_VECT
|
||||
#endif
|
||||
|
||||
// SX128x registers
|
||||
#define OP_RF_FREQ_8X 0x86
|
||||
#define OP_SLEEP_8X 0x84
|
||||
#define OP_STANDBY_8X 0x80
|
||||
#define OP_TX_8X 0x83
|
||||
#define OP_RX_8X 0x82
|
||||
#define OP_SET_IRQ_FLAGS_8X 0x8D
|
||||
#define OP_CLEAR_IRQ_STATUS_8X 0x97
|
||||
#define OP_GET_IRQ_STATUS_8X 0x15
|
||||
#define OP_RX_BUFFER_STATUS_8X 0x17
|
||||
#define OP_PACKET_STATUS_8X 0x1D
|
||||
#define OP_CURRENT_RSSI_8X 0x1F
|
||||
#define OP_MODULATION_PARAMS_8X 0x8B
|
||||
#define OP_PACKET_PARAMS_8X 0x8C
|
||||
#define OP_STATUS_8X 0xC0
|
||||
#define OP_TX_PARAMS_8X 0x8E
|
||||
#define OP_PACKET_TYPE_8X 0x8A
|
||||
#define OP_BUFFER_BASE_ADDR_8X 0x8F
|
||||
#define OP_READ_REGISTER_8X 0x19
|
||||
#define OP_WRITE_REGISTER_8X 0x18
|
||||
#define IRQ_TX_DONE_MASK_8X 0x01
|
||||
#define IRQ_RX_DONE_MASK_8X 0x02
|
||||
#define IRQ_HEADER_DET_MASK_8X 0x10
|
||||
#define IRQ_HEADER_ERROR_MASK_8X 0x20
|
||||
#define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40
|
||||
|
||||
#define MODE_LONG_RANGE_MODE_8X 0x01
|
||||
|
||||
#define OP_FIFO_WRITE_8X 0x1A
|
||||
#define OP_FIFO_READ_8X 0x1B
|
||||
#define IRQ_PREAMBLE_DET_MASK_8X 0x80
|
||||
|
||||
#define REG_PACKET_SIZE 0x901
|
||||
#define REG_FIRM_VER_MSB 0x154
|
||||
#define REG_FIRM_VER_LSB 0x153
|
||||
|
||||
#define XTAL_FREQ_8X (double)52000000
|
||||
#define FREQ_DIV_8X (double)pow(2.0, 18.0)
|
||||
#define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X)
|
||||
|
||||
#if defined(NRF52840_XXAA)
|
||||
extern SPIClass spiModem;
|
||||
#define SPI spiModem
|
||||
#endif
|
||||
|
||||
extern SPIClass SPI;
|
||||
|
||||
#define MAX_PKT_LENGTH 255
|
||||
|
||||
sx128x::sx128x() :
|
||||
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
|
||||
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(pin_rxen), _busy(LORA_DEFAULT_BUSY_PIN), _txen(pin_txen),
|
||||
_frequency(0), _txp(0), _sf(0x05), _bw(0x34), _cr(0x01), _packetIndex(0), _implicitHeaderMode(0), _payloadLength(255), _crcMode(0), _fifo_tx_addr_ptr(0),
|
||||
_fifo_rx_addr_ptr(0), _rxPacketLength(0), _preinit_done(false), _tcxo(false) { setTimeout(0); }
|
||||
|
||||
bool ISR_VECT sx128x::getPacketValidity() {
|
||||
uint8_t buf[2];
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
|
||||
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2);
|
||||
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) { return true; }
|
||||
else { return false; }
|
||||
}
|
||||
|
||||
void ISR_VECT sx128x::onDio0Rise() {
|
||||
BaseType_t int_status = taskENTER_CRITICAL_FROM_ISR();
|
||||
// On the SX1280, there is a bug which can cause the busy line
|
||||
// to remain high if a high amount of packets are received when
|
||||
// in continuous RX mode. This is documented as Errata 16.1 in
|
||||
// the SX1280 datasheet v3.2 (page 149)
|
||||
// Therefore, the modem is set into receive mode each time a packet is received.
|
||||
if (sx128x_modem.getPacketValidity()) { sx128x_modem.receive(); sx128x_modem.handleDio0Rise(); }
|
||||
else { sx128x_modem.receive(); }
|
||||
|
||||
taskEXIT_CRITICAL_FROM_ISR(int_status);
|
||||
}
|
||||
|
||||
void sx128x::handleDio0Rise() {
|
||||
_packetIndex = 0;
|
||||
uint8_t rxbuf[2] = {0};
|
||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
|
||||
|
||||
// If implicit header mode is enabled, use pre-set packet length as payload length instead.
|
||||
// See SX1280 datasheet v3.2, page 92
|
||||
if (_implicitHeaderMode == 0x80) { _rxPacketLength = _payloadLength; }
|
||||
else { _rxPacketLength = rxbuf[0]; }
|
||||
|
||||
if (_receive_callback) { _receive_callback(_rxPacketLength); }
|
||||
}
|
||||
|
||||
bool sx128x::preInit() {
|
||||
pinMode(_ss, OUTPUT);
|
||||
digitalWrite(_ss, HIGH);
|
||||
|
||||
// TODO: Check if this change causes issues on any platforms
|
||||
#if MCU_VARIANT == MCU_ESP32
|
||||
#if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_TDECK
|
||||
SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs);
|
||||
#else
|
||||
SPI.begin();
|
||||
#endif
|
||||
#else
|
||||
SPI.begin();
|
||||
#endif
|
||||
|
||||
// Detect modem (retry for up to 500ms)
|
||||
long start = millis();
|
||||
uint8_t version_msb;
|
||||
uint8_t version_lsb;
|
||||
while (((millis() - start) < 500) && (millis() >= start)) {
|
||||
version_msb = readRegister(REG_FIRM_VER_MSB);
|
||||
version_lsb = readRegister(REG_FIRM_VER_LSB);
|
||||
if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { break; }
|
||||
delay(100);
|
||||
}
|
||||
|
||||
if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { return false; }
|
||||
_preinit_done = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT sx128x::readRegister(uint16_t address) { return singleTransfer(OP_READ_REGISTER_8X, address, 0x00); }
|
||||
void sx128x::writeRegister(uint16_t address, uint8_t value) { singleTransfer(OP_WRITE_REGISTER_8X, address, value); }
|
||||
|
||||
uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) {
|
||||
waitOnBusy();
|
||||
uint8_t response;
|
||||
digitalWrite(_ss, LOW);
|
||||
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(opcode);
|
||||
SPI.transfer((address & 0xFF00) >> 8);
|
||||
SPI.transfer(address & 0x00FF);
|
||||
if (opcode == OP_READ_REGISTER_8X) { SPI.transfer(0x00); }
|
||||
response = SPI.transfer(value);
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void sx128x::rxAntEnable() {
|
||||
if (_txen != -1) { digitalWrite(_txen, LOW); }
|
||||
if (_rxen != -1) { digitalWrite(_rxen, HIGH); }
|
||||
}
|
||||
|
||||
void sx128x::txAntEnable() {
|
||||
if (_txen != -1) { digitalWrite(_txen, HIGH); }
|
||||
if (_rxen != -1) { digitalWrite(_rxen, LOW); }
|
||||
}
|
||||
|
||||
void sx128x::loraMode() {
|
||||
uint8_t mode = MODE_LONG_RANGE_MODE_8X;
|
||||
executeOpcode(OP_PACKET_TYPE_8X, &mode, 1);
|
||||
}
|
||||
|
||||
void sx128x::waitOnBusy() {
|
||||
unsigned long time = millis();
|
||||
while (digitalRead(_busy) == HIGH) {
|
||||
if (millis() >= (time + 100)) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(opcode);
|
||||
for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(opcode);
|
||||
SPI.transfer(0x00);
|
||||
for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx128x::writeBuffer(const uint8_t* buffer, size_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(OP_FIFO_WRITE_8X);
|
||||
SPI.transfer(_fifo_tx_addr_ptr);
|
||||
for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); _fifo_tx_addr_ptr++; }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx128x::readBuffer(uint8_t* buffer, size_t size) {
|
||||
waitOnBusy();
|
||||
digitalWrite(_ss, LOW);
|
||||
SPI.beginTransaction(_spiSettings);
|
||||
SPI.transfer(OP_FIFO_READ_8X);
|
||||
SPI.transfer(_fifo_rx_addr_ptr);
|
||||
SPI.transfer(0x00);
|
||||
for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
|
||||
SPI.endTransaction();
|
||||
digitalWrite(_ss, HIGH);
|
||||
}
|
||||
|
||||
void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) {
|
||||
// because there is no access to these registers on the sx1280, we have
|
||||
// to set all these parameters at once or not at all.
|
||||
uint8_t buf[3];
|
||||
buf[0] = sf << 4;
|
||||
buf[1] = bw;
|
||||
buf[2] = cr;
|
||||
executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3);
|
||||
|
||||
if (sf <= 6) { writeRegister(0x925, 0x1E); }
|
||||
else if (sf <= 8) { writeRegister(0x925, 0x37); }
|
||||
else if (sf >= 9) { writeRegister(0x925, 0x32); }
|
||||
writeRegister(0x093C, 0x1);
|
||||
}
|
||||
|
||||
uint8_t preamble_e = 0;
|
||||
uint8_t preamble_m = 0;
|
||||
uint32_t last_me_result_target = 0;
|
||||
extern long lora_preamble_symbols;
|
||||
void sx128x::setPacketParams(uint32_t target_preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc) {
|
||||
if (last_me_result_target != target_preamble_symbols) {
|
||||
// Calculate exponent and mantissa values for modem
|
||||
if (target_preamble_symbols >= 0xF000) target_preamble_symbols = 0xF000;
|
||||
uint32_t calculated_preamble_symbols;
|
||||
uint8_t e = 1;
|
||||
uint8_t m = 1;
|
||||
while (e <= 15) {
|
||||
while (m <= 15) {
|
||||
calculated_preamble_symbols = m * (pow(2,e));
|
||||
if (calculated_preamble_symbols >= target_preamble_symbols-4) break;
|
||||
m++;
|
||||
}
|
||||
|
||||
if (calculated_preamble_symbols >= target_preamble_symbols-4) break;
|
||||
m = 1; e++;
|
||||
}
|
||||
|
||||
last_me_result_target = target_preamble_symbols;
|
||||
lora_preamble_symbols = calculated_preamble_symbols+4;
|
||||
_preambleLength = lora_preamble_symbols;
|
||||
|
||||
preamble_e = e;
|
||||
preamble_m = m;
|
||||
}
|
||||
|
||||
uint8_t buf[7];
|
||||
buf[0] = (preamble_e << 4) | preamble_m;
|
||||
buf[1] = headermode;
|
||||
buf[2] = payload_length;
|
||||
buf[3] = crc;
|
||||
buf[4] = 0x40; // Standard IQ setting (no inversion)
|
||||
buf[5] = 0x00; // Unused params
|
||||
buf[6] = 0x00;
|
||||
|
||||
executeOpcode(OP_PACKET_PARAMS_8X, buf, 7);
|
||||
}
|
||||
|
||||
void sx128x::reset() {
|
||||
if (_reset != -1) {
|
||||
pinMode(_reset, OUTPUT);
|
||||
digitalWrite(_reset, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_reset, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
int sx128x::begin(unsigned long frequency) {
|
||||
reset();
|
||||
|
||||
if (_rxen != -1) { pinMode(_rxen, OUTPUT); }
|
||||
if (_txen != -1) { pinMode(_txen, OUTPUT); }
|
||||
if (_busy != -1) { pinMode(_busy, INPUT); }
|
||||
|
||||
if (!_preinit_done) {
|
||||
if (!preInit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
standby();
|
||||
loraMode();
|
||||
rxAntEnable();
|
||||
setFrequency(frequency);
|
||||
|
||||
// TODO: Implement LNA boost
|
||||
//writeRegister(REG_LNA, 0x96);
|
||||
|
||||
setModulationParams(_sf, _bw, _cr);
|
||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
setTxPower(_txp);
|
||||
|
||||
// Set base addresses
|
||||
uint8_t basebuf[2] = {0};
|
||||
executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2);
|
||||
|
||||
_radio_online = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void sx128x::end() {
|
||||
sleep();
|
||||
SPI.end();
|
||||
_bitrate = 0;
|
||||
_radio_online = false;
|
||||
_preinit_done = false;
|
||||
}
|
||||
|
||||
int sx128x::beginPacket(int implicitHeader) {
|
||||
standby();
|
||||
|
||||
if (implicitHeader) { implicitHeaderMode(); }
|
||||
else { explicitHeaderMode(); }
|
||||
|
||||
_payloadLength = 0;
|
||||
_fifo_tx_addr_ptr = 0;
|
||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sx128x::endPacket() {
|
||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
txAntEnable();
|
||||
|
||||
// Put in single TX mode
|
||||
uint8_t timeout[3] = {0};
|
||||
executeOpcode(OP_TX_8X, timeout, 3);
|
||||
|
||||
uint8_t buf[2];
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
|
||||
|
||||
// Wait for TX done
|
||||
bool timed_out = false;
|
||||
uint32_t w_timeout = millis()+LORA_MODEM_TIMEOUT_MS;
|
||||
while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0)) {
|
||||
buf[0] = 0x00;
|
||||
buf[1] = 0x00;
|
||||
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
|
||||
yield();
|
||||
}
|
||||
|
||||
if (!(millis() < w_timeout)) { timed_out = true; }
|
||||
|
||||
// clear IRQ's
|
||||
uint8_t mask[2];
|
||||
mask[0] = 0x00;
|
||||
mask[1] = IRQ_TX_DONE_MASK_8X;
|
||||
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2);
|
||||
|
||||
if (timed_out) { return 0; }
|
||||
else { return 1; }
|
||||
}
|
||||
|
||||
unsigned long preamble_detected_at = 0;
|
||||
extern long lora_preamble_time_ms;
|
||||
extern long lora_header_time_ms;
|
||||
bool false_preamble_detected = false;
|
||||
bool sx128x::dcd() {
|
||||
uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
|
||||
uint32_t now = millis();
|
||||
|
||||
bool header_detected = false;
|
||||
bool carrier_detected = false;
|
||||
|
||||
if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) { header_detected = true; carrier_detected = true; }
|
||||
else { header_detected = false; }
|
||||
|
||||
if ((buf[0] & IRQ_PREAMBLE_DET_MASK_8X) != 0) {
|
||||
carrier_detected = true;
|
||||
if (preamble_detected_at == 0) { preamble_detected_at = now; }
|
||||
if (now - preamble_detected_at > lora_preamble_time_ms + lora_header_time_ms) {
|
||||
preamble_detected_at = 0;
|
||||
if (!header_detected) { false_preamble_detected = true; }
|
||||
uint8_t clearbuf[2] = {0}; clearbuf[0] = IRQ_PREAMBLE_DET_MASK_8X;
|
||||
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Maybe there's a way of unlatching the RSSI
|
||||
// status without re-activating receive mode?
|
||||
if (false_preamble_detected) { sx128x_modem.receive(); false_preamble_detected = false; }
|
||||
return carrier_detected;
|
||||
}
|
||||
|
||||
|
||||
uint8_t sx128x::currentRssiRaw() {
|
||||
uint8_t byte = 0;
|
||||
executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
|
||||
return byte;
|
||||
}
|
||||
|
||||
int ISR_VECT sx128x::currentRssi() {
|
||||
uint8_t byte = 0;
|
||||
executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
|
||||
int rssi = -byte / 2;
|
||||
return rssi;
|
||||
}
|
||||
|
||||
uint8_t sx128x::packetRssiRaw() {
|
||||
uint8_t buf[5] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
int ISR_VECT sx128x::packetRssi(uint8_t pkt_snr_raw) {
|
||||
// TODO: May need more calculations here
|
||||
uint8_t buf[5] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
|
||||
int pkt_rssi = -buf[0] / 2;
|
||||
return pkt_rssi;
|
||||
}
|
||||
|
||||
uint8_t ISR_VECT sx128x::packetSnrRaw() {
|
||||
uint8_t buf[5] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
|
||||
return buf[1];
|
||||
}
|
||||
|
||||
float ISR_VECT sx128x::packetSnr() {
|
||||
uint8_t buf[5] = {0};
|
||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
|
||||
return float(buf[1]) * 0.25;
|
||||
}
|
||||
|
||||
long sx128x::packetFrequencyError() {
|
||||
// TODO: Implement this, page 120 of sx1280 datasheet
|
||||
int32_t freqError = 0;
|
||||
const float fError = 0.0;
|
||||
return static_cast<long>(fError);
|
||||
}
|
||||
|
||||
void sx128x::flush() { }
|
||||
int ISR_VECT sx128x::available() { return _rxPacketLength - _packetIndex; }
|
||||
size_t sx128x::write(uint8_t byte) { return write(&byte, sizeof(byte)); }
|
||||
size_t sx128x::write(const uint8_t *buffer, size_t size) {
|
||||
if ((_payloadLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - _payloadLength; }
|
||||
writeBuffer(buffer, size);
|
||||
_payloadLength = _payloadLength + size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int ISR_VECT sx128x::read() {
|
||||
if (!available()) { return -1; }
|
||||
|
||||
// If received new packet
|
||||
if (_packetIndex == 0) {
|
||||
uint8_t rxbuf[2] = {0};
|
||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
|
||||
int size;
|
||||
|
||||
// If implicit header mode is enabled, read packet length as payload length instead.
|
||||
// See SX1280 datasheet v3.2, page 92
|
||||
if (_implicitHeaderMode == 0x80) {
|
||||
size = _payloadLength;
|
||||
} else {
|
||||
size = rxbuf[0];
|
||||
}
|
||||
|
||||
_fifo_rx_addr_ptr = rxbuf[1];
|
||||
if (size > 255) { size = 255; }
|
||||
|
||||
readBuffer(_packet, size);
|
||||
}
|
||||
|
||||
uint8_t byte = _packet[_packetIndex];
|
||||
_packetIndex++;
|
||||
return byte;
|
||||
}
|
||||
|
||||
int sx128x::peek() {
|
||||
if (!available()) { return -1; }
|
||||
uint8_t b = _packet[_packetIndex];
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
void sx128x::onReceive(void(*callback)(int)) {
|
||||
_receive_callback = callback;
|
||||
|
||||
if (callback) {
|
||||
pinMode(_dio0, INPUT);
|
||||
|
||||
// Set preamble and header detection irqs, plus dio0 mask
|
||||
uint8_t buf[8];
|
||||
|
||||
// Set irq masks, enable all
|
||||
buf[0] = 0xFF;
|
||||
buf[1] = 0xFF;
|
||||
|
||||
// On the SX1280, no RxDone IRQ is generated if a packet is received with
|
||||
// an invalid header, but the modem will be taken out of single RX mode.
|
||||
// This can cause the modem to not receive packets until it is reset
|
||||
// again. This is documented as Errata 16.2 in the SX1280 datasheet v3.2
|
||||
// (page 150) Below, the header error IRQ is mapped to dio0 so that the
|
||||
// modem can be set into RX mode again on reception of a corrupted
|
||||
// header.
|
||||
// set dio0 masks
|
||||
buf[2] = 0x00;
|
||||
buf[3] = IRQ_RX_DONE_MASK_8X | IRQ_HEADER_ERROR_MASK_8X;
|
||||
|
||||
// Set dio1 masks
|
||||
buf[4] = 0x00;
|
||||
buf[5] = 0x00;
|
||||
|
||||
// Set dio2 masks
|
||||
buf[6] = 0x00;
|
||||
buf[7] = 0x00;
|
||||
|
||||
executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8);
|
||||
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
|
||||
attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
|
||||
|
||||
} else {
|
||||
detachInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
||||
_spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void sx128x::receive(int size) {
|
||||
if (size > 0) {
|
||||
implicitHeaderMode();
|
||||
// Tell radio payload length
|
||||
//_rxPacketLength = size;
|
||||
//_payloadLength = size;
|
||||
//setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
||||
} else {
|
||||
explicitHeaderMode();
|
||||
}
|
||||
|
||||
rxAntEnable();
|
||||
|
||||
// On the SX1280, there is a bug which can cause the busy line
|
||||
// to remain high if a high amount of packets are received when
|
||||
// in continuous RX mode. This is documented as Errata 16.1 in
|
||||
// the SX1280 datasheet v3.2 (page 149)
|
||||
// Therefore, the modem is set to single RX mode below instead.
|
||||
|
||||
// uint8_t mode[3] = {0x03, 0xFF, 0xFF}; // Countinuous RX mode
|
||||
uint8_t mode[3] = {0}; // single RX mode
|
||||
executeOpcode(OP_RX_8X, mode, 3);
|
||||
}
|
||||
|
||||
void sx128x::standby() {
|
||||
uint8_t byte = 0x01; // Always use STDBY_XOSC
|
||||
executeOpcode(OP_STANDBY_8X, &byte, 1);
|
||||
}
|
||||
|
||||
void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen) {
|
||||
_ss = ss;
|
||||
_reset = reset;
|
||||
_dio0 = dio0;
|
||||
_busy = busy;
|
||||
_rxen = rxen;
|
||||
_txen = txen;
|
||||
}
|
||||
|
||||
void sx128x::setTxPower(int level, int outputPin) {
|
||||
uint8_t tx_buf[2];
|
||||
|
||||
// RAK4631 with WisBlock SX1280 module (LIBSYS002)
|
||||
#if BOARD_VARIANT == MODEL_13 || BOARD_VARIANT == MODEL_21
|
||||
if (level > 27) { level = 27; }
|
||||
else if (level < 0) { level = 0; }
|
||||
|
||||
_txp = level;
|
||||
int reg_value;
|
||||
switch (level) {
|
||||
case 0:
|
||||
reg_value = -18;
|
||||
break;
|
||||
case 1:
|
||||
reg_value = -16;
|
||||
break;
|
||||
case 2:
|
||||
reg_value = -15;
|
||||
break;
|
||||
case 3:
|
||||
reg_value = -14;
|
||||
break;
|
||||
case 4:
|
||||
reg_value = -13;
|
||||
break;
|
||||
case 5:
|
||||
reg_value = -12;
|
||||
break;
|
||||
case 6:
|
||||
reg_value = -11;
|
||||
break;
|
||||
case 7:
|
||||
reg_value = -9;
|
||||
break;
|
||||
case 8:
|
||||
reg_value = -8;
|
||||
break;
|
||||
case 9:
|
||||
reg_value = -7;
|
||||
break;
|
||||
case 10:
|
||||
reg_value = -6;
|
||||
break;
|
||||
case 11:
|
||||
reg_value = -5;
|
||||
break;
|
||||
case 12:
|
||||
reg_value = -4;
|
||||
break;
|
||||
case 13:
|
||||
reg_value = -3;
|
||||
break;
|
||||
case 14:
|
||||
reg_value = -2;
|
||||
break;
|
||||
case 15:
|
||||
reg_value = -1;
|
||||
break;
|
||||
case 16:
|
||||
reg_value = 0;
|
||||
break;
|
||||
case 17:
|
||||
reg_value = 1;
|
||||
break;
|
||||
case 18:
|
||||
reg_value = 2;
|
||||
break;
|
||||
case 19:
|
||||
reg_value = 3;
|
||||
break;
|
||||
case 20:
|
||||
reg_value = 4;
|
||||
break;
|
||||
case 21:
|
||||
reg_value = 5;
|
||||
break;
|
||||
case 22:
|
||||
reg_value = 6;
|
||||
break;
|
||||
case 23:
|
||||
reg_value = 7;
|
||||
break;
|
||||
case 24:
|
||||
reg_value = 8;
|
||||
break;
|
||||
case 25:
|
||||
reg_value = 9;
|
||||
break;
|
||||
case 26:
|
||||
reg_value = 12;
|
||||
break;
|
||||
case 27:
|
||||
reg_value = 13;
|
||||
break;
|
||||
default:
|
||||
reg_value = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
tx_buf[0] = reg_value + 18;
|
||||
tx_buf[1] = 0xE0; // Ramping time, 20 microseconds
|
||||
executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
|
||||
|
||||
// T3S3 SX1280 PA
|
||||
#elif BOARD_VARIANT == MODEL_AC
|
||||
if (level > 20) { level = 20; }
|
||||
else if (level < 0) { level = 0; }
|
||||
|
||||
_txp = level;
|
||||
int reg_value;
|
||||
switch (level) {
|
||||
case 0:
|
||||
reg_value = -18;
|
||||
break;
|
||||
case 1:
|
||||
reg_value = -17;
|
||||
break;
|
||||
case 2:
|
||||
reg_value = -16;
|
||||
break;
|
||||
case 3:
|
||||
reg_value = -15;
|
||||
break;
|
||||
case 4:
|
||||
reg_value = -14;
|
||||
break;
|
||||
case 5:
|
||||
reg_value = -13;
|
||||
break;
|
||||
case 6:
|
||||
reg_value = -12;
|
||||
break;
|
||||
case 7:
|
||||
reg_value = -10;
|
||||
break;
|
||||
case 8:
|
||||
reg_value = -9;
|
||||
break;
|
||||
case 9:
|
||||
reg_value = -8;
|
||||
break;
|
||||
case 10:
|
||||
reg_value = -7;
|
||||
break;
|
||||
case 11:
|
||||
reg_value = -6;
|
||||
break;
|
||||
case 12:
|
||||
reg_value = -5;
|
||||
break;
|
||||
case 13:
|
||||
reg_value = -4;
|
||||
break;
|
||||
case 14:
|
||||
reg_value = -3;
|
||||
break;
|
||||
case 15:
|
||||
reg_value = -2;
|
||||
break;
|
||||
case 16:
|
||||
reg_value = -1;
|
||||
break;
|
||||
case 17:
|
||||
reg_value = 0;
|
||||
break;
|
||||
case 18:
|
||||
reg_value = 1;
|
||||
break;
|
||||
case 19:
|
||||
reg_value = 2;
|
||||
break;
|
||||
case 20:
|
||||
reg_value = 3;
|
||||
break;
|
||||
default:
|
||||
reg_value = 0;
|
||||
break;
|
||||
}
|
||||
tx_buf[0] = reg_value;
|
||||
tx_buf[1] = 0xE0; // Ramping time, 20 microseconds
|
||||
|
||||
// For SX1280 boards with no specific PA requirements
|
||||
#else
|
||||
if (level > 13) { level = 13; }
|
||||
else if (level < -18) { level = -18; }
|
||||
_txp = level;
|
||||
tx_buf[0] = level + 18;
|
||||
tx_buf[1] = 0xE0; // Ramping time, 20 microseconds
|
||||
#endif
|
||||
|
||||
executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
|
||||
}
|
||||
|
||||
void sx128x::setFrequency(uint32_t frequency) {
|
||||
_frequency = frequency;
|
||||
uint8_t buf[3];
|
||||
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X);
|
||||
buf[0] = ((freq >> 16) & 0xFF);
|
||||
buf[1] = ((freq >> 8) & 0xFF);
|
||||
buf[2] = (freq & 0xFF);
|
||||
|
||||
executeOpcode(OP_RF_FREQ_8X, buf, 3);
|
||||
}
|
||||
|
||||
uint32_t sx128x::getFrequency() {
|
||||
// We can't read the frequency on the sx1280
|
||||
uint32_t frequency = _frequency;
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void sx128x::setSpreadingFactor(int sf) {
|
||||
if (sf < 5) { sf = 5; }
|
||||
else if (sf > 12) { sf = 12; }
|
||||
_sf = sf;
|
||||
|
||||
setModulationParams(sf, _bw, _cr);
|
||||
handleLowDataRate();
|
||||
}
|
||||
|
||||
uint32_t sx128x::getSignalBandwidth() {
|
||||
int bw = _bw;
|
||||
switch (bw) {
|
||||
case 0x34: return 203.125E3;
|
||||
case 0x26: return 406.25E3;
|
||||
case 0x18: return 812.5E3;
|
||||
case 0x0A: return 1625E3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sx128x::setSignalBandwidth(uint32_t sbw) {
|
||||
if (sbw <= 203.125E3) { _bw = 0x34; }
|
||||
else if (sbw <= 406.25E3) { _bw = 0x26; }
|
||||
else if (sbw <= 812.5E3) { _bw = 0x18; }
|
||||
else { _bw = 0x0A; }
|
||||
|
||||
setModulationParams(_sf, _bw, _cr);
|
||||
handleLowDataRate();
|
||||
optimizeModemSensitivity();
|
||||
}
|
||||
|
||||
// TODO: add support for new interleaving scheme, see page 117 of sx1280 datasheet
|
||||
void sx128x::setCodingRate4(int denominator) {
|
||||
if (denominator < 5) { denominator = 5; }
|
||||
else if (denominator > 8) { denominator = 8; }
|
||||
_cr = denominator - 4;
|
||||
setModulationParams(_sf, _bw, _cr);
|
||||
}
|
||||
|
||||
extern bool lora_low_datarate;
|
||||
void sx128x::handleLowDataRate() {
|
||||
if (_sf > 10) { lora_low_datarate = true; }
|
||||
else { lora_low_datarate = false; }
|
||||
}
|
||||
|
||||
void sx128x::optimizeModemSensitivity() { } // TODO: Check if there's anything the sx1280 can do here
|
||||
uint8_t sx128x::getCodingRate4() { return _cr + 4; }
|
||||
void sx128x::setPreambleLength(long preamble_symbols) { setPacketParams(preamble_symbols, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx128x::setSyncWord(int sw) { } // TODO: Implement
|
||||
void sx128x::enableTCXO() { } // TODO: Need to check how to implement on sx1280
|
||||
void sx128x::disableTCXO() { } // TODO: Need to check how to implement on sx1280
|
||||
void sx128x::sleep() { uint8_t byte = 0x00; executeOpcode(OP_SLEEP_8X, &byte, 1); }
|
||||
uint8_t sx128x::getTxPower() { return _txp; }
|
||||
uint8_t sx128x::getSpreadingFactor() { return _sf; }
|
||||
void sx128x::enableCrc() { _crcMode = 0x20; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx128x::disableCrc() { _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx128x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
|
||||
void sx128x::explicitHeaderMode() { _implicitHeaderMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx128x::implicitHeaderMode() { _implicitHeaderMode = 0x80; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
|
||||
void sx128x::dumpRegisters(Stream& out) { for (int i = 0; i < 128; i++) { out.print("0x"); out.print(i, HEX); out.print(": 0x"); out.println(readRegister(i), HEX); } }
|
||||
|
||||
sx128x sx128x_modem;
|
||||
#endif
|
||||
146
RNode_Firmware/sx128x.h
Normal file
146
RNode_Firmware/sx128x.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright Sandeep Mistry, Mark Qvist and Jacob Eva.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#ifndef SX128X_H
|
||||
#define SX128X_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "Modem.h"
|
||||
|
||||
#define LORA_DEFAULT_SS_PIN 10
|
||||
#define LORA_DEFAULT_RESET_PIN 9
|
||||
#define LORA_DEFAULT_DIO0_PIN 2
|
||||
#define LORA_DEFAULT_RXEN_PIN -1
|
||||
#define LORA_DEFAULT_TXEN_PIN -1
|
||||
#define LORA_DEFAULT_BUSY_PIN -1
|
||||
#define LORA_MODEM_TIMEOUT_MS 15E3
|
||||
#define PA_OUTPUT_RFO_PIN 0
|
||||
#define PA_OUTPUT_PA_BOOST_PIN 1
|
||||
#define RSSI_OFFSET 157
|
||||
|
||||
class sx128x : public Stream {
|
||||
public:
|
||||
sx128x();
|
||||
|
||||
int begin(unsigned long frequency);
|
||||
void end();
|
||||
void reset();
|
||||
|
||||
int beginPacket(int implicitHeader = false);
|
||||
int endPacket();
|
||||
|
||||
int parsePacket(int size = 0);
|
||||
int packetRssi();
|
||||
int packetRssi(uint8_t pkt_snr_raw);
|
||||
int currentRssi();
|
||||
uint8_t packetRssiRaw();
|
||||
uint8_t currentRssiRaw();
|
||||
uint8_t packetSnrRaw();
|
||||
float packetSnr();
|
||||
long packetFrequencyError();
|
||||
|
||||
// from Print
|
||||
virtual size_t write(uint8_t byte);
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
// from Stream
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
|
||||
void onReceive(void(*callback)(int));
|
||||
|
||||
void receive(int size = 0);
|
||||
void standby();
|
||||
void sleep();
|
||||
|
||||
bool preInit();
|
||||
uint8_t getTxPower();
|
||||
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
|
||||
uint32_t getFrequency();
|
||||
void setFrequency(uint32_t frequency);
|
||||
void setSpreadingFactor(int sf);
|
||||
uint8_t getSpreadingFactor();
|
||||
uint32_t getSignalBandwidth();
|
||||
void setSignalBandwidth(uint32_t sbw);
|
||||
void setCodingRate4(int denominator);
|
||||
uint8_t getCodingRate4();
|
||||
void setPreambleLength(long preamble_symbols);
|
||||
void setSyncWord(int sw);
|
||||
bool dcd();
|
||||
void clearIRQStatus();
|
||||
void enableCrc();
|
||||
void disableCrc();
|
||||
void enableTCXO();
|
||||
void disableTCXO();
|
||||
|
||||
void txAntEnable();
|
||||
void rxAntEnable();
|
||||
void loraMode();
|
||||
void waitOnBusy();
|
||||
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
|
||||
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
|
||||
void writeBuffer(const uint8_t* buffer, size_t size);
|
||||
void readBuffer(uint8_t* buffer, size_t size);
|
||||
void setPacketParams(uint32_t target_preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc);
|
||||
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr);
|
||||
|
||||
void crc() { enableCrc(); }
|
||||
void noCrc() { disableCrc(); }
|
||||
|
||||
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN);
|
||||
void setSPIFrequency(uint32_t frequency);
|
||||
|
||||
void dumpRegisters(Stream& out);
|
||||
|
||||
private:
|
||||
void explicitHeaderMode();
|
||||
void implicitHeaderMode();
|
||||
|
||||
bool getPacketValidity();
|
||||
void handleDio0Rise();
|
||||
|
||||
uint8_t readRegister(uint16_t address);
|
||||
void writeRegister(uint16_t address, uint8_t value);
|
||||
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
|
||||
|
||||
static void onDio0Rise();
|
||||
|
||||
void handleLowDataRate();
|
||||
void optimizeModemSensitivity();
|
||||
|
||||
private:
|
||||
SPISettings _spiSettings;
|
||||
int _ss;
|
||||
int _reset;
|
||||
int _dio0;
|
||||
int _rxen;
|
||||
int _txen;
|
||||
int _busy;
|
||||
int _modem;
|
||||
unsigned long _frequency;
|
||||
int _txp;
|
||||
uint8_t _sf;
|
||||
uint8_t _bw;
|
||||
uint8_t _cr;
|
||||
int _packetIndex;
|
||||
uint32_t _preambleLength;
|
||||
int _implicitHeaderMode;
|
||||
int _payloadLength;
|
||||
int _crcMode;
|
||||
int _fifo_tx_addr_ptr;
|
||||
int _fifo_rx_addr_ptr;
|
||||
uint8_t _packet[256];
|
||||
bool _preinit_done;
|
||||
bool _tcxo;
|
||||
bool _radio_online;
|
||||
int _rxPacketLength;
|
||||
uint32_t _bitrate;
|
||||
void (*_receive_callback)(int);
|
||||
};
|
||||
|
||||
extern sx128x sx128x_modem;
|
||||
|
||||
#endif
|
||||
|
|
@ -322,6 +322,14 @@ uint8_t boot_vector = 0x00;
|
|||
void led_tx_off() { digitalWrite(pin_led_tx, LED_OFF); }
|
||||
void led_id_on() { }
|
||||
void led_id_off() { }
|
||||
#elif BOARD_MODEL == BOARD_VME213
|
||||
// VME213 has no physical LEDs, only E-Ink display
|
||||
void led_rx_on() { }
|
||||
void led_rx_off() { }
|
||||
void led_tx_on() { }
|
||||
void led_tx_off() { }
|
||||
void led_id_on() { }
|
||||
void led_id_off() { }
|
||||
#elif BOARD_MODEL == BOARD_HUZZAH32
|
||||
void led_rx_on() { digitalWrite(pin_led_rx, HIGH); }
|
||||
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
|
||||
|
|
|
|||
74
build-vme213.ps1
Normal file
74
build-vme213.ps1
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Build script for Heltec VisionMaster E213
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$ARDUINO_CLI = ".\tools\arduino-cli.exe"
|
||||
$BOARD_MODEL = "0x40"
|
||||
$ARDUINO_ESP_CORE_VER = "2.0.17"
|
||||
|
||||
Write-Host "=== RNode Firmware Build for VME213 ===" -ForegroundColor Cyan
|
||||
|
||||
# Check if arduino-cli exists
|
||||
if (-not (Test-Path $ARDUINO_CLI)) {
|
||||
Write-Host "ERROR: arduino-cli not found at $ARDUINO_CLI" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Update core index
|
||||
Write-Host "`nUpdating Arduino core index..." -ForegroundColor Yellow
|
||||
& $ARDUINO_CLI core update-index --config-file arduino-cli.yaml
|
||||
|
||||
# Install ESP32 core if not installed
|
||||
Write-Host "`nChecking ESP32 core..." -ForegroundColor Yellow
|
||||
$coreInstalled = & $ARDUINO_CLI core list | Select-String "esp32:esp32"
|
||||
if (-not $coreInstalled) {
|
||||
Write-Host "Installing ESP32 core $ARDUINO_ESP_CORE_VER..." -ForegroundColor Yellow
|
||||
& $ARDUINO_CLI core install "esp32:esp32@$ARDUINO_ESP_CORE_VER" --config-file arduino-cli.yaml
|
||||
}
|
||||
|
||||
# Install required libraries
|
||||
Write-Host "`nChecking required libraries..." -ForegroundColor Yellow
|
||||
$requiredLibs = @(
|
||||
"Adafruit SSD1306",
|
||||
"Adafruit SH110X",
|
||||
"Adafruit GFX Library",
|
||||
"Adafruit BusIO",
|
||||
"Crypto"
|
||||
)
|
||||
|
||||
foreach ($lib in $requiredLibs) {
|
||||
$libInstalled = & $ARDUINO_CLI lib list | Select-String $lib
|
||||
if (-not $libInstalled) {
|
||||
Write-Host "Installing $lib..." -ForegroundColor Yellow
|
||||
& $ARDUINO_CLI lib install $lib
|
||||
}
|
||||
}
|
||||
|
||||
# Create build directory
|
||||
if (-not (Test-Path "build")) {
|
||||
New-Item -ItemType Directory -Path "build" | Out-Null
|
||||
}
|
||||
|
||||
# Compile firmware
|
||||
Write-Host "`nCompiling firmware for VME213..." -ForegroundColor Green
|
||||
& $ARDUINO_CLI compile `
|
||||
--fqbn esp32:esp32:esp32s3:CDCOnBoot=cdc,PartitionScheme=no_ota `
|
||||
--build-property "build.extra_flags=-DBOARD_MODEL=$BOARD_MODEL" `
|
||||
--output-dir build `
|
||||
--verbose `
|
||||
RNode_Firmware.ino
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "`n=== Build SUCCESS ===" -ForegroundColor Green
|
||||
Write-Host "Firmware binary: build\RNode_Firmware.ino.bin" -ForegroundColor Cyan
|
||||
|
||||
# Show file size
|
||||
$binFile = "build\RNode_Firmware.ino.bin"
|
||||
if (Test-Path $binFile) {
|
||||
$size = (Get-Item $binFile).Length
|
||||
$sizeKB = [math]::Round($size / 1KB, 2)
|
||||
Write-Host "Binary size: $sizeKB KB" -ForegroundColor Cyan
|
||||
}
|
||||
} else {
|
||||
Write-Host "`n=== Build FAILED ===" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
242
src/LCMEN2R13EFC1.cpp
Normal file
242
src/LCMEN2R13EFC1.cpp
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// E-Ink display driver for LCMEN2R13EFC1
|
||||
// Adapted from Meshtastic NicheGraphics driver
|
||||
|
||||
#include "LCMEN2R13EFC1.h"
|
||||
|
||||
// Look-up tables for FAST refresh
|
||||
static const uint8_t LUT_FAST_VCOMDC[] = {
|
||||
0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01,
|
||||
0x01, 0x06, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_WW[] = {
|
||||
0x01, 0x06, 0x03, 0x02, 0x81, 0x01, 0x01,
|
||||
0x01, 0x06, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_BW[] = {
|
||||
0x01, 0x86, 0x83, 0x82, 0x81, 0x01, 0x01,
|
||||
0x01, 0x86, 0x82, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_WB[] = {
|
||||
0x01, 0x46, 0x43, 0x02, 0x01, 0x01, 0x01,
|
||||
0x01, 0x46, 0x42, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t LUT_FAST_BB[] = {
|
||||
0x01, 0x06, 0x03, 0x42, 0x41, 0x01, 0x01,
|
||||
0x01, 0x06, 0x02, 0x01, 0x01, 0x01, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
LCMEN2R13EFC1::LCMEN2R13EFC1()
|
||||
: _spiSettings(6000000, MSBFIRST, SPI_MODE0)
|
||||
{
|
||||
// Calculate buffer size (8 pixels per byte)
|
||||
_bufferRowSize = ((DISPLAY_WIDTH - 1) / 8) + 1; // 122px -> 16 bytes
|
||||
_bufferSize = _bufferRowSize * DISPLAY_HEIGHT; // 16 * 250 = 4000 bytes
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst) {
|
||||
_spi = spi;
|
||||
_pin_dc = pin_dc;
|
||||
_pin_cs = pin_cs;
|
||||
_pin_busy = pin_busy;
|
||||
_pin_rst = pin_rst;
|
||||
|
||||
pinMode(_pin_dc, OUTPUT);
|
||||
pinMode(_pin_cs, OUTPUT);
|
||||
pinMode(_pin_busy, INPUT);
|
||||
pinMode(_pin_rst, INPUT_PULLUP); // Active low, hold high
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::reset() {
|
||||
pinMode(_pin_rst, OUTPUT);
|
||||
digitalWrite(_pin_rst, LOW);
|
||||
delay(10);
|
||||
pinMode(_pin_rst, INPUT_PULLUP);
|
||||
wait();
|
||||
|
||||
sendCommand(0x12); // Software reset
|
||||
wait();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::wait() {
|
||||
// Busy when LOW
|
||||
while (digitalRead(_pin_busy) == LOW) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
bool LCMEN2R13EFC1::isBusy() {
|
||||
return (digitalRead(_pin_busy) == LOW);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::sendCommand(const uint8_t command) {
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
digitalWrite(_pin_dc, LOW); // DC low = command
|
||||
digitalWrite(_pin_cs, LOW);
|
||||
_spi->transfer(command);
|
||||
digitalWrite(_pin_cs, HIGH);
|
||||
digitalWrite(_pin_dc, HIGH);
|
||||
_spi->endTransaction();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::sendData(uint8_t data) {
|
||||
sendData(&data, 1);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::sendData(const uint8_t *data, uint32_t size) {
|
||||
_spi->beginTransaction(_spiSettings);
|
||||
digitalWrite(_pin_dc, HIGH); // DC high = data
|
||||
digitalWrite(_pin_cs, LOW);
|
||||
|
||||
#if defined(ARCH_ESP32) || defined(ESP32)
|
||||
_spi->transferBytes(data, NULL, size);
|
||||
#else
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
_spi->transfer(data[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
digitalWrite(_pin_cs, HIGH);
|
||||
digitalWrite(_pin_dc, HIGH);
|
||||
_spi->endTransaction();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::configFull() {
|
||||
sendCommand(0x00); // Panel setting register
|
||||
sendData(0b11 << 6 // Display resolution
|
||||
| 1 << 4 // B&W only
|
||||
| 1 << 3 // Vertical scan direction
|
||||
| 1 << 2 // Horizontal scan direction
|
||||
| 1 << 1 // Shutdown: no
|
||||
| 1 << 0 // Reset: no
|
||||
);
|
||||
|
||||
sendCommand(0x50); // VCOM and data interval setting
|
||||
sendData(0b10 << 6 // Border driven white
|
||||
| 0b11 << 4 // Invert image colors: no
|
||||
| 0b0111 // Interval between VCOM on and image data
|
||||
);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::configFast() {
|
||||
sendCommand(0x00); // Panel setting register
|
||||
sendData(0b11 << 6 // Display resolution
|
||||
| 1 << 5 // LUT from registers (set below)
|
||||
| 1 << 4 // B&W only
|
||||
| 1 << 3 // Vertical scan direction
|
||||
| 1 << 2 // Horizontal scan direction
|
||||
| 1 << 1 // Shutdown: no
|
||||
| 1 << 0 // Reset: no
|
||||
);
|
||||
|
||||
sendCommand(0x50); // VCOM and data interval setting
|
||||
sendData(0b11 << 6 // Border floating
|
||||
| 0b01 << 4 // Invert image colors: no
|
||||
| 0b0111 // Interval between VCOM on and image data
|
||||
);
|
||||
|
||||
// Load LUT tables
|
||||
sendCommand(0x20); // VCOM
|
||||
sendData(LUT_FAST_VCOMDC, sizeof(LUT_FAST_VCOMDC));
|
||||
|
||||
sendCommand(0x21); // White -> White
|
||||
sendData(LUT_FAST_WW, sizeof(LUT_FAST_WW));
|
||||
|
||||
sendCommand(0x22); // Black -> White
|
||||
sendData(LUT_FAST_BW, sizeof(LUT_FAST_BW));
|
||||
|
||||
sendCommand(0x23); // White -> Black
|
||||
sendData(LUT_FAST_WB, sizeof(LUT_FAST_WB));
|
||||
|
||||
sendCommand(0x24); // Black -> Black
|
||||
sendData(LUT_FAST_BB, sizeof(LUT_FAST_BB));
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::writeNewImage() {
|
||||
sendCommand(0x13);
|
||||
sendData(_buffer, _bufferSize);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::writeOldImage() {
|
||||
sendCommand(0x10);
|
||||
sendData(_buffer, _bufferSize);
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::update(uint8_t *imageData, UpdateType type) {
|
||||
_updateType = type;
|
||||
_buffer = imageData;
|
||||
|
||||
reset();
|
||||
|
||||
// Configure display
|
||||
if (_updateType == UPDATE_FULL) {
|
||||
configFull();
|
||||
} else {
|
||||
configFast();
|
||||
}
|
||||
|
||||
// Transfer image data
|
||||
if (_updateType == UPDATE_FULL) {
|
||||
writeNewImage();
|
||||
writeOldImage();
|
||||
} else {
|
||||
writeNewImage();
|
||||
}
|
||||
|
||||
// Power on and start refresh
|
||||
sendCommand(0x04); // Power on panel voltage
|
||||
wait();
|
||||
|
||||
sendCommand(0x12); // Begin executing update
|
||||
wait();
|
||||
|
||||
// Power off
|
||||
sendCommand(0x02);
|
||||
wait();
|
||||
|
||||
// Update "old memory" for next differential refresh
|
||||
if (_updateType != UPDATE_FULL) {
|
||||
writeOldImage();
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::powerOn() {
|
||||
sendCommand(0x04);
|
||||
wait();
|
||||
}
|
||||
|
||||
void LCMEN2R13EFC1::powerOff() {
|
||||
sendCommand(0x02);
|
||||
wait();
|
||||
}
|
||||
59
src/LCMEN2R13EFC1.h
Normal file
59
src/LCMEN2R13EFC1.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// E-Ink display driver for LCMEN2R13EFC1 (Heltec VisionMaster E213)
|
||||
// Adapted from Meshtastic firmware for RNode use
|
||||
// Controller IC: Fitipower JD79656
|
||||
// Resolution: 122x250 pixels (width x height)
|
||||
// Supports FAST (partial) and FULL refresh
|
||||
|
||||
#ifndef LCMEN2R13EFC1_H
|
||||
#define LCMEN2R13EFC1_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
|
||||
class LCMEN2R13EFC1 {
|
||||
public:
|
||||
// Display properties
|
||||
static constexpr uint16_t DISPLAY_WIDTH = 122;
|
||||
static constexpr uint16_t DISPLAY_HEIGHT = 250;
|
||||
|
||||
// Update types
|
||||
enum UpdateType {
|
||||
UPDATE_FULL = 0, // Full refresh (slower, ~3.6s)
|
||||
UPDATE_FAST = 1 // Partial refresh (faster, ~720ms)
|
||||
};
|
||||
|
||||
LCMEN2R13EFC1();
|
||||
|
||||
void begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst);
|
||||
void update(uint8_t *imageData, UpdateType type);
|
||||
bool isBusy();
|
||||
void powerOn();
|
||||
void powerOff();
|
||||
|
||||
private:
|
||||
SPIClass *_spi;
|
||||
uint8_t _pin_dc;
|
||||
uint8_t _pin_cs;
|
||||
uint8_t _pin_busy;
|
||||
uint8_t _pin_rst;
|
||||
|
||||
SPISettings _spiSettings;
|
||||
|
||||
uint8_t *_buffer;
|
||||
uint16_t _bufferRowSize;
|
||||
uint32_t _bufferSize;
|
||||
UpdateType _updateType;
|
||||
|
||||
void reset();
|
||||
void wait();
|
||||
void sendCommand(const uint8_t command);
|
||||
void sendData(uint8_t data);
|
||||
void sendData(const uint8_t *data, uint32_t size);
|
||||
|
||||
void configFull();
|
||||
void configFast();
|
||||
void writeNewImage();
|
||||
void writeOldImage();
|
||||
};
|
||||
|
||||
#endif // LCMEN2R13EFC1_H
|
||||
41
src/einkDetect_VME213.h
Normal file
41
src/einkDetect_VME213.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// E-Ink display chip detection for VME213
|
||||
// Detects between LCMEN213EFC1 (V1) and E0213A367 (V1.1)
|
||||
// Based on Meshtastic einkDetect.h
|
||||
|
||||
#ifndef EINK_DETECT_VME213_H
|
||||
#define EINK_DETECT_VME213_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
enum EInkChipType {
|
||||
EINK_LCMEN213EFC1 = 0, // Initial version (Fitipower JD79656)
|
||||
EINK_E0213A367 = 1 // V1.1+ (Solomon Systech SSD1682)
|
||||
};
|
||||
|
||||
// Detect E-Ink controller IC type by BUSY pin logic
|
||||
// Fitipower: BUSY=LOW when busy
|
||||
// Solomon Systech: BUSY=HIGH when busy
|
||||
inline EInkChipType detectEInkChip(uint8_t pin_reset, uint8_t pin_busy) {
|
||||
// Force display BUSY by holding reset pin active
|
||||
pinMode(pin_reset, OUTPUT);
|
||||
digitalWrite(pin_reset, LOW);
|
||||
|
||||
delay(10);
|
||||
|
||||
// Read BUSY pin logic while display is busy
|
||||
pinMode(pin_busy, INPUT);
|
||||
bool busyLogic = digitalRead(pin_busy);
|
||||
|
||||
// Release reset pin
|
||||
pinMode(pin_reset, INPUT);
|
||||
|
||||
// Fitipower = LOW when busy
|
||||
// Solomon = HIGH when busy
|
||||
if (busyLogic == LOW) {
|
||||
return EINK_LCMEN213EFC1;
|
||||
} else {
|
||||
return EINK_E0213A367;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // EINK_DETECT_VME213_H
|
||||
|
|
@ -125,7 +125,7 @@ bool sx126x::preInit() {
|
|||
pinMode(_ss, OUTPUT);
|
||||
digitalWrite(_ss, HIGH);
|
||||
|
||||
#if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_XIAO_S3
|
||||
#if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_XIAO_S3 || BOARD_MODEL == BOARD_VME213
|
||||
SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs);
|
||||
#elif BOARD_MODEL == BOARD_TECHO
|
||||
SPI.setPins(pin_miso, pin_sclk, pin_mosi);
|
||||
|
|
@ -614,7 +614,7 @@ void sx126x::sleep() { uint8_t byte = 0x00; executeOpcode(OP_SLEEP_6X, &byte, 1)
|
|||
|
||||
void sx126x::enableTCXO() {
|
||||
#if HAS_TCXO
|
||||
#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_XIAO_S3
|
||||
#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_XIAO_S3 || BOARD_MODEL == BOARD_VME213
|
||||
uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
|
||||
#elif BOARD_MODEL == BOARD_TBEAM
|
||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
||||
|
|
|
|||
674
tools/LICENSE.txt
Normal file
674
tools/LICENSE.txt
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
BIN
tools/arduino-cli.exe
Normal file
BIN
tools/arduino-cli.exe
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue