mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-05 19:19:18 +00:00
Merge branch 'master' into tsl2561
This commit is contained in:
commit
7d6c4e1e7f
@ -4,19 +4,19 @@ cli:
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
ref: v1.7.1
|
||||
ref: v1.7.2
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.461
|
||||
- renovate@41.74.0
|
||||
- checkov@3.2.467
|
||||
- renovate@41.88.0
|
||||
- prettier@3.6.2
|
||||
- trufflehog@3.90.5
|
||||
- yamllint@1.37.1
|
||||
- bandit@1.8.6
|
||||
- trivy@0.64.1
|
||||
- taplo@0.9.3
|
||||
- ruff@0.12.7
|
||||
- trivy@0.65.0
|
||||
- taplo@0.10.0
|
||||
- ruff@0.12.10
|
||||
- isort@6.0.1
|
||||
- markdownlint@0.45.0
|
||||
- oxipng@9.1.5
|
||||
@ -25,7 +25,7 @@ lint:
|
||||
- flake8@7.3.0
|
||||
- hadolint@2.12.1-beta
|
||||
- shfmt@3.6.0
|
||||
- shellcheck@0.10.0
|
||||
- shellcheck@0.11.0
|
||||
- black@25.1.0
|
||||
- git-diff-check
|
||||
- gitleaks@8.28.0
|
||||
|
@ -87,6 +87,9 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.7" date="2025-08-28">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.7</url>
|
||||
</release>
|
||||
<release version="2.7.6" date="2025-08-12">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.6</url>
|
||||
</release>
|
||||
|
54
boards/heltec_mesh_solar.json
Normal file
54
boards/heltec_mesh_solar.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [
|
||||
["0x239A", "0x4405"],
|
||||
["0x239A", "0x0029"],
|
||||
["0x239A", "0x002A"],
|
||||
["0x239A", "0x0071"]
|
||||
],
|
||||
"usb_product": "HT-n5262",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "heltec_mesh_solar",
|
||||
"variants_dir": "variants",
|
||||
"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",
|
||||
"onboard_tools": ["jlink"],
|
||||
"svd_path": "nrf52840.svd",
|
||||
"openocd_target": "nrf52840-mdk-rs"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Heltec nrf (Adafruit BSP)",
|
||||
"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": "https://heltec.org/project/meshsolar/",
|
||||
"vendor": "Heltec"
|
||||
}
|
5
debian/changelog
vendored
5
debian/changelog
vendored
@ -1,4 +1,4 @@
|
||||
meshtasticd (2.7.6.0) UNRELEASED; urgency=medium
|
||||
meshtasticd (2.7.7.0) UNRELEASED; urgency=medium
|
||||
|
||||
[ Austin Lane ]
|
||||
* Initial packaging
|
||||
@ -39,5 +39,6 @@ meshtasticd (2.7.6.0) UNRELEASED; urgency=medium
|
||||
|
||||
[ ]
|
||||
* GitHub Actions Automatic version bump
|
||||
* GitHub Actions Automatic version bump
|
||||
|
||||
-- <github-actions[bot]@users.noreply.github.com> Tue, 12 Aug 2025 23:48:48 +0000
|
||||
-- Ubuntu <github-actions[bot]@users.noreply.github.com> Thu, 28 Aug 2025 10:33:25 +0000
|
||||
|
@ -118,7 +118,7 @@ lib_deps =
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/3dc7cf3e233aaa8cc23492cca50541fc099ebfa1.zip
|
||||
https://github.com/meshtastic/device-ui/archive/a3e0e1be372d069f47b4c19d718f5267251744d7.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 8985852d752de3f7210f9a4a3e0923120ec438b3
|
||||
Subproject commit 4c4427c4a73c86fed7dc8632188bb8be95349d81
|
@ -681,6 +681,8 @@ bool Power::setup()
|
||||
found = true;
|
||||
} else if (lipoChargerInit()) {
|
||||
found = true;
|
||||
} else if (meshSolarInit()) {
|
||||
found = true;
|
||||
} else if (analogInit()) {
|
||||
found = true;
|
||||
}
|
||||
@ -1450,3 +1452,75 @@ bool Power::lipoChargerInit()
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
#include "meshSolarApp.h"
|
||||
|
||||
/**
|
||||
* meshSolar class for an SMBUS battery sensor.
|
||||
*/
|
||||
class meshSolarBatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* Init the I2C meshSolar battery level sensor
|
||||
*/
|
||||
bool runOnce()
|
||||
{
|
||||
meshSolarStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||
*/
|
||||
virtual int getBatteryPercent() override { return meshSolarGetBatteryPercent(); }
|
||||
|
||||
/**
|
||||
* The raw voltage of the battery in millivolts, or NAN if unknown
|
||||
*/
|
||||
virtual uint16_t getBattVoltage() override { return meshSolarGetBattVoltage(); }
|
||||
|
||||
/**
|
||||
* return true if there is a battery installed in this unit
|
||||
*/
|
||||
virtual bool isBatteryConnect() override { return meshSolarIsBatteryConnect(); }
|
||||
|
||||
/**
|
||||
* return true if there is an external power source detected
|
||||
*/
|
||||
virtual bool isVbusIn() override { return meshSolarIsVbusIn();}
|
||||
|
||||
/**
|
||||
* return true if the battery is currently charging
|
||||
*/
|
||||
virtual bool isCharging() override { return meshSolarIsCharging(); }
|
||||
};
|
||||
|
||||
meshSolarBatteryLevel meshSolarLevel;
|
||||
|
||||
/**
|
||||
* Init the meshSolar battery level sensor
|
||||
*/
|
||||
bool Power::meshSolarInit()
|
||||
{
|
||||
bool result = meshSolarLevel.runOnce();
|
||||
LOG_DEBUG("Power::meshSolarInit mesh solar sensor is %s", result ? "ready" : "not ready yet");
|
||||
if (!result)
|
||||
return false;
|
||||
batteryLevel = &meshSolarLevel;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
/**
|
||||
* The meshSolar battery level sensor is unavailable - default to AnalogBatteryLevel
|
||||
*/
|
||||
bool Power::meshSolarInit()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
@ -64,6 +64,14 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
|
||||
|
||||
int32_t SerialConsole::runOnce()
|
||||
{
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
//After enabling the mesh solar serial port module configuration, command processing is handled by the serial port module.
|
||||
if(moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port
|
||||
&& moduleConfig.serial.mode==meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG)
|
||||
{
|
||||
return 250;
|
||||
}
|
||||
#endif
|
||||
return runOncePart();
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// -----------------------------------------------------------------------------
|
||||
// OLED & Input
|
||||
// -----------------------------------------------------------------------------
|
||||
#if defined(SEEED_WIO_TRACKER_L1)
|
||||
#if defined(SEEED_WIO_TRACKER_L1) && !defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||
#define SSD1306_ADDRESS 0x3D
|
||||
#define USE_SH1106
|
||||
#else
|
||||
|
@ -81,6 +81,7 @@ class ScanI2C
|
||||
BHI260AP,
|
||||
BMM150,
|
||||
TSL2561
|
||||
DRV2605
|
||||
} DeviceType;
|
||||
|
||||
// typedef uint8_t DeviceAddress;
|
||||
|
@ -493,8 +493,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
type = MLX90614;
|
||||
logFoundDevice("MLX90614", (uint8_t)addr.address);
|
||||
} else {
|
||||
type = MPR121KB;
|
||||
logFoundDevice("MPR121KB", (uint8_t)addr.address);
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // DRV2605_REG_STATUS
|
||||
if (registerValue == 0xe0) {
|
||||
type = DRV2605;
|
||||
logFoundDevice("DRV2605", (uint8_t)addr.address);
|
||||
} else {
|
||||
type = MPR121KB;
|
||||
logFoundDevice("MPR121KB", (uint8_t)addr.address);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1504,7 +1504,7 @@ static int32_t toDegInt(RawDegrees d)
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
* Override this method to check for new locations
|
||||
*
|
||||
* @return true if we've acquired a new location
|
||||
* @return true if we've set a new time
|
||||
*/
|
||||
bool GPS::lookForTime()
|
||||
{
|
||||
@ -1544,11 +1544,12 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
if (t.tm_mon > -1) {
|
||||
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
|
||||
t.tm_sec, ti.age());
|
||||
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultInvalidTime) {
|
||||
// Clear the GPS buffer if we got an invalid time
|
||||
clearBuffer();
|
||||
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) {
|
||||
LOG_DEBUG("Time set.");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
|
@ -23,7 +23,7 @@ static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only upda
|
||||
* Reads the current date and time from the RTC module and updates the system time.
|
||||
* @return True if the RTC was successfully read and the system time was updated, false otherwise.
|
||||
*/
|
||||
void readFromRTC()
|
||||
RTCSetResult readFromRTC()
|
||||
{
|
||||
struct timeval tv; /* btw settimeofday() is helpful here too*/
|
||||
#ifdef RV3028_RTC
|
||||
@ -44,8 +44,15 @@ void readFromRTC()
|
||||
t.tm_sec = rtc.getSecond();
|
||||
tv.tv_sec = gm_mktime(&t);
|
||||
tv.tv_usec = 0;
|
||||
|
||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||
|
||||
#ifdef BUILD_EPOCH
|
||||
if (tv.tv_sec < BUILD_EPOCH) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
||||
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||
timeStartMsec = now;
|
||||
@ -53,6 +60,7 @@ void readFromRTC()
|
||||
if (currentQuality == RTCQualityNone) {
|
||||
currentQuality = RTCQualityDevice;
|
||||
}
|
||||
return RTCSetResultSuccess;
|
||||
}
|
||||
#elif defined(PCF8563_RTC)
|
||||
if (rtc_found.address == PCF8563_RTC) {
|
||||
@ -75,8 +83,15 @@ void readFromRTC()
|
||||
t.tm_sec = tc.second;
|
||||
tv.tv_sec = gm_mktime(&t);
|
||||
tv.tv_usec = 0;
|
||||
|
||||
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
|
||||
|
||||
#ifdef BUILD_EPOCH
|
||||
if (tv.tv_sec < BUILD_EPOCH) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
|
||||
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
|
||||
timeStartMsec = now;
|
||||
@ -84,6 +99,7 @@ void readFromRTC()
|
||||
if (currentQuality == RTCQualityNone) {
|
||||
currentQuality = RTCQualityDevice;
|
||||
}
|
||||
return RTCSetResultSuccess;
|
||||
}
|
||||
#else
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
@ -92,8 +108,10 @@ void readFromRTC()
|
||||
LOG_DEBUG("Read RTC time as %ld", printableEpoch);
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
return RTCSetResultSuccess;
|
||||
}
|
||||
#endif
|
||||
return RTCSetResultNotSet;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +119,7 @@ void readFromRTC()
|
||||
*
|
||||
* @param q The quality of the provided time.
|
||||
* @param tv A pointer to a timeval struct containing the time to potentially set the RTC to.
|
||||
* @return True if the RTC was set, false otherwise.
|
||||
* @return RTCSetResult
|
||||
*
|
||||
* If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||
*/
|
||||
|
@ -48,7 +48,7 @@ uint32_t getTime(bool local = false);
|
||||
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
|
||||
uint32_t getValidTime(RTCQuality minQuality, bool local = false);
|
||||
|
||||
void readFromRTC();
|
||||
RTCSetResult readFromRTC();
|
||||
|
||||
time_t gm_mktime(struct tm *tm);
|
||||
|
||||
|
@ -67,20 +67,28 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
||||
|
||||
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
||||
const bool flipped = config.display.flip_screen;
|
||||
// HACK for L1 EInk
|
||||
#if defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||
// For SEEED_WIO_TRACKER_L1_EINK, setRotation(3) is correct but mirrored; flip both axes
|
||||
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||
auto b = buffer[x + (y / 8) * displayWidth];
|
||||
auto isset = b & (1 << (y & 7));
|
||||
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficient
|
||||
auto b = buffer[x + (y / 8) * displayWidth];
|
||||
auto isset = b & (1 << (y & 7));
|
||||
|
||||
// Handle flip here, rather than with setRotation(),
|
||||
// Avoids issues when display width is not a multiple of 8
|
||||
if (flipped)
|
||||
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
else
|
||||
adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Trigger the refresh in GxEPD2
|
||||
LOG_DEBUG("Update E-Paper");
|
||||
@ -235,7 +243,7 @@ bool EInkDisplay::connect()
|
||||
adafruitDisplay->setRotation(1);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
}
|
||||
#elif defined(HELTEC_MESH_POCKET)
|
||||
#elif defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||
{
|
||||
spi1 = &SPI1;
|
||||
spi1->begin();
|
||||
@ -249,6 +257,7 @@ bool EInkDisplay::connect()
|
||||
// Init GxEPD2
|
||||
adafruitDisplay->init();
|
||||
adafruitDisplay->setRotation(3);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
}
|
||||
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
|
||||
|
||||
|
@ -84,7 +84,7 @@ class EInkDisplay : public OLEDDisplay
|
||||
SPIClass *hspi = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(HELTEC_MESH_POCKET)
|
||||
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
|
||||
SPIClass *spi1 = NULL;
|
||||
#endif
|
||||
|
||||
|
@ -318,7 +318,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
|
||||
@ -550,7 +550,7 @@ void Screen::setup()
|
||||
#else
|
||||
if (!config.display.flip_screen) {
|
||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
||||
#elif defined(USE_ST7789)
|
||||
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
||||
|
@ -73,7 +73,7 @@
|
||||
#endif
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS)) && \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
// The screen is bigger so use bigger fonts
|
||||
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
||||
|
@ -562,6 +562,91 @@ class LGFX : public lgfx::LGFX_Device
|
||||
|
||||
static LGFX *tft = nullptr;
|
||||
|
||||
#elif defined(ST7796_CS)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7796 driver chip
|
||||
|
||||
class LGFX : public lgfx::LGFX_Device
|
||||
{
|
||||
lgfx::Panel_ST7796 _panel_instance;
|
||||
lgfx::Bus_SPI _bus_instance;
|
||||
lgfx::Light_PWM _light_instance;
|
||||
|
||||
public:
|
||||
LGFX(void)
|
||||
{
|
||||
{
|
||||
auto cfg = _bus_instance.config();
|
||||
|
||||
// SPI
|
||||
cfg.spi_host = ST7796_SPI_HOST;
|
||||
cfg.spi_mode = 0;
|
||||
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
|
||||
// 80MHz by an integer)
|
||||
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
|
||||
cfg.spi_3wire = false;
|
||||
cfg.use_lock = true; // Set to true to use transaction locking
|
||||
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
|
||||
// SPI_DMA_CH_AUTO=auto setting)
|
||||
cfg.pin_sclk = ST7796_SCK; // Set SPI SCLK pin number
|
||||
cfg.pin_mosi = ST7796_SDA; // Set SPI MOSI pin number
|
||||
cfg.pin_miso = ST7796_MISO; // Set SPI MISO pin number (-1 = disable)
|
||||
cfg.pin_dc = ST7796_RS; // Set SPI DC pin number (-1 = disable)
|
||||
|
||||
_bus_instance.config(cfg); // applies the set value to the bus.
|
||||
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
|
||||
}
|
||||
|
||||
{ // Set the display panel control.
|
||||
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
|
||||
|
||||
cfg.pin_cs = ST7796_CS; // Pin number where CS is connected (-1 = disable)
|
||||
cfg.pin_rst = ST7796_RESET; // Pin number where RST is connected (-1 = disable)
|
||||
cfg.pin_busy = ST7796_BUSY; // Pin number where BUSY is connected (-1 = disable)
|
||||
|
||||
// cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
|
||||
// cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
|
||||
cfg.panel_width = TFT_WIDTH; // actual displayable width
|
||||
cfg.panel_height = TFT_HEIGHT; // actual displayable height
|
||||
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
|
||||
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
|
||||
cfg.offset_rotation = TFT_OFFSET_ROTATION; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
||||
#ifdef TFT_DUMMY_READ_PIXELS
|
||||
cfg.dummy_read_pixel = TFT_DUMMY_READ_PIXELS; // Number of bits for dummy read before pixel readout
|
||||
#else
|
||||
cfg.dummy_read_pixel = 8; // Number of bits for dummy read before pixel readout
|
||||
#endif
|
||||
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
|
||||
cfg.readable = true; // Set to true if data can be read
|
||||
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
|
||||
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||
cfg.dlen_16bit =
|
||||
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
|
||||
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
|
||||
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
#ifdef ST7796_BL
|
||||
// Set the backlight control. (delete if not necessary)
|
||||
{
|
||||
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||
|
||||
cfg.pin_bl = ST7796_BL; // Pin number to which the backlight is connected
|
||||
cfg.invert = false; // true to invert the brightness of the backlight
|
||||
cfg.freq = 44100;
|
||||
cfg.pwm_channel = 7;
|
||||
|
||||
_light_instance.config(cfg);
|
||||
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||
}
|
||||
#endif
|
||||
|
||||
setPanel(&_panel_instance); // Sets the panel to use.
|
||||
}
|
||||
};
|
||||
|
||||
static LGFX *tft = nullptr;
|
||||
|
||||
#elif defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER)
|
||||
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ILI9341/ILI9342 driver chip
|
||||
@ -997,8 +1082,9 @@ static LGFX *tft = nullptr;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || (ARCH_PORTDUINO && HAS_SCREEN != 0)
|
||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ST7796_CS) || defined(ILI9341_DRIVER) || \
|
||||
defined(ILI9342_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || \
|
||||
(ARCH_PORTDUINO && HAS_SCREEN != 0)
|
||||
#include "SPILock.h"
|
||||
#include "TFTDisplay.h"
|
||||
#include <SPI.h>
|
||||
@ -1047,32 +1133,97 @@ void TFTDisplay::display(bool fromBlank)
|
||||
{
|
||||
if (fromBlank)
|
||||
tft->fillScreen(TFT_BLACK);
|
||||
// tft->clear();
|
||||
|
||||
concurrency::LockGuard g(spiLock);
|
||||
|
||||
uint16_t x, y;
|
||||
uint32_t x, y;
|
||||
uint32_t y_byteIndex;
|
||||
uint8_t y_byteMask;
|
||||
uint32_t x_FirstPixelUpdate;
|
||||
uint32_t x_LastPixelUpdate;
|
||||
bool isset, dblbuf_isset;
|
||||
uint16_t colorTftMesh, colorTftBlack;
|
||||
bool somethingChanged = false;
|
||||
|
||||
for (y = 0; y < displayHeight; y++) {
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
auto isset = buffer[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
||||
// Store colors byte-reversed so that TFT_eSPI doesn't have to swap bytes in a separate step
|
||||
colorTftMesh = (TFT_MESH >> 8) | ((TFT_MESH & 0xFF) << 8);
|
||||
colorTftBlack = (TFT_BLACK >> 8) | ((TFT_BLACK & 0xFF) << 8);
|
||||
|
||||
y = 0;
|
||||
while (y < displayHeight) {
|
||||
y_byteIndex = (y / 8) * displayWidth;
|
||||
y_byteMask = (1 << (y & 7));
|
||||
|
||||
// Step 1: Do a quick scan of 8 rows together. This allows fast-forwarding over unchanged screen areas.
|
||||
if (y_byteMask == 1) {
|
||||
if (!fromBlank) {
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
||||
auto dblbuf_isset = buffer_back[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
||||
if (isset != dblbuf_isset) {
|
||||
tft->drawPixel(x, y, isset ? TFT_MESH : TFT_BLACK);
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
if (buffer[x + y_byteIndex] != buffer_back[x + y_byteIndex])
|
||||
break;
|
||||
}
|
||||
} else if (isset) {
|
||||
tft->drawPixel(x, y, TFT_MESH);
|
||||
} else {
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
if (buffer[x + y_byteIndex] != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (x >= displayWidth) {
|
||||
// No changed pixels found in these 8 rows, fast-forward to the next 8
|
||||
y = y + 8;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Scan each of the 8 rows individually. Find the first pixel in each row that needs updating
|
||||
for (x_FirstPixelUpdate = 0; x_FirstPixelUpdate < displayWidth; x_FirstPixelUpdate++) {
|
||||
isset = buffer[x_FirstPixelUpdate + y_byteIndex] & y_byteMask;
|
||||
|
||||
if (!fromBlank) {
|
||||
// get src pixel in the page based ordering the OLED lib uses
|
||||
dblbuf_isset = buffer_back[x_FirstPixelUpdate + y_byteIndex] & y_byteMask;
|
||||
if (isset != dblbuf_isset) {
|
||||
break;
|
||||
}
|
||||
} else if (isset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Did we find a pixel that needs updating on this row?
|
||||
if (x_FirstPixelUpdate < displayWidth) {
|
||||
|
||||
// Quickly write out the first changed pixel (saves another array lookup)
|
||||
linePixelBuffer[x_FirstPixelUpdate] = isset ? colorTftMesh : colorTftBlack;
|
||||
x_LastPixelUpdate = x_FirstPixelUpdate;
|
||||
|
||||
// Step 3: copy all remaining pixels in this row into the pixel line buffer,
|
||||
// while also recording the last pixel in the row that needs updating
|
||||
for (x = x_FirstPixelUpdate + 1; x < displayWidth; x++) {
|
||||
isset = buffer[x + y_byteIndex] & y_byteMask;
|
||||
linePixelBuffer[x] = isset ? colorTftMesh : colorTftBlack;
|
||||
|
||||
if (!fromBlank) {
|
||||
dblbuf_isset = buffer_back[x + y_byteIndex] & y_byteMask;
|
||||
if (isset != dblbuf_isset) {
|
||||
x_LastPixelUpdate = x;
|
||||
}
|
||||
} else if (isset) {
|
||||
x_LastPixelUpdate = x;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Send the changed pixels on this line to the screen as a single block transfer.
|
||||
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
|
||||
tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
|
||||
&linePixelBuffer[x_FirstPixelUpdate]);
|
||||
|
||||
somethingChanged = true;
|
||||
}
|
||||
y++;
|
||||
}
|
||||
// Copy the Buffer to the Back Buffer
|
||||
for (y = 0; y < (displayHeight / 8); y++) {
|
||||
for (x = 0; x < displayWidth; x++) {
|
||||
uint16_t pos = x + y * displayWidth;
|
||||
buffer_back[pos] = buffer[pos];
|
||||
}
|
||||
}
|
||||
if (somethingChanged)
|
||||
memcpy(buffer_back, buffer, displayBufferSize);
|
||||
}
|
||||
|
||||
void TFTDisplay::sdlLoop()
|
||||
@ -1264,13 +1415,21 @@ bool TFTDisplay::connect()
|
||||
tft->setRotation(1); // T-Deck has the TFT in landscape
|
||||
#elif defined(T_WATCH_S3)
|
||||
tft->setRotation(2); // T-Watch S3 left-handed orientation
|
||||
#elif ARCH_PORTDUINO || defined(SENSECAP_INDICATOR)
|
||||
#elif ARCH_PORTDUINO || defined(SENSECAP_INDICATOR) || defined(T_LORA_PAGER)
|
||||
tft->setRotation(0); // use config.yaml to set rotation
|
||||
#else
|
||||
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
|
||||
#endif
|
||||
tft->fillScreen(TFT_BLACK);
|
||||
|
||||
if (this->linePixelBuffer == NULL) {
|
||||
this->linePixelBuffer = (uint16_t *)malloc(sizeof(uint16_t) * displayWidth);
|
||||
|
||||
if (!this->linePixelBuffer) {
|
||||
LOG_ERROR("Not enough memory to create TFT line buffer\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -58,4 +58,6 @@ class TFTDisplay : public OLEDDisplay
|
||||
|
||||
// Connect to the display
|
||||
virtual bool connect() override;
|
||||
|
||||
uint16_t *linePixelBuffer = nullptr;
|
||||
};
|
@ -94,7 +94,8 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
||||
if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
|
||||
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
||||
ARCH_PORTDUINO) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
|
||||
8, imgQuestionL1);
|
||||
@ -106,7 +107,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
||||
#endif
|
||||
} else {
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
|
||||
8, imgSFL1);
|
||||
@ -121,7 +122,8 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
||||
} else {
|
||||
// TODO: Raspberry Pi supports more than just the one screen size
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
||||
ARCH_PORTDUINO) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
imgInfoL1);
|
||||
|
@ -434,8 +434,8 @@ void menuHandler::systemBaseMenu()
|
||||
|
||||
optionsArray[options] = "Notifications";
|
||||
optionsEnumArray[options++] = Notifications;
|
||||
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || \
|
||||
defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||
#if defined(ST7789_CS) || defined(ST7796_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || \
|
||||
defined(USE_SH1107) || defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
|
||||
optionsArray[options] = "Screen Options";
|
||||
optionsEnumArray[options++] = ScreenOptions;
|
||||
#endif
|
||||
@ -725,7 +725,7 @@ void menuHandler::BrightnessPickerMenu()
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190)
|
||||
// For HELTEC devices, use analogWrite to control backlight
|
||||
analogWrite(VTFT_LEDA, uiconfig.screen_brightness);
|
||||
#elif defined(ST7789_CS)
|
||||
#elif defined(ST7789_CS) || defined(ST7796_CS)
|
||||
static_cast<TFTDisplay *>(screen->getDisplayDevice())->setDisplayBrightness(uiconfig.screen_brightness);
|
||||
#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
|
||||
screen->getDisplayDevice()->setBrightness(uiconfig.screen_brightness);
|
||||
@ -768,7 +768,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 10;
|
||||
bannerOptions.bannerCallback = [display](int selected) -> void {
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
|
||||
uint8_t TFT_MESH_r = 0;
|
||||
uint8_t TFT_MESH_g = 0;
|
||||
uint8_t TFT_MESH_b = 0;
|
||||
@ -1045,7 +1045,7 @@ void menuHandler::screenOptionsMenu()
|
||||
}
|
||||
|
||||
// Only show screen color for TFT displays
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
|
||||
optionsArray[options] = "Screen Color";
|
||||
optionsEnumArray[options++] = ScreenColor;
|
||||
#endif
|
||||
|
@ -194,7 +194,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
|
||||
}
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
|
||||
if (isHighResolution) {
|
||||
|
@ -27,7 +27,8 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
|
||||
0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f};
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || ARCH_PORTDUINO) && \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
||||
ARCH_PORTDUINO) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
|
||||
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
|
||||
|
68
src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.cpp
Normal file
68
src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "./ZJY122250_0213BAAMFGN.h"
|
||||
|
||||
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
||||
|
||||
using namespace NicheGraphics::Drivers;
|
||||
|
||||
// Map the display controller IC's output to the connected panel
|
||||
void ZJY122250_0213BAAMFGN::configScanning()
|
||||
{
|
||||
// "Driver output control"
|
||||
// Scan gates from 0 to 249 (vertical resolution 250px)
|
||||
sendCommand(0x01);
|
||||
sendData(0xF9);
|
||||
sendData(0x00);
|
||||
sendData(0x00);
|
||||
}
|
||||
|
||||
// Specify which information is used to control the sequence of voltages applied to move the pixels
|
||||
// - For this display, configUpdateSequence() specifies that a suitable LUT will be loaded from
|
||||
// the controller IC's OTP memory, when the update procedure begins.
|
||||
void ZJY122250_0213BAAMFGN::configWaveform()
|
||||
{
|
||||
switch (updateType) {
|
||||
case FAST:
|
||||
sendCommand(0x3C); // Border waveform:
|
||||
sendData(0x80); // VCOM
|
||||
break;
|
||||
case FULL:
|
||||
default:
|
||||
sendCommand(0x3C); // Border waveform:
|
||||
sendData(0x01); // Follow LUT 1 (blink same as white pixels)
|
||||
break;
|
||||
}
|
||||
|
||||
sendCommand(0x18); // Temperature sensor:
|
||||
sendData(0x80); // Use internal temperature sensor to select an appropriate refresh waveform
|
||||
}
|
||||
|
||||
void ZJY122250_0213BAAMFGN::configUpdateSequence()
|
||||
{
|
||||
switch (updateType) {
|
||||
case FAST:
|
||||
sendCommand(0x22); // Set "update sequence"
|
||||
sendData(0xFF); // Will load LUT from OTP memory, Display mode 2 "differential refresh"
|
||||
break;
|
||||
|
||||
case FULL:
|
||||
default:
|
||||
sendCommand(0x22); // Set "update sequence"
|
||||
sendData(0xF7); // Will load LUT from OTP memory
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Once the refresh operation has been started,
|
||||
// begin periodically polling the display to check for completion, using the normal Meshtastic threading code
|
||||
// Only used when refresh is "async"
|
||||
void ZJY122250_0213BAAMFGN::detachFromUpdate()
|
||||
{
|
||||
switch (updateType) {
|
||||
case FAST:
|
||||
return beginPolling(50, 500); // At least 500ms for fast refresh
|
||||
case FULL:
|
||||
default:
|
||||
return beginPolling(100, 2000); // At least 2 seconds for full refresh
|
||||
}
|
||||
}
|
||||
#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
42
src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.h
Normal file
42
src/graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
|
||||
E-Ink display driver
|
||||
- ZJY122250_0213BAAMFGN
|
||||
- Manufacturer: Zhongjingyuan
|
||||
- Size: 2.13 inch
|
||||
- Resolution: 250px x 122px
|
||||
- Flex connector marking (not a unique identifier): FPC-A002
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
#include "./SSD16XX.h"
|
||||
|
||||
namespace NicheGraphics::Drivers
|
||||
{
|
||||
class ZJY122250_0213BAAMFGN : public SSD16XX
|
||||
{
|
||||
// Display properties
|
||||
private:
|
||||
static constexpr uint32_t width = 122;
|
||||
static constexpr uint32_t height = 250;
|
||||
static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST);
|
||||
|
||||
public:
|
||||
ZJY122250_0213BAAMFGN() : SSD16XX(width, height, supported) {}
|
||||
|
||||
protected:
|
||||
virtual void configScanning() override;
|
||||
virtual void configWaveform() override;
|
||||
virtual void configUpdateSequence() override;
|
||||
void detachFromUpdate() override;
|
||||
};
|
||||
|
||||
} // namespace NicheGraphics::Drivers
|
||||
|
||||
#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
|
@ -7,12 +7,7 @@ using namespace NicheGraphics;
|
||||
|
||||
// Timing for "maintenance"
|
||||
// Paying off full-refresh debt with unprovoked updates, if the display is not very active
|
||||
|
||||
#ifdef SEEED_WIO_TRACKER_L1
|
||||
static constexpr uint32_t MAINTENANCE_MS_INITIAL = 5 * 1000UL;
|
||||
#else
|
||||
static constexpr uint32_t MAINTENANCE_MS_INITIAL = 60 * 1000UL;
|
||||
#endif
|
||||
static constexpr uint32_t MAINTENANCE_MS = 60 * 60 * 1000UL;
|
||||
|
||||
InkHUD::DisplayHealth::DisplayHealth() : concurrency::OSThread("Mediator")
|
||||
|
76
src/input/RotaryEncoderImpl.cpp
Normal file
76
src/input/RotaryEncoderImpl.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
#ifdef T_LORA_PAGER
|
||||
|
||||
#include "RotaryEncoderImpl.h"
|
||||
#include "InputBroker.h"
|
||||
#include "RotaryEncoder.h"
|
||||
|
||||
#define ORIGIN_NAME "RotaryEncoder"
|
||||
|
||||
RotaryEncoderImpl *rotaryEncoderImpl;
|
||||
|
||||
RotaryEncoderImpl::RotaryEncoderImpl() : concurrency::OSThread(ORIGIN_NAME), originName(ORIGIN_NAME)
|
||||
{
|
||||
rotary = nullptr;
|
||||
}
|
||||
|
||||
bool RotaryEncoderImpl::init()
|
||||
{
|
||||
if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 ||
|
||||
moduleConfig.canned_message.inputbroker_pin_b == 0) {
|
||||
// Input device is disabled.
|
||||
disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
eventCw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_cw);
|
||||
eventCcw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_ccw);
|
||||
eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press);
|
||||
|
||||
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
|
||||
moduleConfig.canned_message.inputbroker_pin_press);
|
||||
rotary->resetButton();
|
||||
|
||||
inputBroker->registerSource(this);
|
||||
|
||||
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
|
||||
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
|
||||
eventPressed);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t RotaryEncoderImpl::runOnce()
|
||||
{
|
||||
InputEvent e;
|
||||
e.inputEvent = INPUT_BROKER_NONE;
|
||||
e.source = this->originName;
|
||||
|
||||
static uint32_t lastPressed = millis();
|
||||
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
|
||||
if (lastPressed + 200 < millis()) {
|
||||
LOG_DEBUG("Rotary event Press");
|
||||
lastPressed = millis();
|
||||
e.inputEvent = this->eventPressed;
|
||||
}
|
||||
} else {
|
||||
switch (rotary->process()) {
|
||||
case RotaryEncoder::DIRECTION_CW:
|
||||
LOG_DEBUG("Rotary event CW");
|
||||
e.inputEvent = this->eventCw;
|
||||
break;
|
||||
case RotaryEncoder::DIRECTION_CCW:
|
||||
LOG_DEBUG("Rotary event CCW");
|
||||
e.inputEvent = this->eventCcw;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.inputEvent != INPUT_BROKER_NONE) {
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
|
||||
return 20;
|
||||
}
|
||||
|
||||
#endif
|
28
src/input/RotaryEncoderImpl.h
Normal file
28
src/input/RotaryEncoderImpl.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// This is a non-interrupt version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
|
||||
|
||||
#include "InputBroker.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "mesh/NodeDB.h"
|
||||
|
||||
class RotaryEncoder;
|
||||
|
||||
class RotaryEncoderImpl : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
RotaryEncoderImpl();
|
||||
bool init(void);
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
input_broker_event eventCw = INPUT_BROKER_NONE;
|
||||
input_broker_event eventCcw = INPUT_BROKER_NONE;
|
||||
input_broker_event eventPressed = INPUT_BROKER_NONE;
|
||||
|
||||
RotaryEncoder *rotary;
|
||||
const char *originName;
|
||||
};
|
||||
|
||||
extern RotaryEncoderImpl *rotaryEncoderImpl;
|
@ -18,14 +18,23 @@ void RotaryEncoderInterruptBase::init(
|
||||
this->_eventCcw = eventCcw;
|
||||
this->_eventPressed = eventPressed;
|
||||
|
||||
pinMode(pinPress, INPUT_PULLUP);
|
||||
pinMode(this->_pinA, INPUT_PULLUP);
|
||||
pinMode(this->_pinB, INPUT_PULLUP);
|
||||
bool isRAK = false;
|
||||
#ifdef RAK_4631
|
||||
isRAK = true;
|
||||
#endif
|
||||
|
||||
// attachInterrupt(pinPress, onIntPress, RISING);
|
||||
attachInterrupt(pinPress, onIntPress, RISING);
|
||||
attachInterrupt(this->_pinA, onIntA, CHANGE);
|
||||
attachInterrupt(this->_pinB, onIntB, CHANGE);
|
||||
if (!isRAK || pinPress != 0) {
|
||||
pinMode(pinPress, INPUT_PULLUP);
|
||||
attachInterrupt(pinPress, onIntPress, RISING);
|
||||
}
|
||||
if (!isRAK || this->_pinA != 0) {
|
||||
pinMode(this->_pinA, INPUT_PULLUP);
|
||||
attachInterrupt(this->_pinA, onIntA, CHANGE);
|
||||
}
|
||||
if (!isRAK || this->_pinA != 0) {
|
||||
pinMode(this->_pinB, INPUT_PULLUP);
|
||||
attachInterrupt(this->_pinB, onIntB, CHANGE);
|
||||
}
|
||||
|
||||
this->rotaryLevelA = digitalRead(this->_pinA);
|
||||
this->rotaryLevelB = digitalRead(this->_pinB);
|
||||
|
230
src/input/TLoraPagerKeyboard.cpp
Normal file
230
src/input/TLoraPagerKeyboard.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
#if defined(T_LORA_PAGER)
|
||||
|
||||
#include "TLoraPagerKeyboard.h"
|
||||
#include "main.h"
|
||||
|
||||
#ifndef LEDC_BACKLIGHT_CHANNEL
|
||||
#define LEDC_BACKLIGHT_CHANNEL 4
|
||||
#endif
|
||||
|
||||
#ifndef LEDC_BACKLIGHT_BIT_WIDTH
|
||||
#define LEDC_BACKLIGHT_BIT_WIDTH 8
|
||||
#endif
|
||||
|
||||
#ifndef LEDC_BACKLIGHT_FREQ
|
||||
#define LEDC_BACKLIGHT_FREQ 1000 // Hz
|
||||
#endif
|
||||
|
||||
#define _TCA8418_COLS 10
|
||||
#define _TCA8418_ROWS 4
|
||||
#define _TCA8418_NUM_KEYS 31
|
||||
|
||||
#define _TCA8418_MULTI_TAP_THRESHOLD 1500
|
||||
|
||||
using Key = TCA8418KeyboardBase::TCA8418Key;
|
||||
|
||||
constexpr uint8_t modifierRightShiftKey = 29 - 1; // keynum -1
|
||||
constexpr uint8_t modifierRightShift = 0b0001;
|
||||
constexpr uint8_t modifierSymKey = 21 - 1;
|
||||
constexpr uint8_t modifierSym = 0b0010;
|
||||
|
||||
// Num chars per key, Modulus for rotating through characters
|
||||
static uint8_t TLoraPagerTapMod[_TCA8418_NUM_KEYS] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3};
|
||||
|
||||
static unsigned char TLoraPagerTapMap[_TCA8418_NUM_KEYS][3] = {{'q', 'Q', '1'},
|
||||
{'w', 'W', '2'},
|
||||
{'e', 'E', '3'},
|
||||
{'r', 'R', '4'},
|
||||
{'t', 'T', '5'},
|
||||
{'y', 'Y', '6'},
|
||||
{'u', 'U', '7'},
|
||||
{'i', 'I', '8'},
|
||||
{'o', 'O', '9'},
|
||||
{'p', 'P', '0'},
|
||||
{'a', 'A', '*'},
|
||||
{'s', 'S', '/'},
|
||||
{'d', 'D', '+'},
|
||||
{'f', 'F', '-'},
|
||||
{'g', 'G', '='},
|
||||
{'h', 'H', ':'},
|
||||
{'j', 'J', '\''},
|
||||
{'k', 'K', '"'},
|
||||
{'l', 'L', '@'},
|
||||
{Key::SELECT, 0x00, Key::TAB},
|
||||
{0x00, 0x00, 0x00},
|
||||
{'z', 'Z', '_'},
|
||||
{'x', 'X', '$'},
|
||||
{'c', 'C', ';'},
|
||||
{'v', 'V', '?'},
|
||||
{'b', 'B', '!'},
|
||||
{'n', 'N', ','},
|
||||
{'m', 'M', '.'},
|
||||
{0x00, 0x00, 0x00},
|
||||
{Key::BSP, 0x00, Key::ESC},
|
||||
{' ', 0x00, Key::BL_TOGGLE}};
|
||||
|
||||
TLoraPagerKeyboard::TLoraPagerKeyboard()
|
||||
: TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
|
||||
last_tap(0L), char_idx(0), tap_interval(0)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ledcAttach(KB_BL_PIN, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
#else
|
||||
ledcSetup(LEDC_BACKLIGHT_CHANNEL, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
ledcAttachPin(KB_BL_PIN, LEDC_BACKLIGHT_CHANNEL);
|
||||
#endif
|
||||
reset();
|
||||
}
|
||||
|
||||
void TLoraPagerKeyboard::reset(void)
|
||||
{
|
||||
TCA8418KeyboardBase::reset();
|
||||
pinMode(KB_BL_PIN, OUTPUT);
|
||||
digitalWrite(KB_BL_PIN, LOW);
|
||||
setBacklight(false);
|
||||
}
|
||||
|
||||
// handle multi-key presses (shift and alt)
|
||||
void TLoraPagerKeyboard::trigger()
|
||||
{
|
||||
uint8_t count = keyCount();
|
||||
if (count == 0)
|
||||
return;
|
||||
for (uint8_t i = 0; i < count; ++i) {
|
||||
uint8_t k = readRegister(TCA8418_REG_KEY_EVENT_A + i);
|
||||
uint8_t key = k & 0x7F;
|
||||
if (k & 0x80) {
|
||||
pressed(key);
|
||||
} else {
|
||||
released();
|
||||
state = Idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TLoraPagerKeyboard::setBacklight(bool on)
|
||||
{
|
||||
toggleBacklight(!on);
|
||||
}
|
||||
|
||||
void TLoraPagerKeyboard::pressed(uint8_t key)
|
||||
{
|
||||
if (state == Init || state == Busy) {
|
||||
return;
|
||||
}
|
||||
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_ALL_ENABLED ||
|
||||
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY) {
|
||||
hapticFeedback();
|
||||
}
|
||||
|
||||
if (modifierFlag && (millis() - last_modifier_time > _TCA8418_MULTI_TAP_THRESHOLD)) {
|
||||
modifierFlag = 0;
|
||||
}
|
||||
|
||||
uint8_t next_key = 0;
|
||||
int row = (key - 1) / 10;
|
||||
int col = (key - 1) % 10;
|
||||
|
||||
if (row >= _TCA8418_ROWS || col >= _TCA8418_COLS) {
|
||||
return; // Invalid key
|
||||
}
|
||||
|
||||
next_key = row * _TCA8418_COLS + col;
|
||||
state = Held;
|
||||
|
||||
uint32_t now = millis();
|
||||
tap_interval = now - last_tap;
|
||||
|
||||
updateModifierFlag(next_key);
|
||||
if (isModifierKey(next_key)) {
|
||||
last_modifier_time = now;
|
||||
}
|
||||
|
||||
if (tap_interval < 0) {
|
||||
last_tap = 0;
|
||||
state = Busy;
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_key != last_key || tap_interval > _TCA8418_MULTI_TAP_THRESHOLD) {
|
||||
char_idx = 0;
|
||||
} else {
|
||||
char_idx += 1;
|
||||
}
|
||||
|
||||
last_key = next_key;
|
||||
last_tap = now;
|
||||
}
|
||||
|
||||
void TLoraPagerKeyboard::released()
|
||||
{
|
||||
if (state != Held) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
|
||||
last_key = -1;
|
||||
state = Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t now = millis();
|
||||
last_tap = now;
|
||||
|
||||
if (TLoraPagerTapMap[last_key][modifierFlag % TLoraPagerTapMod[last_key]] == Key::BL_TOGGLE) {
|
||||
toggleBacklight();
|
||||
return;
|
||||
}
|
||||
|
||||
queueEvent(TLoraPagerTapMap[last_key][modifierFlag % TLoraPagerTapMod[last_key]]);
|
||||
if (isModifierKey(last_key) == false)
|
||||
modifierFlag = 0;
|
||||
}
|
||||
|
||||
void TLoraPagerKeyboard::hapticFeedback()
|
||||
{
|
||||
drv.setWaveform(0, 14); // strong buzz 100%
|
||||
drv.setWaveform(1, 0); // end waveform
|
||||
drv.go();
|
||||
}
|
||||
|
||||
// toggle brightness of the backlight in three steps
|
||||
void TLoraPagerKeyboard::toggleBacklight(bool off)
|
||||
{
|
||||
static uint32_t brightness = 0;
|
||||
if (off) {
|
||||
brightness = 0;
|
||||
} else {
|
||||
if (brightness == 0) {
|
||||
brightness = 40;
|
||||
} else if (brightness == 40) {
|
||||
brightness = 127;
|
||||
} else if (brightness >= 127) {
|
||||
brightness = 0;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("Toggle backlight: %d", brightness);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ledcWrite(KB_BL_PIN, brightness);
|
||||
#else
|
||||
ledcWrite(LEDC_BACKLIGHT_CHANNEL, brightness);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TLoraPagerKeyboard::updateModifierFlag(uint8_t key)
|
||||
{
|
||||
if (key == modifierRightShiftKey) {
|
||||
modifierFlag ^= modifierRightShift;
|
||||
} else if (key == modifierSymKey) {
|
||||
modifierFlag ^= modifierSym;
|
||||
}
|
||||
}
|
||||
|
||||
bool TLoraPagerKeyboard::isModifierKey(uint8_t key)
|
||||
{
|
||||
return (key == modifierRightShiftKey || key == modifierSymKey);
|
||||
}
|
||||
|
||||
#endif
|
@ -4,9 +4,26 @@ class TLoraPagerKeyboard : public TCA8418KeyboardBase
|
||||
{
|
||||
public:
|
||||
TLoraPagerKeyboard();
|
||||
void setBacklight(bool on) override{};
|
||||
void reset(void);
|
||||
void trigger(void) override;
|
||||
void setBacklight(bool on) override;
|
||||
virtual ~TLoraPagerKeyboard() {}
|
||||
|
||||
protected:
|
||||
void pressed(uint8_t key) override{};
|
||||
void released(void) override{};
|
||||
void pressed(uint8_t key) override;
|
||||
void released(void) override;
|
||||
void hapticFeedback(void);
|
||||
|
||||
void updateModifierFlag(uint8_t key);
|
||||
bool isModifierKey(uint8_t key);
|
||||
void toggleBacklight(bool off = false);
|
||||
|
||||
private:
|
||||
uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
|
||||
uint32_t last_modifier_time; // Timestamp of the last modifier key press
|
||||
int8_t last_key;
|
||||
int8_t next_key;
|
||||
uint32_t last_tap;
|
||||
uint8_t char_idx;
|
||||
int32_t tap_interval;
|
||||
};
|
||||
|
@ -15,14 +15,23 @@ void UpDownInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinPress,
|
||||
this->_eventDown = eventDown;
|
||||
this->_eventUp = eventUp;
|
||||
this->_eventPressed = eventPressed;
|
||||
bool isRAK = false;
|
||||
#ifdef RAK_4631
|
||||
isRAK = true;
|
||||
#endif
|
||||
|
||||
pinMode(pinPress, INPUT_PULLUP);
|
||||
pinMode(this->_pinDown, INPUT_PULLUP);
|
||||
pinMode(this->_pinUp, INPUT_PULLUP);
|
||||
|
||||
attachInterrupt(pinPress, onIntPress, RISING);
|
||||
attachInterrupt(this->_pinDown, onIntDown, RISING);
|
||||
attachInterrupt(this->_pinUp, onIntUp, RISING);
|
||||
if (!isRAK || pinPress != 0) {
|
||||
pinMode(pinPress, INPUT_PULLUP);
|
||||
attachInterrupt(pinPress, onIntPress, RISING);
|
||||
}
|
||||
if (!isRAK || this->_pinDown != 0) {
|
||||
pinMode(this->_pinDown, INPUT_PULLUP);
|
||||
attachInterrupt(this->_pinDown, onIntDown, RISING);
|
||||
}
|
||||
if (!isRAK || this->_pinUp != 0) {
|
||||
pinMode(this->_pinUp, INPUT_PULLUP);
|
||||
attachInterrupt(this->_pinUp, onIntUp, RISING);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Up/down/press GPIO initialized (%d, %d, %d)", this->_pinUp, this->_pinDown, pinPress);
|
||||
|
||||
|
@ -12,8 +12,8 @@ void CardKbI2cImpl::init()
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(I2C_NO_RESCAN)
|
||||
if (cardkb_found.address == 0x00) {
|
||||
LOG_DEBUG("Rescan for I2C keyboard");
|
||||
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR, XPOWERS_AXP192_AXP2101_ADDRESS};
|
||||
uint8_t i2caddr_asize = 5;
|
||||
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR, TCA8418_KB_ADDR};
|
||||
uint8_t i2caddr_asize = sizeof(i2caddr_scan) / sizeof(i2caddr_scan[0]);
|
||||
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
|
||||
|
||||
#if WIRE_INTERFACES_COUNT == 2
|
||||
|
37
src/main.cpp
37
src/main.cpp
@ -135,8 +135,9 @@ AccelerometerThread *accelerometerThread = nullptr;
|
||||
AudioThread *audioThread = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef USE_PCA9557
|
||||
PCA9557 IOEXP;
|
||||
#ifdef USE_XL9555
|
||||
#include "ExtensionIOXL9555.hpp"
|
||||
ExtensionIOXL9555 io;
|
||||
#endif
|
||||
|
||||
#if HAS_TFT
|
||||
@ -201,7 +202,7 @@ ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE,
|
||||
/// The I2C address of our Air Quality Indicator (if found)
|
||||
ScanI2C::DeviceAddress aqi_found = ScanI2C::ADDRESS_NONE;
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
|
||||
@ -359,6 +360,30 @@ void setup()
|
||||
digitalWrite(SDCARD_CS, HIGH);
|
||||
pinMode(PIN_EINK_CS, OUTPUT);
|
||||
digitalWrite(PIN_EINK_CS, HIGH);
|
||||
#elif defined(T_LORA_PAGER)
|
||||
pinMode(LORA_CS, OUTPUT);
|
||||
digitalWrite(LORA_CS, HIGH);
|
||||
pinMode(SDCARD_CS, OUTPUT);
|
||||
digitalWrite(SDCARD_CS, HIGH);
|
||||
pinMode(TFT_CS, OUTPUT);
|
||||
digitalWrite(TFT_CS, HIGH);
|
||||
// io expander
|
||||
io.begin(Wire, XL9555_SLAVE_ADDRESS0, SDA, SCL);
|
||||
io.pinMode(EXPANDS_DRV_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_DRV_EN, HIGH);
|
||||
io.pinMode(EXPANDS_AMP_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||
io.pinMode(EXPANDS_LORA_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_LORA_EN, HIGH);
|
||||
io.pinMode(EXPANDS_GPS_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_GPS_EN, HIGH);
|
||||
io.pinMode(EXPANDS_KB_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_KB_EN, HIGH);
|
||||
io.pinMode(EXPANDS_SD_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_SD_EN, HIGH);
|
||||
io.pinMode(EXPANDS_GPIO_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
|
||||
io.pinMode(EXPANDS_SD_PULLEN, INPUT);
|
||||
#endif
|
||||
|
||||
concurrency::hasBeenSetup = true;
|
||||
@ -806,7 +831,7 @@ void setup()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
drv.begin();
|
||||
drv.selectLibrary(1);
|
||||
// I2C trigger by sending 'go' command
|
||||
@ -852,7 +877,7 @@ void setup()
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
|
||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS)
|
||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) &&
|
||||
@ -1115,7 +1140,7 @@ void setup()
|
||||
// Don't call screen setup until after nodedb is setup (because we need
|
||||
// the current region name)
|
||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS)
|
||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||
if (screen)
|
||||
screen->setup();
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
|
@ -41,7 +41,7 @@ extern bool eink_found;
|
||||
extern bool pmu_found;
|
||||
extern bool isUSBPowered;
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
#include <Adafruit_DRV2605.h>
|
||||
extern Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
|
@ -85,11 +85,8 @@ meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error e
|
||||
return r;
|
||||
}
|
||||
|
||||
void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src, const char *specificModule)
|
||||
void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
||||
{
|
||||
if (specificModule) {
|
||||
LOG_DEBUG("Calling specific module: %s", specificModule);
|
||||
}
|
||||
// LOG_DEBUG("In call modules");
|
||||
bool moduleFound = false;
|
||||
|
||||
@ -103,15 +100,11 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src, const char
|
||||
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
||||
auto ourNodeNum = nodeDB->getNodeNum();
|
||||
bool toUs = isBroadcast(mp.to) || isToUs(&mp);
|
||||
bool fromUs = mp.from == ourNodeNum;
|
||||
|
||||
for (auto i = modules->begin(); i != modules->end(); ++i) {
|
||||
auto &pi = **i;
|
||||
|
||||
// If specificModule is provided, only call that specific module
|
||||
if (specificModule && (!pi.name || strcmp(pi.name, specificModule) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pi.currentRequest = ∓
|
||||
|
||||
/// We only call modules that are interested in the packet (and the message is destined to us or we are promiscious)
|
||||
|
@ -73,7 +73,7 @@ class MeshModule
|
||||
|
||||
/** For use only by MeshService
|
||||
*/
|
||||
static void callModules(meshtastic_MeshPacket &mp, RxSource src = RX_SRC_RADIO, const char *specificModule = nullptr);
|
||||
static void callModules(meshtastic_MeshPacket &mp, RxSource src = RX_SRC_RADIO);
|
||||
|
||||
static std::vector<MeshModule *> GetMeshModulesWithUIFrames(int startIndex);
|
||||
static void observeUIEvents(Observer<const UIFrameEvent *> *observer);
|
||||
|
@ -663,7 +663,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
config.bluetooth.fixed_pin = defaultBLEPin;
|
||||
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
||||
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS)
|
||||
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||
bool hasScreen = true;
|
||||
#ifdef HELTEC_MESH_NODE_T114
|
||||
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
|
||||
@ -830,6 +830,15 @@ void NodeDB::installDefaultModuleConfig()
|
||||
moduleConfig.external_notification.alert_message = true;
|
||||
moduleConfig.external_notification.output_ms = 1000;
|
||||
moduleConfig.external_notification.nag_timeout = 60;
|
||||
#endif
|
||||
#ifdef T_LORA_PAGER
|
||||
moduleConfig.canned_message.updown1_enabled = true;
|
||||
moduleConfig.canned_message.inputbroker_pin_a = ROTARY_A;
|
||||
moduleConfig.canned_message.inputbroker_pin_b = ROTARY_B;
|
||||
moduleConfig.canned_message.inputbroker_pin_press = ROTARY_PRESS;
|
||||
moduleConfig.canned_message.inputbroker_event_cw = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar(28);
|
||||
moduleConfig.canned_message.inputbroker_event_ccw = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar(29);
|
||||
moduleConfig.canned_message.inputbroker_event_press = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
|
||||
#endif
|
||||
moduleConfig.has_canned_message = true;
|
||||
#if USERPREFS_MQTT_ENABLED && !MESHTASTIC_EXCLUDE_MQTT
|
||||
@ -1702,10 +1711,10 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
|
||||
void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
// if (mp.from == getNodeNum()) {
|
||||
// LOG_DEBUG("Ignore update from self");
|
||||
// return;
|
||||
// }
|
||||
if (mp.from == getNodeNum()) {
|
||||
LOG_DEBUG("Ignore update from self");
|
||||
return;
|
||||
}
|
||||
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
||||
LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time);
|
||||
|
||||
|
@ -529,8 +529,9 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
#endif
|
||||
// Don't use PKC with Ham mode
|
||||
!owner.is_licensed &&
|
||||
// Don't use PKC if it's not explicitly requested and a non-primary channel is requested
|
||||
!(p->pki_encrypted != true && p->channel > 0) &&
|
||||
// Don't use PKC on 'serial' or 'gpio' channels unless explicitly requested
|
||||
!(p->pki_encrypted != true && (strcasecmp(channels.getName(chIndex), Channels::serialChannel) == 0 ||
|
||||
strcasecmp(channels.getName(chIndex), Channels::gpioChannel) == 0)) &&
|
||||
// Check for valid keys and single node destination
|
||||
config.security.private_key.size == 32 && !isBroadcast(p->to) && node != nullptr &&
|
||||
// Check for a known public key for the destination
|
||||
@ -561,7 +562,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
||||
p->channel = hash;
|
||||
if (hash < 0) {
|
||||
// No suitable channel could be found for sending
|
||||
// No suitable channel could be found for
|
||||
return meshtastic_Routing_Error_NO_CHANNEL;
|
||||
}
|
||||
crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes);
|
||||
@ -577,7 +578,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
||||
p->channel = hash;
|
||||
if (hash < 0) {
|
||||
// No suitable channel could be found for sending
|
||||
// No suitable channel could be found for
|
||||
return meshtastic_Routing_Error_NO_CHANNEL;
|
||||
}
|
||||
crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes);
|
||||
@ -670,7 +671,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
mqtt->onSend(*p_encrypted, *p, p->channel);
|
||||
#endif
|
||||
} else if (p->from == nodeDB->getNodeNum() && !skipHandle) {
|
||||
MeshModule::callModules(*p, src, ROUTING_MODULE);
|
||||
MeshModule::callModules(*p, src);
|
||||
}
|
||||
|
||||
packetPool.release(p_encrypted); // Release the encrypted packet
|
||||
|
@ -16,6 +16,95 @@ int32_t StreamAPI::runOncePart()
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t StreamAPI::runOncePart(char *buf, uint16_t bufLen)
|
||||
{
|
||||
auto result = readStream(buf, bufLen);
|
||||
writeStream();
|
||||
checkConnectionTimeout();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read any rx chars from the link and call handleRecStream
|
||||
*/
|
||||
int32_t StreamAPI::readStream(char *buf, uint16_t bufLen)
|
||||
{
|
||||
if (bufLen < 1) {
|
||||
// Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time
|
||||
bool recentRx = Throttle::isWithinTimespanMs(lastRxMsec, 2000);
|
||||
return recentRx ? 5 : 250;
|
||||
} else {
|
||||
handleRecStream(buf, bufLen);
|
||||
// we had bytes available this time, so assume we might have them next time also
|
||||
lastRxMsec = millis();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* call getFromRadio() and deliver encapsulated packets to the Stream
|
||||
*/
|
||||
void StreamAPI::writeStream()
|
||||
{
|
||||
if (canWrite) {
|
||||
uint32_t len;
|
||||
do {
|
||||
// Send every packet we can
|
||||
len = getFromRadio(txBuf + HEADER_LEN);
|
||||
emitTxBuffer(len);
|
||||
} while (len);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t StreamAPI::handleRecStream(char *buf, uint16_t bufLen)
|
||||
{
|
||||
uint16_t index = 0;
|
||||
while (bufLen > index) { // Currently we never want to block
|
||||
int cInt = buf[index++];
|
||||
if (cInt < 0)
|
||||
break; // We ran out of characters (even though available said otherwise) - this can happen on rf52 adafruit
|
||||
// arduino
|
||||
|
||||
uint8_t c = (uint8_t)cInt;
|
||||
|
||||
// Use the read pointer for a little state machine, first look for framing, then length bytes, then payload
|
||||
size_t ptr = rxPtr;
|
||||
|
||||
rxPtr++; // assume we will probably advance the rxPtr
|
||||
rxBuf[ptr] = c; // store all bytes (including framing)
|
||||
|
||||
// console->printf("rxPtr %d ptr=%d c=0x%x\n", rxPtr, ptr, c);
|
||||
|
||||
if (ptr == 0) { // looking for START1
|
||||
if (c != START1)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr == 1) { // looking for START2
|
||||
if (c != START2)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr >= HEADER_LEN - 1) { // we have at least read our 4 byte framing
|
||||
uint32_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing
|
||||
|
||||
// console->printf("len %d\n", len);
|
||||
|
||||
if (ptr == HEADER_LEN - 1) {
|
||||
// we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid
|
||||
// protobuf also)
|
||||
if (len > MAX_TO_FROM_RADIO_SIZE)
|
||||
rxPtr = 0; // length is bogus, restart search for framing
|
||||
}
|
||||
|
||||
if (rxPtr != 0) // Is packet still considered 'good'?
|
||||
if (ptr + 1 >= len + HEADER_LEN) { // have we received all of the payload?
|
||||
rxPtr = 0; // start over again on the next packet
|
||||
|
||||
// If we didn't just fail the packet and we now have the right # of bytes, parse it
|
||||
handleToRadio(rxBuf + HEADER_LEN, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read any rx chars from the link and call handleToRadio
|
||||
*/
|
||||
@ -76,21 +165,6 @@ int32_t StreamAPI::readStream()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* call getFromRadio() and deliver encapsulated packets to the Stream
|
||||
*/
|
||||
void StreamAPI::writeStream()
|
||||
{
|
||||
if (canWrite) {
|
||||
uint32_t len;
|
||||
do {
|
||||
// Send every packet we can
|
||||
len = getFromRadio(txBuf + HEADER_LEN);
|
||||
emitTxBuffer(len);
|
||||
} while (len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the current txBuffer over our stream
|
||||
*/
|
||||
|
@ -50,12 +50,15 @@ class StreamAPI : public PhoneAPI
|
||||
* phone.
|
||||
*/
|
||||
virtual int32_t runOncePart();
|
||||
virtual int32_t runOncePart(char *buf,uint16_t bufLen);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Read any rx chars from the link and call handleToRadio
|
||||
*/
|
||||
int32_t readStream();
|
||||
int32_t readStream(char *buf,uint16_t bufLen);
|
||||
int32_t handleRecStream(char *buf,uint16_t bufLen);
|
||||
|
||||
/**
|
||||
* call getFromRadio() and deliver encapsulated packets to the Stream
|
||||
|
@ -505,7 +505,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
if (mp.decoded.want_response && !myReply) {
|
||||
myReply = allocErrorResponse(meshtastic_Routing_Error_NONE, &mp);
|
||||
}
|
||||
|
||||
if (mp.pki_encrypted && myReply) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
@ -941,6 +943,9 @@ void AdminModule::handleGetOwner(const meshtastic_MeshPacket &req)
|
||||
res.which_payload_variant = meshtastic_AdminMessage_get_owner_response_tag;
|
||||
setPassKey(&res);
|
||||
myReply = allocDataProtobuf(res);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1012,6 +1017,9 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32
|
||||
res.which_payload_variant = meshtastic_AdminMessage_get_config_response_tag;
|
||||
setPassKey(&res);
|
||||
myReply = allocDataProtobuf(res);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1099,6 +1107,9 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
|
||||
res.which_payload_variant = meshtastic_AdminMessage_get_module_config_response_tag;
|
||||
setPassKey(&res);
|
||||
myReply = allocDataProtobuf(res);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1123,6 +1134,9 @@ void AdminModule::handleGetNodeRemoteHardwarePins(const meshtastic_MeshPacket &r
|
||||
}
|
||||
setPassKey(&r);
|
||||
myReply = allocDataProtobuf(r);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AdminModule::handleGetDeviceMetadata(const meshtastic_MeshPacket &req)
|
||||
@ -1132,6 +1146,9 @@ void AdminModule::handleGetDeviceMetadata(const meshtastic_MeshPacket &req)
|
||||
r.which_payload_variant = meshtastic_AdminMessage_get_device_metadata_response_tag;
|
||||
setPassKey(&r);
|
||||
myReply = allocDataProtobuf(r);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &req)
|
||||
@ -1200,6 +1217,9 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
|
||||
r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag;
|
||||
setPassKey(&r);
|
||||
myReply = allocDataProtobuf(r);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t channelIndex)
|
||||
@ -1211,6 +1231,9 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
|
||||
r.which_payload_variant = meshtastic_AdminMessage_get_channel_response_tag;
|
||||
setPassKey(&r);
|
||||
myReply = allocDataProtobuf(r);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1220,6 +1243,9 @@ void AdminModule::handleGetDeviceUIConfig(const meshtastic_MeshPacket &req)
|
||||
r.which_payload_variant = meshtastic_AdminMessage_get_ui_config_response_tag;
|
||||
r.get_ui_config_response = uiconfig;
|
||||
myReply = allocDataProtobuf(r);
|
||||
if (req.pki_encrypted) {
|
||||
myReply->pki_encrypted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AdminModule::reboot(int32_t seconds)
|
||||
|
@ -632,10 +632,10 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo
|
||||
// Normal canned message selection
|
||||
if (runState == CANNED_MESSAGE_RUN_STATE_INACTIVE || runState == CANNED_MESSAGE_RUN_STATE_DISABLED) {
|
||||
} else {
|
||||
#if CANNED_MESSAGE_ADD_CONFIRMATION
|
||||
// Show confirmation dialog before sending canned message
|
||||
NodeNum destNode = dest;
|
||||
ChannelIndex chan = channel;
|
||||
#if CANNED_MESSAGE_ADD_CONFIRMATION
|
||||
graphics::menuHandler::showConfirmationBanner("Send message?", [this, destNode, chan, current]() {
|
||||
this->sendText(destNode, chan, current, false);
|
||||
payload = runState;
|
||||
@ -991,7 +991,6 @@ int32_t CannedMessageModule::runOnce()
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
}
|
||||
}
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = "";
|
||||
this->cursor = 0;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "buzz/BuzzerFeedbackThread.h"
|
||||
#include "input/ExpressLRSFiveWay.h"
|
||||
#include "input/InputBroker.h"
|
||||
#include "input/RotaryEncoderImpl.h"
|
||||
#include "input/RotaryEncoderInterruptImpl1.h"
|
||||
#include "input/SerialKeyboardImpl.h"
|
||||
#include "input/TrackballInterruptImpl1.h"
|
||||
@ -170,11 +171,20 @@ void setupModules()
|
||||
delete rotaryEncoderInterruptImpl1;
|
||||
rotaryEncoderInterruptImpl1 = nullptr;
|
||||
}
|
||||
#ifdef T_LORA_PAGER
|
||||
// use a special FSM based rotary encoder version for T-LoRa Pager
|
||||
rotaryEncoderImpl = new RotaryEncoderImpl();
|
||||
if (!rotaryEncoderImpl->init()) {
|
||||
delete rotaryEncoderImpl;
|
||||
rotaryEncoderImpl = nullptr;
|
||||
}
|
||||
#else
|
||||
upDownInterruptImpl1 = new UpDownInterruptImpl1();
|
||||
if (!upDownInterruptImpl1->init()) {
|
||||
delete upDownInterruptImpl1;
|
||||
upDownInterruptImpl1 = nullptr;
|
||||
}
|
||||
#endif
|
||||
cardKbI2cImpl = new CardKbI2cImpl();
|
||||
cardKbI2cImpl->init();
|
||||
#ifdef INPUTBROKER_MATRIX_TYPE
|
||||
|
@ -105,14 +105,15 @@ void NeighborInfoModule::sendNeighborInfo(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
meshtastic_NeighborInfo neighborInfo = meshtastic_NeighborInfo_init_zero;
|
||||
collectNeighborInfo(&neighborInfo);
|
||||
meshtastic_MeshPacket *p = allocDataProtobuf(neighborInfo);
|
||||
// send regardless of whether or not we have neighbors in our DB,
|
||||
// because we want to get neighbors for the next cycle
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||
printNeighborInfo("SENDING", &neighborInfo);
|
||||
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
||||
// only send neighbours if we have some to send
|
||||
if (neighborInfo.neighbors_count > 0) {
|
||||
meshtastic_MeshPacket *p = allocDataProtobuf(neighborInfo);
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||
printNeighborInfo("SENDING", &neighborInfo);
|
||||
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -214,4 +215,4 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
|
||||
neighbors.push_back(new_nbr);
|
||||
}
|
||||
return &neighbors.back();
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit
|
||||
return Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); // Use the default hop limit
|
||||
}
|
||||
|
||||
RoutingModule::RoutingModule() : ProtobufModule(ROUTING_MODULE, meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
|
||||
RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
|
||||
{
|
||||
isPromiscuous = true;
|
||||
|
||||
|
@ -2,8 +2,6 @@
|
||||
#include "Channels.h"
|
||||
#include "ProtobufModule.h"
|
||||
|
||||
static const char *ROUTING_MODULE = "routing";
|
||||
|
||||
/**
|
||||
* Routing module for router control messages
|
||||
*/
|
||||
|
@ -45,6 +45,9 @@
|
||||
|
||||
|
||||
*/
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
#include "meshSolarApp.h"
|
||||
#endif
|
||||
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
@ -60,8 +63,8 @@
|
||||
SerialModule *serialModule;
|
||||
SerialModuleRadio *serialModuleRadio;
|
||||
|
||||
#if defined(TTGO_T_ECHO) || defined(T_ECHO_LITE) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
|
||||
defined(ELECROW_ThinkNode_M5)
|
||||
#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
|
||||
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE)
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {}
|
||||
static Print *serialPrint = &Serial;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
@ -78,7 +81,8 @@ size_t serialPayloadSize;
|
||||
bool SerialModule::isValidConfig(const meshtastic_ModuleConfig_SerialConfig &config)
|
||||
{
|
||||
if (config.override_console_serial_port && !IS_ONE_OF(config.mode, meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA,
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO)) {
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO,
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG)) {
|
||||
const char *warning =
|
||||
"Invalid Serial config: override console serial port is only supported in NMEA and CalTopo output-only modes.";
|
||||
LOG_ERROR(warning);
|
||||
@ -241,7 +245,17 @@ int32_t SerialModule::runOnce()
|
||||
else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) {
|
||||
processWXSerial();
|
||||
|
||||
} else {
|
||||
}
|
||||
#if defined(HELTEC_MESH_SOLAR)
|
||||
else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG)) {
|
||||
serialPayloadSize = Serial.readBytes(serialBytes, sizeof(serialBytes) - 1);
|
||||
// If the parsing fails, the following parsing will be performed.
|
||||
if ((serialPayloadSize > 0) && (meshSolarCmdHandle(serialBytes) != 0)) {
|
||||
return runOncePart(serialBytes, serialPayloadSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
while (Serial1.available()) {
|
||||
serialPayloadSize = Serial1.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
|
||||
|
@ -192,6 +192,8 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_LINK_32
|
||||
#elif defined(T_DECK_PRO)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_DECK_PRO
|
||||
#elif defined(T_LORA_PAGER)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_LORA_PAGER
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
27
src/platform/extra_variants/t_lora_pager/variant.cpp
Normal file
27
src/platform/extra_variants/t_lora_pager/variant.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef T_LORA_PAGER
|
||||
|
||||
#include "AudioBoard.h"
|
||||
|
||||
DriverPins PinsAudioBoardES8311;
|
||||
AudioBoard board(AudioDriverES8311, PinsAudioBoardES8311);
|
||||
|
||||
// TLora Pager specific init
|
||||
void lateInitVariant()
|
||||
{
|
||||
// AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Debug);
|
||||
// I2C: function, scl, sda
|
||||
PinsAudioBoardES8311.addI2C(PinFunction::CODEC, Wire);
|
||||
// I2S: function, mclk, bck, ws, data_out, data_in
|
||||
PinsAudioBoardES8311.addI2S(PinFunction::CODEC, DAC_I2S_MCLK, DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_DIN);
|
||||
|
||||
// configure codec
|
||||
CodecConfig cfg;
|
||||
cfg.input_device = ADC_INPUT_LINE1;
|
||||
cfg.output_device = DAC_OUTPUT_ALL;
|
||||
cfg.i2s.bits = BIT_LENGTH_16BITS;
|
||||
cfg.i2s.rate = RATE_44K;
|
||||
board.begin(cfg);
|
||||
}
|
||||
#endif
|
@ -98,6 +98,8 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK
|
||||
#elif defined(SEEED_WIO_TRACKER_L1)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1
|
||||
#elif defined(HELTEC_MESH_SOLAR)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_SOLAR
|
||||
#else
|
||||
#define HW_VENDOR meshtastic_HardwareModel_NRF52_UNKNOWN
|
||||
#endif
|
||||
|
@ -323,7 +323,7 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_MESH_NODE_T114
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_MESH_SOLAR)
|
||||
nrf_gpio_cfg_default(PIN_GPS_PPS);
|
||||
detachInterrupt(PIN_GPS_PPS);
|
||||
detachInterrupt(PIN_BUTTON1);
|
||||
|
@ -128,6 +128,8 @@ class Power : private concurrency::OSThread
|
||||
bool lipoInit();
|
||||
/// Setup a Lipo charger
|
||||
bool lipoChargerInit();
|
||||
/// Setup a meshSolar battery sensor
|
||||
bool meshSolarInit();
|
||||
|
||||
private:
|
||||
void shutdown();
|
||||
|
@ -19,8 +19,6 @@ build_flags = ${esp32s3_base.build_flags} -Os
|
||||
-D MESHTASTIC_EXCLUDE_SERIAL=1
|
||||
-D MESHTASTIC_EXCLUDE_SOCKETAPI=1
|
||||
-D MESHTASTIC_EXCLUDE_SCREEN=1
|
||||
-D MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1
|
||||
-D HAS_TELEMETRY=0
|
||||
-D CONFIG_DISABLE_HAL_LOCKS=1
|
||||
-D USE_PIN_BUZZER
|
||||
-D HAS_SCREEN=0
|
||||
|
19
variants/esp32s3/tlora-pager/pins_arduino.h
Normal file
19
variants/esp32s3/tlora-pager/pins_arduino.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_VID 0x303a
|
||||
#define USB_PID 0x1001
|
||||
|
||||
// used for keyboard, battery gauge, charger and haptic driver
|
||||
static const uint8_t SDA = 3;
|
||||
static const uint8_t SCL = 2;
|
||||
|
||||
// Default SPI will be mapped to Radio
|
||||
static const uint8_t SS = 36;
|
||||
static const uint8_t MOSI = 34;
|
||||
static const uint8_t MISO = 33;
|
||||
static const uint8_t SCK = 35;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
71
variants/esp32s3/tlora-pager/platformio.ini
Normal file
71
variants/esp32s3/tlora-pager/platformio.ini
Normal file
@ -0,0 +1,71 @@
|
||||
; LilyGo T-Lora-Pager
|
||||
[env:tlora-pager]
|
||||
extends = esp32s3_base
|
||||
board = t-deck-pro ; same as T-Deck Pro
|
||||
board_check = true
|
||||
board_build.partitions = default_16MB.csv
|
||||
upload_protocol = esptool
|
||||
|
||||
build_flags = ${esp32s3_base.build_flags}
|
||||
-I variants/esp32s3/tlora-pager
|
||||
-D T_LORA_PAGER
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D GPS_POWER_TOGGLE
|
||||
-D HAS_SDCARD
|
||||
-D SDCARD_USE_SPI1
|
||||
-D ENABLE_ROTARY_PULLUP
|
||||
-D ENABLE_BUTTON_PULLUP
|
||||
-D HALF_STEP
|
||||
|
||||
lib_deps = ${esp32s3_base.lib_deps}
|
||||
lovyan03/LovyanGFX@1.2.7
|
||||
earlephilhower/ESP8266Audio@1.9.9
|
||||
earlephilhower/ESP8266SAM@1.0.1
|
||||
adafruit/Adafruit DRV2605 Library@1.2.4
|
||||
lewisxhe/PCF8563_Library@1.0.1
|
||||
lewisxhe/SensorLib@0.3.1
|
||||
https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip
|
||||
https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip
|
||||
https://github.com/mverch67/RotaryEncoder
|
||||
|
||||
[env:tlora-pager-tft]
|
||||
board_level = extra
|
||||
extends = env:tlora-pager
|
||||
build_flags =
|
||||
${env:tlora-pager.build_flags}
|
||||
-D CONFIG_DISABLE_HAL_LOCKS=1
|
||||
-D INPUTDRIVER_ROTARY_TYPE=1
|
||||
-D INPUTDRIVER_ROTARY_UP=40
|
||||
-D INPUTDRIVER_ROTARY_DOWN=41
|
||||
-D INPUTDRIVER_ROTARY_BTN=7
|
||||
-D INPUTDRIVER_BUTTON_TYPE=0
|
||||
-D HAS_SCREEN=1
|
||||
-D HAS_TFT=1
|
||||
-D USE_I2S_BUZZER
|
||||
-D RAM_SIZE=5120
|
||||
-D LV_LVGL_H_INCLUDE_SIMPLE
|
||||
-D LV_CONF_INCLUDE_SIMPLE
|
||||
-D LV_COMP_CONF_INCLUDE_SIMPLE
|
||||
-D LV_USE_SYSMON=0
|
||||
-D LV_USE_PROFILER=0
|
||||
-D LV_USE_PERF_MONITOR=0
|
||||
-D LV_USE_MEM_MONITOR=0
|
||||
-D LV_USE_LOG=0
|
||||
-D USE_LOG_DEBUG
|
||||
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
|
||||
-D RADIOLIB_SPI_PARANOID=0
|
||||
-D LGFX_SCREEN_WIDTH=222
|
||||
-D LGFX_SCREEN_HEIGHT=480
|
||||
-D DISPLAY_SIZE=480x222 ; landscape mode
|
||||
-D DISPLAY_SET_RESOLUTION
|
||||
-D LGFX_DRIVER=LGFX_TLORA_PAGER
|
||||
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_T_LORA_PAGER.h\"
|
||||
; -D LVGL_DRIVER=LVGL_T_LORA_PAGER
|
||||
; -D LV_USE_ST7796=1
|
||||
-D VIEW_480x222
|
||||
-D USE_PACKET_API
|
||||
-D MAP_FULL_REDRAW
|
||||
|
||||
lib_deps =
|
||||
${env:tlora-pager.lib_deps}
|
||||
${device-ui_base.lib_deps}
|
125
variants/esp32s3/tlora-pager/variant.h
Normal file
125
variants/esp32s3/tlora-pager/variant.h
Normal file
@ -0,0 +1,125 @@
|
||||
// ST7796 TFT LCD
|
||||
#define TFT_CS 38
|
||||
#define ST7796_CS TFT_CS
|
||||
#define ST7796_RS 37 // DC
|
||||
#define ST7796_SDA MOSI // MOSI
|
||||
#define ST7796_SCK SCK
|
||||
#define ST7796_RESET -1
|
||||
#define ST7796_MISO MISO
|
||||
#define ST7796_BUSY -1
|
||||
#define ST7796_BL 42
|
||||
#define ST7796_SPI_HOST SPI2_HOST
|
||||
#define TFT_BL 42
|
||||
#define SPI_FREQUENCY 75000000
|
||||
#define SPI_READ_FREQUENCY 16000000
|
||||
#define TFT_HEIGHT 480
|
||||
#define TFT_WIDTH 222
|
||||
#define TFT_OFFSET_X 49
|
||||
#define TFT_OFFSET_Y 0
|
||||
#define TFT_OFFSET_ROTATION 3
|
||||
#define SCREEN_ROTATE
|
||||
#define SCREEN_TRANSITION_FRAMERATE 5
|
||||
#define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness
|
||||
|
||||
#define I2C_SDA SDA
|
||||
#define I2C_SCL SCL
|
||||
|
||||
#define USE_POWERSAVE
|
||||
#define SLEEP_TIME 120
|
||||
|
||||
// GNNS
|
||||
#define HAS_GPS 1
|
||||
#define GPS_BAUDRATE 38400
|
||||
#define GPS_RX_PIN 4
|
||||
#define GPS_TX_PIN 12
|
||||
#define PIN_GPS_PPS 13
|
||||
|
||||
// PCF8563 RTC Module
|
||||
#if __has_include("pcf8563.h")
|
||||
#include "pcf8563.h"
|
||||
#endif
|
||||
#define PCF8563_RTC 0x51
|
||||
#define HAS_RTC 1
|
||||
|
||||
// Rotary
|
||||
#define ROTARY_A (40)
|
||||
#define ROTARY_B (41)
|
||||
#define ROTARY_PRESS (7)
|
||||
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
// SPI interface SD card slot
|
||||
#define SPI_MOSI MOSI
|
||||
#define SPI_SCK SCK
|
||||
#define SPI_MISO MISO
|
||||
#define SPI_CS 21
|
||||
#define SDCARD_CS SPI_CS
|
||||
#define SD_SPI_FREQUENCY 75000000U
|
||||
|
||||
// TCA8418 keyboard
|
||||
#define I2C_NO_RESCAN
|
||||
#define KB_BL_PIN 46
|
||||
#define KB_INT 6
|
||||
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
||||
|
||||
// audio codec ES8311
|
||||
#define HAS_I2S
|
||||
#define DAC_I2S_BCK 11
|
||||
#define DAC_I2S_WS 18
|
||||
#define DAC_I2S_DOUT 45
|
||||
#define DAC_I2S_DIN 17
|
||||
#define DAC_I2S_MCLK 10
|
||||
|
||||
// gyroscope BHI260AP
|
||||
#define HAS_BHI260AP
|
||||
|
||||
// battery charger BQ25896
|
||||
#define HAS_PPM 1
|
||||
#define XPOWERS_CHIP_BQ25896
|
||||
|
||||
// battery quality management BQ27220
|
||||
#define HAS_BQ27220 1
|
||||
#define BQ27220_I2C_SDA SDA
|
||||
#define BQ27220_I2C_SCL SCL
|
||||
#define BQ27220_DESIGN_CAPACITY 1500
|
||||
|
||||
// NFC ST25R3916
|
||||
#define NFC_INT 5
|
||||
#define NFC_CS 39
|
||||
|
||||
// External expansion chip XL9555
|
||||
#define USE_XL9555
|
||||
#define EXPANDS_DRV_EN (0)
|
||||
#define EXPANDS_AMP_EN (1)
|
||||
#define EXPANDS_KB_RST (2)
|
||||
#define EXPANDS_LORA_EN (3)
|
||||
#define EXPANDS_GPS_EN (4)
|
||||
#define EXPANDS_NFC_EN (5)
|
||||
#define EXPANDS_GPS_RST (7)
|
||||
#define EXPANDS_KB_EN (8)
|
||||
#define EXPANDS_GPIO_EN (9)
|
||||
#define EXPANDS_SD_DET (10)
|
||||
#define EXPANDS_SD_PULLEN (11)
|
||||
#define EXPANDS_SD_EN (12)
|
||||
|
||||
// LoRa
|
||||
#define USE_SX1262
|
||||
#define USE_SX1268
|
||||
|
||||
#define LORA_SCK 35
|
||||
#define LORA_MISO 33
|
||||
#define LORA_MOSI 34
|
||||
#define LORA_CS 36
|
||||
|
||||
#define LORA_DIO0 -1 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 47
|
||||
#define LORA_DIO1 14 // SX1262 IRQ
|
||||
#define LORA_DIO2 48 // SX1262 BUSY
|
||||
#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled
|
||||
|
||||
#define SX126X_CS LORA_CS
|
||||
#define SX126X_DIO1 LORA_DIO1
|
||||
#define SX126X_BUSY LORA_DIO2
|
||||
#define SX126X_RESET LORA_RESET
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 3.0
|
@ -32,6 +32,7 @@ lib_deps = ${esp32s3_base.lib_deps}
|
||||
|
||||
|
||||
[env:unphone-tft]
|
||||
board_level = extra
|
||||
extends = env:unphone
|
||||
build_flags =
|
||||
${env:unphone.build_flags}
|
||||
@ -52,8 +53,6 @@ build_flags =
|
||||
-D LV_USE_PERF_MONITOR=0
|
||||
-D LV_USE_MEM_MONITOR=0
|
||||
-D LV_USE_LOG=0
|
||||
-D USE_LOG_DEBUG
|
||||
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
|
||||
-D LGFX_SCREEN_WIDTH=320
|
||||
-D LGFX_SCREEN_HEIGHT=480
|
||||
-D DISPLAY_SIZE=320x480 ; portrait mode
|
||||
|
19
variants/nrf52840/heltec_mesh_solar/platformio.ini
Normal file
19
variants/nrf52840/heltec_mesh_solar/platformio.ini
Normal file
@ -0,0 +1,19 @@
|
||||
; First prototype nrf52840/sx1262 device
|
||||
[env:heltec-mesh-solar]
|
||||
extends = nrf52840_base
|
||||
board = heltec_mesh_solar
|
||||
board_level = pr
|
||||
debug_tool = jlink
|
||||
|
||||
# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling.
|
||||
build_flags = ${nrf52840_base.build_flags}
|
||||
-Ivariants/nrf52840/heltec_mesh_solar
|
||||
-DGPS_POWER_TOGGLE
|
||||
-DHELTEC_MESH_SOLAR
|
||||
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_solar>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip
|
||||
lewisxhe/PCF8563_Library@^1.0.1
|
||||
ArduinoJson@6.21.4
|
36
variants/nrf52840/heltec_mesh_solar/variant.cpp
Normal file
36
variants/nrf52840/heltec_mesh_solar/variant.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 - pins 0 and 1 are hardwired for xtal and should never be enabled
|
||||
0xff, 0xff, 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()
|
||||
{
|
||||
pinMode(BQ4050_EMERGENCY_SHUTDOWN_PIN, INPUT);
|
||||
}
|
157
variants/nrf52840/heltec_mesh_solar/variant.h
Normal file
157
variants/nrf52840/heltec_mesh_solar/variant.h
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
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_HELTEC_NRF_
|
||||
#define _VARIANT_HELTEC_NRF_
|
||||
/** Master clock frequency */
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
#define USE_LFXO // Board uses 32khz crystal 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 (1)
|
||||
#define NUM_ANALOG_OUTPUTS (0)
|
||||
|
||||
|
||||
#define PIN_LED1 (0 + 12) // green (confirmed on 1.0 board)
|
||||
#define LED_BLUE PIN_LED1 // fake for bluefruit library
|
||||
#define LED_GREEN PIN_LED1
|
||||
#define LED_BUILTIN LED_GREEN
|
||||
#define LED_STATE_ON 0 // State when LED is lit
|
||||
|
||||
#define HAS_NEOPIXEL // Enable the use of neopixels
|
||||
#define NEOPIXEL_COUNT 1 // How many neopixels are connected
|
||||
#define NEOPIXEL_DATA (32+15) // gpio pin used to send data to the neopixels
|
||||
#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
*/
|
||||
#define PIN_BUTTON1 (32 + 10)
|
||||
// #define PIN_BUTTON2 (0 + 18) // 0.18 is labeled on the board as RESET but we configure it in the bootloader as a regular
|
||||
// GPIO
|
||||
|
||||
/*
|
||||
No longer populated on PCB
|
||||
*/
|
||||
#define PIN_SERIAL2_RX (0 + 9)
|
||||
#define PIN_SERIAL2_TX (0 + 10)
|
||||
// #define PIN_SERIAL2_EN (0 + 17)
|
||||
|
||||
/*
|
||||
* I2C
|
||||
*/
|
||||
|
||||
#define WIRE_INTERFACES_COUNT 2
|
||||
|
||||
// I2C bus 0
|
||||
// Routed to footprint for PCF8563TS RTC
|
||||
// Not populated on T114 V1, maybe in future?
|
||||
#define PIN_WIRE_SDA (0 + 6) // P0.26
|
||||
#define PIN_WIRE_SCL (0 + 26) // P0.26
|
||||
|
||||
// I2C bus 1
|
||||
// Available on header pins, for general use
|
||||
#define PIN_WIRE1_SDA (0 + 30) // P0.30
|
||||
#define PIN_WIRE1_SCL (0 + 5) // P0.13
|
||||
|
||||
/*
|
||||
* Lora radio
|
||||
*/
|
||||
|
||||
#define USE_SX1262
|
||||
// #define USE_SX1268
|
||||
#define SX126X_CS (0 + 24) // FIXME - we really should define LORA_CS instead
|
||||
#define LORA_CS (0 + 24)
|
||||
#define SX126X_DIO1 (0 + 20)
|
||||
// Note DIO2 is attached internally to the module to an analog switch for TX/RX switching
|
||||
// #define SX1262_DIO3 (0 + 21)
|
||||
// This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the
|
||||
// main
|
||||
// CPU?
|
||||
#define SX126X_BUSY (0 + 17)
|
||||
#define SX126X_RESET (0 + 25)
|
||||
// Not really an E22 but TTGO seems to be trying to clone that
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
|
||||
/*
|
||||
* GPS pins
|
||||
*/
|
||||
|
||||
#define GPS_L76K
|
||||
|
||||
// #define PIN_GPS_RESET (32 + 6) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K
|
||||
// #define GPS_RESET_MODE LOW
|
||||
// #define PIN_GPS_EN (21)
|
||||
#define PERIPHERAL_WARMUP_MS 1000 // Make sure I2C QuickLink has stable power before continuing
|
||||
#define VEXT_ON_VALUE HIGH
|
||||
// #define GPS_EN_ACTIVE HIGH
|
||||
#define PIN_GPS_STANDBY (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake
|
||||
#define PIN_GPS_PPS (32 + 4)
|
||||
// Seems to be missing on this new board
|
||||
// #define PIN_GPS_PPS (32 + 4) // Pulse per second input from the GPS
|
||||
#define GPS_TX_PIN (32 + 5) // This is for bits going TOWARDS the CPU
|
||||
#define GPS_RX_PIN (32 + 7) // This is for bits going TOWARDS the GPS
|
||||
|
||||
#define GPS_THREAD_INTERVAL 50
|
||||
|
||||
#define PIN_SERIAL1_RX GPS_TX_PIN
|
||||
#define PIN_SERIAL1_TX GPS_RX_PIN
|
||||
|
||||
/*
|
||||
* SPI Interfaces
|
||||
*/
|
||||
#define SPI_INTERFACES_COUNT 1
|
||||
|
||||
// For LORA, spi 0
|
||||
#define PIN_SPI_MISO (0 + 23)
|
||||
#define PIN_SPI_MOSI (0 + 22)
|
||||
#define PIN_SPI_SCK (0 + 19)
|
||||
|
||||
// #define PIN_PWR_EN (0 + 6)
|
||||
|
||||
// To debug via the segger JLINK console rather than the CDC-ACM serial device
|
||||
// #define USE_SEGGER
|
||||
|
||||
#define BQ4050_SDA_PIN (32+1) // I2C data line pin
|
||||
#define BQ4050_SCL_PIN (32+0) // I2C clock line pin
|
||||
#define BQ4050_EMERGENCY_SHUTDOWN_PIN (32+3) // Emergency shutdown pin
|
||||
|
||||
#define HAS_RTC 0
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Arduino objects - C++ only
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
@ -18,16 +18,9 @@
|
||||
|
||||
// Shared NicheGraphics components
|
||||
// --------------------------------
|
||||
#include "graphics/niche/Drivers/Backlight/LatchingBacklight.h"
|
||||
#include "graphics/niche/Drivers/EInk/GDEY0213B74.h"
|
||||
#include "graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.h"
|
||||
#include "graphics/niche/Inputs/TwoButton.h"
|
||||
|
||||
// Special case - fix T-Echo's touch button
|
||||
// ----------------------------------------
|
||||
// On a handful of T-Echos, LoRa TX triggers the capacitive touch
|
||||
// To avoid this, we lockout the button during TX
|
||||
#include "mesh/RadioLibInterface.h"
|
||||
|
||||
void setupNicheGraphics()
|
||||
{
|
||||
using namespace NicheGraphics;
|
||||
@ -41,7 +34,7 @@ void setupNicheGraphics()
|
||||
// E-Ink Driver
|
||||
// -----------------------------
|
||||
|
||||
Drivers::EInk *driver = new Drivers::GDEY0213B74;
|
||||
Drivers::EInk *driver = new Drivers::ZJY122250_0213BAAMFGN;
|
||||
driver->begin(&SPI1, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES);
|
||||
|
||||
// InkHUD
|
||||
@ -53,8 +46,7 @@ void setupNicheGraphics()
|
||||
inkhud->setDriver(driver);
|
||||
|
||||
// Set how many FAST updates per FULL update
|
||||
// Set how unhealthy additional FAST updates beyond this number are
|
||||
inkhud->setDisplayResilience(7, 1.5);
|
||||
inkhud->setDisplayResilience(15);
|
||||
|
||||
// Select fonts
|
||||
InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252;
|
||||
@ -62,16 +54,10 @@ void setupNicheGraphics()
|
||||
InkHUD::Applet::fontSmall = FREESANS_6PT_WIN1252;
|
||||
|
||||
// Customize default settings
|
||||
inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side
|
||||
// 270 degrees clockwise
|
||||
inkhud->persistence->settings.rotation = 1; // 90 degrees clockwise
|
||||
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
|
||||
inkhud->persistence->settings.optionalMenuItems.backlight = true; // Until proves capacitive button works by touching it
|
||||
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
|
||||
|
||||
// Setup backlight controller
|
||||
// Note: AUX button attached further down
|
||||
Drivers::LatchingBacklight *backlight = Drivers::LatchingBacklight::getInstance();
|
||||
backlight->setPin(PIN_EINK_EN);
|
||||
inkhud->persistence->settings.userTiles.count = 1; // One tile only by default, keep things simple for new users
|
||||
inkhud->persistence->settings.userTiles.maxCount = 2; // Two applets side-by-side
|
||||
|
||||
// Pick applets
|
||||
// Note: order of applets determines priority of "auto-show" feature
|
||||
@ -83,11 +69,9 @@ void setupNicheGraphics()
|
||||
inkhud->addApplet("Recents List", new InkHUD::RecentsListApplet); // -
|
||||
inkhud->addApplet("Heard", new InkHUD::HeardApplet, true, false, 0); // Activated, no autoshow, default on tile 0
|
||||
|
||||
inkhud->persistence->settings.rotation = 1;
|
||||
// inkhud->persistence->printSettings(&inkhud->persistence->settings);
|
||||
// Start running InkHUD
|
||||
inkhud->begin();
|
||||
// inkhud->persistence->printSettings(&inkhud->persistence->settings);
|
||||
|
||||
// Buttons
|
||||
// --------------------------
|
||||
|
||||
|
@ -1,17 +1,47 @@
|
||||
[env:seeed_wio_tracker_L1_eink]
|
||||
board = seeed_wio_tracker_L1
|
||||
extends = nrf52840_base, inkhud
|
||||
extends = nrf52840_base
|
||||
;board_level = extra
|
||||
build_flags = ${nrf52840_base.build_flags}
|
||||
${inkhud.build_flags}
|
||||
-I variants/nrf52840/seeed_wio_tracker_L1_eink
|
||||
-D SEEED_WIO_TRACKER_L1_EINK
|
||||
-D SEEED_WIO_TRACKER_L1
|
||||
-I src/platform/nrf52/softdevice
|
||||
-I src/platform/nrf52/softdevice/nrf52
|
||||
-DUSE_EINK
|
||||
-DEINK_DISPLAY_MODEL=GxEPD2_213_B74
|
||||
-DEINK_WIDTH=250
|
||||
-DEINK_HEIGHT=122
|
||||
-DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk
|
||||
-DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted
|
||||
-DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates
|
||||
-DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates
|
||||
; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated
|
||||
-DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
|
||||
-DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting"
|
||||
-DEINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight
|
||||
board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/seeed_wio_tracker_L1_eink> ${inkhud.build_src_filter}
|
||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/seeed_wio_tracker_L1_eink>
|
||||
lib_deps =
|
||||
${inkhud.lib_deps}
|
||||
${nrf52840_base.lib_deps}
|
||||
https://github.com/meshtastic/GxEPD2#b202ebfec6a4821e098cf7a625ba0f6f2400292d
|
||||
debug_tool = jlink
|
||||
|
||||
[env:seeed_wio_tracker_L1_eink-inkhud]
|
||||
board = seeed_wio_tracker_L1
|
||||
extends = nrf52840_base, inkhud
|
||||
build_flags =
|
||||
${nrf52840_base.build_flags}
|
||||
${inkhud.build_flags}
|
||||
-I variants/nrf52840/seeed_wio_tracker_L1_eink
|
||||
-D SEEED_WIO_TRACKER_L1
|
||||
-D BUTTON_PIN=D13
|
||||
board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
|
||||
build_src_filter =
|
||||
${nrf52_base.build_src_filter}
|
||||
${inkhud.build_src_filter}
|
||||
+<../variants/nrf52840/seeed_wio_tracker_L1_eink>
|
||||
lib_deps =
|
||||
${inkhud.lib_deps} ; Before base libs_deps, so we use ZinggJM/GFXRoot instead of AdafruitGFX (saves space)
|
||||
${nrf52840_base.lib_deps}
|
||||
debug_tool = jlink
|
@ -33,17 +33,10 @@
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// Button Configuration
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
#undef BUTTON_PIN
|
||||
#endif
|
||||
|
||||
#define BUTTON_PIN D13 // This is the Program Button
|
||||
#define CANCEL_BUTTON_PIN D13 // This is the Program Button
|
||||
// #define BUTTON_NEED_PULLUP 1
|
||||
#define BUTTON_ACTIVE_LOW true
|
||||
#define BUTTON_ACTIVE_PULLUP false
|
||||
|
||||
#define BUTTON_PIN_TOUCH 13 // Touch button
|
||||
#define CANCEL_BUTTON_ACTIVE_LOW true
|
||||
#define CANCEL_BUTTON_ACTIVE_PULLUP false
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// Digital Pin Mapping (D0-D10)
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
@ -116,7 +109,7 @@ static const uint8_t SCL = PIN_WIRE_SCL;
|
||||
#define PIN_EINK_SCLK 31
|
||||
#define PIN_EINK_MOSI 33
|
||||
#define PIN_EINK_EN 14 // unused
|
||||
#define PIN_SPI1_MISO 15 // unused
|
||||
#define PIN_SPI1_MISO -1 // 15 unused
|
||||
#define PIN_SPI1_MOSI PIN_EINK_MOSI
|
||||
#define PIN_SPI1_SCK PIN_EINK_SCLK
|
||||
|
||||
@ -175,7 +168,17 @@ static const uint8_t SCL = PIN_WIRE_SCL;
|
||||
// joystick
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// trackball
|
||||
#define HAS_TRACKBALL 1
|
||||
#define TB_UP 25
|
||||
#define TB_DOWN 26
|
||||
#define TB_LEFT 27
|
||||
#define TB_RIGHT 28
|
||||
#define TB_PRESS 29
|
||||
#define TB_DIRECTION FALLING
|
||||
|
||||
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
||||
#define CANNED_MESSAGE_ADD_CONFIRMATION 1
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// Compatibility Definitions
|
||||
|
@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 2
|
||||
minor = 7
|
||||
build = 6
|
||||
build = 7
|
||||
|
Loading…
Reference in New Issue
Block a user