Compare commits

...

5 Commits

Author SHA1 Message Date
TSAO
b5bcaf1f2c
Merge 4d9dbce55f into 3b82d55176 2025-09-02 06:32:16 -05:00
Ben Meadors
3b82d55176
Revert "Add gat562_mesh_tracker_pro device. (#7815)" (#7824)
This reverts commit 7d1300ab66.
2025-09-02 06:17:01 -05:00
Tom Fifield
a6b8202cd4 Hold for 20s after GPS lock (#7801)
* Hold for >20s after GPS lock

GPS chips are designed to stay locked for a while to download some data and save it.
This data is important for speeding up future locks, and making them higher quality.
Our present configuration could make every lock perform similar to first lock.

This patch sets a hold of between 20s and 10% of the lock search time after lock
is acquired. This should allow the GPS to finish its work before we turn it off.

Fixes https://github.com/meshtastic/firmware/issues/7466

* Remove T1000E-specific GPS holds

The new code does the same thing, for all devices.

* Fix publishing settings

* Cleanups, removing unused variables.

* ifdef log line with GPS_DEBUG

* fixQual is not a bool.
2025-09-02 06:06:06 -05:00
Jason P
cfc1bf10c9 If usePreset is False, show value as Custom (#7812) 2025-09-02 06:05:55 -05:00
csrutil
4d9dbce55f feat: add virtual keyboard implementation for text input
- Implement VirtualKeyboard class with full keyboard functionality
- Support cursor navigation, text input, and special keys (DEL, SPACE, CAPS, OK)
- Compatible with both MESHCORE and MESHTASTIC display systems
- Include multi-language layout support (EN/CN keyboards)
- Add key press callback system for extensibility
- Handle caps lock toggle and text buffer management
2025-09-01 20:38:21 +08:00
15 changed files with 434 additions and 489 deletions

View File

@ -1,52 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
],
"usb_product": "GAT562 Mesh Tracker Pro",
"mcu": "nrf52840",
"variant": "gat562_mesh_tracker_pro",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino", "freertos"],
"name": "GAT562 Mesh Tracker Pro",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "http://www.gat-iot.com/",
"vendor": "GAT"
}

View File

@ -1,7 +1,14 @@
#include "DisplayFormatters.h"
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
bool usePreset)
{
// If use_preset is false, always return "Custom"
if (!usePreset) {
return "Custom";
}
switch (preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
return useShortName ? "ShortT" : "ShortTurbo";

View File

@ -4,5 +4,6 @@
class DisplayFormatters
{
public:
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName);
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
bool usePreset);
};

View File

@ -843,9 +843,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
setPowerPMU(true); // Power (PMU): on
writePinStandby(false); // Standby (pin): awake (not standby)
setPowerUBLOX(true); // Standby (UBLOX): awake
#ifdef GNSS_AIROHA
lastFixStartMsec = 0;
#endif
break;
case GPS_SOFTSLEEP:
@ -863,9 +860,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
writePinStandby(true); // Standby (pin): asleep (not awake)
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
#ifdef GNSS_AIROHA
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
digitalWrite(PIN_GPS_EN, LOW);
}
digitalWrite(PIN_GPS_EN, LOW);
#endif
break;
@ -877,9 +872,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
writePinStandby(true); // Standby (pin): asleep
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
#ifdef GNSS_AIROHA
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
digitalWrite(PIN_GPS_EN, LOW);
}
digitalWrite(PIN_GPS_EN, LOW);
#endif
break;
}
@ -1062,6 +1055,8 @@ void GPS::down()
}
// If update interval long enough (or softsleep unsupported): hardsleep instead
setPowerState(GPS_HARDSLEEP, sleepTime);
// Reset the fix quality to 0, since we're off.
fixQual = 0;
}
}
@ -1121,11 +1116,19 @@ int32_t GPS::runOnce()
shouldPublish = true;
}
uint8_t prev_fixQual = fixQual;
bool gotLoc = lookForLocation();
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
LOG_DEBUG("hasValidLocation RISING EDGE");
hasValidLocation = true;
shouldPublish = true;
// Hold for 20secs after getting a lock to download ephemeris etc
fixHoldEnds = millis() + 20000;
}
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
fixHoldEnds = millis() + 20000;
shouldPublish = true; // Publish immediately, since next publish is at end of hold
}
bool tooLong = scheduling.searchedTooLong();
@ -1134,8 +1137,7 @@ int32_t GPS::runOnce()
// Once we get a location we no longer desperately want an update
if ((gotLoc && gotTime) || tooLong) {
if (tooLong) {
if (tooLong && !gotLoc) {
// we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) {
LOG_DEBUG("hasValidLocation FALLING EDGE");
@ -1143,9 +1145,15 @@ int32_t GPS::runOnce()
p = meshtastic_Position_init_default;
hasValidLocation = false;
}
down();
shouldPublish = true; // publish our update for this just finished acquisition window
if (millis() > fixHoldEnds) {
shouldPublish = true; // publish our update at the end of the lock hold
publishUpdate();
down();
#ifdef GPS_DEBUG
} else {
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
#endif
}
}
// If state has changed do a publish
@ -1508,24 +1516,6 @@ static int32_t toDegInt(RawDegrees d)
*/
bool GPS::lookForTime()
{
#ifdef GNSS_AIROHA
uint8_t fix = reader.fixQuality();
if (fix >= 1 && fix <= 5) {
if (lastFixStartMsec > 0) {
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false;
} else {
clearBuffer();
}
} else {
lastFixStartMsec = millis();
return false;
}
} else {
return false;
}
#endif
auto ti = reader.time;
auto d = reader.date;
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
@ -1564,25 +1554,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
*/
bool GPS::lookForLocation()
{
#ifdef GNSS_AIROHA
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
uint8_t fix = reader.fixQuality();
if (fix >= 1 && fix <= 5) {
if (lastFixStartMsec > 0) {
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false;
} else {
clearBuffer();
}
} else {
lastFixStartMsec = millis();
return false;
}
} else {
return false;
}
}
#endif
// By default, TinyGPS++ does not parse GPGSA lines, which give us
// the 2D/3D fixType (see NMEAGPS.h)
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)

View File

@ -159,7 +159,7 @@ class GPS : private concurrency::OSThread
uint8_t fixType = 0; // fix type from GPGSA
#endif
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
uint32_t fixHoldEnds = 0;
uint32_t rx_gpio = 0;
uint32_t tx_gpio = 0;

View File

@ -0,0 +1,285 @@
/*
* VirtualKeyboard.cpp
* Author TSAO (hey@tsao.dev) 2025
*/
#include "VirtualKeyboard.h"
#ifdef MESHCORE
#include "MeshCore.h"
#elif defined(MESHTASTIC)
#include "configuration.h"
#include "graphics/ScreenFonts.h"
#endif
#include <Arduino.h>
#include <string.h>
#ifdef MESHCORE
VirtualKeyboard::VirtualKeyboard(DisplayDriver *displayDriver, int startX, int startY, int keyboardWidth,
int keyboardHeight)
: display(displayDriver) {
#elif defined(MESHTASTIC)
VirtualKeyboard::VirtualKeyboard(OLEDDisplay *displayDriver, int startX, int startY, int keyboardWidth,
int keyboardHeight)
: display(displayDriver) {
#endif
memset(inputBuffer, 0, sizeof(inputBuffer));
bufferLength = 0;
this->startX = startX;
this->startY = startY;
this->keyboardWidth = keyboardWidth;
this->keyboardHeight = keyboardHeight;
}
void VirtualKeyboard::moveCursor(int deltaRow, int deltaColumn) {
cursorRow += deltaRow;
cursorColumn += deltaColumn;
// Handle column overflow/underflow with row wrapping
if (cursorColumn >= getCols()) {
cursorColumn = 0;
cursorRow++;
} else if (cursorColumn < 0) {
cursorColumn = getCols() - 1;
cursorRow--;
}
// Handle row overflow/underflow
if (cursorRow >= getRows()) {
cursorRow = 0;
} else if (cursorRow < 0) {
cursorRow = getRows() - 1;
}
}
void VirtualKeyboard::moveRight(int steps) {
moveCursor(0, steps);
}
void VirtualKeyboard::moveLeft(int steps) {
moveCursor(0, -steps);
}
void VirtualKeyboard::moveUp(int steps) {
moveCursor(-steps, 0);
}
void VirtualKeyboard::moveDown(int steps) {
moveCursor(steps, 0);
}
const char *VirtualKeyboard::getKeyText(int row, int column) {
const char *keyString = getKeyAt(row, column);
if (!keyString || strlen(keyString) == 0) {
return "";
}
// Handle caps lock for letters
if (capsLockEnabled && strlen(keyString) == 1 && keyString[0] >= 'a' && keyString[0] <= 'z') {
static char uppercaseKey[2];
uppercaseKey[0] = keyString[0] - 'a' + 'A';
uppercaseKey[1] = '\0';
return uppercaseKey;
}
return keyString;
}
const char *VirtualKeyboard::pressCurrentKey() {
const char *pressedKey = getKeyText(cursorRow, cursorColumn);
if (strcmp(pressedKey, "DEL") == 0) {
// Delete/Backspace
if (bufferLength > 0) {
bufferLength--;
inputBuffer[bufferLength] = '\0';
}
} else if (strcmp(pressedKey, "OK") == 0) {
// Enter - could trigger submission or new line
if (bufferLength < sizeof(inputBuffer) - 1) {
inputBuffer[bufferLength++] = '\n';
inputBuffer[bufferLength] = '\0';
}
} else if (strcmp(pressedKey, "SPACE") == 0) {
// Space
if (bufferLength < sizeof(inputBuffer) - 1) {
inputBuffer[bufferLength++] = ' ';
inputBuffer[bufferLength] = '\0';
}
} else if (strcmp(pressedKey, "CAPS") == 0) {
// Toggle caps lock
capsLockEnabled = !capsLockEnabled;
} else {
// Regular character
int keyLength = strlen(pressedKey);
if (bufferLength + keyLength < sizeof(inputBuffer) - 1) {
strcat(inputBuffer, pressedKey);
bufferLength += keyLength;
}
}
// Call user callback if one is set
if (keyPressCallback != nullptr) {
keyPressCallback(pressedKey, inputBuffer);
}
return pressedKey;
}
void VirtualKeyboard::clear() {
memset(inputBuffer, 0, sizeof(inputBuffer));
bufferLength = 0;
}
void VirtualKeyboard::reset() {
clear();
cursorRow = 0;
cursorColumn = 0;
}
void VirtualKeyboard::setKeyPressCallback(KeyPressCallback callback) {
keyPressCallback = callback;
}
void VirtualKeyboard::getTextBounds(const char *text, uint16_t *width, uint16_t *height) {
if (!display || !text) {
if (width) *width = 0;
if (height) *height = 0;
return;
}
#ifdef MESHCORE
Adafruit_SSD1306 *ssd1306Display = static_cast<Adafruit_SSD1306 *>(display->getDisplay());
if (ssd1306Display) {
int16_t x1, y1;
uint16_t w, h;
ssd1306Display->getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
if (width) *width = w;
if (height) *height = h;
} else {
// Fallback if getDisplay() doesn't work
if (width) *width = display->getTextWidth(text);
if (height) *height = 8; // Default text height
}
#elif defined(MESHTASTIC)
if (width) *width = display->getStringWidth(text);
// FONT_SMALL is 7 by 🧐
if (height) *height = 7; // FONT_HEIGHT_SMALL;
#endif
}
void VirtualKeyboard::drawKeyboard() {
if (!display) return;
#ifdef MESHCORE
display->setTextSize(1);
#elif defined(MESHTASTIC)
display->setFont(FONT_SMALL);
// display->clear();
#endif
// Calculate max standard key width (kw)
uint16_t kw = 0;
for (int row = 0; row < getRows(); row++) {
for (int col = 0; col < getCols() - 1; col++) { // Exclude rightmost column (control keys)
uint16_t keyWidth = 0;
const char *keyText = getKeyText(row, col);
getTextBounds(keyText, &keyWidth, nullptr);
if (keyWidth > kw) {
kw = keyWidth;
}
}
}
// Calculate max control key width (cw)
uint16_t cw = 0;
for (int row = 0; row < getRows(); row++) {
int col = getCols() - 1; // Only check rightmost column
uint16_t keyWidth = 0;
const char *keyText = getKeyText(row, col);
getTextBounds(keyText, &keyWidth, nullptr);
if (keyWidth > cw) {
cw = keyWidth;
}
}
// Calculate horizontal spacing
uint16_t totalKeyWidth = (getCols() - 1) * kw + cw;
uint16_t spacingX = (keyboardWidth - totalKeyWidth) / (getCols() - 1);
uint16_t fraction = (keyboardWidth - totalKeyWidth) % (getCols() - 1);
// Calculate key height and vertical spacing
uint16_t keyH = 0;
getTextBounds(getKeyText(0, 0), nullptr, &keyH);
uint16_t spacingY = (keyboardHeight - getRows() * keyH) / (getRows() - 1);
#ifdef MESHTASTIC
spacingY = 2;
#endif
for (int row = 0; row < getRows(); row++) {
for (int col = 0; col < getCols(); col++) {
const char *label = getKeyText(row, col);
uint16_t currentKeyWidth = (col == getCols() - 1) ? cw : kw;
// Calculate x position dynamically
int currentX = startX + col * (kw + spacingX) + ((col == getCols() - 1) ? (cw - kw) : 0);
if (col == getCols() - 1) {
currentX = keyboardWidth - cw;
// currentX += fraction * (getCols() - 1);
}
// Calculate y position
int y = startY + row * keyH + row * spacingY;
// Check if this is the currently selected key
bool selected = (row == cursorRow && col == cursorColumn);
if (selected) {
// Highlight the selected key with inverted colors
#ifdef MESHCORE
display->setColor(DisplayDriver::LIGHT);
display->fillRect(currentX - 1, y - 1, currentKeyWidth + 2, keyH + 2);
display->setColor(DisplayDriver::DARK); // Dark text on light background
#elif defined(MESHTASTIC)
display->setColor(OLEDDISPLAY_COLOR::WHITE);
if (col == 0 && startX > 0) {
display->fillRect(currentX - 1, y + 2, currentKeyWidth + 1, keyH + 2);
} else {
display->fillRect(currentX - 2, y + 2, currentKeyWidth + 1, keyH + 2);
}
display->setColor(OLEDDISPLAY_COLOR::BLACK); // Dark text on light background
#endif
} else {
#ifdef MESHCORE
display->setColor(DisplayDriver::LIGHT); // Light text on dark background
#elif defined(MESHTASTIC)
display->setColor(OLEDDISPLAY_COLOR::WHITE); // Light text on dark background
#endif
}
// Draw the key text at the key position
#ifdef MESHCORE
display->setCursor(currentX, y);
display->print(label);
#elif defined(MESHTASTIC)
display->drawString(currentX, y, label);
#endif
}
}
// Reset text color to default
#ifdef MESHCORE
display->setColor(DisplayDriver::LIGHT);
#elif defined(MESHTASTIC)
display->setColor(OLEDDISPLAY_COLOR::WHITE);
#endif
}
void VirtualKeyboard::render() {
drawKeyboard();
}

View File

@ -0,0 +1,108 @@
/*
* VirtualKeyboard.h
* Author TSAO (hey@tsao.dev) 2025
*/
#pragma once
#ifdef MESHCORE
#include <Adafruit_SSD1306.h>
#include <helpers/ui/DisplayDriver.h>
#elif defined(MESHTASTIC)
#include "graphics/Screen.h"
#include <OLEDDisplay.h>
#endif
// Callback function type for key press events
typedef void (*KeyPressCallback)(const char *pressedKey, const char *currentText);
#ifdef VIRTUAL_KEYBOARD_EN
static const int VIRTUAL_KEYBOARD_ROWS = 4;
static const int VIRTUAL_KEYBOARD_COLS = 11;
static const char *keyboardLayout[VIRTUAL_KEYBOARD_ROWS][VIRTUAL_KEYBOARD_COLS] = {
{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "DEL" },
{ "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "OK" },
{ "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "SPACE" },
{ "z", "x", "c", "v", "b", "n", "m", ".", ",", "?", "CAPS" },
};
#endif
#ifdef VIRTUAL_KEYBOARD_CN
static const int VIRTUAL_KEYBOARD_ROWS = 4;
static const int VIRTUAL_KEYBOARD_COLS = 11;
static const char *keyboardLayout[VIRTUAL_KEYBOARD_ROWS][VIRTUAL_KEYBOARD_COLS] = {
{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "DEL" },
{ "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "OK" },
{ "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "SPACE" },
{ "z", "x", "c", "v", "b", "n", "m", ".", ",", "?", "EN/CN" },
};
#endif
class VirtualKeyboard {
private:
#ifdef MESHCORE
DisplayDriver *display;
#elif defined(MESHTASTIC)
OLEDDisplay *display;
#endif
int cursorRow = 0;
int cursorColumn = 0;
bool capsLockEnabled = false;
char inputBuffer[160];
int bufferLength = 0;
// Keyboard positioning and dimensions
int startX = 0;
int startY = 0;
int keyboardWidth = 0;
int keyboardHeight = 0;
// Callback function for key press events
KeyPressCallback keyPressCallback = nullptr;
void drawKeyboard();
const char *getKeyText(int row, int col);
public:
#ifdef MESHCORE
VirtualKeyboard(DisplayDriver *display, int startX = 0, int startY = 0, int keyboardWidth = 0,
int keyboardHeight = 0);
#elif defined(MESHTASTIC)
VirtualKeyboard(OLEDDisplay *display, int startX = 0, int startY = 0, int keyboardWidth = 0,
int keyboardHeight = 0);
#endif
void moveCursor(int deltaRow, int deltaColumn);
void moveRight(int steps = 1);
void moveLeft(int steps = 1);
void moveUp(int steps = 1);
void moveDown(int steps = 1);
const char *pressCurrentKey();
void clear();
void reset();
const char *getText() const { return inputBuffer; }
int getTextLength() const { return bufferLength; }
// Usage: kb.setKeyPressCallback([](const char* key, const char* text) { ... });
void setKeyPressCallback(KeyPressCallback callback);
void getTextBounds(const char *text, uint16_t *width, uint16_t *height);
void render();
int getRows() const { return VIRTUAL_KEYBOARD_ROWS; }
int getCols() const { return VIRTUAL_KEYBOARD_COLS; }
const char *getKeyAt(int row, int col) const {
if (row >= 0 && row < VIRTUAL_KEYBOARD_ROWS && col >= 0 && col < VIRTUAL_KEYBOARD_COLS) {
return keyboardLayout[row][col];
}
return "";
}
};

View File

@ -263,12 +263,6 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
display->drawString(x + 1, y, "USB");
}
// auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
// if (config.display.heading_bold)
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
uint32_t currentMillis = millis();
uint32_t seconds = currentMillis / 1000;
uint32_t minutes = seconds / 60;
@ -398,7 +392,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
// === Second Row: Radio Preset ===
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
char regionradiopreset[25];
const char *region = myRegion ? myRegion->name : NULL;
if (region != nullptr) {

View File

@ -368,7 +368,7 @@ const char *Channels::getName(size_t chIndex)
// Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case
// the app effed up and forgot to set channelSettings.name
if (config.lora.use_preset) {
channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
} else {
channelName = "Custom";
}
@ -382,7 +382,8 @@ bool Channels::isDefaultChannel(ChannelIndex chIndex)
const auto &ch = getByIndex(chIndex);
if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) {
const char *name = getName(chIndex);
const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
const char *presetName =
DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
// Check if the name is the default derived from the modem preset
if (strcmp(name, presetName) == 0)
return true;

View File

@ -586,7 +586,8 @@ void RadioInterface::applyModemConfig()
// Check if we use the default frequency slot
RadioInterface::uses_default_frequency_slot =
channel_num == hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false)) % numChannels;
channel_num ==
hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset)) % numChannels;
// Old frequency selection formula
// float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num);

View File

@ -1,15 +0,0 @@
; GAT562 Mesh Tracker Pro with Trackball support
[env:gat562_mesh_tracker_pro]
extends = nrf52840_base
board = gat562_mesh_tracker_pro
board_check = true
build_flags = ${nrf52840_base.build_flags}
-I variants/nrf52840/gat562_mesh_tracker_pro
-D GAT562_MESH_TRACKER_PRO
-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/gat562_mesh_tracker_pro>
lib_deps =
${nrf52840_base.lib_deps}

View File

@ -1,54 +0,0 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "variant.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
// P0
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// P1
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
void initVariant()
{
// LED1 & LED2
pinMode(PIN_LED1, OUTPUT);
ledOff(PIN_LED1);
pinMode(PIN_LED2, OUTPUT);
ledOff(PIN_LED2);
// 3V3 Power Rail
pinMode(PIN_3V3_EN, OUTPUT);
digitalWrite(PIN_3V3_EN, HIGH);
// Initialize trackball pins as inputs with pullup
#ifdef HAS_TRACKBALL
pinMode(TB_UP, INPUT_PULLUP);
pinMode(TB_DOWN, INPUT_PULLUP);
pinMode(TB_LEFT, INPUT_PULLUP);
pinMode(TB_RIGHT, INPUT_PULLUP);
pinMode(TB_PRESS, INPUT_PULLUP);
#endif
}

View File

@ -1,300 +0,0 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _VARIANT_GAT562_MESH_TRACKER_PRO_
#define _VARIANT_GAT562_MESH_TRACKER_PRO_
// led pin 2 (blue), see https://github.com/meshtastic/firmware/blob/master/src/mesh/NodeDB.cpp#L723
#define RAK4630
/** Master clock frequency */
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
// define USE_LFRC // Board uses RC for LF
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "WVariant.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Number of pins defined in PinDescription array
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (6)
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1
#define LED_BLUE PIN_LED2
#define LED_STATE_ON 1 // State when LED is litted
/*
* Buttons
*/
#define CANCEL_BUTTON_PIN 9
#define BUTTON_NEED_PULLUP
#define CANCEL_BUTTON_ACTIVE_LOW true
#define CANCEL_BUTTON_ACTIVE_PULLUP false
/*
* Analog pins
*/
#define PIN_A0 (5)
#define PIN_A1 (31)
#define PIN_A2 (28)
#define PIN_A3 (29)
#define PIN_A4 (30)
#define PIN_A5 (31)
#define PIN_A6 (0xff)
#define PIN_A7 (0xff)
static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
static const uint8_t A6 = PIN_A6;
static const uint8_t A7 = PIN_A7;
#define ADC_RESOLUTION 14
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
/*
* Serial interfaces
*/
#define PIN_SERIAL1_RX (15)
#define PIN_SERIAL1_TX (16)
// Connected to Jlink CDC
#define PIN_SERIAL2_RX (8)
#define PIN_SERIAL2_TX (6)
/*
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI_MISO (45)
#define PIN_SPI_MOSI (44)
#define PIN_SPI_SCK (43)
#define PIN_SPI1_MISO (29) // (0 + 29)
#define PIN_SPI1_MOSI (30) // (0 + 30)
#define PIN_SPI1_SCK (3) // (0 + 3)
static const uint8_t SS = 42;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
/*
* eink display pins
*/
// #define PIN_EINK_CS (0 + 26)
// #define PIN_EINK_BUSY (0 + 4)
// #define PIN_EINK_DC (0 + 17)
// #define PIN_EINK_RES (-1)
// #define PIN_EINK_SCLK (0 + 3)
// #define PIN_EINK_MOSI (0 + 30) // also called SDI
// #define USE_EINK
// Display - OLED connected via I2C
#define HAS_SCREEN 1
#define USE_SSD1306
// RAKRGB
// #define HAS_NCP5623
/*
* Wire Interfaces
*/
#define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE_SDA (13)
#define PIN_WIRE_SCL (14)
// QSPI Pins
#define PIN_QSPI_SCK 3
#define PIN_QSPI_CS 26
#define PIN_QSPI_IO0 30
#define PIN_QSPI_IO1 29
#define PIN_QSPI_IO2 28
#define PIN_QSPI_IO3 2
// On-board QSPI Flash
#define EXTERNAL_FLASH_DEVICES IS25LP080D
#define EXTERNAL_FLASH_USE_QSPI
/* @note RAK5005-O GPIO mapping to RAK4631 GPIO ports
RAK5005-O <-> nRF52840
IO1 <-> P0.17 (Arduino GPIO number 17)
IO2 <-> P1.02 (Arduino GPIO number 34)
IO3 <-> P0.21 (Arduino GPIO number 21)
IO4 <-> P0.04 (Arduino GPIO number 4)
IO5 <-> P0.09 (Arduino GPIO number 9)
IO6 <-> P0.10 (Arduino GPIO number 10)
IO7 <-> P0.28 (Arduino GPIO number 28)
SW1 <-> P0.01 (Arduino GPIO number 1)
A0 <-> P0.04/AIN2 (Arduino Analog A2
A1 <-> P0.31/AIN7 (Arduino Analog A7
SPI_CS <-> P0.26 (Arduino GPIO number 26)
*/
// RAK4630 LoRa module
/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ )
P1.10 NSS SPI NSS (Arduino GPIO number 42)
P1.11 SCK SPI CLK (Arduino GPIO number 43)
P1.12 MOSI SPI MOSI (Arduino GPIO number 44)
P1.13 MISO SPI MISO (Arduino GPIO number 45)
P1.14 BUSY BUSY signal (Arduino GPIO number 46)
P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47)
P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38)
Important for successful SX1262 initialization:
* Setup DIO2 to control the antenna switch
* Setup DIO3 to control the TCXO power supply
* Setup the SX1262 to use it's DCDC regulator and not the LDO
* RAK4630 schematics show GPIO P1.07 connected to the antenna switch, but it should not be initialized, as DIO2 will do the
control of the antenna switch
SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
*/
// configure the SET pin on the RAK12039 sensor board to disable the sensor while not reading
// air quality telemetry. PIN_NFC2 doesn't seem to be used anywhere else in the codebase, but if
// you're having problems with your node behaving weirdly when a RAK12039 board isn't connected,
// try disabling this.
// #define PMSA003I_ENABLE_PIN PIN_NFC2
// #define DETECTION_SENSOR_EN 4
#define USE_SX1262
#define SX126X_CS (42)
#define SX126X_DIO1 (47)
#define SX126X_BUSY (46)
#define SX126X_RESET (38)
// #define SX126X_TXEN (39)
// #define SX126X_RXEN (37)
#define SX126X_POWER_EN (37)
// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Testing USB detection
#define NRF_APM
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base
// IO1 is hooked to PPS (pin 12 on header) = gpio 17
// IO2 is hooked to GPS RESET = gpio 34, but it can not be used to this because IO2 is ALSO used to control 3V3_S power (1 is on).
// Therefore must be 1 to keep peripherals powered
// Power is on the controllable 3V3_S rail
// #define PIN_GPS_RESET (34)
// #define PIN_GPS_EN PIN_3V3_EN
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#define GPS_BAUDRATE 9600
#define GPS_RX_PIN PIN_SERIAL1_RX
#define GPS_TX_PIN PIN_SERIAL1_TX
// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press
// RAK12002 RTC Module
// #define RV3028_RTC (uint8_t)0b1010010
// RAK18001 Buzzer in Slot C
// #define PIN_BUZZER 21 // IO3 is PWM2
// NEW: set this via protobuf instead!
// Battery
// The battery sense is hooked to pin A0 (5)
#define BATTERY_PIN PIN_A0
// and has 12 bit resolution
#define BATTERY_SENSE_RESOLUTION_BITS 12
#define BATTERY_SENSE_RESOLUTION 4096.0
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER 1.73
// #define HAS_RTC 1
// #define HAS_ETHERNET 1
// #define RAK_4631 1
// #define PIN_ETHERNET_RESET 21
// #define PIN_ETHERNET_SS PIN_EINK_CS
// #define ETH_SPI_PORT SPI1
// #define AQ_SET_PIN 10
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Trackball Configuration
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define CANNED_MESSAGE_ADD_CONFIRMATION 1
// Trackball pins
#define HAS_TRACKBALL 1
#define TB_LEFT 30 // P0.30
#define TB_DOWN 4 // P0.04
#define TB_RIGHT 31 // P0.31
#define TB_UP 28 // P0.28
#define TB_PRESS 26 // P0.26 (SELECT)
#define TB_DIRECTION FALLING
#ifdef __cplusplus
}
#endif
/*----------------------------------------------------------------------------
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif

View File

@ -124,8 +124,7 @@ extern "C" {
#define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH
#define GPS_RESETB_OUT (32 + 14) // P1.14, always input pull_up
#define GPS_FIX_HOLD_TIME 15000 // ms
#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC
#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC
#define BATTERY_IMMUTABLE
#define ADC_MULTIPLIER (2.0F)
// P0.04/AIN2 is VCC_ADC, P0.05/AIN3 is CHARGER_DET, P1.03 is CHARGE_STA, P1.04 is CHARGE_DONE

View File

@ -123,7 +123,6 @@ extern "C" {
#define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up
// #define GPS_THREAD_INTERVAL 50
#define GPS_FIX_HOLD_TIME 15000 // ms
#define BATTERY_PIN 2
// #define ADC_CHANNEL ADC1_GPIO2_CHANNEL