mirror of https://github.com/meshcore-dev/MeshCore
committed by
GitHub
71 changed files with 3465 additions and 181 deletions
@ -0,0 +1,90 @@ |
|||
name: Firmware Builder |
|||
|
|||
on: |
|||
workflow_call: |
|||
inputs: |
|||
firmware_type: |
|||
required: true |
|||
type: string |
|||
release_title_prefix: |
|||
required: true |
|||
type: string |
|||
|
|||
jobs: |
|||
|
|||
generate-build-matrix: |
|||
runs-on: ubuntu-latest |
|||
outputs: |
|||
targets: ${{ steps.get-build-targets.outputs.targets }} |
|||
steps: |
|||
|
|||
- name: Clone Repo |
|||
uses: actions/checkout@v6 |
|||
|
|||
- name: Setup Build Environment |
|||
uses: ./.github/actions/setup-build-environment |
|||
|
|||
- name: Get Build Targets |
|||
id: get-build-targets |
|||
run: | |
|||
# get list of firmwares to build |
|||
TARGET_LIST=$(/usr/bin/env bash build.sh get-${{ inputs.firmware_type }}-firmwares-to-build) |
|||
|
|||
# convert targets separated by new line into a json array string |
|||
JSON_ARRAY=$(echo "$TARGET_LIST" | jq -R -s -c 'split("\n") | map(select(length > 0))') |
|||
|
|||
# use json array as targets result |
|||
echo "targets=$JSON_ARRAY" >> $GITHUB_OUTPUT |
|||
|
|||
build: |
|||
needs: generate-build-matrix |
|||
runs-on: ubuntu-latest |
|||
continue-on-error: true # don't fail entire build if one board fails to build |
|||
strategy: |
|||
matrix: |
|||
target: ${{ fromJson(needs.generate-build-matrix.outputs.targets) }} |
|||
fail-fast: false # don't cancel other builds if one board fails to build |
|||
steps: |
|||
|
|||
- name: Clone Repo |
|||
uses: actions/checkout@v6 |
|||
|
|||
- name: Setup Build Environment |
|||
uses: ./.github/actions/setup-build-environment |
|||
|
|||
- name: Build Firmware |
|||
env: |
|||
FIRMWARE_VERSION: ${{ env.GIT_TAG_VERSION }} |
|||
run: /usr/bin/env bash build.sh build-firmware ${{ matrix.target }} |
|||
|
|||
- name: Upload Workflow Artifacts |
|||
uses: actions/upload-artifact@v7 |
|||
with: |
|||
name: "${{ matrix.target }}" |
|||
path: out |
|||
|
|||
create-release: |
|||
needs: build |
|||
runs-on: ubuntu-latest |
|||
if: startsWith(github.ref, 'refs/tags/') # only create release for tagged builds |
|||
steps: |
|||
|
|||
- name: Clone Repo |
|||
uses: actions/checkout@v6 |
|||
|
|||
- name: Setup Build Environment |
|||
uses: ./.github/actions/setup-build-environment |
|||
|
|||
- name: Download All Artifacts |
|||
uses: actions/download-artifact@v8 |
|||
with: |
|||
merge-multiple: true |
|||
path: out |
|||
|
|||
- name: Create Release |
|||
uses: softprops/action-gh-release@v3 |
|||
with: |
|||
name: "${{ inputs.release_title_prefix }} ${{ env.GIT_TAG_VERSION }}" |
|||
body: "" |
|||
draft: true |
|||
files: out/* |
|||
@ -0,0 +1,32 @@ |
|||
name: 'Run Stale Bot' |
|||
on: |
|||
schedule: |
|||
- cron: '30 1 * * *' # daily at 1:30am |
|||
workflow_dispatch: {} |
|||
|
|||
permissions: |
|||
actions: write |
|||
issues: write |
|||
pull-requests: write |
|||
|
|||
jobs: |
|||
close-issues: |
|||
# only run on main repo, not forks |
|||
if: github.repository == 'meshcore-dev/MeshCore' |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- name: Close Stale Issues |
|||
uses: actions/stale@v10 |
|||
with: |
|||
repo-token: ${{ secrets.GITHUB_TOKEN }} |
|||
# auto close issues |
|||
days-before-issue-stale: 60 |
|||
days-before-issue-close: 7 |
|||
exempt-issue-labels: "keep-open" |
|||
stale-issue-label: "stale" |
|||
stale-issue-message: "This issue is stale because it has been open for 60 days with no activity. Remove the stale label or add a comment if this issue is still relevant, otherwise this issue will automatically close in 7 days." |
|||
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale." |
|||
# don't auto close prs |
|||
days-before-pr-stale: -1 |
|||
days-before-pr-close: -1 |
|||
|
|||
@ -0,0 +1,57 @@ |
|||
# Security Policy |
|||
|
|||
## Supported Versions |
|||
|
|||
Security fixes are applied to the latest release only. We do not backport |
|||
fixes to older versions. |
|||
|
|||
| Version | Supported | |
|||
|---------|-----------| |
|||
| 1.15+ | ✅ | |
|||
| <1.15 | ❌ | |
|||
|
|||
## Reporting a Vulnerability |
|||
|
|||
**Please do not report security vulnerabilities through public GitHub issues.** |
|||
|
|||
Use GitHub's private vulnerability reporting instead: |
|||
1. Go to the **Security** tab of this repository |
|||
2. Click **Report a vulnerability** |
|||
3. Fill in the details and submit |
|||
|
|||
### What to include |
|||
|
|||
A useful report tells us: |
|||
- Which component or file is affected |
|||
- What an attacker can do (impact) and under what conditions |
|||
- A minimal reproduction case or proof-of-concept if you have one |
|||
- Whether you believe it is remotely exploitable |
|||
|
|||
You do not need a working exploit to report. An incomplete report is better |
|||
than no report. |
|||
|
|||
## What to expect |
|||
|
|||
This is a volunteer-maintained open-source project. We will do our best to |
|||
respond in a reasonable timeframe, but cannot commit to specific deadlines. |
|||
|
|||
We ask that you give us a fair opportunity to investigate and address the |
|||
issue before any public disclosure. If you have not heard back after |
|||
**90 days**, feel free to follow up or proceed with disclosure at your |
|||
discretion. |
|||
|
|||
## Scope |
|||
|
|||
In scope: |
|||
- Remote code execution, memory corruption, or denial-of-service via crafted |
|||
radio packets |
|||
- Authentication or encryption bypasses |
|||
- Vulnerabilities in the packet routing or path handling logic |
|||
|
|||
Out of scope: |
|||
- Physical access attacks (e.g., JTAG, UART extraction of keys) |
|||
- Regulatory compliance (duty cycle, frequency restrictions) |
|||
- Jamming or other physical-layer radio interference |
|||
- Issues in third-party libraries (RadioLib, Crypto, etc.) — report those |
|||
upstream |
|||
- "Best practice" suggestions without a demonstrated attack path |
|||
@ -0,0 +1,43 @@ |
|||
{ |
|||
"build": { |
|||
"arduino": { |
|||
"ldscript": "esp32s3_out.ld", |
|||
"partitions": "default_16MB.csv", |
|||
"memory_type": "qio_opi" |
|||
}, |
|||
"core": "esp32", |
|||
"extra_flags": [ |
|||
"-DBOARD_HAS_PSRAM", |
|||
"-DARDUINO_USB_CDC_ON_BOOT=1", |
|||
"-DARDUINO_USB_MODE=1", |
|||
"-DARDUINO_RUNNING_CORE=1", |
|||
"-DARDUINO_EVENT_RUNNING_CORE=1" |
|||
], |
|||
"f_cpu": "240000000L", |
|||
"f_flash": "80000000L", |
|||
"flash_mode": "qio", |
|||
"psram_type": "opi", |
|||
"hwids": [["0x303A", "0x1001"]], |
|||
"mcu": "esp32s3", |
|||
"variant": "heltec_rc32" |
|||
}, |
|||
"connectivity": ["wifi", "bluetooth", "lora"], |
|||
"debug": { |
|||
"default_tool": "esp-builtin", |
|||
"onboard_tools": ["esp-builtin"], |
|||
"openocd_target": "esp32s3.cfg" |
|||
}, |
|||
"frameworks": ["arduino", "espidf"], |
|||
"name": "Heltec RC32 (16 MB FLASH, 8 MB PSRAM)", |
|||
"upload": { |
|||
"flash_size": "16MB", |
|||
"maximum_ram_size": 327680, |
|||
"maximum_size": 16777216, |
|||
"use_1200bps_touch": true, |
|||
"wait_for_upload_port": true, |
|||
"require_upload_port": true, |
|||
"speed": 921600 |
|||
}, |
|||
"url": "https://heltec.org/", |
|||
"vendor": "Heltec" |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
{ |
|||
"build": { |
|||
"arduino": { |
|||
"ldscript": "esp32s3_out.ld", |
|||
"partitions": "default_16MB.csv", |
|||
"memory_type": "qio_opi" |
|||
}, |
|||
"core": "esp32", |
|||
"extra_flags": [ |
|||
"-DBOARD_HAS_PSRAM", |
|||
"-DARDUINO_USB_CDC_ON_BOOT=1", |
|||
"-DARDUINO_USB_MODE=1", |
|||
"-DARDUINO_RUNNING_CORE=1", |
|||
"-DARDUINO_EVENT_RUNNING_CORE=1" |
|||
], |
|||
"f_cpu": "240000000L", |
|||
"f_flash": "80000000L", |
|||
"flash_mode": "qio", |
|||
"psram_type": "opi", |
|||
"hwids": [["0x303A", "0x1001"]], |
|||
"mcu": "esp32s3", |
|||
"variant": "heltec_v4_r8" |
|||
}, |
|||
"connectivity": ["wifi", "bluetooth", "lora"], |
|||
"debug": { |
|||
"default_tool": "esp-builtin", |
|||
"onboard_tools": ["esp-builtin"], |
|||
"openocd_target": "esp32s3.cfg" |
|||
}, |
|||
"frameworks": ["arduino", "espidf"], |
|||
"name": "heltec_wifi_lora_32 v4 r8 (16 MB FLASH, 8 MB PSRAM)", |
|||
"upload": { |
|||
"flash_size": "16MB", |
|||
"maximum_ram_size": 327680, |
|||
"maximum_size": 16777216, |
|||
"use_1200bps_touch": true, |
|||
"wait_for_upload_port": true, |
|||
"require_upload_port": true, |
|||
"speed": 921600 |
|||
}, |
|||
"url": "https://heltec.org/", |
|||
"vendor": "heltec" |
|||
} |
|||
@ -0,0 +1,157 @@ |
|||
#pragma once |
|||
|
|||
#ifdef ETHERNET_ENABLED |
|||
|
|||
#include <Arduino.h> |
|||
#include <SPI.h> |
|||
#include <RAK13800_W5100S.h> |
|||
#include <helpers/nrf52/EthernetMac.h> |
|||
|
|||
#define PIN_SPI1_MISO (29) |
|||
#define PIN_SPI1_MOSI (30) |
|||
#define PIN_SPI1_SCK (3) |
|||
|
|||
static SPIClass ETHERNET_SPI_PORT(NRF_SPIM1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI); |
|||
|
|||
#define PIN_ETHERNET_POWER_EN WB_IO2 |
|||
#define PIN_ETHERNET_RESET 21 |
|||
#define PIN_ETHERNET_SS 26 |
|||
|
|||
#ifndef ETHERNET_TCP_PORT |
|||
#define ETHERNET_TCP_PORT 23 // telnet port for CLI access
|
|||
#endif |
|||
|
|||
#ifndef ETHERNET_CLI_BANNER |
|||
#define ETHERNET_CLI_BANNER "MeshCore CLI" |
|||
#endif |
|||
|
|||
#define ETHERNET_RETRY_INTERVAL_MS 30000 |
|||
|
|||
static EthernetServer ethernet_server(ETHERNET_TCP_PORT); |
|||
static EthernetClient ethernet_client; |
|||
static volatile bool ethernet_running = false; |
|||
|
|||
// FreeRTOS task: handles hw init, DHCP, and retries in the background
|
|||
static void ethernet_task(void* param) { |
|||
(void)param; |
|||
|
|||
Serial.println("ETH: Initializing hardware"); |
|||
// WB_IO2 (power enable) is already driven HIGH by early constructor
|
|||
// in RAK4631Board.cpp to support POE boot.
|
|||
// Skip hardware reset — the W5100S comes out of power-on reset cleanly,
|
|||
// and toggling reset kills the PHY link which breaks POE power.
|
|||
pinMode(PIN_ETHERNET_RESET, OUTPUT); |
|||
digitalWrite(PIN_ETHERNET_RESET, HIGH); |
|||
|
|||
ETHERNET_SPI_PORT.begin(); |
|||
Ethernet.init(ETHERNET_SPI_PORT, PIN_ETHERNET_SS); |
|||
|
|||
uint8_t mac[6]; |
|||
generateEthernetMac(mac); |
|||
Serial.printf("ETH: MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", |
|||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
|||
|
|||
// Retry loop: keep trying until we get an IP
|
|||
while (!ethernet_running) { |
|||
Serial.println("ETH: Attempting DHCP..."); |
|||
if (Ethernet.begin(mac, 10000, 2000) == 0) { |
|||
if (Ethernet.hardwareStatus() == EthernetNoHardware) { |
|||
Serial.println("ETH: Hardware not found, giving up"); |
|||
vTaskDelete(NULL); |
|||
return; |
|||
} |
|||
if (Ethernet.linkStatus() == LinkOFF) { |
|||
Serial.println("ETH: Cable not connected, will retry"); |
|||
} else { |
|||
Serial.println("ETH: DHCP failed, will retry"); |
|||
} |
|||
vTaskDelay(pdMS_TO_TICKS(ETHERNET_RETRY_INTERVAL_MS)); |
|||
continue; |
|||
} |
|||
|
|||
IPAddress ip = Ethernet.localIP(); |
|||
Serial.printf("ETH: IP: %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); |
|||
Serial.printf("ETH: Listening on TCP port %d\n", ETHERNET_TCP_PORT); |
|||
ethernet_server.begin(); |
|||
ethernet_running = true; |
|||
} |
|||
|
|||
// DHCP succeeded, task is done
|
|||
vTaskDelete(NULL); |
|||
} |
|||
|
|||
static void ethernet_start_task() { |
|||
xTaskCreate(ethernet_task, "eth_init", 1024, NULL, 1, NULL); |
|||
} |
|||
|
|||
// Format ethernet status into reply buffer. Returns true if command was handled.
|
|||
static bool ethernet_handle_command(const char* command, char* reply) { |
|||
if (strcmp(command, "eth.status") == 0) { |
|||
if (!ethernet_running) { |
|||
strcpy(reply, "ETH: not connected"); |
|||
} else { |
|||
IPAddress ip = Ethernet.localIP(); |
|||
sprintf(reply, "ETH: %u.%u.%u.%u:%d", ip[0], ip[1], ip[2], ip[3], ETHERNET_TCP_PORT); |
|||
} |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// Check for new TCP client connections, replacing any existing connection.
|
|||
// Use accept() (not available()) so we only see newly-accepted sockets;
|
|||
// available() also returns existing connected sockets that have data, which
|
|||
// would force us to disambiguate every inbound packet from a real new client.
|
|||
static void ethernet_check_client() { |
|||
auto newClient = ethernet_server.accept(); |
|||
if (newClient) { |
|||
if (ethernet_client) ethernet_client.stop(); |
|||
ethernet_client = newClient; |
|||
IPAddress ip = ethernet_client.remoteIP(); |
|||
Serial.printf("ETH: Client connected from %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); |
|||
ethernet_client.println(ETHERNET_CLI_BANNER); |
|||
} |
|||
} |
|||
|
|||
// Call from loop() to maintain DHCP and check for new clients
|
|||
static void ethernet_loop_maintain() { |
|||
if (ethernet_running) { |
|||
ethernet_check_client(); |
|||
Ethernet.maintain(); |
|||
} |
|||
} |
|||
|
|||
// Read a line from the Ethernet client into the command buffer.
|
|||
// Returns true when a complete line is ready to process (command is null-terminated).
|
|||
// The caller should process the command and then reset ethernet_command[0] = 0.
|
|||
static bool ethernet_read_line(char* ethernet_command, size_t buf_size) { |
|||
if (!ethernet_running || !ethernet_client || !ethernet_client.connected()) return false; |
|||
|
|||
int elen = strlen(ethernet_command); |
|||
while (ethernet_client.available() && elen < (int)buf_size - 1) { |
|||
char c = ethernet_client.read(); |
|||
if (c == '\n' && elen == 0) continue; // ignore leading LF (from CR+LF)
|
|||
if (c == '\r' || c == '\n') { ethernet_command[elen++] = '\r'; break; } |
|||
ethernet_command[elen++] = c; |
|||
ethernet_command[elen] = 0; |
|||
} |
|||
if (elen == (int)buf_size - 1) { |
|||
ethernet_command[buf_size - 1] = '\r'; |
|||
} |
|||
|
|||
if (elen > 0 && ethernet_command[elen - 1] == '\r') { |
|||
ethernet_command[elen - 1] = 0; |
|||
ethernet_client.println(); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// Send a reply to the Ethernet client
|
|||
static void ethernet_send_reply(const char* reply) { |
|||
if (reply[0]) { |
|||
ethernet_client.print(" -> "); ethernet_client.println(reply); |
|||
} |
|||
} |
|||
|
|||
#endif // ETHERNET_ENABLED
|
|||
@ -0,0 +1,13 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
|
|||
static inline void generateEthernetMac(uint8_t mac[6]) { |
|||
uint32_t device_id = NRF_FICR->DEVICEID[0]; |
|||
mac[0] = 0x02; |
|||
mac[1] = 0x92; |
|||
mac[2] = 0x1F; |
|||
mac[3] = (device_id >> 16) & 0xFF; |
|||
mac[4] = (device_id >> 8) & 0xFF; |
|||
mac[5] = device_id & 0xFF; |
|||
} |
|||
@ -0,0 +1,268 @@ |
|||
#ifdef ETHERNET_ENABLED |
|||
|
|||
#include "SerialEthernetInterface.h" |
|||
#include "EthernetMac.h" |
|||
#include <SPI.h> |
|||
#include <EthernetUdp.h> |
|||
|
|||
#define PIN_SPI1_MISO (29) // (0 + 29)
|
|||
#define PIN_SPI1_MOSI (30) // (0 + 30)
|
|||
#define PIN_SPI1_SCK (3) // (0 + 3)
|
|||
|
|||
SPIClass ETHERNET_SPI_PORT(NRF_SPIM1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SPI1_MOSI); |
|||
|
|||
#define PIN_ETHERNET_POWER_EN WB_IO2 // output, high to enable
|
|||
#define PIN_ETHERNET_RESET 21 |
|||
#define PIN_ETHERNET_SS 26 |
|||
|
|||
#define RECV_STATE_IDLE 0 |
|||
#define RECV_STATE_HDR_FOUND 1 |
|||
#define RECV_STATE_LEN1_FOUND 2 |
|||
#define RECV_STATE_LEN2_FOUND 3 |
|||
|
|||
bool SerialEthernetInterface::begin() { |
|||
|
|||
ETHERNET_DEBUG_PRINTLN("Ethernet initializing"); |
|||
|
|||
// WB_IO2 (power enable) is already driven HIGH by early constructor
|
|||
// in RAK4631Board.cpp to support POE boot.
|
|||
// Skip hardware reset — the W5100S comes out of power-on reset cleanly,
|
|||
// and toggling reset kills the PHY link which breaks POE power.
|
|||
#ifdef PIN_ETHERNET_RESET |
|||
pinMode(PIN_ETHERNET_RESET, OUTPUT); |
|||
digitalWrite(PIN_ETHERNET_RESET, HIGH); |
|||
#endif |
|||
|
|||
uint8_t mac[6]; |
|||
generateEthernetMac(mac); |
|||
ETHERNET_DEBUG_PRINTLN( |
|||
"Ethernet MAC: %02X:%02X:%02X:%02X:%02X:%02X", |
|||
mac[0], |
|||
mac[1], |
|||
mac[2], |
|||
mac[3], |
|||
mac[4], |
|||
mac[5]); |
|||
ETHERNET_DEBUG_PRINTLN("Init"); |
|||
ETHERNET_SPI_PORT.begin(); |
|||
Ethernet.init(ETHERNET_SPI_PORT, PIN_ETHERNET_SS); |
|||
|
|||
// Use static IP if build flags are defined, otherwise DHCP
|
|||
#if defined(ETHERNET_STATIC_IP) && defined(ETHERNET_STATIC_GATEWAY) && defined(ETHERNET_STATIC_SUBNET) && defined(ETHERNET_STATIC_DNS) |
|||
IPAddress ip(ETHERNET_STATIC_IP); |
|||
IPAddress gateway(ETHERNET_STATIC_GATEWAY); |
|||
IPAddress subnet(ETHERNET_STATIC_SUBNET); |
|||
IPAddress dns(ETHERNET_STATIC_DNS); |
|||
Ethernet.begin(mac, ip, dns, gateway, subnet); |
|||
#else |
|||
ETHERNET_DEBUG_PRINTLN("Begin"); |
|||
if (Ethernet.begin(mac) == 0) { |
|||
ETHERNET_DEBUG_PRINTLN("Begin failed."); |
|||
|
|||
// DHCP failed -- let's figure out why
|
|||
if (Ethernet.hardwareStatus() == EthernetNoHardware) // Check for Ethernet hardware present.
|
|||
{ |
|||
ETHERNET_DEBUG_PRINTLN("Ethernet hardware not found."); |
|||
return false; |
|||
} |
|||
if (Ethernet.linkStatus() == LinkOFF) // No physical connection
|
|||
{ |
|||
ETHERNET_DEBUG_PRINTLN("Ethernet cable not connected."); |
|||
return false; |
|||
} |
|||
ETHERNET_DEBUG_PRINTLN("Ethernet: DHCP failed for unknown reason."); |
|||
return false; |
|||
} |
|||
#endif |
|||
ETHERNET_DEBUG_PRINTLN("Ethernet begin complete"); |
|||
ETHERNET_DEBUG_PRINT_IP("IP", Ethernet.localIP()); |
|||
ETHERNET_DEBUG_PRINT_IP("Subnet", Ethernet.subnetMask()); |
|||
ETHERNET_DEBUG_PRINT_IP("Gateway", Ethernet.gatewayIP()); |
|||
|
|||
server.begin(); // start listening for clients
|
|||
ETHERNET_DEBUG_PRINTLN("Ethernet: listening on TCP port: %d", ETHERNET_TCP_PORT); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void SerialEthernetInterface::enable() { |
|||
if (_isEnabled) return; |
|||
|
|||
_isEnabled = true; |
|||
clearBuffers(); |
|||
} |
|||
|
|||
void SerialEthernetInterface::disable() { |
|||
_isEnabled = false; |
|||
} |
|||
|
|||
size_t SerialEthernetInterface::writeFrame(const uint8_t src[], size_t len) { |
|||
if (len > MAX_FRAME_SIZE) { |
|||
ETHERNET_DEBUG_PRINTLN("writeFrame(), frame too big, len=%d\n", len); |
|||
return 0; |
|||
} |
|||
|
|||
if (deviceConnected && len > 0) { |
|||
if (send_queue_len >= FRAME_QUEUE_SIZE) { |
|||
ETHERNET_DEBUG_PRINTLN("writeFrame(), send_queue is full!"); |
|||
return 0; |
|||
} |
|||
|
|||
send_queue[send_queue_len].len = len; // add to send queue
|
|||
memcpy(send_queue[send_queue_len].buf, src, len); |
|||
send_queue_len++; |
|||
|
|||
return len; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
bool SerialEthernetInterface::isWriteBusy() const { |
|||
return false; |
|||
} |
|||
|
|||
size_t SerialEthernetInterface::checkRecvFrame(uint8_t dest[]) { |
|||
// Use accept() (not available()) so we only see newly-accepted sockets.
|
|||
// available() also returns existing connected sockets that have data,
|
|||
// which would cause us to treat each inbound packet as a "new client"
|
|||
// and stop() the underlying socket — disconnecting the companion.
|
|||
auto newClient = server.accept(); |
|||
if (newClient) { |
|||
IPAddress new_ip = newClient.remoteIP(); |
|||
uint16_t new_port = newClient.remotePort(); |
|||
ETHERNET_DEBUG_PRINTLN( |
|||
"New client accepted %u.%u.%u.%u:%u", |
|||
new_ip[0], |
|||
new_ip[1], |
|||
new_ip[2], |
|||
new_ip[3], |
|||
new_port); |
|||
|
|||
deviceConnected = false; |
|||
if (client) { |
|||
ETHERNET_DEBUG_PRINTLN("Closing previous client"); |
|||
client.stop(); |
|||
} |
|||
_state = RECV_STATE_IDLE; |
|||
_frame_len = 0; |
|||
_rx_len = 0; |
|||
client = newClient; |
|||
ETHERNET_DEBUG_PRINTLN("Switched to new client"); |
|||
} |
|||
|
|||
if (client.connected()) { |
|||
if (!deviceConnected) { |
|||
ETHERNET_DEBUG_PRINTLN( |
|||
"Got connection %u.%u.%u.%u:%u", |
|||
client.remoteIP()[0], |
|||
client.remoteIP()[1], |
|||
client.remoteIP()[2], |
|||
client.remoteIP()[3], |
|||
client.remotePort()); |
|||
deviceConnected = true; |
|||
} |
|||
} else { |
|||
if (deviceConnected) { |
|||
deviceConnected = false; |
|||
ETHERNET_DEBUG_PRINTLN("Disconnected"); |
|||
} |
|||
} |
|||
|
|||
if (deviceConnected) { |
|||
if (send_queue_len > 0) { // first, check send queue
|
|||
|
|||
_last_write = millis(); |
|||
int len = send_queue[0].len; |
|||
|
|||
#if ETHERNET_RAW_LINE |
|||
ETHERNET_DEBUG_PRINTLN("TX line len=%d", len); |
|||
client.write(send_queue[0].buf, len); |
|||
client.write("\r\n", 2); |
|||
#else |
|||
uint8_t pkt[3+len]; // use same header as serial interface so client can delimit frames
|
|||
pkt[0] = '>'; |
|||
pkt[1] = (len & 0xFF); // LSB
|
|||
pkt[2] = (len >> 8); // MSB
|
|||
memcpy(&pkt[3], send_queue[0].buf, send_queue[0].len); |
|||
ETHERNET_DEBUG_PRINTLN("Sending frame len=%d", len); |
|||
#if ETHERNET_DEBUG_LOGGING && ARDUINO |
|||
ETHERNET_DEBUG_PRINTLN("TX frame len=%d", len); |
|||
#endif |
|||
client.write(pkt, 3 + len); |
|||
#endif |
|||
send_queue_len--; |
|||
for (int i = 0; i < send_queue_len; i++) { // delete top item from queue
|
|||
send_queue[i] = send_queue[i + 1]; |
|||
} |
|||
} else { |
|||
while (client.available()) { |
|||
int c = client.read(); |
|||
if (c < 0) break; |
|||
|
|||
#if ETHERNET_RAW_LINE |
|||
if (c == '\r' || c == '\n') { |
|||
if (_rx_len == 0) { |
|||
continue; |
|||
} |
|||
uint16_t out_len = _rx_len; |
|||
if (out_len > MAX_FRAME_SIZE) { |
|||
out_len = MAX_FRAME_SIZE; |
|||
} |
|||
memcpy(dest, _rx_buf, out_len); |
|||
_rx_len = 0; |
|||
return out_len; |
|||
} |
|||
if (_rx_len < MAX_FRAME_SIZE) { |
|||
_rx_buf[_rx_len] = (uint8_t)c; |
|||
_rx_len++; |
|||
} |
|||
#else |
|||
switch (_state) { |
|||
case RECV_STATE_IDLE: |
|||
if (c == '<') { |
|||
_state = RECV_STATE_HDR_FOUND; |
|||
} |
|||
break; |
|||
case RECV_STATE_HDR_FOUND: |
|||
_frame_len = (uint8_t)c; |
|||
_state = RECV_STATE_LEN1_FOUND; |
|||
break; |
|||
case RECV_STATE_LEN1_FOUND: |
|||
_frame_len |= ((uint16_t)c) << 8; |
|||
_rx_len = 0; |
|||
_state = _frame_len > 0 ? RECV_STATE_LEN2_FOUND : RECV_STATE_IDLE; |
|||
break; |
|||
default: |
|||
if (_rx_len < MAX_FRAME_SIZE) { |
|||
_rx_buf[_rx_len] = (uint8_t)c; |
|||
} |
|||
_rx_len++; |
|||
if (_rx_len >= _frame_len) { |
|||
if (_frame_len > MAX_FRAME_SIZE) { |
|||
_frame_len = MAX_FRAME_SIZE; |
|||
} |
|||
#if ETHERNET_DEBUG_LOGGING && ARDUINO |
|||
ETHERNET_DEBUG_PRINTLN("RX frame len=%d", _frame_len); |
|||
#endif |
|||
memcpy(dest, _rx_buf, _frame_len); |
|||
_state = RECV_STATE_IDLE; |
|||
return _frame_len; |
|||
} |
|||
} |
|||
#endif |
|||
} |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
bool SerialEthernetInterface::isConnected() const { |
|||
return deviceConnected; |
|||
} |
|||
|
|||
void SerialEthernetInterface::loop() { |
|||
Ethernet.maintain(); |
|||
} |
|||
|
|||
#endif // ETHERNET_ENABLED
|
|||
@ -0,0 +1,82 @@ |
|||
#pragma once |
|||
|
|||
#ifdef ETHERNET_ENABLED |
|||
|
|||
#include "helpers/BaseSerialInterface.h" |
|||
#include <SPI.h> |
|||
#include <RAK13800_W5100S.h> |
|||
|
|||
#ifndef ETHERNET_TCP_PORT |
|||
#define ETHERNET_TCP_PORT 5000 |
|||
#endif |
|||
// define ETHERNET_RAW_LINE=1 to use raw line-based CLI instead of framed packets
|
|||
|
|||
class SerialEthernetInterface : public BaseSerialInterface { |
|||
bool deviceConnected; |
|||
bool _isEnabled; |
|||
unsigned long _last_write; |
|||
uint8_t _state; |
|||
uint16_t _frame_len; |
|||
uint16_t _rx_len; |
|||
uint8_t _rx_buf[MAX_FRAME_SIZE]; |
|||
|
|||
EthernetServer server; |
|||
EthernetClient client; |
|||
|
|||
struct Frame { |
|||
uint8_t len; |
|||
uint8_t buf[MAX_FRAME_SIZE]; |
|||
}; |
|||
|
|||
#define FRAME_QUEUE_SIZE 4 |
|||
int send_queue_len; |
|||
Frame send_queue[FRAME_QUEUE_SIZE]; |
|||
|
|||
void clearBuffers() { |
|||
send_queue_len = 0; |
|||
_state = 0; |
|||
_frame_len = 0; |
|||
_rx_len = 0; |
|||
} |
|||
|
|||
protected: |
|||
|
|||
public: |
|||
SerialEthernetInterface() : server(EthernetServer(ETHERNET_TCP_PORT)) { |
|||
deviceConnected = false; |
|||
_isEnabled = false; |
|||
_last_write = 0; |
|||
send_queue_len = 0; |
|||
_state = 0; |
|||
_frame_len = 0; |
|||
_rx_len = 0; |
|||
} |
|||
bool begin(); |
|||
|
|||
// BaseSerialInterface methods
|
|||
void enable() override; |
|||
void disable() override; |
|||
bool isEnabled() const override { return _isEnabled; } |
|||
|
|||
bool isConnected() const override; |
|||
bool isWriteBusy() const override; |
|||
|
|||
size_t writeFrame(const uint8_t src[], size_t len) override; |
|||
size_t checkRecvFrame(uint8_t dest[]) override; |
|||
|
|||
void loop(); |
|||
}; |
|||
|
|||
|
|||
#if ETHERNET_DEBUG_LOGGING && ARDUINO |
|||
#include <Arduino.h> |
|||
#define ETHERNET_DEBUG_PRINT(F, ...) Serial.printf("ETH: " F, ##__VA_ARGS__) |
|||
#define ETHERNET_DEBUG_PRINTLN(F, ...) Serial.printf("ETH: " F "\n", ##__VA_ARGS__) |
|||
#define ETHERNET_DEBUG_PRINT_IP(name, ip) Serial.printf(name ": %u.%u.%u.%u" "\n", ip[0], ip[1], ip[2], ip[3]) |
|||
#else |
|||
#define ETHERNET_DEBUG_PRINT(...) {} |
|||
#define ETHERNET_DEBUG_PRINTLN(...) {} |
|||
#define ETHERNET_DEBUG_PRINT_IP(...) {} |
|||
#endif |
|||
|
|||
#endif // ETHERNET_ENABLED
|
|||
@ -0,0 +1,553 @@ |
|||
#include "NV3001BDisplay.h" |
|||
#include <Arduino.h> |
|||
#include <string.h> |
|||
|
|||
#ifndef SPI_FREQUENCY |
|||
#define SPI_FREQUENCY 8000000 |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_SCL |
|||
#error "PIN_TFT_SCL must be defined" |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_SDA |
|||
#error "PIN_TFT_SDA must be defined" |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_CS |
|||
#error "PIN_TFT_CS must be defined" |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_DC |
|||
#error "PIN_TFT_DC must be defined" |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_MISO |
|||
#define PIN_TFT_MISO -1 |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_RST |
|||
#define PIN_TFT_RST -1 |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_EN |
|||
#define PIN_TFT_EN -1 |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_BL |
|||
#define PIN_TFT_BL -1 |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_EN_ACTIVE |
|||
#define PIN_TFT_EN_ACTIVE LOW |
|||
#endif |
|||
|
|||
#ifndef PIN_TFT_BL_ACTIVE |
|||
#define PIN_TFT_BL_ACTIVE HIGH |
|||
#endif |
|||
|
|||
#ifndef DISPLAY_ROTATION |
|||
#define DISPLAY_ROTATION 0 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_SCREEN_WIDTH |
|||
#define NV3001B_SCREEN_WIDTH 220 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_SCREEN_HEIGHT |
|||
#define NV3001B_SCREEN_HEIGHT 128 |
|||
#endif |
|||
|
|||
#ifndef DISPLAY_SCALE_X |
|||
#define DISPLAY_SCALE_X ((float)NV3001B_SCREEN_WIDTH / NV3001B_LOGICAL_WIDTH) |
|||
#endif |
|||
|
|||
#ifndef DISPLAY_SCALE_Y |
|||
#define DISPLAY_SCALE_Y ((float)NV3001B_SCREEN_HEIGHT / NV3001B_LOGICAL_HEIGHT) |
|||
#endif |
|||
|
|||
#define NV3001B_SWRESET 0x01 |
|||
#define NV3001B_SLPOUT 0x11 |
|||
#define NV3001B_DISPON 0x29 |
|||
#define NV3001B_CASET 0x2A |
|||
#define NV3001B_RASET 0x2B |
|||
#define NV3001B_RAMWR 0x2C |
|||
#define NV3001B_MADCTL 0x36 |
|||
#define NV3001B_COLMOD 0x3A |
|||
|
|||
#define NV3001B_MADCTL_MY 0x80 |
|||
#define NV3001B_MADCTL_MX 0x40 |
|||
#define NV3001B_MADCTL_MV 0x20 |
|||
#define NV3001B_MADCTL_RGB 0x00 |
|||
|
|||
#ifndef NV3001B_TEXT_SIZE1_SCALE_X |
|||
#define NV3001B_TEXT_SIZE1_SCALE_X 1 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_TEXT_SIZE1_SCALE_Y |
|||
#define NV3001B_TEXT_SIZE1_SCALE_Y 2 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_TEXT_SIZE2_SCALE_X |
|||
#define NV3001B_TEXT_SIZE2_SCALE_X 2 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_TEXT_SIZE2_SCALE_Y |
|||
#define NV3001B_TEXT_SIZE2_SCALE_Y 3 |
|||
#endif |
|||
|
|||
static uint16_t mapColor(DisplayDriver::Color c) { |
|||
switch (c) { |
|||
case DisplayDriver::DARK: return 0x0000; |
|||
case DisplayDriver::LIGHT: return 0xffff; |
|||
case DisplayDriver::RED: return 0xf800; |
|||
case DisplayDriver::GREEN: return 0x07e0; |
|||
case DisplayDriver::BLUE: return 0x001f; |
|||
case DisplayDriver::YELLOW: return 0xffe0; |
|||
case DisplayDriver::ORANGE: return 0xfd20; |
|||
default: return 0xffff; |
|||
} |
|||
} |
|||
|
|||
static int scaleX(int x) { |
|||
return (int)(x * DISPLAY_SCALE_X); |
|||
} |
|||
|
|||
static int scaleY(int y) { |
|||
return (int)(y * DISPLAY_SCALE_Y); |
|||
} |
|||
|
|||
static int scaleWidth(int x, int w) { |
|||
if (w <= 0) return 0; |
|||
int scaled = scaleX(x + w) - scaleX(x); |
|||
return scaled > 0 ? scaled : 1; |
|||
} |
|||
|
|||
static int scaleHeight(int y, int h) { |
|||
if (h <= 0) return 0; |
|||
int scaled = scaleY(y + h) - scaleY(y); |
|||
return scaled > 0 ? scaled : 1; |
|||
} |
|||
|
|||
static uint8_t nv3001bMADCTL(uint8_t rotation) { |
|||
uint8_t madctl; |
|||
switch (rotation & 3) { |
|||
case 0: |
|||
madctl = NV3001B_MADCTL_MY | NV3001B_MADCTL_MV | NV3001B_MADCTL_RGB; |
|||
break; |
|||
case 1: |
|||
madctl = NV3001B_MADCTL_MY | NV3001B_MADCTL_MX | NV3001B_MADCTL_RGB; |
|||
break; |
|||
case 2: |
|||
madctl = NV3001B_MADCTL_RGB; |
|||
break; |
|||
default: |
|||
madctl = NV3001B_MADCTL_MX | NV3001B_MADCTL_MV | NV3001B_MADCTL_RGB; |
|||
break; |
|||
} |
|||
return madctl; |
|||
} |
|||
|
|||
static const uint8_t font5x7[] PROGMEM = { |
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, |
|||
0x7f, 0x14, 0x7f, 0x14, 0x24, 0x2a, 0x7f, 0x2a, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49, |
|||
0x55, 0x22, 0x50, 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, 0x00, 0x41, 0x22, |
|||
0x1c, 0x00, 0x14, 0x08, 0x3e, 0x08, 0x14, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x50, 0x30, 0x00, |
|||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x60, 0x60, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, |
|||
0x3e, 0x51, 0x49, 0x45, 0x3e, 0x00, 0x42, 0x7f, 0x40, 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, 0x21, |
|||
0x41, 0x45, 0x4b, 0x31, 0x18, 0x14, 0x12, 0x7f, 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3c, 0x4a, |
|||
0x49, 0x49, 0x30, 0x01, 0x71, 0x09, 0x05, 0x03, 0x36, 0x49, 0x49, 0x49, 0x36, 0x06, 0x49, 0x49, |
|||
0x29, 0x1e, 0x00, 0x36, 0x36, 0x00, 0x00, 0x00, 0x56, 0x36, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, |
|||
0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x51, 0x09, 0x06, |
|||
0x32, 0x49, 0x79, 0x41, 0x3e, 0x7e, 0x11, 0x11, 0x11, 0x7e, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x3e, |
|||
0x41, 0x41, 0x41, 0x22, 0x7f, 0x41, 0x41, 0x22, 0x1c, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x7f, 0x09, |
|||
0x09, 0x09, 0x01, 0x3e, 0x41, 0x49, 0x49, 0x7a, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x41, 0x7f, |
|||
0x41, 0x00, 0x20, 0x40, 0x41, 0x3f, 0x01, 0x7f, 0x08, 0x14, 0x22, 0x41, 0x7f, 0x40, 0x40, 0x40, |
|||
0x40, 0x7f, 0x02, 0x0c, 0x02, 0x7f, 0x7f, 0x04, 0x08, 0x10, 0x7f, 0x3e, 0x41, 0x41, 0x41, 0x3e, |
|||
0x7f, 0x09, 0x09, 0x09, 0x06, 0x3e, 0x41, 0x51, 0x21, 0x5e, 0x7f, 0x09, 0x19, 0x29, 0x46, 0x46, |
|||
0x49, 0x49, 0x49, 0x31, 0x01, 0x01, 0x7f, 0x01, 0x01, 0x3f, 0x40, 0x40, 0x40, 0x3f, 0x1f, 0x20, |
|||
0x40, 0x20, 0x1f, 0x3f, 0x40, 0x38, 0x40, 0x3f, 0x63, 0x14, 0x08, 0x14, 0x63, 0x07, 0x08, 0x70, |
|||
0x08, 0x07, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x7f, 0x41, 0x41, 0x00, 0x02, 0x04, 0x08, 0x10, |
|||
0x20, 0x00, 0x41, 0x41, 0x7f, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, |
|||
0x00, 0x01, 0x02, 0x04, 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, 0x7f, 0x48, 0x44, 0x44, 0x38, 0x38, |
|||
0x44, 0x44, 0x44, 0x20, 0x38, 0x44, 0x44, 0x48, 0x7f, 0x38, 0x54, 0x54, 0x54, 0x18, 0x08, 0x7e, |
|||
0x09, 0x01, 0x02, 0x0c, 0x52, 0x52, 0x52, 0x3e, 0x7f, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7d, |
|||
0x40, 0x00, 0x20, 0x40, 0x44, 0x3d, 0x00, 0x7f, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7f, 0x40, |
|||
0x00, 0x7c, 0x04, 0x18, 0x04, 0x78, 0x7c, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, |
|||
0x7c, 0x14, 0x14, 0x14, 0x08, 0x08, 0x14, 0x14, 0x18, 0x7c, 0x7c, 0x08, 0x04, 0x04, 0x08, 0x48, |
|||
0x54, 0x54, 0x54, 0x20, 0x04, 0x3f, 0x44, 0x40, 0x20, 0x3c, 0x40, 0x40, 0x20, 0x7c, 0x1c, 0x20, |
|||
0x40, 0x20, 0x1c, 0x3c, 0x40, 0x30, 0x40, 0x3c, 0x44, 0x28, 0x10, 0x28, 0x44, 0x0c, 0x50, 0x50, |
|||
0x50, 0x3c, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x7f, 0x00, |
|||
0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x08, 0x08, 0x2a, 0x1c, 0x08, 0x00, 0x06, 0x09, 0x09, 0x06 |
|||
}; |
|||
|
|||
static int textPixelScaleX(uint8_t size) { |
|||
return size <= 1 ? NV3001B_TEXT_SIZE1_SCALE_X : NV3001B_TEXT_SIZE2_SCALE_X; |
|||
} |
|||
|
|||
static int textPixelScaleY(uint8_t size) { |
|||
return size <= 1 ? NV3001B_TEXT_SIZE1_SCALE_Y : NV3001B_TEXT_SIZE2_SCALE_Y; |
|||
} |
|||
|
|||
static void setupOptionalOutput(int pin, int level) { |
|||
if (pin < 0) return; |
|||
|
|||
pinMode(pin, OUTPUT); |
|||
digitalWrite(pin, level); |
|||
} |
|||
|
|||
static void writeOptionalPin(int pin, int level) { |
|||
if (pin < 0) return; |
|||
|
|||
digitalWrite(pin, level); |
|||
} |
|||
|
|||
void NV3001BDisplay::writeCommand(uint8_t cmd) { |
|||
spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0)); |
|||
digitalWrite(PIN_TFT_DC, LOW); |
|||
digitalWrite(PIN_TFT_CS, LOW); |
|||
spi.transfer(cmd); |
|||
digitalWrite(PIN_TFT_CS, HIGH); |
|||
spi.endTransaction(); |
|||
} |
|||
|
|||
void NV3001BDisplay::writeBytes(const uint8_t* data, size_t len) { |
|||
if (!data || len == 0) return; |
|||
|
|||
spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0)); |
|||
digitalWrite(PIN_TFT_DC, HIGH); |
|||
digitalWrite(PIN_TFT_CS, LOW); |
|||
for (size_t i = 0; i < len; i++) { |
|||
spi.transfer(data[i]); |
|||
} |
|||
digitalWrite(PIN_TFT_CS, HIGH); |
|||
spi.endTransaction(); |
|||
} |
|||
|
|||
void NV3001BDisplay::writeCommandData(uint8_t cmd, const uint8_t* data, size_t len) { |
|||
writeCommand(cmd); |
|||
writeBytes(data, len); |
|||
} |
|||
|
|||
void NV3001BDisplay::setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { |
|||
uint16_t x2 = x + w - 1; |
|||
uint16_t y2 = y + h - 1; |
|||
uint8_t data[4]; |
|||
|
|||
data[0] = x >> 8; |
|||
data[1] = x & 0xff; |
|||
data[2] = x2 >> 8; |
|||
data[3] = x2 & 0xff; |
|||
writeCommandData(NV3001B_CASET, data, sizeof(data)); |
|||
|
|||
data[0] = y >> 8; |
|||
data[1] = y & 0xff; |
|||
data[2] = y2 >> 8; |
|||
data[3] = y2 & 0xff; |
|||
writeCommandData(NV3001B_RASET, data, sizeof(data)); |
|||
|
|||
writeCommand(NV3001B_RAMWR); |
|||
} |
|||
|
|||
void NV3001BDisplay::writeColor(uint16_t rgb, uint32_t count) { |
|||
uint8_t hi = rgb >> 8; |
|||
uint8_t lo = rgb & 0xff; |
|||
|
|||
spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0)); |
|||
digitalWrite(PIN_TFT_DC, HIGH); |
|||
digitalWrite(PIN_TFT_CS, LOW); |
|||
while (count--) { |
|||
spi.transfer(hi); |
|||
spi.transfer(lo); |
|||
} |
|||
digitalWrite(PIN_TFT_CS, HIGH); |
|||
spi.endTransaction(); |
|||
} |
|||
|
|||
void NV3001BDisplay::initPanel() { |
|||
#define CMD0(C) do { writeCommand(C); } while (0) |
|||
#define CMD1(C, A) do { const uint8_t d[] = { A }; writeCommandData(C, d, sizeof(d)); } while (0) |
|||
#define CMD2(C, A, B) do { const uint8_t d[] = { A, B }; writeCommandData(C, d, sizeof(d)); } while (0) |
|||
|
|||
CMD0(NV3001B_SWRESET); |
|||
delay(120); |
|||
CMD1(0xFF, 0xA5); |
|||
CMD1(0x41, 0x00); |
|||
CMD1(0x50, 0x02); |
|||
CMD1(0x52, 0x6E); |
|||
CMD1(0x57, 0x02); |
|||
CMD1(0x46, 0x11); |
|||
CMD2(0x47, 0x00, 0x01); |
|||
CMD2(0x8F, 0x22, 0x03); |
|||
CMD1(0x9A, 0x78); |
|||
CMD1(0x9B, 0x78); |
|||
CMD1(0x9C, 0xA0); |
|||
CMD1(0x9D, 0x17); |
|||
CMD1(0x9E, 0xC1); |
|||
CMD1(0x83, 0x5A); |
|||
CMD1(0x84, 0xB6); |
|||
CMD1(0xFF, 0xA5); |
|||
CMD1(0x85, 0x5F); |
|||
CMD1(0x6E, 0x0F); |
|||
CMD1(0x7E, 0x0F); |
|||
CMD1(0x60, 0x00); |
|||
CMD1(0x70, 0x00); |
|||
CMD1(0x6D, 0x33); |
|||
CMD1(0x7D, 0x37); |
|||
CMD1(0x61, 0x09); |
|||
CMD1(0x71, 0x0A); |
|||
CMD1(0x6C, 0x2A); |
|||
CMD1(0x7C, 0x36); |
|||
CMD1(0x62, 0x11); |
|||
CMD1(0x72, 0x10); |
|||
CMD1(0x68, 0x4E); |
|||
CMD1(0x78, 0x4E); |
|||
CMD1(0x66, 0x36); |
|||
CMD1(0x76, 0x3C); |
|||
CMD1(0x1A, 0x1C); |
|||
CMD1(0x7B, 0x14); |
|||
CMD1(0x63, 0x0D); |
|||
CMD1(0x73, 0x0A); |
|||
CMD1(0x6A, 0x16); |
|||
CMD1(0x7A, 0x12); |
|||
CMD1(0x64, 0x0B); |
|||
CMD1(0x74, 0x0A); |
|||
CMD1(0x69, 0x08); |
|||
CMD1(0x79, 0x0A); |
|||
CMD1(0x65, 0x06); |
|||
CMD1(0x75, 0x07); |
|||
CMD1(0x67, 0x23); |
|||
CMD1(0x77, 0x44); |
|||
CMD1(0xE0, 0x00); |
|||
CMD1(0xE9, 0x30); |
|||
CMD1(0xEB, 0xB7); |
|||
CMD1(0xEC, 0x00); |
|||
CMD1(0xED, 0x11); |
|||
CMD1(0xF0, 0xB7); |
|||
CMD1(0x53, 0x04); |
|||
CMD1(0x54, 0x04); |
|||
CMD1(0x55, 0x40); |
|||
CMD1(0x56, 0x40); |
|||
CMD2(0xA0, 0x60, 0x01); |
|||
CMD1(0xA1, 0x84); |
|||
CMD1(0xA2, 0x85); |
|||
CMD2(0xAB, 0x00, 0x02); |
|||
CMD2(0xAC, 0x00, 0x06); |
|||
CMD2(0xAD, 0x00, 0x03); |
|||
CMD2(0xAE, 0x00, 0x07); |
|||
CMD1(0xC7, 0x01); |
|||
CMD1(0xB9, 0x82); |
|||
CMD1(0xBA, 0x83); |
|||
CMD1(0xBB, 0x00); |
|||
CMD1(0xBC, 0x81); |
|||
CMD1(0xBD, 0x02); |
|||
CMD1(0xBE, 0x01); |
|||
CMD1(0xBF, 0x04); |
|||
CMD1(0xC0, 0x03); |
|||
CMD1(0xC8, 0x55); |
|||
CMD1(0xC9, 0xC9); |
|||
CMD1(0xCA, 0xC8); |
|||
CMD1(0xCB, 0xCB); |
|||
CMD1(0xCC, 0xCA); |
|||
CMD1(0xCD, 0x55); |
|||
CMD1(0xCE, 0xCE); |
|||
CMD1(0xCF, 0xCD); |
|||
CMD1(0xD0, 0xD0); |
|||
CMD1(0xD1, 0xCF); |
|||
CMD1(0xF2, 0x46); |
|||
CMD1(0xA8, 0x04); |
|||
CMD1(0xA9, 0xB0); |
|||
CMD1(0xAA, 0xA3); |
|||
CMD1(0xB6, 0x00); |
|||
CMD1(0xB7, 0xB0); |
|||
CMD1(0xB8, 0xA3); |
|||
CMD1(0xC4, 0x03); |
|||
CMD1(0xC5, 0xB0); |
|||
CMD1(0xC6, 0xA3); |
|||
CMD1(0x80, 0x10); |
|||
CMD1(0xFF, 0x00); |
|||
CMD1(0x35, 0x00); |
|||
CMD0(NV3001B_SLPOUT); |
|||
delay(120); |
|||
CMD1(NV3001B_COLMOD, 0x05); |
|||
CMD1(NV3001B_MADCTL, nv3001bMADCTL(DISPLAY_ROTATION)); |
|||
CMD0(NV3001B_DISPON); |
|||
delay(10); |
|||
|
|||
#undef CMD0 |
|||
#undef CMD1 |
|||
#undef CMD2 |
|||
} |
|||
|
|||
void NV3001BDisplay::fillPhysicalRect(int x, int y, int w, int h) { |
|||
if (!is_on || w <= 0 || h <= 0) return; |
|||
|
|||
if (x < 0) { |
|||
w += x; |
|||
x = 0; |
|||
} |
|||
if (y < 0) { |
|||
h += y; |
|||
y = 0; |
|||
} |
|||
if (x + w > NV3001B_SCREEN_WIDTH) w = NV3001B_SCREEN_WIDTH - x; |
|||
if (y + h > NV3001B_SCREEN_HEIGHT) h = NV3001B_SCREEN_HEIGHT - y; |
|||
if (w <= 0 || h <= 0) return; |
|||
|
|||
setAddrWindow(x, y, w, h); |
|||
writeColor(color, (uint32_t)w * h); |
|||
} |
|||
|
|||
void NV3001BDisplay::drawChar(int x, int y, char ch) { |
|||
if (ch < 32 || ch > 127) ch = '?'; |
|||
|
|||
uint16_t index = (uint16_t)(ch - 32) * 5; |
|||
int scale_x = textPixelScaleX(text_size); |
|||
int scale_y = textPixelScaleY(text_size); |
|||
for (int col = 0; col < 5; col++) { |
|||
uint8_t line = pgm_read_byte(font5x7 + index + col); |
|||
for (int row = 0; row < 7; row++) { |
|||
if (line & (1 << row)) { |
|||
fillPhysicalRect(x + col * scale_x, y + row * scale_y, scale_x, scale_y); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
bool NV3001BDisplay::begin() { |
|||
if (is_on) return true; |
|||
|
|||
if (periph_power) periph_power->claim(); |
|||
|
|||
setupOptionalOutput(PIN_TFT_EN, PIN_TFT_EN_ACTIVE); |
|||
setupOptionalOutput(PIN_TFT_BL, !PIN_TFT_BL_ACTIVE); |
|||
pinMode(PIN_TFT_CS, OUTPUT); |
|||
pinMode(PIN_TFT_DC, OUTPUT); |
|||
digitalWrite(PIN_TFT_CS, HIGH); |
|||
digitalWrite(PIN_TFT_DC, HIGH); |
|||
delay(20); |
|||
|
|||
spi.begin(PIN_TFT_SCL, PIN_TFT_MISO, PIN_TFT_SDA, PIN_TFT_CS); |
|||
if (PIN_TFT_RST >= 0) { |
|||
pinMode(PIN_TFT_RST, OUTPUT); |
|||
digitalWrite(PIN_TFT_RST, HIGH); |
|||
delay(10); |
|||
digitalWrite(PIN_TFT_RST, LOW); |
|||
delay(20); |
|||
digitalWrite(PIN_TFT_RST, HIGH); |
|||
delay(120); |
|||
} |
|||
|
|||
initPanel(); |
|||
is_on = true; |
|||
color = 0x0000; |
|||
fillPhysicalRect(0, 0, NV3001B_SCREEN_WIDTH, NV3001B_SCREEN_HEIGHT); |
|||
color = 0xffff; |
|||
text_size = 1; |
|||
cursor_x = 0; |
|||
cursor_y = 0; |
|||
writeOptionalPin(PIN_TFT_BL, PIN_TFT_BL_ACTIVE); |
|||
return true; |
|||
} |
|||
|
|||
void NV3001BDisplay::turnOn() { |
|||
begin(); |
|||
} |
|||
|
|||
void NV3001BDisplay::turnOff() { |
|||
if (!is_on) return; |
|||
|
|||
writeOptionalPin(PIN_TFT_BL, !PIN_TFT_BL_ACTIVE); |
|||
writeOptionalPin(PIN_TFT_EN, !PIN_TFT_EN_ACTIVE); |
|||
is_on = false; |
|||
if (periph_power) periph_power->release(); |
|||
} |
|||
|
|||
void NV3001BDisplay::clear() { |
|||
uint16_t saved = color; |
|||
color = 0x0000; |
|||
fillPhysicalRect(0, 0, NV3001B_SCREEN_WIDTH, NV3001B_SCREEN_HEIGHT); |
|||
color = saved; |
|||
} |
|||
|
|||
void NV3001BDisplay::startFrame(Color bkg) { |
|||
color = mapColor(bkg); |
|||
fillPhysicalRect(0, 0, NV3001B_SCREEN_WIDTH, NV3001B_SCREEN_HEIGHT); |
|||
color = 0xffff; |
|||
text_size = 1; |
|||
cursor_x = 0; |
|||
cursor_y = 0; |
|||
} |
|||
|
|||
void NV3001BDisplay::setTextSize(int sz) { |
|||
text_size = sz < 1 ? 1 : sz; |
|||
} |
|||
|
|||
void NV3001BDisplay::setColor(Color c) { |
|||
color = mapColor(c); |
|||
} |
|||
|
|||
void NV3001BDisplay::setCursor(int x, int y) { |
|||
cursor_x = scaleX(x); |
|||
cursor_y = scaleY(y); |
|||
} |
|||
|
|||
void NV3001BDisplay::print(const char* str) { |
|||
if (!str || !is_on) return; |
|||
|
|||
int scale_x = textPixelScaleX(text_size); |
|||
int scale_y = textPixelScaleY(text_size); |
|||
while (*str) { |
|||
if (*str == '\n') { |
|||
cursor_x = 0; |
|||
cursor_y += 8 * scale_y; |
|||
} else if (*str == '\r') { |
|||
cursor_x = 0; |
|||
} else { |
|||
drawChar(cursor_x, cursor_y, *str); |
|||
cursor_x += 6 * scale_x; |
|||
} |
|||
str++; |
|||
} |
|||
} |
|||
|
|||
void NV3001BDisplay::fillRect(int x, int y, int w, int h) { |
|||
fillPhysicalRect(scaleX(x), scaleY(y), scaleWidth(x, w), scaleHeight(y, h)); |
|||
} |
|||
|
|||
void NV3001BDisplay::drawRect(int x, int y, int w, int h) { |
|||
int x1 = scaleX(x); |
|||
int y1 = scaleY(y); |
|||
int sw = scaleWidth(x, w); |
|||
int sh = scaleHeight(y, h); |
|||
|
|||
fillPhysicalRect(x1, y1, sw, 1); |
|||
fillPhysicalRect(x1, y1 + sh - 1, sw, 1); |
|||
fillPhysicalRect(x1, y1, 1, sh); |
|||
fillPhysicalRect(x1 + sw - 1, y1, 1, sh); |
|||
} |
|||
|
|||
void NV3001BDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { |
|||
if (!bits || !is_on) return; |
|||
|
|||
int byte_width = (w + 7) / 8; |
|||
for (int j = 0; j < h; j++) { |
|||
for (int i = 0; i < w; i++) { |
|||
uint8_t byte = pgm_read_byte(bits + j * byte_width + i / 8); |
|||
if (byte & (0x80 >> (i & 7))) { |
|||
fillPhysicalRect(scaleX(x + i), scaleY(y + j), scaleWidth(x + i, 1), scaleHeight(y + j, 1)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
uint16_t NV3001BDisplay::getTextWidth(const char* str) { |
|||
if (!str) return 0; |
|||
|
|||
uint16_t len = 0; |
|||
while (str[len] && str[len] != '\n' && str[len] != '\r') len++; |
|||
return (uint16_t)((len * 6 * textPixelScaleX(text_size)) / DISPLAY_SCALE_X); |
|||
} |
|||
|
|||
void NV3001BDisplay::endFrame() { |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
#pragma once |
|||
|
|||
#include "DisplayDriver.h" |
|||
#include <SPI.h> |
|||
#include <helpers/RefCountedDigitalPin.h> |
|||
|
|||
#ifndef NV3001B_LOGICAL_WIDTH |
|||
#define NV3001B_LOGICAL_WIDTH 128 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_LOGICAL_HEIGHT |
|||
#define NV3001B_LOGICAL_HEIGHT 64 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_PANEL_WIDTH |
|||
#define NV3001B_PANEL_WIDTH 128 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_PANEL_HEIGHT |
|||
#define NV3001B_PANEL_HEIGHT 220 |
|||
#endif |
|||
|
|||
#ifndef NV3001B_SPI_HOST |
|||
#define NV3001B_SPI_HOST HSPI |
|||
#endif |
|||
|
|||
class NV3001BDisplay : public DisplayDriver { |
|||
SPIClass spi; |
|||
RefCountedDigitalPin* periph_power; |
|||
bool is_on = false; |
|||
uint16_t color = 0xffff; |
|||
uint8_t text_size = 1; |
|||
int cursor_x = 0; |
|||
int cursor_y = 0; |
|||
|
|||
void writeCommand(uint8_t cmd); |
|||
void writeBytes(const uint8_t* data, size_t len); |
|||
void writeCommandData(uint8_t cmd, const uint8_t* data, size_t len); |
|||
void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h); |
|||
void writeColor(uint16_t rgb, uint32_t count); |
|||
void fillPhysicalRect(int x, int y, int w, int h); |
|||
void initPanel(); |
|||
void drawChar(int x, int y, char ch); |
|||
|
|||
public: |
|||
NV3001BDisplay(RefCountedDigitalPin* power = nullptr) : |
|||
DisplayDriver(NV3001B_LOGICAL_WIDTH, NV3001B_LOGICAL_HEIGHT), spi(NV3001B_SPI_HOST), periph_power(power) { } |
|||
|
|||
bool begin(); |
|||
static const char* driverName() { return "NV3001B"; } |
|||
static uint16_t physicalWidth() { return NV3001B_PANEL_WIDTH; } |
|||
static uint16_t physicalHeight() { return NV3001B_PANEL_HEIGHT; } |
|||
|
|||
bool isOn() override { return is_on; } |
|||
void turnOn() override; |
|||
void turnOff() override; |
|||
void clear() override; |
|||
void startFrame(Color bkg = DARK) override; |
|||
void setTextSize(int sz) override; |
|||
void setColor(Color c) override; |
|||
void setCursor(int x, int y) override; |
|||
void print(const char* str) override; |
|||
void fillRect(int x, int y, int w, int h) override; |
|||
void drawRect(int x, int y, int w, int h) override; |
|||
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; |
|||
uint16_t getTextWidth(const char* str) override; |
|||
void endFrame() override; |
|||
}; |
|||
@ -0,0 +1,18 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
|
|||
enum class RotaryInputEvent : uint8_t { |
|||
None, |
|||
Next, |
|||
Prev, |
|||
}; |
|||
|
|||
class RotaryInput { |
|||
public: |
|||
virtual ~RotaryInput() = default; |
|||
|
|||
virtual bool begin() = 0; |
|||
virtual RotaryInputEvent poll() = 0; |
|||
virtual bool isReady() const = 0; |
|||
}; |
|||
@ -0,0 +1,68 @@ |
|||
#include "HeltecRC32Board.h" |
|||
|
|||
void HeltecRC32Board::begin() { |
|||
ESP32Board::begin(); |
|||
|
|||
pinMode(PIN_ADC_CTRL, OUTPUT); |
|||
digitalWrite(PIN_ADC_CTRL, !ADC_CTRL_ENABLED); |
|||
|
|||
#ifdef SENSOR_RST_PIN |
|||
pinMode(SENSOR_RST_PIN, OUTPUT); |
|||
digitalWrite(SENSOR_RST_PIN, HIGH); |
|||
#endif |
|||
|
|||
#ifdef LED_POWER |
|||
pinMode(LED_POWER, OUTPUT); |
|||
digitalWrite(LED_POWER, LOW); |
|||
#endif |
|||
|
|||
periph_power.begin(); |
|||
periph_power.claim(); |
|||
|
|||
esp_reset_reason_t reason = esp_reset_reason(); |
|||
if (reason == ESP_RST_DEEPSLEEP) { |
|||
long wakeup_source = esp_sleep_get_ext1_wakeup_status(); |
|||
if (wakeup_source & (1L << P_LORA_DIO_1)) { |
|||
startup_reason = BD_STARTUP_RX_PACKET; |
|||
} |
|||
|
|||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); |
|||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); |
|||
} |
|||
} |
|||
|
|||
void HeltecRC32Board::powerOff() { |
|||
enterDeepSleep(0); |
|||
} |
|||
|
|||
void HeltecRC32Board::onBeforeTransmit() { |
|||
digitalWrite(P_LORA_TX_LED, HIGH); |
|||
} |
|||
|
|||
void HeltecRC32Board::onAfterTransmit() { |
|||
digitalWrite(P_LORA_TX_LED, LOW); |
|||
} |
|||
|
|||
uint16_t HeltecRC32Board::getBattMilliVolts() { |
|||
analogReadResolution(12); |
|||
analogSetAttenuation(ADC_2_5db); |
|||
digitalWrite(PIN_ADC_CTRL, ADC_CTRL_ENABLED); |
|||
delay(10); |
|||
uint32_t raw = 0; |
|||
for (int i = 0; i < 8; i++) { |
|||
raw += analogReadMilliVolts(PIN_VBAT_READ); |
|||
} |
|||
raw = raw / 8; |
|||
digitalWrite(PIN_ADC_CTRL, !ADC_CTRL_ENABLED); |
|||
|
|||
return (adc_mult * raw); |
|||
} |
|||
|
|||
bool HeltecRC32Board::setAdcMultiplier(float multiplier) { |
|||
adc_mult = multiplier == 0.0f ? ADC_MULTIPLIER : multiplier; |
|||
return true; |
|||
} |
|||
|
|||
const char* HeltecRC32Board::getManufacturerName() const { |
|||
return "Heltec RC32"; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
#include <driver/rtc_io.h> |
|||
#include <helpers/ESP32Board.h> |
|||
#include <helpers/RefCountedDigitalPin.h> |
|||
|
|||
#ifndef ADC_MULTIPLIER |
|||
#define ADC_MULTIPLIER 4.9f |
|||
#endif |
|||
|
|||
class HeltecRC32Board : public ESP32Board { |
|||
protected: |
|||
float adc_mult = ADC_MULTIPLIER; |
|||
|
|||
public: |
|||
RefCountedDigitalPin periph_power; |
|||
|
|||
HeltecRC32Board() : periph_power(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON){} |
|||
|
|||
void begin(); |
|||
void onBeforeTransmit() override; |
|||
void onAfterTransmit() override; |
|||
void powerOff() override; |
|||
uint16_t getBattMilliVolts() override; |
|||
bool setAdcMultiplier(float multiplier) override; |
|||
float getAdcMultiplier() const override { return adc_mult; } |
|||
const char* getManufacturerName() const override; |
|||
}; |
|||
@ -0,0 +1,122 @@ |
|||
#include "HeltecRC32RotaryInput.h" |
|||
|
|||
#include <Wire.h> |
|||
|
|||
namespace { |
|||
constexpr uint8_t TCA6408_ADDR = 0x20; |
|||
constexpr uint8_t TCA6408_INPUT_REG = 0x00; |
|||
constexpr uint8_t TCA6408_POLARITY_REG = 0x02; |
|||
constexpr uint8_t TCA6408_CONFIG_REG = 0x03; |
|||
constexpr uint8_t TCA6408_ROTARY_A_MASK = 0x01; |
|||
constexpr uint8_t TCA6408_ROTARY_B_MASK = 0x02; |
|||
constexpr uint8_t TCA6408_ROTARY_MASK = TCA6408_ROTARY_A_MASK | TCA6408_ROTARY_B_MASK; |
|||
constexpr uint32_t TCA6408_DEBOUNCE_MS = 5; |
|||
} |
|||
|
|||
bool HeltecRC32RotaryInput::begin() { |
|||
initialized = true; |
|||
ready = false; |
|||
input_state = TCA6408_ROTARY_MASK; |
|||
active_low_phase = false; |
|||
|
|||
if (periph_power && !power_claimed) { |
|||
periph_power->claim(); |
|||
power_claimed = true; |
|||
delay(12); |
|||
} |
|||
|
|||
if (!writeRegister(TCA6408_POLARITY_REG, 0x00) || !writeRegister(TCA6408_CONFIG_REG, 0xFF)) { |
|||
return false; |
|||
} |
|||
|
|||
uint8_t state = 0; |
|||
if (!readInput(state)) { |
|||
return false; |
|||
} |
|||
|
|||
input_state = state & TCA6408_ROTARY_MASK; |
|||
ready = true; |
|||
return true; |
|||
} |
|||
|
|||
RotaryInputEvent HeltecRC32RotaryInput::poll() { |
|||
if (!initialized) { |
|||
begin(); |
|||
return RotaryInputEvent::None; |
|||
} |
|||
|
|||
if (!ready) { |
|||
return RotaryInputEvent::None; |
|||
} |
|||
|
|||
uint8_t new_state = 0; |
|||
if (!readInput(new_state)) { |
|||
return RotaryInputEvent::None; |
|||
} |
|||
|
|||
new_state &= TCA6408_ROTARY_MASK; |
|||
RotaryInputEvent event = handleTransition(new_state); |
|||
input_state = new_state; |
|||
return event; |
|||
} |
|||
|
|||
bool HeltecRC32RotaryInput::writeRegister(uint8_t reg, uint8_t value) { |
|||
Wire.beginTransmission(TCA6408_ADDR); |
|||
Wire.write(reg); |
|||
Wire.write(value); |
|||
return Wire.endTransmission() == 0; |
|||
} |
|||
|
|||
bool HeltecRC32RotaryInput::readInput(uint8_t& value) { |
|||
Wire.beginTransmission(TCA6408_ADDR); |
|||
Wire.write(TCA6408_INPUT_REG); |
|||
if (Wire.endTransmission(false) != 0) { |
|||
return false; |
|||
} |
|||
if (Wire.requestFrom(TCA6408_ADDR, static_cast<uint8_t>(1)) != 1) { |
|||
return false; |
|||
} |
|||
|
|||
value = Wire.read(); |
|||
return true; |
|||
} |
|||
|
|||
RotaryInputEvent HeltecRC32RotaryInput::handleTransition(uint8_t newState) { |
|||
uint8_t changed = (input_state ^ newState) & TCA6408_ROTARY_MASK; |
|||
RotaryInputEvent event = RotaryInputEvent::None; |
|||
bool a_low = (newState & TCA6408_ROTARY_A_MASK) == 0; |
|||
bool b_low = (newState & TCA6408_ROTARY_B_MASK) == 0; |
|||
|
|||
if (!a_low && !b_low) { |
|||
active_low_phase = false; |
|||
} |
|||
|
|||
if (!active_low_phase && (changed & TCA6408_ROTARY_A_MASK) && a_low && !b_low) { |
|||
event = RotaryInputEvent::Prev; |
|||
active_low_phase = true; |
|||
} else if (!active_low_phase && (changed & TCA6408_ROTARY_B_MASK) && b_low && !a_low) { |
|||
event = RotaryInputEvent::Next; |
|||
active_low_phase = true; |
|||
} |
|||
|
|||
if (event == RotaryInputEvent::None && !active_low_phase && (changed & TCA6408_ROTARY_A_MASK)) { |
|||
bool a_rising = (newState & TCA6408_ROTARY_A_MASK) != 0; |
|||
if (a_rising && b_low) { |
|||
event = RotaryInputEvent::Prev; |
|||
} |
|||
} |
|||
|
|||
if (event == RotaryInputEvent::None && !active_low_phase && (changed & TCA6408_ROTARY_B_MASK)) { |
|||
bool b_rising = (newState & TCA6408_ROTARY_B_MASK) != 0; |
|||
if (b_rising && a_low) { |
|||
event = RotaryInputEvent::Next; |
|||
} |
|||
} |
|||
|
|||
if (event == RotaryInputEvent::None || (millis() - last_event_ms) < TCA6408_DEBOUNCE_MS) { |
|||
return RotaryInputEvent::None; |
|||
} |
|||
|
|||
last_event_ms = millis(); |
|||
return event; |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
#pragma once |
|||
|
|||
#include <helpers/RefCountedDigitalPin.h> |
|||
#include <helpers/ui/RotaryInput.h> |
|||
|
|||
class HeltecRC32RotaryInput : public RotaryInput { |
|||
public: |
|||
explicit HeltecRC32RotaryInput(RefCountedDigitalPin* periphPower = nullptr) : periph_power(periphPower) { } |
|||
|
|||
bool begin() override; |
|||
RotaryInputEvent poll() override; |
|||
bool isReady() const override { return ready; } |
|||
|
|||
private: |
|||
bool writeRegister(uint8_t reg, uint8_t value); |
|||
bool readInput(uint8_t& value); |
|||
RotaryInputEvent handleTransition(uint8_t newState); |
|||
|
|||
uint8_t input_state = 0x03; |
|||
uint32_t last_event_ms = 0; |
|||
bool ready = false; |
|||
bool initialized = false; |
|||
bool power_claimed = false; |
|||
bool active_low_phase = false; |
|||
RefCountedDigitalPin* periph_power = nullptr; |
|||
}; |
|||
@ -0,0 +1,60 @@ |
|||
#ifndef Pins_Arduino_h |
|||
#define Pins_Arduino_h |
|||
|
|||
#include <stdint.h> |
|||
|
|||
#define USB_VID 0x303a |
|||
#define USB_PID 0x1001 |
|||
|
|||
static const uint8_t TX = 43; |
|||
static const uint8_t RX = 44; |
|||
|
|||
static const uint8_t SDA = 21; |
|||
static const uint8_t SCL = 18; |
|||
|
|||
static const uint8_t SS = 10; |
|||
static const uint8_t MOSI = 12; |
|||
static const uint8_t MISO = 13; |
|||
static const uint8_t SCK = 11; |
|||
|
|||
static const uint8_t A0 = 1; |
|||
static const uint8_t A1 = 2; |
|||
static const uint8_t A2 = 3; |
|||
static const uint8_t A3 = 4; |
|||
static const uint8_t A4 = 5; |
|||
static const uint8_t A5 = 6; |
|||
static const uint8_t A6 = 7; |
|||
static const uint8_t A7 = 8; |
|||
static const uint8_t A8 = 9; |
|||
static const uint8_t A9 = 10; |
|||
static const uint8_t A10 = 11; |
|||
static const uint8_t A11 = 12; |
|||
static const uint8_t A12 = 13; |
|||
static const uint8_t A13 = 14; |
|||
static const uint8_t A14 = 15; |
|||
static const uint8_t A15 = 16; |
|||
static const uint8_t A16 = 17; |
|||
static const uint8_t A17 = 18; |
|||
static const uint8_t A18 = 19; |
|||
static const uint8_t A19 = 20; |
|||
|
|||
static const uint8_t T1 = 1; |
|||
static const uint8_t T2 = 2; |
|||
static const uint8_t T3 = 3; |
|||
static const uint8_t T4 = 4; |
|||
static const uint8_t T5 = 5; |
|||
static const uint8_t T6 = 6; |
|||
static const uint8_t T7 = 7; |
|||
static const uint8_t T8 = 8; |
|||
static const uint8_t T9 = 9; |
|||
static const uint8_t T10 = 10; |
|||
static const uint8_t T11 = 11; |
|||
static const uint8_t T12 = 12; |
|||
static const uint8_t T13 = 13; |
|||
static const uint8_t T14 = 14; |
|||
|
|||
static const uint8_t RST_LoRa = 9; |
|||
static const uint8_t BUSY_LoRa = 1; |
|||
static const uint8_t DIO1_LoRa = 14; |
|||
|
|||
#endif |
|||
@ -0,0 +1,348 @@ |
|||
[Heltec_RC32] |
|||
extends = esp32_base |
|||
board = heltec-rc32 |
|||
board_build.partitions = default_16MB.csv |
|||
build_flags = |
|||
${esp32_base.build_flags} |
|||
${sensor_base.build_flags} |
|||
-I variants/heltec_rc32 |
|||
-I src/helpers/ui |
|||
-D HELTEC_RC32 |
|||
-D USE_SX1262 |
|||
-D ESP32_CPU_FREQ=160 |
|||
-D RADIO_CLASS=CustomSX1262 |
|||
-D WRAPPER_CLASS=CustomSX1262Wrapper |
|||
-D P_LORA_DIO_1=14 |
|||
-D P_LORA_NSS=10 |
|||
-D P_LORA_RESET=9 |
|||
-D P_LORA_BUSY=1 |
|||
-D P_LORA_SCLK=11 |
|||
-D P_LORA_MISO=13 |
|||
-D P_LORA_MOSI=12 |
|||
-D LORA_TX_POWER=22 |
|||
-D PIN_USER_BTN=0 |
|||
-D PIN_BOARD_SDA=21 |
|||
-D PIN_BOARD_SCL=18 |
|||
-D SENSOR_POWER_CTRL_PIN=46 |
|||
-D SENSOR_POWER_ON=HIGH |
|||
-D SENSOR_RST_PIN=2 |
|||
-D P_LORA_TX_LED=47 |
|||
-D PIN_BUZZER=48 |
|||
-D PIN_TFT_SCL=17 |
|||
-D PIN_TFT_SDA=38 |
|||
-D PIN_TFT_CS=39 |
|||
-D PIN_TFT_DC=16 |
|||
-D PIN_TFT_RST=4 |
|||
-D PIN_TFT_EN=6 |
|||
-D PIN_TFT_EN_ACTIVE=LOW |
|||
-D PIN_TFT_BL=5 |
|||
-D PIN_TFT_BL_ACTIVE=HIGH |
|||
-D SPI_FREQUENCY=8000000 |
|||
-D PIN_GPS_TX=44 |
|||
-D PIN_GPS_RX=43 |
|||
-D PIN_GPS_EN=45 |
|||
-D PIN_GPS_EN_ACTIVE=HIGH |
|||
-D PIN_GPS_RESET=40 |
|||
-D PIN_GPS_RESET_ACTIVE=LOW |
|||
-D PIN_GPS_PPS=41 |
|||
-D GPS_BAUD_RATE=9600 |
|||
-D ENV_INCLUDE_GPS=1 |
|||
-D PIN_ADC_CTRL=15 |
|||
-D PIN_VBAT_READ=7 |
|||
-D ADC_CTRL_ENABLED=HIGH |
|||
-D ADC_MULTIPLIER=4.9 |
|||
-D SX126X_DIO2_AS_RF_SWITCH=true |
|||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8 |
|||
-D SX126X_CURRENT_LIMIT=140 |
|||
-D SX126X_RX_BOOSTED_GAIN=1 |
|||
-D SX126X_REGISTER_PATCH=1 ; Patch register 0x8B5 for improved RX |
|||
build_src_filter = ${esp32_base.build_src_filter} |
|||
+<../variants/heltec_rc32> |
|||
+<helpers/sensors> |
|||
lib_deps = |
|||
${esp32_base.lib_deps} |
|||
${sensor_base.lib_deps} |
|||
|
|||
[Heltec_RC32_with_display] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-D HELTEC_RC32_WITH_DISPLAY |
|||
-D DISPLAY_CLASS=NV3001BDisplay |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<helpers/ui/NV3001BDisplay.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
|
|||
[env:heltec_rc32_without_display_repeater] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-D ADVERT_NAME='"Heltec RC32 Repeater"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D MAX_NEIGHBOURS=50 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<../examples/simple_repeater> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
bakercp/CRC32 @ ^2.0.0 |
|||
|
|||
[env:heltec_rc32_without_display_repeater_bridge_espnow] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-D ADVERT_NAME='"ESPNow Bridge"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D MAX_NEIGHBOURS=50 |
|||
-D WITH_ESPNOW_BRIDGE=1 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<helpers/bridges/ESPNowBridge.cpp> |
|||
+<../examples/simple_repeater> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_rc32_without_display_room_server] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-D ADVERT_NAME='"Heltec RC32 Room"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D ROOM_PASSWORD='"hello"' |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<../examples/simple_room_server> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_rc32_without_display_companion_radio_usb] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D DISPLAY_CLASS=NullDisplayDriver |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/ui/buzzer.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
end2endzone/NonBlockingRTTTL@^1.3.0 |
|||
|
|||
[env:heltec_rc32_without_display_companion_radio_ble] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D DISPLAY_CLASS=NullDisplayDriver |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D BLE_PIN_CODE=123456 |
|||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400 |
|||
-D BLE_DEBUG_LOGGING=1 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/ui/buzzer.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
end2endzone/NonBlockingRTTTL@^1.3.0 |
|||
|
|||
[env:heltec_rc32_without_display_companion_radio_wifi] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D DISPLAY_CLASS=NullDisplayDriver |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D WIFI_DEBUG_LOGGING=1 |
|||
-D WIFI_SSID='"myssid"' |
|||
-D WIFI_PWD='"mypwd"' |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/ui/buzzer.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
end2endzone/NonBlockingRTTTL@^1.3.0 |
|||
|
|||
[env:heltec_rc32_without_display_sensor] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-D ADVERT_NAME='"Heltec RC32 Sensor"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D ENV_PIN_SDA=21 |
|||
-D ENV_PIN_SCL=18 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<../examples/simple_sensor> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_rc32_repeater] |
|||
extends = Heltec_RC32_with_display |
|||
build_flags = |
|||
${Heltec_RC32_with_display.build_flags} |
|||
-D ADVERT_NAME='"Heltec RC32 Repeater"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D MAX_NEIGHBOURS=50 |
|||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter} |
|||
+<../examples/simple_repeater> |
|||
lib_deps = |
|||
${Heltec_RC32_with_display.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
bakercp/CRC32 @ ^2.0.0 |
|||
|
|||
[env:heltec_rc32_repeater_bridge_espnow] |
|||
extends = Heltec_RC32_with_display |
|||
build_flags = |
|||
${Heltec_RC32_with_display.build_flags} |
|||
-D ADVERT_NAME='"ESPNow Bridge"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D MAX_NEIGHBOURS=50 |
|||
-D WITH_ESPNOW_BRIDGE=1 |
|||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter} |
|||
+<helpers/bridges/ESPNowBridge.cpp> |
|||
+<../examples/simple_repeater> |
|||
lib_deps = |
|||
${Heltec_RC32_with_display.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_rc32_room_server] |
|||
extends = Heltec_RC32_with_display |
|||
build_flags = |
|||
${Heltec_RC32_with_display.build_flags} |
|||
-D ADVERT_NAME='"Heltec RC32 Room"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D ROOM_PASSWORD='"hello"' |
|||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter} |
|||
+<../examples/simple_room_server> |
|||
lib_deps = |
|||
${Heltec_RC32_with_display.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_rc32_terminal_chat] |
|||
extends = Heltec_RC32 |
|||
build_flags = |
|||
${Heltec_RC32.build_flags} |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=1 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<../examples/simple_secure_chat/main.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_rc32_companion_radio_usb] |
|||
extends = Heltec_RC32_with_display |
|||
build_flags = |
|||
${Heltec_RC32_with_display.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D UI_HAS_ROTARY_INPUT |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter} |
|||
+<helpers/ui/buzzer.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32_with_display.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
end2endzone/NonBlockingRTTTL@^1.3.0 |
|||
|
|||
[env:heltec_rc32_companion_radio_ble] |
|||
extends = Heltec_RC32_with_display |
|||
build_flags = |
|||
${Heltec_RC32_with_display.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D UI_HAS_ROTARY_INPUT |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D BLE_PIN_CODE=123456 |
|||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400 |
|||
-D BLE_DEBUG_LOGGING=1 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter} |
|||
+<helpers/ui/buzzer.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32_with_display.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
end2endzone/NonBlockingRTTTL@^1.3.0 |
|||
|
|||
[env:heltec_rc32_companion_radio_wifi] |
|||
extends = Heltec_RC32_with_display |
|||
build_flags = |
|||
${Heltec_RC32_with_display.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D UI_HAS_ROTARY_INPUT |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D WIFI_DEBUG_LOGGING=1 |
|||
-D WIFI_SSID='"myssid"' |
|||
-D WIFI_PWD='"mypwd"' |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter} |
|||
+<helpers/ui/buzzer.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${Heltec_RC32_with_display.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
end2endzone/NonBlockingRTTTL@^1.3.0 |
|||
|
|||
[env:heltec_rc32_sensor] |
|||
extends = Heltec_RC32_with_display |
|||
build_flags = |
|||
${Heltec_RC32_with_display.build_flags} |
|||
-D ADVERT_NAME='"Heltec RC32 Sensor"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D ENV_PIN_SDA=21 |
|||
-D ENV_PIN_SCL=18 |
|||
build_src_filter = ${Heltec_RC32_with_display.build_src_filter} |
|||
+<../examples/simple_sensor> |
|||
lib_deps = |
|||
${Heltec_RC32_with_display.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_rc32_kiss_modem] |
|||
extends = Heltec_RC32 |
|||
build_src_filter = ${Heltec_RC32.build_src_filter} |
|||
+<../examples/kiss_modem/> |
|||
@ -0,0 +1,52 @@ |
|||
#include <Arduino.h> |
|||
#include "target.h" |
|||
#if defined(UI_HAS_ROTARY_INPUT) |
|||
#include "HeltecRC32RotaryInput.h" |
|||
#endif |
|||
|
|||
HeltecRC32Board board; |
|||
|
|||
#if defined(P_LORA_SCLK) |
|||
static SPIClass spi(FSPI); |
|||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); |
|||
#else |
|||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); |
|||
#endif |
|||
|
|||
WRAPPER_CLASS radio_driver(radio, board); |
|||
|
|||
ESP32RTCClock fallback_clock; |
|||
AutoDiscoverRTCClock rtc_clock(fallback_clock); |
|||
|
|||
#if ENV_INCLUDE_GPS |
|||
#include <helpers/sensors/MicroNMEALocationProvider.h> |
|||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, PIN_GPS_RESET, PIN_GPS_EN); |
|||
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); |
|||
#else |
|||
EnvironmentSensorManager sensors; |
|||
#endif |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
DISPLAY_CLASS display; |
|||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true); |
|||
#if defined(UI_HAS_ROTARY_INPUT) |
|||
static HeltecRC32RotaryInput rotaryInputImpl(&board.periph_power); |
|||
RotaryInput& rotary_input = rotaryInputImpl; |
|||
#endif |
|||
#endif |
|||
|
|||
bool radio_init() { |
|||
fallback_clock.begin(); |
|||
rtc_clock.begin(Wire); |
|||
|
|||
#if defined(P_LORA_SCLK) |
|||
return radio.std_init(&spi); |
|||
#else |
|||
return radio.std_init(); |
|||
#endif |
|||
} |
|||
|
|||
mesh::LocalIdentity radio_new_identity() { |
|||
RadioNoiseListener rng(radio); |
|||
return mesh::LocalIdentity(&rng); |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
#pragma once |
|||
|
|||
#define RADIOLIB_STATIC_ONLY 1 |
|||
#include <RadioLib.h> |
|||
#include <HeltecRC32Board.h> |
|||
#include <helpers/AutoDiscoverRTCClock.h> |
|||
#include <helpers/radiolib/CustomSX1262Wrapper.h> |
|||
#include <helpers/radiolib/RadioLibWrappers.h> |
|||
#include <helpers/sensors/EnvironmentSensorManager.h> |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
#include <helpers/ui/MomentaryButton.h> |
|||
#if defined(UI_HAS_ROTARY_INPUT) |
|||
#include <helpers/ui/RotaryInput.h> |
|||
#endif |
|||
#ifdef HELTEC_RC32_WITH_DISPLAY |
|||
#include <helpers/ui/NV3001BDisplay.h> |
|||
#else |
|||
#include <helpers/ui/NullDisplayDriver.h> |
|||
#endif |
|||
#endif |
|||
|
|||
extern HeltecRC32Board board; |
|||
extern WRAPPER_CLASS radio_driver; |
|||
extern AutoDiscoverRTCClock rtc_clock; |
|||
extern EnvironmentSensorManager sensors; |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
extern DISPLAY_CLASS display; |
|||
extern MomentaryButton user_btn; |
|||
#if defined(UI_HAS_ROTARY_INPUT) |
|||
extern RotaryInput& rotary_input; |
|||
#endif |
|||
#endif |
|||
|
|||
bool radio_init(); |
|||
mesh::LocalIdentity radio_new_identity(); |
|||
@ -0,0 +1,29 @@ |
|||
#ifndef _VARIANT_HELTEC_RC32_ |
|||
#define _VARIANT_HELTEC_RC32_ |
|||
|
|||
#define BUTTON_PIN 0 |
|||
|
|||
#define I2C_SCL 18 |
|||
#define I2C_SDA 21 |
|||
#define SENSOR_INT_PIN 42 |
|||
#define PERIPHERAL_WARMUP_MS 100 |
|||
|
|||
#define LORA_SCK 11 |
|||
#define LORA_MISO 13 |
|||
#define LORA_MOSI 12 |
|||
#define LORA_CS 10 |
|||
#define LORA_DIO0 RADIOLIB_NC |
|||
#define LORA_DIO1 14 |
|||
#define LORA_RESET 9 |
|||
|
|||
#define SX126X_CS LORA_CS |
|||
#define SX126X_DIO1 LORA_DIO1 |
|||
#define SX126X_BUSY 1 |
|||
#define SX126X_RESET LORA_RESET |
|||
|
|||
#define BATTERY_PIN 7 |
|||
#define ADC_CHANNEL ADC_CHANNEL_6 |
|||
#define ADC_CTRL 15 |
|||
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 |
|||
|
|||
#endif |
|||
@ -0,0 +1,86 @@ |
|||
#include "HeltecV4R8Board.h" |
|||
|
|||
void HeltecV4R8Board::begin() { |
|||
ESP32Board::begin(); |
|||
|
|||
periph_power.begin(); |
|||
periph_power.claim(); // R8 VEXT also feeds the LoRa antenna boost rail.
|
|||
|
|||
loRaFEMControl.init(); |
|||
|
|||
#ifdef PIN_TOUCH_RST |
|||
pinMode(PIN_TOUCH_RST, OUTPUT); |
|||
digitalWrite(PIN_TOUCH_RST, HIGH); |
|||
delay(10); |
|||
digitalWrite(PIN_TOUCH_RST, LOW); |
|||
delay(100); |
|||
digitalWrite(PIN_TOUCH_RST, HIGH); |
|||
#endif |
|||
|
|||
esp_reset_reason_t reason = esp_reset_reason(); |
|||
if (reason == ESP_RST_DEEPSLEEP) { |
|||
long wakeup_source = esp_sleep_get_ext1_wakeup_status(); |
|||
if (wakeup_source & (1 << P_LORA_DIO_1)) { |
|||
startup_reason = BD_STARTUP_RX_PACKET; |
|||
} |
|||
|
|||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); |
|||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); |
|||
} |
|||
} |
|||
|
|||
void HeltecV4R8Board::onBeforeTransmit(void) { |
|||
digitalWrite(P_LORA_TX_LED, HIGH); |
|||
loRaFEMControl.setTxModeEnable(); |
|||
} |
|||
|
|||
void HeltecV4R8Board::onAfterTransmit(void) { |
|||
digitalWrite(P_LORA_TX_LED, LOW); |
|||
loRaFEMControl.setRxModeEnable(); |
|||
} |
|||
|
|||
void HeltecV4R8Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { |
|||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); |
|||
|
|||
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); |
|||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); |
|||
|
|||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); |
|||
loRaFEMControl.setRxModeEnableWhenMCUSleep(); |
|||
|
|||
if (pin_wake_btn < 0) { |
|||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); |
|||
} else { |
|||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); |
|||
} |
|||
|
|||
if (secs > 0) { |
|||
esp_sleep_enable_timer_wakeup(secs * 1000000); |
|||
} |
|||
|
|||
esp_deep_sleep_start(); |
|||
} |
|||
|
|||
void HeltecV4R8Board::powerOff() { |
|||
enterDeepSleep(0); |
|||
} |
|||
|
|||
uint16_t HeltecV4R8Board::getBattMilliVolts() { |
|||
analogReadResolution(12); |
|||
|
|||
uint32_t raw = 0; |
|||
for (int i = 0; i < 8; i++) { |
|||
raw += analogReadMilliVolts(PIN_VBAT_READ); |
|||
} |
|||
raw = raw / 8; |
|||
|
|||
return (adc_mult * raw); |
|||
} |
|||
|
|||
const char* HeltecV4R8Board::getManufacturerName() const { |
|||
#ifdef HELTEC_V4_R8_TFT |
|||
return "Heltec V4 R8 TFT"; |
|||
#else |
|||
return "Heltec V4 R8 OLED"; |
|||
#endif |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
#include <driver/rtc_io.h> |
|||
#include <helpers/ESP32Board.h> |
|||
#include <helpers/RefCountedDigitalPin.h> |
|||
#include "LoRaFEMControl.h" |
|||
|
|||
#ifndef ADC_MULTIPLIER |
|||
#define ADC_MULTIPLIER (4.9f * 1.035f) |
|||
#endif |
|||
|
|||
class HeltecV4R8Board : public ESP32Board { |
|||
protected: |
|||
float adc_mult = ADC_MULTIPLIER; |
|||
|
|||
public: |
|||
RefCountedDigitalPin periph_power; |
|||
LoRaFEMControl loRaFEMControl; |
|||
|
|||
HeltecV4R8Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { } |
|||
|
|||
void begin(); |
|||
void onBeforeTransmit(void) override; |
|||
void onAfterTransmit(void) override; |
|||
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); |
|||
void powerOff() override; |
|||
uint16_t getBattMilliVolts() override; |
|||
bool setAdcMultiplier(float multiplier) override { |
|||
if (multiplier == 0.0f) { |
|||
adc_mult = ADC_MULTIPLIER; |
|||
} else { |
|||
adc_mult = multiplier; |
|||
} |
|||
return true; |
|||
} |
|||
float getAdcMultiplier() const override { return adc_mult; } |
|||
const char* getManufacturerName() const override; |
|||
}; |
|||
@ -0,0 +1,52 @@ |
|||
#include "LoRaFEMControl.h" |
|||
|
|||
#include <Arduino.h> |
|||
#include <driver/rtc_io.h> |
|||
#include <esp_sleep.h> |
|||
|
|||
void LoRaFEMControl::init(void) { |
|||
pinMode(P_LORA_PA_POWER, OUTPUT); |
|||
digitalWrite(P_LORA_PA_POWER, HIGH); |
|||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_PA_POWER); |
|||
|
|||
esp_reset_reason_t reason = esp_reset_reason(); |
|||
if (reason != ESP_RST_DEEPSLEEP) { |
|||
delay(1); |
|||
} |
|||
|
|||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_KCT8103L_PA_CSD); |
|||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_KCT8103L_PA_CTX); |
|||
|
|||
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT); |
|||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); |
|||
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT); |
|||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH); |
|||
} |
|||
|
|||
void LoRaFEMControl::setSleepModeEnable(void) { |
|||
digitalWrite(P_LORA_KCT8103L_PA_CSD, LOW); |
|||
} |
|||
|
|||
void LoRaFEMControl::setTxModeEnable(void) { |
|||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); |
|||
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); |
|||
} |
|||
|
|||
void LoRaFEMControl::setRxModeEnable(void) { |
|||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); |
|||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH); |
|||
} |
|||
|
|||
void LoRaFEMControl::setRxModeEnableWhenMCUSleep(void) { |
|||
digitalWrite(P_LORA_PA_POWER, HIGH); |
|||
rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); |
|||
|
|||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); |
|||
rtc_gpio_hold_en((gpio_num_t)P_LORA_KCT8103L_PA_CSD); |
|||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH); |
|||
rtc_gpio_hold_en((gpio_num_t)P_LORA_KCT8103L_PA_CTX); |
|||
} |
|||
|
|||
void LoRaFEMControl::setLNAEnable(bool enabled) { |
|||
lna_enabled = enabled; |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
#pragma once |
|||
|
|||
typedef enum { |
|||
KCT8103L_PA, |
|||
OTHER_FEM_TYPES |
|||
} LoRaFEMType; |
|||
|
|||
class LoRaFEMControl { |
|||
public: |
|||
LoRaFEMControl() { } |
|||
virtual ~LoRaFEMControl() { } |
|||
void init(void); |
|||
void setSleepModeEnable(void); |
|||
void setTxModeEnable(void); |
|||
void setRxModeEnable(void); |
|||
void setRxModeEnableWhenMCUSleep(void); |
|||
void setLNAEnable(bool enabled); |
|||
bool isLnaCanControl(void) { return true; } |
|||
void setLnaCanControl(bool can_control) { } |
|||
LoRaFEMType getFEMType(void) const { return KCT8103L_PA; } |
|||
|
|||
private: |
|||
bool lna_enabled = false; |
|||
}; |
|||
@ -0,0 +1,56 @@ |
|||
#ifndef Pins_Arduino_h |
|||
#define Pins_Arduino_h |
|||
|
|||
#include <stdint.h> |
|||
|
|||
#define USB_VID 0x303a |
|||
#define USB_PID 0x1001 |
|||
|
|||
static const uint8_t TX = 43; |
|||
static const uint8_t RX = 44; |
|||
|
|||
static const uint8_t SDA = 17; |
|||
static const uint8_t SCL = 18; |
|||
|
|||
static const uint8_t SS = 8; |
|||
static const uint8_t MOSI = 10; |
|||
static const uint8_t MISO = 11; |
|||
static const uint8_t SCK = 9; |
|||
|
|||
static const uint8_t A0 = 1; |
|||
static const uint8_t A1 = 2; |
|||
static const uint8_t A2 = 3; |
|||
static const uint8_t A3 = 4; |
|||
static const uint8_t A4 = 5; |
|||
static const uint8_t A5 = 6; |
|||
static const uint8_t A6 = 7; |
|||
static const uint8_t A7 = 8; |
|||
static const uint8_t A8 = 9; |
|||
static const uint8_t A9 = 10; |
|||
static const uint8_t A10 = 11; |
|||
static const uint8_t A11 = 12; |
|||
static const uint8_t A12 = 13; |
|||
static const uint8_t A13 = 14; |
|||
static const uint8_t A14 = 15; |
|||
static const uint8_t A15 = 16; |
|||
static const uint8_t A16 = 17; |
|||
static const uint8_t A17 = 18; |
|||
static const uint8_t A18 = 19; |
|||
static const uint8_t A19 = 20; |
|||
|
|||
static const uint8_t T1 = 1; |
|||
static const uint8_t T2 = 2; |
|||
static const uint8_t T3 = 3; |
|||
static const uint8_t T4 = 4; |
|||
static const uint8_t T5 = 5; |
|||
static const uint8_t T6 = 6; |
|||
static const uint8_t T7 = 7; |
|||
static const uint8_t T8 = 8; |
|||
static const uint8_t T9 = 9; |
|||
static const uint8_t T10 = 10; |
|||
static const uint8_t T11 = 11; |
|||
static const uint8_t T12 = 12; |
|||
static const uint8_t T13 = 13; |
|||
static const uint8_t T14 = 14; |
|||
|
|||
#endif |
|||
@ -0,0 +1,342 @@ |
|||
[Heltec_v4_r8] |
|||
extends = esp32_base |
|||
board = heltec_v4_r8 |
|||
build_flags = |
|||
${esp32_base.build_flags} |
|||
${sensor_base.build_flags} |
|||
-I variants/heltec_v4_r8 |
|||
-D HELTEC_V4_R8 |
|||
-D USE_SX1262 |
|||
-D ESP32_CPU_FREQ=80 |
|||
-D RADIO_CLASS=CustomSX1262 |
|||
-D WRAPPER_CLASS=CustomSX1262Wrapper |
|||
-D P_LORA_DIO_1=14 |
|||
-D P_LORA_NSS=8 |
|||
-D P_LORA_RESET=12 |
|||
-D P_LORA_BUSY=13 |
|||
-D P_LORA_SCLK=9 |
|||
-D P_LORA_MISO=11 |
|||
-D P_LORA_MOSI=10 |
|||
-D P_LORA_PA_POWER=7 |
|||
-D P_LORA_KCT8103L_PA_CSD=2 |
|||
-D P_LORA_KCT8103L_PA_CTX=5 |
|||
-D P_LORA_TX_LED=46 |
|||
-D PIN_USER_BTN=0 |
|||
-D PIN_VEXT_EN=40 |
|||
-D PIN_VEXT_EN_ACTIVE=LOW |
|||
-D ADC_MULTIPLIER=5.0715f |
|||
-D PIN_VBAT_READ=1 |
|||
-D LORA_TX_POWER=10 |
|||
-D MAX_LORA_TX_POWER=22 |
|||
-D SX126X_REGISTER_PATCH=1 |
|||
-D SX126X_DIO2_AS_RF_SWITCH=true |
|||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8 |
|||
-D SX126X_CURRENT_LIMIT=140 |
|||
-D SX126X_RX_BOOSTED_GAIN=1 |
|||
-D PIN_GPS_RX=38 |
|||
-D PIN_GPS_TX=39 |
|||
-D PIN_GPS_EN=42 |
|||
-D PIN_GPS_EN_ACTIVE=LOW |
|||
-D ENV_INCLUDE_GPS=1 |
|||
build_src_filter = ${esp32_base.build_src_filter} |
|||
+<../variants/heltec_v4_r8> |
|||
+<helpers/sensors> |
|||
lib_deps = |
|||
${esp32_base.lib_deps} |
|||
${sensor_base.lib_deps} |
|||
|
|||
[heltec_v4_r8_oled] |
|||
extends = Heltec_v4_r8 |
|||
build_flags = |
|||
${Heltec_v4_r8.build_flags} |
|||
-D HELTEC_V4_R8_OLED |
|||
-D PIN_BOARD_SDA=17 |
|||
-D PIN_BOARD_SCL=18 |
|||
-D PIN_OLED_RESET=21 |
|||
build_src_filter = ${Heltec_v4_r8.build_src_filter} |
|||
lib_deps = ${Heltec_v4_r8.lib_deps} |
|||
|
|||
[heltec_v4_r8_tft] |
|||
extends = Heltec_v4_r8 |
|||
build_flags = |
|||
${Heltec_v4_r8.build_flags} |
|||
-D HELTEC_V4_R8_TFT |
|||
-D PIN_BOARD_SDA=17 |
|||
-D PIN_BOARD_SCL=18 |
|||
-D DISPLAY_SCALE_X=2.5 |
|||
-D DISPLAY_SCALE_Y=3.75 |
|||
-D PIN_TFT_RST=-1 |
|||
-D PIN_TFT_VDD_CTL=-1 |
|||
-D PIN_TFT_LEDA_CTL=44 |
|||
-D PIN_TFT_LEDA_CTL_ACTIVE=HIGH |
|||
-D PIN_TFT_CS=47 |
|||
-D PIN_TFT_DC=48 |
|||
-D PIN_TFT_SCL=16 |
|||
-D PIN_TFT_SDA=15 |
|||
-D PIN_TFT_MISO=45 |
|||
-D PIN_BUZZER=4 |
|||
-D PIN_TOUCH_RST=21 |
|||
build_src_filter = ${Heltec_v4_r8.build_src_filter} |
|||
+<helpers/ui/buzzer.cpp> |
|||
lib_deps = |
|||
${Heltec_v4_r8.lib_deps} |
|||
adafruit/Adafruit ST7735 and ST7789 Library @ ^1.11.0 |
|||
end2endzone/NonBlockingRTTTL@^1.3.0 |
|||
|
|||
[env:heltec_v4_r8_repeater] |
|||
extends = heltec_v4_r8_oled |
|||
build_flags = |
|||
${heltec_v4_r8_oled.build_flags} |
|||
-D DISPLAY_CLASS=SSD1306Display |
|||
-D ADVERT_NAME='"Heltec R8 Repeater"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D MAX_NEIGHBOURS=50 |
|||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter} |
|||
+<helpers/ui/SSD1306Display.cpp> |
|||
+<../examples/simple_repeater> |
|||
lib_deps = |
|||
${heltec_v4_r8_oled.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
bakercp/CRC32 @ ^2.0.0 |
|||
|
|||
[env:heltec_v4_r8_room_server] |
|||
extends = heltec_v4_r8_oled |
|||
build_flags = |
|||
${heltec_v4_r8_oled.build_flags} |
|||
-D DISPLAY_CLASS=SSD1306Display |
|||
-D ADVERT_NAME='"Heltec R8 Room"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D ROOM_PASSWORD='"hello"' |
|||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter} |
|||
+<helpers/ui/SSD1306Display.cpp> |
|||
+<../examples/simple_room_server> |
|||
lib_deps = |
|||
${heltec_v4_r8_oled.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_v4_r8_terminal_chat] |
|||
extends = heltec_v4_r8_oled |
|||
build_flags = |
|||
${heltec_v4_r8_oled.build_flags} |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=1 |
|||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter} |
|||
+<../examples/simple_secure_chat/main.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_oled.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_companion_radio_usb] |
|||
extends = heltec_v4_r8_oled |
|||
build_flags = |
|||
${heltec_v4_r8_oled.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D DISPLAY_CLASS=SSD1306Display |
|||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter} |
|||
+<helpers/ui/SSD1306Display.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_oled.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_companion_radio_ble] |
|||
extends = heltec_v4_r8_oled |
|||
build_flags = |
|||
${heltec_v4_r8_oled.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D DISPLAY_CLASS=SSD1306Display |
|||
-D BLE_PIN_CODE=123456 |
|||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400 |
|||
-D BLE_DEBUG_LOGGING=1 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter} |
|||
+<helpers/ui/SSD1306Display.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_oled.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_companion_radio_wifi] |
|||
extends = heltec_v4_r8_oled |
|||
build_flags = |
|||
${heltec_v4_r8_oled.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
-D DISPLAY_CLASS=SSD1306Display |
|||
-D WIFI_DEBUG_LOGGING=1 |
|||
-D WIFI_SSID='"myssid"' |
|||
-D WIFI_PWD='"mypwd"' |
|||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter} |
|||
+<helpers/ui/SSD1306Display.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_oled.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_sensor] |
|||
extends = heltec_v4_r8_oled |
|||
build_flags = |
|||
${heltec_v4_r8_oled.build_flags} |
|||
-D ADVERT_NAME='"Heltec R8 Sensor"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D DISPLAY_CLASS=SSD1306Display |
|||
build_src_filter = ${heltec_v4_r8_oled.build_src_filter} |
|||
+<helpers/ui/SSD1306Display.cpp> |
|||
+<../examples/simple_sensor> |
|||
lib_deps = |
|||
${heltec_v4_r8_oled.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_v4_r8_tft_repeater] |
|||
extends = heltec_v4_r8_tft |
|||
build_flags = |
|||
${heltec_v4_r8_tft.build_flags} |
|||
-D DISPLAY_CLASS=ST7789LCDDisplay |
|||
-D ADVERT_NAME='"Heltec R8 Repeater"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D MAX_NEIGHBOURS=50 |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<helpers/ui/ST7789LCDDisplay.cpp> |
|||
+<../examples/simple_repeater> |
|||
lib_deps = |
|||
${heltec_v4_r8_tft.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
bakercp/CRC32 @ ^2.0.0 |
|||
|
|||
[env:heltec_v4_r8_tft_room_server] |
|||
extends = heltec_v4_r8_tft |
|||
build_flags = |
|||
${heltec_v4_r8_tft.build_flags} |
|||
-D DISPLAY_CLASS=ST7789LCDDisplay |
|||
-D ADVERT_NAME='"Heltec R8 Room"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D ROOM_PASSWORD='"hello"' |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<helpers/ui/ST7789LCDDisplay.cpp> |
|||
+<../examples/simple_room_server> |
|||
lib_deps = |
|||
${heltec_v4_r8_tft.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_v4_r8_tft_terminal_chat] |
|||
extends = heltec_v4_r8_tft |
|||
build_flags = |
|||
${heltec_v4_r8_tft.build_flags} |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=1 |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<../examples/simple_secure_chat/main.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_tft.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_tft_companion_radio_usb] |
|||
extends = heltec_v4_r8_tft |
|||
build_flags = |
|||
${heltec_v4_r8_tft.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D DISPLAY_CLASS=ST7789LCDDisplay |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<helpers/ui/ST7789LCDDisplay.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_tft.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_tft_companion_radio_ble] |
|||
extends = heltec_v4_r8_tft |
|||
build_flags = |
|||
${heltec_v4_r8_tft.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D DISPLAY_CLASS=ST7789LCDDisplay |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D BLE_PIN_CODE=123456 |
|||
-D AUTO_SHUTDOWN_MILLIVOLTS=3400 |
|||
-D BLE_DEBUG_LOGGING=1 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<helpers/ui/ST7789LCDDisplay.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_tft.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_tft_companion_radio_wifi] |
|||
extends = heltec_v4_r8_tft |
|||
build_flags = |
|||
${heltec_v4_r8_tft.build_flags} |
|||
-I examples/companion_radio/ui-new |
|||
-D MAX_CONTACTS=350 |
|||
-D MAX_GROUP_CHANNELS=40 |
|||
-D OFFLINE_QUEUE_SIZE=256 |
|||
-D DISPLAY_CLASS=ST7789LCDDisplay |
|||
-D WIFI_DEBUG_LOGGING=1 |
|||
-D WIFI_SSID='"myssid"' |
|||
-D WIFI_PWD='"mypwd"' |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<helpers/ui/ST7789LCDDisplay.cpp> |
|||
+<helpers/ui/MomentaryButton.cpp> |
|||
+<helpers/esp32/*.cpp> |
|||
+<../examples/companion_radio/*.cpp> |
|||
+<../examples/companion_radio/ui-new/*.cpp> |
|||
lib_deps = |
|||
${heltec_v4_r8_tft.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
|
|||
[env:heltec_v4_r8_tft_sensor] |
|||
extends = heltec_v4_r8_tft |
|||
build_flags = |
|||
${heltec_v4_r8_tft.build_flags} |
|||
-D ADVERT_NAME='"Heltec R8 Sensor"' |
|||
-D ADVERT_LAT=0.0 |
|||
-D ADVERT_LON=0.0 |
|||
-D ADMIN_PASSWORD='"password"' |
|||
-D DISPLAY_CLASS=ST7789LCDDisplay |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<helpers/ui/ST7789LCDDisplay.cpp> |
|||
+<../examples/simple_sensor> |
|||
lib_deps = |
|||
${heltec_v4_r8_tft.lib_deps} |
|||
${esp32_ota.lib_deps} |
|||
|
|||
[env:heltec_v4_r8_kiss_modem] |
|||
extends = Heltec_v4_r8 |
|||
build_src_filter = ${Heltec_v4_r8.build_src_filter} |
|||
+<../examples/kiss_modem/> |
|||
|
|||
[env:heltec_v4_r8_tft_kiss_modem] |
|||
extends = heltec_v4_r8_tft |
|||
build_src_filter = ${heltec_v4_r8_tft.build_src_filter} |
|||
+<../examples/kiss_modem/> |
|||
@ -0,0 +1,45 @@ |
|||
#include <Arduino.h> |
|||
#include "target.h" |
|||
|
|||
HeltecV4R8Board board; |
|||
|
|||
#if defined(P_LORA_SCLK) |
|||
static SPIClass spi; |
|||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); |
|||
#else |
|||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); |
|||
#endif |
|||
|
|||
WRAPPER_CLASS radio_driver(radio, board); |
|||
|
|||
ESP32RTCClock fallback_clock; |
|||
AutoDiscoverRTCClock rtc_clock(fallback_clock); |
|||
|
|||
#if ENV_INCLUDE_GPS |
|||
#include <helpers/sensors/MicroNMEALocationProvider.h> |
|||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock, GPS_RESET, GPS_EN, &board.periph_power); |
|||
EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); |
|||
#else |
|||
EnvironmentSensorManager sensors; |
|||
#endif |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
DISPLAY_CLASS display(&board.periph_power); |
|||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true); |
|||
#endif |
|||
|
|||
bool radio_init() { |
|||
fallback_clock.begin(); |
|||
rtc_clock.begin(Wire); |
|||
|
|||
#if defined(P_LORA_SCLK) |
|||
return radio.std_init(&spi); |
|||
#else |
|||
return radio.std_init(); |
|||
#endif |
|||
} |
|||
|
|||
mesh::LocalIdentity radio_new_identity() { |
|||
RadioNoiseListener rng(radio); |
|||
return mesh::LocalIdentity(&rng); |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
#pragma once |
|||
|
|||
#define RADIOLIB_STATIC_ONLY 1 |
|||
#include <RadioLib.h> |
|||
#include <HeltecV4R8Board.h> |
|||
#include <helpers/AutoDiscoverRTCClock.h> |
|||
#include <helpers/SensorManager.h> |
|||
#include <helpers/radiolib/CustomSX1262Wrapper.h> |
|||
#include <helpers/radiolib/RadioLibWrappers.h> |
|||
#include <helpers/sensors/EnvironmentSensorManager.h> |
|||
#ifdef DISPLAY_CLASS |
|||
#ifdef HELTEC_V4_R8_OLED |
|||
#include <helpers/ui/SSD1306Display.h> |
|||
#elif defined(HELTEC_V4_R8_TFT) |
|||
#include <helpers/ui/ST7789LCDDisplay.h> |
|||
#endif |
|||
#include <helpers/ui/MomentaryButton.h> |
|||
#endif |
|||
|
|||
extern HeltecV4R8Board board; |
|||
extern WRAPPER_CLASS radio_driver; |
|||
extern AutoDiscoverRTCClock rtc_clock; |
|||
extern EnvironmentSensorManager sensors; |
|||
|
|||
#ifdef DISPLAY_CLASS |
|||
extern DISPLAY_CLASS display; |
|||
extern MomentaryButton user_btn; |
|||
#endif |
|||
|
|||
bool radio_init(); |
|||
mesh::LocalIdentity radio_new_identity(); |
|||
Loading…
Reference in new issue