Compare commits

..

1 Commits

Author SHA1 Message Date
Jonathan Bennett
d508de9568 Add Store and Forward++ module 2025-12-20 13:05:13 -06:00
67 changed files with 1441 additions and 1737 deletions

View File

@@ -21,7 +21,7 @@ jobs:
# Use 'arctastic' self-hosted runner pool when building in the main repo
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
outputs:
artifact-id: ${{ steps.upload-firmware.outputs.artifact-id }}
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- uses: actions/checkout@v6
with:
@@ -71,7 +71,7 @@ jobs:
- name: Store binaries as an artifact
uses: actions/upload-artifact@v6
id: upload-firmware
id: upload
with:
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
overwrite: true
@@ -84,12 +84,3 @@ jobs:
release/*.zip
release/device-*.sh
release/device-*.bat
- name: Store manifests as an artifact
uses: actions/upload-artifact@v6
id: upload-manifest
with:
name: manifest-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
overwrite: true
path: |
release/*.mt.json

View File

@@ -233,40 +233,6 @@ jobs:
description: "Download firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}
shame:
if: github.repository == 'meshtastic/firmware'
continue-on-error: true
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v6
with:
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
fetch-depth: 0
- name: Download the current manifests
uses: actions/download-artifact@v7
with:
path: ./manifests-new/
pattern: manifest-*
merge-multiple: true
- name: Upload combined manifests for later commit and global stats crunching.
uses: actions/upload-artifact@v6
id: upload-manifest
with:
name: manifests-all
overwrite: true
path: |
manifests-new/*.mt.json
- name: Find the merge base
run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV
env:
base: ${{ github.base_ref }}
head: ${{ github.head_ref }}
- name: Download the old manifests
run: gh run download -R ${{ github.repository }} --commit ${{ env.MERGE_BASE }} --name manifests-all --dir manifest-old/
- name: Do scan and post comment
run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
release-artifacts:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}

View File

@@ -1,101 +0,0 @@
import sys
import os
import json
from github import Github
def parseFile(path):
with open(path, "r") as f:
data = json.loads(f)
for file in data["files"]:
if file["name"].endswith(".bin"):
return file["name"], file["bytes"]
if len(sys.argv) != 4:
print(f"expected usage: {sys.argv[0]} <PR number> <path to old-manifests> <path to new-manifests>")
sys.exit(1)
pr_number = int(sys.argv[1])
token = os.getenv("GITHUB_TOKEN")
if not token:
raise EnvironmentError("GITHUB_TOKEN not found in environment.")
repo_name = os.getenv("GITHUB_REPOSITORY") # "owner/repo"
if not repo_name:
raise EnvironmentError("GITHUB_REPOSITORY not found in environment.")
oldFiles = sys.argv[2]
old = set(os.path.join(oldFiles, f) for f in os.listdir(oldFiles) if os.path.isfile(f))
newFiles = sys.argv[3]
new = set(os.path.join(newFiles, f) for f in os.listdir(newFiles) if os.path.isfile(f))
startMarkdown = "# Target Size Changes\n\n"
markdown = ""
newlyIntroduced = new - old
if len(newlyIntroduced) > 0:
markdown += "## Newly Introduced Targets\n\n"
# create a table
markdown += "| File | Size |\n"
markdown += "| ---- | ---- |\n"
for f in newlyIntroduced:
name, size = parseFile(f)
markdown += f"| `{name}` | {size}b |\n"
removed = old - new
if len(removed) > 0:
markdown += "\n## Removed Targets\n\n"
# create a table
markdown += "| File | Size |\n"
markdown += "| ---- | ---- |\n"
for f in removed:
name, size = parseFile(f)
markdown += f"| `{name}` | {size}b |\n"
both = old & new
degradations = []
improvements = []
for f in both:
oldName, oldSize = parseFile(f)
_, newSize = parseFile(f)
if oldSize != newSize:
if newSize < oldSize:
improvements.append((oldName, oldSize, newSize))
else:
degradations.append((oldName, oldSize, newSize))
if len(degradations) > 0:
markdown += "\n## Degradation\n\n"
# create a table
markdown += "| File | Difference | Old Size | New Size |\n"
markdown += "| ---- | ---------- | -------- | -------- |\n"
for oldName, oldSize, newSize in degradations:
markdown += f"| `{oldName}` | **{oldSize - newSize}b** | {oldSize}b | {newSize}b |\n"
if len(improvements) > 0:
markdown += "\n## Improvement\n\n"
# create a table
markdown += "| File | Difference | Old Size | New Size |\n"
markdown += "| ---- | ---------- | -------- | -------- |\n"
for oldName, oldSize, newSize in improvements:
markdown += f"| `{oldName}` | **{oldSize - newSize}b** | {oldSize}b | {newSize}b |\n"
if len(markdown) == 0:
markdown = "No changes in target sizes detected."
g = Github(token)
repo = g.get_repo(repo_name)
pr = repo.get_pull(pr_number)
existing_comment = None
for comment in pr.get_issue_comments():
if comment.body.startswith(startMarkdown):
existing_comment = comment
break
final_markdown = startMarkdown + markdown
if existing_comment:
existing_comment.edit(body=final_markdown)
else:
pr.create_issue_comment(body=final_markdown)

View File

@@ -29,8 +29,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#if __has_include("Melopero_RV3028.h")
#include "Melopero_RV3028.h"
#endif
#if __has_include("SensorRtcHelper.hpp")
#include "SensorRtcHelper.hpp"
#if __has_include("pcf8563.h")
#include "pcf8563.h"
#endif
/* Offer chance for variant-specific defines */

View File

@@ -25,8 +25,8 @@ ScanI2C::FoundDevice ScanI2C::firstScreen() const
ScanI2C::FoundDevice ScanI2C::firstRTC() const
{
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_PCF85063, RTC_RX8130CE};
return firstOfOrNONE(4, types);
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_RX8130CE};
return firstOfOrNONE(3, types);
}
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const

View File

@@ -14,7 +14,6 @@ class ScanI2C
SCREEN_ST7567,
RTC_RV3028,
RTC_PCF8563,
RTC_PCF85063,
RTC_RX8130CE,
CARDKB,
TDECKKB,

View File

@@ -202,10 +202,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(RX8130CE_RTC, RTC_RX8130CE, "RX8130CE", (uint8_t)addr.address)
#endif
#ifdef PCF85063_RTC
SCAN_SIMPLE_CASE(PCF85063_RTC, RTC_PCF85063, "PCF85063", (uint8_t)addr.address)
#endif
case CARDKB_ADDR:
// Do we have the RAK14006 instead?
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x04), 1);

View File

@@ -66,26 +66,26 @@ RTCSetResult readFromRTC()
currentQuality = RTCQualityDevice;
}
return RTCSetResultSuccess;
} else {
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
}
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
#if defined(PCF8563_RTC)
#elif defined(PCF8563_RTC)
if (rtc_found.address == PCF8563_RTC) {
#elif defined(PCF85063_RTC)
if (rtc_found.address == PCF85063_RTC) {
#endif
uint32_t now = millis();
SensorRtcHelper rtc;
PCF8563_Class rtc;
#if WIRE_INTERFACES_COUNT == 2
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
#else
rtc.begin(Wire);
rtc.begin();
#endif
RTC_DateTime datetime = rtc.getDateTime();
tm t = datetime.toUnixTime();
auto tc = rtc.getDateTime();
tm t;
t.tm_year = tc.year - 1900;
t.tm_mon = tc.month - 1;
t.tm_mday = tc.day;
t.tm_hour = tc.hour;
t.tm_min = tc.minute;
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
@@ -100,16 +100,14 @@ RTCSetResult readFromRTC()
}
#endif
LOG_DEBUG("Read RTC time from %s getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t.tm_year + 1900,
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
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);
if (currentQuality == RTCQualityNone) {
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
currentQuality = RTCQualityDevice;
}
return RTCSetResultSuccess;
} else {
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
}
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {
@@ -234,28 +232,20 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
LOG_DEBUG("RV3028_RTC setTime %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);
} else {
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
}
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
#if defined(PCF8563_RTC)
#elif defined(PCF8563_RTC)
if (rtc_found.address == PCF8563_RTC) {
#elif defined(PCF85063_RTC)
if (rtc_found.address == PCF85063_RTC) {
#endif
SensorRtcHelper rtc;
PCF8563_Class rtc;
#if WIRE_INTERFACES_COUNT == 2
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
#else
rtc.begin(Wire);
rtc.begin();
#endif
tm *t = gmtime(&tv->tv_sec);
rtc.setDateTime(*t);
LOG_DEBUG("%s setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t->tm_year + 1900, t->tm_mon + 1,
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
} else {
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
rtc.setDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
LOG_DEBUG("PCF8563_RTC setDateTime %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);
}
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {

View File

@@ -88,14 +88,8 @@ class Applet : public GFX
virtual void onForeground() {}
virtual void onBackground() {}
virtual void onShutdown() {}
virtual void onButtonShortPress() {}
virtual void onButtonLongPress() {}
virtual void onExitShort() {}
virtual void onExitLong() {}
virtual void onNavUp() {}
virtual void onNavDown() {}
virtual void onNavLeft() {}
virtual void onNavRight() {}
virtual void onButtonShortPress() {} // (System Applets only)
virtual void onButtonLongPress() {} // (System Applets only)
virtual bool approveNotification(Notification &n); // Allow an applet to veto a notification

View File

@@ -1,205 +0,0 @@
#ifdef MESHTASTIC_INCLUDE_INKHUD
#include "./AlignStickApplet.h"
using namespace NicheGraphics;
InkHUD::AlignStickApplet::AlignStickApplet()
{
if (!settings->joystick.aligned)
bringToForeground();
}
void InkHUD::AlignStickApplet::onRender()
{
setFont(fontMedium);
printAt(0, 0, "Align Joystick:");
setFont(fontSmall);
std::string instructions = "Move joystick in the direction indicated";
printWrapped(0, fontMedium.lineHeight() * 1.5, width(), instructions);
// Size of the region in which the joystick graphic should fit
uint16_t joyXLimit = X(0.8);
uint16_t contentH = fontMedium.lineHeight() * 1.5 + fontSmall.lineHeight() * 1;
if (getTextWidth(instructions) > width())
contentH += fontSmall.lineHeight();
uint16_t freeY = height() - contentH - fontSmall.lineHeight() * 1.2;
uint16_t joyYLimit = freeY * 0.8;
// Use the shorter of the two
uint16_t joyWidth = joyXLimit < joyYLimit ? joyXLimit : joyYLimit;
// Center the joystick graphic
uint16_t centerX = X(0.5);
uint16_t centerY = contentH + freeY * 0.5;
// Draw joystick graphic
drawStick(centerX, centerY, joyWidth);
setFont(fontSmall);
printAt(X(0.5), Y(1.0) - fontSmall.lineHeight() * 0.2, "Long press to skip", CENTER, BOTTOM);
}
// Draw a scalable joystick graphic
void InkHUD::AlignStickApplet::drawStick(uint16_t centerX, uint16_t centerY, uint16_t width)
{
if (width < 9) // too small to draw
return;
else if (width < 40) { // only draw up arrow
uint16_t chamfer = width < 20 ? 1 : 2;
// Draw filled up arrow
drawDirection(centerX, centerY - width / 4, Direction::UP, width, chamfer, BLACK);
} else { // large enough to draw the full thing
uint16_t chamfer = width < 80 ? 1 : 2;
uint16_t stroke = 3; // pixels
uint16_t arrowW = width * 0.22;
uint16_t hollowW = arrowW - stroke * 2;
// Draw center circle
fillCircle((int16_t)centerX, (int16_t)centerY, (int16_t)(width * 0.2), BLACK);
fillCircle((int16_t)centerX, (int16_t)centerY, (int16_t)(width * 0.2) - stroke, WHITE);
// Draw filled up arrow
drawDirection(centerX, centerY - width / 2, Direction::UP, arrowW, chamfer, BLACK);
// Draw down arrow
drawDirection(centerX, centerY + width / 2, Direction::DOWN, arrowW, chamfer, BLACK);
drawDirection(centerX, centerY + width / 2 - stroke, Direction::DOWN, hollowW, 0, WHITE);
// Draw left arrow
drawDirection(centerX - width / 2, centerY, Direction::LEFT, arrowW, chamfer, BLACK);
drawDirection(centerX - width / 2 + stroke, centerY, Direction::LEFT, hollowW, 0, WHITE);
// Draw right arrow
drawDirection(centerX + width / 2, centerY, Direction::RIGHT, arrowW, chamfer, BLACK);
drawDirection(centerX + width / 2 - stroke, centerY, Direction::RIGHT, hollowW, 0, WHITE);
}
}
// Draw a scalable joystick direction arrow
// a right-triangle with blunted tips
/*
_ <--point
^ / \
| / \
size / \
| / \
v |_________|
*/
void InkHUD::AlignStickApplet::drawDirection(uint16_t pointX, uint16_t pointY, Direction direction, uint16_t size,
uint16_t chamfer, Color color)
{
uint16_t chamferW = chamfer * 2 + 1;
uint16_t triangleW = size - chamferW;
// Draw arrow
switch (direction) {
case Direction::UP:
fillRect(pointX - chamfer, pointY, chamferW, triangleW, color);
fillRect(pointX - chamfer - triangleW, pointY + triangleW, chamferW + triangleW * 2, chamferW, color);
fillTriangle(pointX - chamfer, pointY, pointX - chamfer - triangleW, pointY + triangleW, pointX - chamfer,
pointY + triangleW, color);
fillTriangle(pointX + chamfer, pointY, pointX + chamfer + triangleW, pointY + triangleW, pointX + chamfer,
pointY + triangleW, color);
break;
case Direction::DOWN:
fillRect(pointX - chamfer, pointY - triangleW + 1, chamferW, triangleW, color);
fillRect(pointX - chamfer - triangleW, pointY - size + 1, chamferW + triangleW * 2, chamferW, color);
fillTriangle(pointX - chamfer, pointY, pointX - chamfer - triangleW, pointY - triangleW, pointX - chamfer,
pointY - triangleW, color);
fillTriangle(pointX + chamfer, pointY, pointX + chamfer + triangleW, pointY - triangleW, pointX + chamfer,
pointY - triangleW, color);
break;
case Direction::LEFT:
fillRect(pointX, pointY - chamfer, triangleW, chamferW, color);
fillRect(pointX + triangleW, pointY - chamfer - triangleW, chamferW, chamferW + triangleW * 2, color);
fillTriangle(pointX, pointY - chamfer, pointX + triangleW, pointY - chamfer - triangleW, pointX + triangleW,
pointY - chamfer, color);
fillTriangle(pointX, pointY + chamfer, pointX + triangleW, pointY + chamfer + triangleW, pointX + triangleW,
pointY + chamfer, color);
break;
case Direction::RIGHT:
fillRect(pointX - triangleW + 1, pointY - chamfer, triangleW, chamferW, color);
fillRect(pointX - size + 1, pointY - chamfer - triangleW, chamferW, chamferW + triangleW * 2, color);
fillTriangle(pointX, pointY - chamfer, pointX - triangleW, pointY - chamfer - triangleW, pointX - triangleW,
pointY - chamfer, color);
fillTriangle(pointX, pointY + chamfer, pointX - triangleW, pointY + chamfer + triangleW, pointX - triangleW,
pointY + chamfer, color);
break;
}
}
void InkHUD::AlignStickApplet::onForeground()
{
// Prevent most other applets from requesting update, and skip their rendering entirely
// Another system applet with a higher precedence can potentially ignore this
SystemApplet::lockRendering = true;
SystemApplet::lockRequests = true;
handleInput = true; // Intercept the button input for our applet
}
void InkHUD::AlignStickApplet::onBackground()
{
// Allow normal update behavior to resume
SystemApplet::lockRendering = false;
SystemApplet::lockRequests = false;
SystemApplet::handleInput = false;
// Need to force an update, as a polite request wouldn't be honored, seeing how we are now in the background
// Usually, onBackground is followed by another applet's onForeground (which requests update), but not in this case
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::AlignStickApplet::onButtonLongPress()
{
sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::AlignStickApplet::onExitLong()
{
sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::AlignStickApplet::onNavUp()
{
settings->joystick.aligned = true;
sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::AlignStickApplet::onNavDown()
{
inkhud->rotateJoystick(2); // 180 deg
settings->joystick.aligned = true;
sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::AlignStickApplet::onNavLeft()
{
inkhud->rotateJoystick(3); // 270 deg
settings->joystick.aligned = true;
sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::AlignStickApplet::onNavRight()
{
inkhud->rotateJoystick(1); // 90 deg
settings->joystick.aligned = true;
sendToBackground();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
#endif

View File

@@ -1,50 +0,0 @@
#ifdef MESHTASTIC_INCLUDE_INKHUD
/*
System Applet for manually aligning the joystick with the screen
should be run at startup if the joystick is enabled
and not aligned to the screen
*/
#pragma once
#include "configuration.h"
#include "graphics/niche/InkHUD/SystemApplet.h"
namespace NicheGraphics::InkHUD
{
class AlignStickApplet : public SystemApplet
{
public:
AlignStickApplet();
void onRender() override;
void onForeground() override;
void onBackground() override;
void onButtonLongPress() override;
void onExitLong() override;
void onNavUp() override;
void onNavDown() override;
void onNavLeft() override;
void onNavRight() override;
protected:
enum Direction {
UP,
DOWN,
LEFT,
RIGHT,
};
void drawStick(uint16_t centerX, uint16_t centerY, uint16_t width);
void drawDirection(uint16_t pointX, uint16_t pointY, Direction direction, uint16_t size, uint16_t chamfer, Color color);
};
} // namespace NicheGraphics::InkHUD
#endif

View File

@@ -30,7 +30,6 @@ enum MenuAction {
TOGGLE_AUTOSHOW_APPLET,
SET_RECENTS,
ROTATE,
ALIGN_JOYSTICK,
LAYOUT,
TOGGLE_BATTERY_ICON,
TOGGLE_NOTIFICATIONS,

View File

@@ -178,10 +178,6 @@ void InkHUD::MenuApplet::execute(MenuItem item)
inkhud->rotate();
break;
case ALIGN_JOYSTICK:
inkhud->openAlignStick();
break;
case LAYOUT:
// Todo: smarter incrementing of tile count
settings->userTiles.count++;
@@ -291,17 +287,14 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
// items.push_back(MenuItem("Display Off", MenuPage::EXIT)); // TODO
items.push_back(MenuItem("Save & Shut Down", MenuAction::SHUTDOWN));
items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::EXIT;
break;
case SEND:
populateSendPage();
previousPage = MenuPage::ROOT;
break;
case CANNEDMESSAGE_RECIPIENT:
populateRecipientPage();
previousPage = MenuPage::OPTIONS;
break;
case OPTIONS:
@@ -328,8 +321,6 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
if (settings->userTiles.maxCount > 1)
items.push_back(MenuItem("Layout", MenuAction::LAYOUT, MenuPage::OPTIONS));
items.push_back(MenuItem("Rotate", MenuAction::ROTATE, MenuPage::OPTIONS));
if (settings->joystick.enabled)
items.push_back(MenuItem("Align Joystick", MenuAction::ALIGN_JOYSTICK, MenuPage::EXIT));
items.push_back(MenuItem("Notifications", MenuAction::TOGGLE_NOTIFICATIONS, MenuPage::OPTIONS,
&settings->optionalFeatures.notifications));
items.push_back(MenuItem("Battery Icon", MenuAction::TOGGLE_BATTERY_ICON, MenuPage::OPTIONS,
@@ -341,24 +332,20 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
items.push_back(
MenuItem("12-Hour Clock", MenuAction::TOGGLE_12H_CLOCK, MenuPage::OPTIONS, &config.display.use_12h_clock));
items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::ROOT;
break;
case APPLETS:
populateAppletPage();
items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::OPTIONS;
break;
case AUTOSHOW:
populateAutoshowPage();
items.push_back(MenuItem("Exit", MenuPage::EXIT));
previousPage = MenuPage::OPTIONS;
break;
case RECENTS:
populateRecentsPage();
previousPage = MenuPage::OPTIONS;
break;
case EXIT:
@@ -492,21 +479,12 @@ void InkHUD::MenuApplet::onButtonShortPress()
// Push the auto-close timer back
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
if (!settings->joystick.enabled) {
// Move menu cursor to next entry, then update
if (cursorShown)
cursor = (cursor + 1) % items.size();
else
cursorShown = true;
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
} else {
if (cursorShown)
execute(items.at(cursor));
else
showPage(MenuPage::EXIT);
if (!wantsToRender())
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
}
void InkHUD::MenuApplet::onButtonLongPress()
@@ -526,62 +504,6 @@ void InkHUD::MenuApplet::onButtonLongPress()
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
void InkHUD::MenuApplet::onExitShort()
{
// Exit the menu
showPage(MenuPage::EXIT);
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
void InkHUD::MenuApplet::onNavUp()
{
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
// Move menu cursor to previous entry, then update
if (cursor == 0)
cursor = items.size() - 1;
else
cursor--;
if (!cursorShown)
cursorShown = true;
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
void InkHUD::MenuApplet::onNavDown()
{
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
// Move menu cursor to next entry, then update
if (cursorShown)
cursor = (cursor + 1) % items.size();
else
cursorShown = true;
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
void InkHUD::MenuApplet::onNavLeft()
{
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
// Go to the previous menu page
showPage(previousPage);
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
void InkHUD::MenuApplet::onNavRight()
{
OSThread::setIntervalFromNow(MENU_TIMEOUT_SEC * 1000UL);
if (cursorShown)
execute(items.at(cursor));
if (!wantsToRender())
requestUpdate(Drivers::EInk::UpdateTypes::FAST);
}
// Dynamically create MenuItem entries for activating / deactivating Applets, for the "Applet Selection" submenu
void InkHUD::MenuApplet::populateAppletPage()
{

View File

@@ -27,11 +27,6 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
void onBackground() override;
void onButtonShortPress() override;
void onButtonLongPress() override;
void onExitShort() override;
void onNavUp() override;
void onNavDown() override;
void onNavLeft() override;
void onNavRight() override;
void onRender() override;
void show(Tile *t); // Open the menu, onto a user tile
@@ -57,7 +52,6 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
void freeCannedMessageResources(); // Clear MenuApplet's canned message processing data
MenuPage currentPage = MenuPage::ROOT;
MenuPage previousPage = MenuPage::EXIT;
uint8_t cursor = 0; // Which menu item is currently highlighted
bool cursorShown = false; // Is *any* item highlighted? (Root menu: no initial selection)

View File

@@ -153,42 +153,6 @@ void InkHUD::NotificationApplet::onButtonLongPress()
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::NotificationApplet::onExitShort()
{
dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::NotificationApplet::onExitLong()
{
dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::NotificationApplet::onNavUp()
{
dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::NotificationApplet::onNavDown()
{
dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::NotificationApplet::onNavLeft()
{
dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
void InkHUD::NotificationApplet::onNavRight()
{
dismiss();
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
}
// Ask the WindowManager to check whether any displayed applets are already displaying the info from this notification
// Called internally when we first get a "notifiable event", and then again before render,
// in case autoshow swapped which applet was displayed

View File

@@ -31,12 +31,6 @@ class NotificationApplet : public SystemApplet
void onBackground() override;
void onButtonShortPress() override;
void onButtonLongPress() override;
void onExitShort() override;
void onExitLong() override;
void onNavUp() override;
void onNavDown() override;
void onNavLeft() override;
void onNavRight() override;
int onReceiveTextMessage(const meshtastic_MeshPacket *p);

View File

@@ -112,21 +112,12 @@ void InkHUD::TipsApplet::onRender()
setFont(fontSmall);
int16_t cursorY = fontMedium.lineHeight() * 1.5;
if (!settings->joystick.enabled) {
printAt(0, cursorY, "User Button");
cursorY += fontSmall.lineHeight() * 1.2;
printAt(0, cursorY, "- short press: next");
cursorY += fontSmall.lineHeight() * 1.2;
printAt(0, cursorY, "- long press: select / open menu");
} else {
printAt(0, cursorY, "Joystick");
cursorY += fontSmall.lineHeight() * 1.2;
printAt(0, cursorY, "- open menu / select");
cursorY += fontSmall.lineHeight() * 1.5;
printAt(0, cursorY, "Exit Button");
cursorY += fontSmall.lineHeight() * 1.2;
printAt(0, cursorY, "- switch tile / close menu");
}
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
} break;
@@ -136,13 +127,8 @@ void InkHUD::TipsApplet::onRender()
printAt(0, 0, "Tip: Rotation");
setFont(fontSmall);
if (!settings->joystick.enabled) {
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
"To rotate the display, use the InkHUD menu. Long-press the user button > Options > Rotate.");
} else {
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
"To rotate the display, use the InkHUD menu. Press the user button > Options > Rotate.");
}
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
@@ -246,10 +232,4 @@ void InkHUD::TipsApplet::onButtonShortPress()
requestUpdate();
}
// Functions the same as the user button in this instance
void InkHUD::TipsApplet::onExitShort()
{
onButtonShortPress();
}
#endif

View File

@@ -36,7 +36,6 @@ class TipsApplet : public SystemApplet
void onForeground() override;
void onBackground() override;
void onButtonShortPress() override;
void onExitShort() override;
protected:
void renderWelcome(); // Very first screen of tutorial

View File

@@ -55,15 +55,10 @@ void InkHUD::Events::onButtonShort()
}
// If no system applet is handling input, default behavior instead is to cycle applets
// or open menu if joystick is enabled
if (consumer) {
if (consumer)
consumer->onButtonShortPress();
} else if (!dismissedExt) { // Don't change applet if this button press silenced the external notification module
if (!settings->joystick.enabled)
else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
inkhud->nextApplet();
else
inkhud->openMenu();
}
}
void InkHUD::Events::onButtonLong()
@@ -88,156 +83,6 @@ void InkHUD::Events::onButtonLong()
inkhud->openMenu();
}
void InkHUD::Events::onExitShort()
{
if (settings->joystick.enabled) {
// Audio feedback (via buzzer)
// Short tone
playChirp();
// Cancel any beeping, buzzing, blinking
// Some button handling suppressed if we are dismissing an external notification (see below)
bool dismissedExt = dismissExternalNotification();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleInput) {
consumer = sa;
break;
}
}
// If no system applet is handling input, default behavior instead is change tiles
if (consumer)
consumer->onExitShort();
else if (!dismissedExt) // Don't change tile if this button press silenced the external notification module
inkhud->nextTile();
}
}
void InkHUD::Events::onExitLong()
{
if (settings->joystick.enabled) {
// Audio feedback (via buzzer)
// Slightly longer than playChirp
playBoop();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleInput) {
consumer = sa;
break;
}
}
if (consumer)
consumer->onExitLong();
}
}
void InkHUD::Events::onNavUp()
{
if (settings->joystick.enabled) {
// Audio feedback (via buzzer)
// Short tone
playChirp();
// Cancel any beeping, buzzing, blinking
// Some button handling suppressed if we are dismissing an external notification (see below)
bool dismissedExt = dismissExternalNotification();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleInput) {
consumer = sa;
break;
}
}
if (consumer)
consumer->onNavUp();
}
}
void InkHUD::Events::onNavDown()
{
if (settings->joystick.enabled) {
// Audio feedback (via buzzer)
// Short tone
playChirp();
// Cancel any beeping, buzzing, blinking
// Some button handling suppressed if we are dismissing an external notification (see below)
bool dismissedExt = dismissExternalNotification();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleInput) {
consumer = sa;
break;
}
}
if (consumer)
consumer->onNavDown();
}
}
void InkHUD::Events::onNavLeft()
{
if (settings->joystick.enabled) {
// Audio feedback (via buzzer)
// Short tone
playChirp();
// Cancel any beeping, buzzing, blinking
// Some button handling suppressed if we are dismissing an external notification (see below)
bool dismissedExt = dismissExternalNotification();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleInput) {
consumer = sa;
break;
}
}
// If no system applet is handling input, default behavior instead is to cycle applets
if (consumer)
consumer->onNavLeft();
else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
inkhud->prevApplet();
}
}
void InkHUD::Events::onNavRight()
{
if (settings->joystick.enabled) {
// Audio feedback (via buzzer)
// Short tone
playChirp();
// Cancel any beeping, buzzing, blinking
// Some button handling suppressed if we are dismissing an external notification (see below)
bool dismissedExt = dismissExternalNotification();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;
for (SystemApplet *sa : inkhud->systemApplets) {
if (sa->handleInput) {
consumer = sa;
break;
}
}
// If no system applet is handling input, default behavior instead is to cycle applets
if (consumer)
consumer->onNavRight();
else if (!dismissedExt) // Don't change applet if this button press silenced the external notification module
inkhud->nextApplet();
}
}
// Callback for deepSleepObserver
// Returns 0 to signal that we agree to sleep now
int InkHUD::Events::beforeDeepSleep(void *unused)

View File

@@ -29,12 +29,6 @@ class Events
void onButtonShort(); // User button: short press
void onButtonLong(); // User button: long press
void onExitShort(); // Exit button: short press
void onExitLong(); // Exit button: long press
void onNavUp(); // Navigate up
void onNavDown(); // Navigate down
void onNavLeft(); // Navigate left
void onNavRight(); // Navigate right
int beforeDeepSleep(void *unused); // Prepare for shutdown
int beforeReboot(void *unused); // Prepare for reboot

View File

@@ -80,94 +80,6 @@ void InkHUD::InkHUD::longpress()
events->onButtonLong();
}
// Call this when your exit button gets a short press
void InkHUD::InkHUD::exitShort()
{
events->onExitShort();
}
// Call this when your exit button gets a long press
void InkHUD::InkHUD::exitLong()
{
events->onExitLong();
}
// Call this when your joystick gets an up input
void InkHUD::InkHUD::navUp()
{
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
case 1: // 90 deg
events->onNavLeft();
break;
case 2: // 180 deg
events->onNavDown();
break;
case 3: // 270 deg
events->onNavRight();
break;
default: // 0 deg
events->onNavUp();
break;
}
}
// Call this when your joystick gets a down input
void InkHUD::InkHUD::navDown()
{
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
case 1: // 90 deg
events->onNavRight();
break;
case 2: // 180 deg
events->onNavUp();
break;
case 3: // 270 deg
events->onNavLeft();
break;
default: // 0 deg
events->onNavDown();
break;
}
}
// Call this when your joystick gets a left input
void InkHUD::InkHUD::navLeft()
{
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
case 1: // 90 deg
events->onNavDown();
break;
case 2: // 180 deg
events->onNavRight();
break;
case 3: // 270 deg
events->onNavUp();
break;
default: // 0 deg
events->onNavLeft();
break;
}
}
// Call this when your joystick gets a right input
void InkHUD::InkHUD::navRight()
{
switch ((persistence->settings.rotation + persistence->settings.joystick.alignment) % 4) {
case 1: // 90 deg
events->onNavUp();
break;
case 2: // 180 deg
events->onNavLeft();
break;
case 3: // 270 deg
events->onNavDown();
break;
default: // 0 deg
events->onNavRight();
break;
}
}
// Cycle the next user applet to the foreground
// Only activated applets are cycled
// If user has a multi-applet layout, the applets will cycle on the "focused tile"
@@ -176,14 +88,6 @@ void InkHUD::InkHUD::nextApplet()
windowManager->nextApplet();
}
// Cycle the previous user applet to the foreground
// Only activated applets are cycled
// If user has a multi-applet layout, the applets will cycle on the "focused tile"
void InkHUD::InkHUD::prevApplet()
{
windowManager->prevApplet();
}
// Show the menu (on the the focused tile)
// The applet previously displayed there will be restored once the menu closes
void InkHUD::InkHUD::openMenu()
@@ -191,12 +95,6 @@ void InkHUD::InkHUD::openMenu()
windowManager->openMenu();
}
// Bring AlignStick applet to the foreground
void InkHUD::InkHUD::openAlignStick()
{
windowManager->openAlignStick();
}
// In layouts where multiple applets are shown at once, change which tile is focused
// The focused tile in the one which cycles applets on button short press, and displays menu on long press
void InkHUD::InkHUD::nextTile()
@@ -204,26 +102,12 @@ void InkHUD::InkHUD::nextTile()
windowManager->nextTile();
}
// In layouts where multiple applets are shown at once, change which tile is focused
// The focused tile in the one which cycles applets on button short press, and displays menu on long press
void InkHUD::InkHUD::prevTile()
{
windowManager->prevTile();
}
// Rotate the display image by 90 degrees
void InkHUD::InkHUD::rotate()
{
windowManager->rotate();
}
// rotate the joystick in 90 degree increments
void InkHUD::InkHUD::rotateJoystick(uint8_t angle)
{
persistence->settings.joystick.alignment += angle;
persistence->settings.joystick.alignment %= 4;
}
// Show / hide the battery indicator in top-right
void InkHUD::InkHUD::toggleBatteryIcon()
{

View File

@@ -55,25 +55,15 @@ class InkHUD
void shortpress();
void longpress();
void exitShort();
void exitLong();
void navUp();
void navDown();
void navLeft();
void navRight();
// Trigger UI changes
// - called by various InkHUD components
// - suitable(?) for use by aux button, connected in variant nicheGraphics.h
void nextApplet();
void prevApplet();
void openMenu();
void openAlignStick();
void nextTile();
void prevTile();
void rotate();
void rotateJoystick(uint8_t angle = 1); // rotate 90 deg by default
void toggleBatteryIcon();
// Updating the display

View File

@@ -29,7 +29,7 @@ class Persistence
// Used to invalidate old settings, if needed
// Version 0 is reserved for testing, and will always load defaults
static constexpr uint32_t SETTINGS_VERSION = 3;
static constexpr uint32_t SETTINGS_VERSION = 2;
struct Settings {
struct Meta {
@@ -96,19 +96,6 @@ class Persistence
bool safeShutdownSeen = false;
} tips;
// Joystick settings for enabling and aligning to the screen
struct Joystick {
// Modifies the UI for joystick use
bool enabled = false;
// gets set to true when AlignStick applet is completed
bool aligned = false;
// Rotation of the joystick
// Multiples of 90 degrees clockwise
uint8_t alignment = 0;
} joystick;
// Rotation of the display
// Multiples of 90 degrees clockwise
// Most commonly: rotation is 0 when flex connector is oriented below display

View File

@@ -2,7 +2,6 @@
#include "./WindowManager.h"
#include "./Applets/System/AlignStick/AlignStickApplet.h"
#include "./Applets/System/BatteryIcon/BatteryIconApplet.h"
#include "./Applets/System/Logo/LogoApplet.h"
#include "./Applets/System/Menu/MenuApplet.h"
@@ -99,38 +98,6 @@ void InkHUD::WindowManager::nextTile()
userTiles.at(settings->userTiles.focused)->requestHighlight();
}
// Focus on a different tile but decrement index
void InkHUD::WindowManager::prevTile()
{
// Close the menu applet if open
// We don't *really* want to do this, but it simplifies handling *a lot*
MenuApplet *menu = (MenuApplet *)inkhud->getSystemApplet("Menu");
bool menuWasOpen = false;
if (menu->isForeground()) {
menu->sendToBackground();
menuWasOpen = true;
}
// Swap to next tile
if (settings->userTiles.focused == 0)
settings->userTiles.focused = settings->userTiles.count - 1;
else
settings->userTiles.focused--;
// Make sure that we don't get stuck on the placeholder tile
refocusTile();
if (menuWasOpen)
menu->show(userTiles.at(settings->userTiles.focused));
// Ask the tile to draw an indicator showing which tile is now focused
// Requests a render
// We only draw this indicator if the device uses an aux button to switch tiles.
// Assume aux button is used to switch tiles if the "next tile" menu item is hidden
if (!settings->optionalMenuItems.nextTile)
userTiles.at(settings->userTiles.focused)->requestHighlight();
}
// Show the menu (on the the focused tile)
// The applet previously displayed there will be restored once the menu closes
void InkHUD::WindowManager::openMenu()
@@ -139,15 +106,6 @@ void InkHUD::WindowManager::openMenu()
menu->show(userTiles.at(settings->userTiles.focused));
}
// Bring the AlignStick applet to the foreground
void InkHUD::WindowManager::openAlignStick()
{
if (settings->joystick.enabled) {
AlignStickApplet *alignStick = (AlignStickApplet *)inkhud->getSystemApplet("AlignStick");
alignStick->bringToForeground();
}
}
// On the currently focussed tile: cycle to the next available user applet
// Applets available for this must be activated, and not already displayed on another tile
void InkHUD::WindowManager::nextApplet()
@@ -197,59 +155,6 @@ void InkHUD::WindowManager::nextApplet()
inkhud->forceUpdate(EInk::UpdateTypes::FAST); // bringToForeground already requested, but we're manually forcing FAST
}
// On the currently focussed tile: cycle to the previous available user applet
// Applets available for this must be activated, and not already displayed on another tile
void InkHUD::WindowManager::prevApplet()
{
Tile *t = userTiles.at(settings->userTiles.focused);
// Abort if zero applets available
// nullptr means WindowManager::refocusTile determined that there were no available applets
if (!t->getAssignedApplet())
return;
// Find the index of the applet currently shown on the tile
uint8_t appletIndex = -1;
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
if (inkhud->userApplets.at(i) == t->getAssignedApplet()) {
appletIndex = i;
break;
}
}
// Confirm that we did find the applet
assert(appletIndex != (uint8_t)-1);
// Iterate forward through the WindowManager::applets, looking for the previous valid applet
Applet *prevValidApplet = nullptr;
for (uint8_t i = 1; i < inkhud->userApplets.size(); i++) {
uint8_t newAppletIndex = 0;
if (i > appletIndex)
newAppletIndex = inkhud->userApplets.size() + appletIndex - i;
else
newAppletIndex = (appletIndex - i);
Applet *a = inkhud->userApplets.at(newAppletIndex);
// Looking for an applet which is active (enabled by user), but currently in background
if (a->isActive() && !a->isForeground()) {
prevValidApplet = a;
settings->userTiles.displayedUserApplet[settings->userTiles.focused] =
newAppletIndex; // Remember this setting between boots!
break;
}
}
// Confirm that we found another applet
if (!prevValidApplet)
return;
// Hide old applet, show new applet
t->getAssignedApplet()->sendToBackground();
t->assignApplet(prevValidApplet);
prevValidApplet->bringToForeground();
inkhud->forceUpdate(EInk::UpdateTypes::FAST); // bringToForeground already requested, but we're manually forcing FAST
}
// Rotate the display image by 90 degrees
void InkHUD::WindowManager::rotate()
{
@@ -433,8 +338,6 @@ void InkHUD::WindowManager::createSystemApplets()
addSystemApplet("Logo", new LogoApplet, new Tile);
addSystemApplet("Pairing", new PairingApplet, new Tile);
addSystemApplet("Tips", new TipsApplet, new Tile);
if (settings->joystick.enabled)
addSystemApplet("AlignStick", new AlignStickApplet, new Tile);
addSystemApplet("Menu", new MenuApplet, nullptr);
@@ -457,8 +360,6 @@ void InkHUD::WindowManager::placeSystemTiles()
inkhud->getSystemApplet("Logo")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
inkhud->getSystemApplet("Pairing")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
inkhud->getSystemApplet("Tips")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
if (settings->joystick.enabled)
inkhud->getSystemApplet("AlignStick")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
inkhud->getSystemApplet("Notification")->getTile()->setRegion(0, 0, inkhud->width(), 20);

View File

@@ -28,11 +28,8 @@ class WindowManager
// - call these to make stuff change
void nextTile();
void prevTile();
void openMenu();
void openAlignStick();
void nextApplet();
void prevApplet();
void rotate();
void toggleBatteryIcon();

View File

@@ -1,523 +0,0 @@
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
#include "./TwoButtonExtended.h"
#include "NodeDB.h" // For the helper function TwoButtonExtended::getUserButtonPin
#include "PowerFSM.h"
#include "sleep.h"
using namespace NicheGraphics::Inputs;
TwoButtonExtended::TwoButtonExtended() : concurrency::OSThread("TwoButtonExtended")
{
// Don't start polling buttons for release immediately
// Assume they are in a "released" state at boot
OSThread::disable();
#ifdef ARCH_ESP32
// Register callbacks for before and after lightsleep
lsObserver.observe(&notifyLightSleep);
lsEndObserver.observe(&notifyLightSleepEnd);
#endif
// Explicitly initialize these, just to keep cppcheck quiet..
buttons[0] = Button();
buttons[1] = Button();
joystick[Direction::UP] = SimpleButton();
joystick[Direction::DOWN] = SimpleButton();
joystick[Direction::LEFT] = SimpleButton();
joystick[Direction::RIGHT] = SimpleButton();
}
// Get access to (or create) the singleton instance of this class
// Accessible inside the ISRs, even though we maybe shouldn't
TwoButtonExtended *TwoButtonExtended::getInstance()
{
// Instantiate the class the first time this method is called
static TwoButtonExtended *const singletonInstance = new TwoButtonExtended;
return singletonInstance;
}
// Begin receiving button input
// We probably need to do this after sleep, as well as at boot
void TwoButtonExtended::start()
{
if (buttons[0].pin != 0xFF)
attachInterrupt(buttons[0].pin, TwoButtonExtended::isrPrimary, buttons[0].activeLogic == LOW ? FALLING : RISING);
if (buttons[1].pin != 0xFF)
attachInterrupt(buttons[1].pin, TwoButtonExtended::isrSecondary, buttons[1].activeLogic == LOW ? FALLING : RISING);
if (joystick[Direction::UP].pin != 0xFF)
attachInterrupt(joystick[Direction::UP].pin, TwoButtonExtended::isrJoystickUp,
joystickActiveLogic == LOW ? FALLING : RISING);
if (joystick[Direction::DOWN].pin != 0xFF)
attachInterrupt(joystick[Direction::DOWN].pin, TwoButtonExtended::isrJoystickDown,
joystickActiveLogic == LOW ? FALLING : RISING);
if (joystick[Direction::LEFT].pin != 0xFF)
attachInterrupt(joystick[Direction::LEFT].pin, TwoButtonExtended::isrJoystickLeft,
joystickActiveLogic == LOW ? FALLING : RISING);
if (joystick[Direction::RIGHT].pin != 0xFF)
attachInterrupt(joystick[Direction::RIGHT].pin, TwoButtonExtended::isrJoystickRight,
joystickActiveLogic == LOW ? FALLING : RISING);
}
// Stop receiving button input, and run custom sleep code
// Called before device sleeps. This might be power-off, or just ESP32 light sleep
// Some devices will want to attach interrupts here, for the user button to wake from sleep
void TwoButtonExtended::stop()
{
if (buttons[0].pin != 0xFF)
detachInterrupt(buttons[0].pin);
if (buttons[1].pin != 0xFF)
detachInterrupt(buttons[1].pin);
if (joystick[Direction::UP].pin != 0xFF)
detachInterrupt(joystick[Direction::UP].pin);
if (joystick[Direction::DOWN].pin != 0xFF)
detachInterrupt(joystick[Direction::DOWN].pin);
if (joystick[Direction::LEFT].pin != 0xFF)
detachInterrupt(joystick[Direction::LEFT].pin);
if (joystick[Direction::RIGHT].pin != 0xFF)
detachInterrupt(joystick[Direction::RIGHT].pin);
}
// Attempt to resolve a GPIO pin for the user button, honoring userPrefs.jsonc and device settings
// This helper method isn't used by the TwoButtonExtended class itself, it could be moved elsewhere.
// Intention is to pass this value to TwoButtonExtended::setWiring in the setupNicheGraphics method.
uint8_t TwoButtonExtended::getUserButtonPin()
{
uint8_t pin = 0xFF; // Unset
// Use default pin for variant, if no better source
#ifdef BUTTON_PIN
pin = BUTTON_PIN;
#endif
// From userPrefs.jsonc, if set
#ifdef USERPREFS_BUTTON_PIN
pin = USERPREFS_BUTTON_PIN;
#endif
// From user's override in device settings, if set
if (config.device.button_gpio)
pin = config.device.button_gpio;
return pin;
}
// Configures the wiring and logic of either button
// Called when outlining your NicheGraphics implementation, in variant/nicheGraphics.cpp
void TwoButtonExtended::setWiring(uint8_t whichButton, uint8_t pin, bool internalPullup)
{
// Prevent the same GPIO being assigned to multiple buttons
// Allows an edge case when the user remaps hardware buttons using device settings, due to a broken user button
for (uint8_t i = 0; i < whichButton; i++) {
if (buttons[i].pin == pin) {
LOG_WARN("Attempted reuse of GPIO %d. Ignoring assignment whichButton=%d", pin, whichButton);
return;
}
}
assert(whichButton < 2);
buttons[whichButton].pin = pin;
buttons[whichButton].activeLogic = LOW;
pinMode(buttons[whichButton].pin, internalPullup ? INPUT_PULLUP : INPUT);
}
// Configures the wiring and logic of the joystick buttons
// Called when outlining your NicheGraphics implementation, in variant/nicheGraphics.cpp
void TwoButtonExtended::setJoystickWiring(uint8_t uPin, uint8_t dPin, uint8_t lPin, uint8_t rPin, bool internalPullup)
{
if (joystick[Direction::UP].pin == uPin || joystick[Direction::DOWN].pin == dPin || joystick[Direction::LEFT].pin == lPin ||
joystick[Direction::RIGHT].pin == rPin) {
LOG_WARN("Attempted reuse of Joystick GPIO. Ignoring assignment");
return;
}
joystick[Direction::UP].pin = uPin;
joystick[Direction::DOWN].pin = dPin;
joystick[Direction::LEFT].pin = lPin;
joystick[Direction::RIGHT].pin = rPin;
joystickActiveLogic = LOW;
pinMode(joystick[Direction::UP].pin, internalPullup ? INPUT_PULLUP : INPUT);
pinMode(joystick[Direction::DOWN].pin, internalPullup ? INPUT_PULLUP : INPUT);
pinMode(joystick[Direction::LEFT].pin, internalPullup ? INPUT_PULLUP : INPUT);
pinMode(joystick[Direction::RIGHT].pin, internalPullup ? INPUT_PULLUP : INPUT);
}
void TwoButtonExtended::setTiming(uint8_t whichButton, uint32_t debounceMs, uint32_t longpressMs)
{
assert(whichButton < 2);
buttons[whichButton].debounceLength = debounceMs;
buttons[whichButton].longpressLength = longpressMs;
}
void TwoButtonExtended::setJoystickDebounce(uint32_t debounceMs)
{
joystickDebounceLength = debounceMs;
}
// Set what should happen when a button becomes pressed
// Use this to implement a "while held" behavior
void TwoButtonExtended::setHandlerDown(uint8_t whichButton, Callback onDown)
{
assert(whichButton < 2);
buttons[whichButton].onDown = onDown;
}
// Set what should happen when a button becomes unpressed
// Use this to implement a "While held" behavior
void TwoButtonExtended::setHandlerUp(uint8_t whichButton, Callback onUp)
{
assert(whichButton < 2);
buttons[whichButton].onUp = onUp;
}
// Set what should happen when a "short press" event has occurred
void TwoButtonExtended::setHandlerShortPress(uint8_t whichButton, Callback onPress)
{
assert(whichButton < 2);
buttons[whichButton].onPress = onPress;
}
// Set what should happen when a "long press" event has fired
// Note: this will occur while the button is still held
void TwoButtonExtended::setHandlerLongPress(uint8_t whichButton, Callback onLongPress)
{
assert(whichButton < 2);
buttons[whichButton].onLongPress = onLongPress;
}
// Set what should happen when a joystick button becomes pressed
// Use this to implement a "while held" behavior
void TwoButtonExtended::setJoystickDownHandlers(Callback uDown, Callback dDown, Callback lDown, Callback rDown)
{
joystick[Direction::UP].onDown = uDown;
joystick[Direction::DOWN].onDown = dDown;
joystick[Direction::LEFT].onDown = lDown;
joystick[Direction::RIGHT].onDown = rDown;
}
// Set what should happen when a joystick button becomes unpressed
// Use this to implement a "while held" behavior
void TwoButtonExtended::setJoystickUpHandlers(Callback uUp, Callback dUp, Callback lUp, Callback rUp)
{
joystick[Direction::UP].onUp = uUp;
joystick[Direction::DOWN].onUp = dUp;
joystick[Direction::LEFT].onUp = lUp;
joystick[Direction::RIGHT].onUp = rUp;
}
// Set what should happen when a "press" event has fired
// Note: this will occur while the joystick button is still held
void TwoButtonExtended::setJoystickPressHandlers(Callback uPress, Callback dPress, Callback lPress, Callback rPress)
{
joystick[Direction::UP].onPress = uPress;
joystick[Direction::DOWN].onPress = dPress;
joystick[Direction::LEFT].onPress = lPress;
joystick[Direction::RIGHT].onPress = rPress;
}
// Handle the start of a press to the primary button
// Wakes our button thread
void TwoButtonExtended::isrPrimary()
{
static volatile bool isrRunning = false;
if (!isrRunning) {
isrRunning = true;
TwoButtonExtended *b = TwoButtonExtended::getInstance();
if (b->buttons[0].state == State::REST) {
b->buttons[0].state = State::IRQ;
b->buttons[0].irqAtMillis = millis();
b->startThread();
}
isrRunning = false;
}
}
// Handle the start of a press to the secondary button
// Wakes our button thread
void TwoButtonExtended::isrSecondary()
{
static volatile bool isrRunning = false;
if (!isrRunning) {
isrRunning = true;
TwoButtonExtended *b = TwoButtonExtended::getInstance();
if (b->buttons[1].state == State::REST) {
b->buttons[1].state = State::IRQ;
b->buttons[1].irqAtMillis = millis();
b->startThread();
}
isrRunning = false;
}
}
// Handle the start of a press to the joystick buttons
// Also wakes our button thread
void TwoButtonExtended::isrJoystickUp()
{
static volatile bool isrRunning = false;
if (!isrRunning) {
isrRunning = true;
TwoButtonExtended *b = TwoButtonExtended::getInstance();
if (b->joystick[Direction::UP].state == State::REST) {
b->joystick[Direction::UP].state = State::IRQ;
b->joystick[Direction::UP].irqAtMillis = millis();
b->startThread();
}
isrRunning = false;
}
}
void TwoButtonExtended::isrJoystickDown()
{
static volatile bool isrRunning = false;
if (!isrRunning) {
isrRunning = true;
TwoButtonExtended *b = TwoButtonExtended::getInstance();
if (b->joystick[Direction::DOWN].state == State::REST) {
b->joystick[Direction::DOWN].state = State::IRQ;
b->joystick[Direction::DOWN].irqAtMillis = millis();
b->startThread();
}
isrRunning = false;
}
}
void TwoButtonExtended::isrJoystickLeft()
{
static volatile bool isrRunning = false;
if (!isrRunning) {
isrRunning = true;
TwoButtonExtended *b = TwoButtonExtended::getInstance();
if (b->joystick[Direction::LEFT].state == State::REST) {
b->joystick[Direction::LEFT].state = State::IRQ;
b->joystick[Direction::LEFT].irqAtMillis = millis();
b->startThread();
}
isrRunning = false;
}
}
void TwoButtonExtended::isrJoystickRight()
{
static volatile bool isrRunning = false;
if (!isrRunning) {
isrRunning = true;
TwoButtonExtended *b = TwoButtonExtended::getInstance();
if (b->joystick[Direction::RIGHT].state == State::REST) {
b->joystick[Direction::RIGHT].state = State::IRQ;
b->joystick[Direction::RIGHT].irqAtMillis = millis();
b->startThread();
}
isrRunning = false;
}
}
// Concise method to start our button thread
// Follows an ISR, listening for button release
void TwoButtonExtended::startThread()
{
if (!OSThread::enabled) {
OSThread::setInterval(10);
OSThread::enabled = true;
}
}
// Concise method to stop our button thread
// Called when we no longer need to poll for button release
void TwoButtonExtended::stopThread()
{
if (OSThread::enabled) {
OSThread::disable();
}
// Reset both buttons manually
// Just in case an IRQ fires during the process of resetting the system
// Can occur with super rapid presses?
buttons[0].state = REST;
buttons[1].state = REST;
joystick[Direction::UP].state = REST;
joystick[Direction::DOWN].state = REST;
joystick[Direction::LEFT].state = REST;
joystick[Direction::RIGHT].state = REST;
}
// Our button thread
// Started by an IRQ, on either button
// Polls for button releases
// Stops when both buttons released
int32_t TwoButtonExtended::runOnce()
{
constexpr uint8_t BUTTON_COUNT = sizeof(buttons) / sizeof(Button);
constexpr uint8_t JOYSTICK_COUNT = sizeof(joystick) / sizeof(SimpleButton);
// Allow either button to request that our thread should continue polling
bool awaitingRelease = false;
// Check both primary and secondary buttons
for (uint8_t i = 0; i < BUTTON_COUNT; i++) {
switch (buttons[i].state) {
// No action: button has not been pressed
case REST:
break;
// New press detected by interrupt
case IRQ:
powerFSM.trigger(EVENT_PRESS); // Tell PowerFSM that press occurred (resets sleep timer)
buttons[i].onDown(); // Run callback: press has begun (possible hold behavior)
buttons[i].state = State::POLLING_UNFIRED; // Mark that button-down has been handled
awaitingRelease = true; // Mark that polling-for-release should continue
break;
// An existing press continues
// Not held long enough to register as longpress
case POLLING_UNFIRED: {
uint32_t length = millis() - buttons[i].irqAtMillis;
// If button released since last thread tick,
if (digitalRead(buttons[i].pin) != buttons[i].activeLogic) {
buttons[i].onUp(); // Run callback: press has ended (possible release of a hold)
buttons[i].state = State::REST; // Mark that the button has reset
if (length > buttons[i].debounceLength && length < buttons[i].longpressLength) // If too short for longpress,
buttons[i].onPress(); // Run callback: press
}
// If button not yet released
else {
awaitingRelease = true; // Mark that polling-for-release should continue
if (length >= buttons[i].longpressLength) {
// Run callback: long press (once)
// Then continue waiting for release, to rearm
buttons[i].state = State::POLLING_FIRED;
buttons[i].onLongPress();
}
}
break;
}
// Button still held, but duration long enough that longpress event already fired
// Just waiting for release
case POLLING_FIRED:
// Release detected
if (digitalRead(buttons[i].pin) != buttons[i].activeLogic) {
buttons[i].state = State::REST;
buttons[i].onUp(); // Callback: release of hold (in this case: *after* longpress has fired)
}
// Not yet released, keep polling
else
awaitingRelease = true;
break;
}
}
// Check all the joystick directions
for (uint8_t i = 0; i < JOYSTICK_COUNT; i++) {
switch (joystick[i].state) {
// No action: button has not been pressed
case REST:
break;
// New press detected by interrupt
case IRQ:
powerFSM.trigger(EVENT_PRESS); // Tell PowerFSM that press occurred (resets sleep timer)
joystick[i].onDown(); // Run callback: press has begun (possible hold behavior)
joystick[i].state = State::POLLING_UNFIRED; // Mark that button-down has been handled
awaitingRelease = true; // Mark that polling-for-release should continue
break;
// An existing press continues
// Not held long enough to register as press
case POLLING_UNFIRED: {
uint32_t length = millis() - joystick[i].irqAtMillis;
// If button released since last thread tick,
if (digitalRead(joystick[i].pin) != joystickActiveLogic) {
joystick[i].onUp(); // Run callback: press has ended (possible release of a hold)
joystick[i].state = State::REST; // Mark that the button has reset
}
// If button not yet released
else {
awaitingRelease = true; // Mark that polling-for-release should continue
if (length >= joystickDebounceLength) {
// Run callback: long press (once)
// Then continue waiting for release, to rearm
joystick[i].state = State::POLLING_FIRED;
joystick[i].onPress();
}
}
break;
}
// Button still held after press
// Just waiting for release
case POLLING_FIRED:
// Release detected
if (digitalRead(joystick[i].pin) != joystickActiveLogic) {
joystick[i].state = State::REST;
joystick[i].onUp(); // Callback: release of hold
}
// Not yet released, keep polling
else
awaitingRelease = true;
break;
}
}
// If all buttons are now released
// we don't need to waste cpu resources polling
// IRQ will restart this thread when we next need it
if (!awaitingRelease)
stopThread();
// Run this method again, or don't..
// Use whatever behavior was previously set by stopThread() or startThread()
return OSThread::interval;
}
#ifdef ARCH_ESP32
// Detach our class' interrupts before lightsleep
// Allows sleep.cpp to configure its own interrupts, which wake the device on user-button press
int TwoButtonExtended::beforeLightSleep(void *unused)
{
stop();
return 0; // Indicates success
}
// Reconfigure our interrupts
// Our class' interrupts were disconnected during sleep, to allow the user button to wake the device from sleep
int TwoButtonExtended::afterLightSleep(esp_sleep_wakeup_cause_t cause)
{
start();
// Manually trigger the button-down ISR
// - during light sleep, our ISR is disabled
// - if light sleep ends by button press, pretend our own ISR caught it
// - need to manually confirm by reading pin ourselves, to avoid occasional false positives
// (false positive only when using internal pullup resistors?)
if (cause == ESP_SLEEP_WAKEUP_GPIO && digitalRead(buttons[0].pin) == buttons[0].activeLogic)
isrPrimary();
return 0; // Indicates success
}
#endif
#endif

View File

@@ -1,136 +0,0 @@
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
/*
Re-usable NicheGraphics input source
Short and Long press for up to two buttons
Interrupt driven
*/
/*
This expansion adds support for four more buttons
These buttons are single-action only, no long press
Interrupt driven
*/
#pragma once
#include "configuration.h"
#include "assert.h"
#include "functional"
#ifdef ARCH_ESP32
#include "esp_sleep.h" // For light-sleep handling
#endif
#include "Observer.h"
namespace NicheGraphics::Inputs
{
class TwoButtonExtended : protected concurrency::OSThread
{
public:
typedef std::function<void()> Callback;
static uint8_t getUserButtonPin(); // Resolve the GPIO, considering the various possible source of definition
static TwoButtonExtended *getInstance(); // Create or get the singleton instance
void start(); // Start handling button input
void stop(); // Stop handling button input (disconnect ISRs for sleep)
void setWiring(uint8_t whichButton, uint8_t pin, bool internalPullup = false);
void setJoystickWiring(uint8_t uPin, uint8_t dPin, uint8_t lPin, uint8_t rPin, bool internalPullup = false);
void setTiming(uint8_t whichButton, uint32_t debounceMs, uint32_t longpressMs);
void setJoystickDebounce(uint32_t debounceMs);
void setHandlerDown(uint8_t whichButton, Callback onDown);
void setHandlerUp(uint8_t whichButton, Callback onUp);
void setHandlerShortPress(uint8_t whichButton, Callback onShortPress);
void setHandlerLongPress(uint8_t whichButton, Callback onLongPress);
void setJoystickDownHandlers(Callback uDown, Callback dDown, Callback ldown, Callback rDown);
void setJoystickUpHandlers(Callback uUp, Callback dUp, Callback lUp, Callback rUp);
void setJoystickPressHandlers(Callback uPress, Callback dPress, Callback lPress, Callback rPress);
// Disconnect and reconnect interrupts for light sleep
#ifdef ARCH_ESP32
int beforeLightSleep(void *unused);
int afterLightSleep(esp_sleep_wakeup_cause_t cause);
#endif
private:
// Internal state of a specific button
enum State {
REST, // Up, no activity
IRQ, // Down detected, not yet handled
POLLING_UNFIRED, // Down handled, polling for release
POLLING_FIRED, // Longpress fired, button still held
};
// Joystick Directions
enum Direction { UP = 0, DOWN, LEFT, RIGHT };
// Data used for direction (single-action) buttons
class SimpleButton
{
public:
// Per-button config
uint8_t pin = 0xFF; // 0xFF: unset
volatile State state = State::REST; // Internal state
volatile uint32_t irqAtMillis; // millis() when button went down
// Per-button event callbacks
static void noop(){};
std::function<void()> onDown = noop;
std::function<void()> onUp = noop;
std::function<void()> onPress = noop;
};
// Data used for double-action buttons
class Button : public SimpleButton
{
public:
// Per-button extended config
bool activeLogic = LOW; // Active LOW by default.
uint32_t debounceLength = 50; // Minimum length for shortpress in ms
uint32_t longpressLength = 500; // Time until longpress in ms
// Per-button event callbacks
std::function<void()> onLongPress = noop;
};
#ifdef ARCH_ESP32
// Get notified when lightsleep begins and ends
CallbackObserver<TwoButtonExtended, void *> lsObserver =
CallbackObserver<TwoButtonExtended, void *>(this, &TwoButtonExtended::beforeLightSleep);
CallbackObserver<TwoButtonExtended, esp_sleep_wakeup_cause_t> lsEndObserver =
CallbackObserver<TwoButtonExtended, esp_sleep_wakeup_cause_t>(this, &TwoButtonExtended::afterLightSleep);
#endif
int32_t runOnce() override; // Timer method. Polls for button release
void startThread(); // Start polling for release
void stopThread(); // Stop polling for release
static void isrPrimary(); // User Button ISR
static void isrSecondary(); // optional aux button or joystick center
static void isrJoystickUp();
static void isrJoystickDown();
static void isrJoystickLeft();
static void isrJoystickRight();
TwoButtonExtended(); // Constructor made private: force use of Button::instance()
// Info about both buttons
Button buttons[2];
bool joystickActiveLogic = LOW; // Active LOW by default
uint32_t joystickDebounceLength = 50; // time until press in ms
SimpleButton joystick[4];
};
}; // namespace NicheGraphics::Inputs
#endif

View File

@@ -61,6 +61,7 @@
#if ARCH_PORTDUINO
#include "input/LinuxInputImpl.h"
#include "input/SeesawRotary.h"
#include "modules/Native/StoreForwardPlusPlus.h"
#include "modules/Telemetry/HostMetrics.h"
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h"
@@ -243,6 +244,7 @@ void setupModules()
#endif
#if ARCH_PORTDUINO
new HostMetricsModule();
new StoreForwardPlusPlusModule();
#endif
#if HAS_TELEMETRY
new DeviceTelemetryModule();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,212 @@
#pragma once
#include "Channels.h"
#include "ProtobufModule.h"
#include "Router.h"
#include "SinglePortModule.h"
#include "sqlite3.h"
/**
* Store and forward ++ module
* There's an obvious need for a store-and-forward mechanism in Meshtastic.
* This module takes heavy inspiration from Git, building a chain of messages that can be synced between nodes.
* Each message is hashed, and the chain is built by hashing the previous commit hash and the current message hash.
* Nodes can request missing messages by requesting the next message after a given commit hash.
*
* The current focus is text messages, limited to the primary channel.
*
* Each chain is identified by a root hash, which is derived from the channelHash, the local nodenum, and the timestamp when
* created.
*
* Each message is also given a message hash, derived from the encrypted payload, the to, from, id.
* Notably not the timestamp, as we want these to match across nodes, even if the timestamps differ.
*
* The authoritative node for the chain will generate a commit hash for each message when adding it to the chain.
* The first message's commit hash is derived from the root hash and the message hash.
* Subsequent messages' commit hashes are derived from the previous commit hash and the current message hash.
* This allows a node to see only the last commit hash, and confirm it hasn't missed any messages.
*
* Nodes can request the next message in the chain by sending a LINK_REQUEST message with the root hash and the last known commit
* hash. Any node that has the next message can respond with a LINK_PROVIDE message containing the next message.
*
* When a satellite node sees a new text message, it stores it in a scratch database.
* These messages are periodically offered to the authoritative node for inclusion in the chain.
*
* The LINK_PROVIDE message does double-duty, sending both on-chain and off-chain messages.
* The differentiator is whether the commit hash is set or left empty.
*
* When a satellite node receives a canonical link message, it checks if it has the message in scratch.
* And evicts it when adding it to the canonical chain.
*
* This approach allows a node to know whether it has seen a given message before, or if it is new coming via SFPP.
* If new, and the timestamp is within the rebroadcast timeout, it will process that message as if it were just received from the
* mesh, allowing it to be decrypted, shown to the user, and rebroadcast.
*/
class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForwardPlusPlus>, private concurrency::OSThread
{
struct link_object {
uint32_t to;
uint32_t from;
uint32_t id;
uint32_t rx_time = 0;
ChannelHash channel_hash;
uint8_t encrypted_bytes[256] = {0};
size_t encrypted_len;
uint8_t message_hash[32] = {0};
size_t message_hash_len = 0;
uint8_t root_hash[32] = {0};
size_t root_hash_len = 0;
uint8_t commit_hash[32] = {0};
size_t commit_hash_len = 0;
uint32_t counter = 0;
std::string payload;
bool validObject = true; // set this false when a chain calulation fails, etc.
};
public:
/** Constructor
*
*/
StoreForwardPlusPlusModule();
/*
-Override the wantPacket method.
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP:
return true;
default:
return false;
}
}
protected:
/** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
it
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_StoreForwardPlusPlus *t) override;
virtual int32_t runOnce() override;
private:
sqlite3 *ppDb;
sqlite3_stmt *chain_insert_stmt;
sqlite3_stmt *scratch_insert_stmt;
sqlite3_stmt *checkDup;
sqlite3_stmt *checkScratch;
sqlite3_stmt *removeScratch;
sqlite3_stmt *updatePayloadStmt;
sqlite3_stmt *getPayloadFromScratchStmt;
sqlite3_stmt *fromScratchStmt;
sqlite3_stmt *fromScratchByHashStmt;
sqlite3_stmt *getNextHashStmt;
sqlite3_stmt *getChainEndStmt;
sqlite3_stmt *getLinkStmt;
sqlite3_stmt *getHashFromRootStmt;
sqlite3_stmt *addRootToMappingsStmt;
sqlite3_stmt *getRootFromChannelHashStmt;
sqlite3_stmt *getFullRootHashStmt;
sqlite3_stmt *setChainCountStmt;
sqlite3_stmt *getChainCountStmt;
// For a given Meshtastic ChannelHash, fills the root_hash buffer with a 32-byte root hash
// returns true if the root hash was found
bool getRootFromChannelHash(ChannelHash, uint8_t *);
// For a given root hash, returns the ChannelHash
// can handle partial root hashes
ChannelHash getChannelHashFromRoot(uint8_t *_root_hash, size_t);
// given a root hash and commit hash, returns the next commit hash in the chain
// can handle partial root and commit hashes, always fills the buffer with 32 bytes
// returns true if a next hash was found
bool getNextHash(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
// For a given Meshtastic ChannelHash, fills the root_hash buffer with a 32-byte root hash
// but this function will add the root hash if it is not already present
// returns true if the hash is new
bool getOrAddRootFromChannelHash(ChannelHash, uint8_t *);
// adds the ChannelHash and root_hash to the mappings table
void addRootToMappings(ChannelHash, uint8_t *);
// gets the tip of the chain for the given root hash
link_object getChainEndLinkObject(uint8_t *, size_t);
// requests the next message in the chain from the mesh network
// Sends a LINK_REQUEST message
void requestNextMessage(uint8_t *, size_t, uint8_t *, size_t);
// sends a LINK_PROVIDE message broadcasting the given link object
void broadcastLink(uint8_t *, size_t);
// sends a LINK_PROVIDE message broadcasting the given link object from scratch message store
bool sendFromScratch(uint8_t);
// Adds the given link object to the canonical chain database
bool addToChain(link_object &);
// Adds an incoming text message to the scratch database
bool addToScratch(link_object &);
// sends a CANON_ANNOUNCE message, specifying the given root and commit hashes
void canonAnnounce(uint8_t *, uint8_t *, uint8_t *, uint32_t);
// checks if the message hash is present in the canonical chain database
bool isInDB(uint8_t *, size_t);
// checks if the message hash is present in the scratch database
bool isInScratch(uint8_t *, size_t);
// retrieves a link object from the scratch database
link_object getFromScratch(uint8_t *, size_t);
// removes a link object from the scratch database
void removeFromScratch(uint8_t *, size_t);
// fills the payload section with the decrypted data for the given message hash
// probably not needed for production, but useful for testing
void updatePayload(uint8_t *, size_t, std::string);
// Takes the decrypted MeshPacket and the encrypted packet copy, and builds a link_object
// Generates a message hash, but does not set the commit hash
link_object ingestTextPacket(const meshtastic_MeshPacket &, const meshtastic_MeshPacket *);
// ingests a LINK_PROVIDE message and builds a link_object
// confirms the root hash and commit hash
link_object ingestLinkMessage(meshtastic_StoreForwardPlusPlus *);
// retrieves a link object from the canonical chain database given a message hash
link_object getLink(uint8_t *, size_t);
// puts the encrypted payload back into the queue as if it were just received
void rebroadcastLinkObject(link_object &);
// Check if an incoming link object's commit hash matches the calculated commit hash
bool checkCommitHash(link_object &lo, uint8_t *commit_hash_bytes, size_t hash_len);
// given a partial root hash, looks up the full 32-byte root hash
// returns true if found
bool lookUpFullRootHash(uint8_t *partial_root_hash, size_t partial_root_hash_len, uint8_t *full_root_hash);
// update the mappings table to set the chain count for the given root hash
void setChainCount(uint8_t *, size_t, uint32_t);
// query the mappings table for the chain count for the given root hash
uint32_t getChainCount(uint8_t *, size_t);
// Track if we have a scheduled runOnce pending
// useful to not accudentally delay a scheduled runOnce
bool pendingRun = false;
// Once we have multiple chain types, we can extend this
enum chain_types {
channel_chain = 0,
};
uint32_t rebroadcastTimeout = 3600; // Messages older than this (in seconds) will not be rebroadcast
};

View File

@@ -2,14 +2,16 @@
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_BMA423) && __has_include(<SensorBMA423.hpp>)
using namespace MotionSensorI2C;
BMA423Sensor::BMA423Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {}
bool BMA423Sensor::init()
{
if (sensor.begin(Wire, deviceAddress())) {
if (sensor.begin(deviceAddress(), &MotionSensorI2C::readRegister, &MotionSensorI2C::writeRegister)) {
sensor.configAccelerometer(sensor.RANGE_2G, sensor.ODR_100HZ, sensor.BW_NORMAL_AVG4, sensor.PERF_CONTINUOUS_MODE);
sensor.enableAccelerometer();
sensor.configInterrupt();
sensor.configInterrupt(BMA4_LEVEL_TRIGGER, BMA4_ACTIVE_HIGH, BMA4_PUSH_PULL, BMA4_OUTPUT_ENABLE, BMA4_INPUT_DISABLE);
#ifdef BMA423_INT
pinMode(BMA4XX_INT, INPUT);
@@ -24,9 +26,9 @@ bool BMA423Sensor::init()
#ifdef T_WATCH_S3
// Need to raise the wrist function, need to set the correct axis
sensor.setRemapAxes(sensor.REMAP_TOP_LAYER_RIGHT_CORNER);
sensor.setReampAxes(sensor.REMAP_TOP_LAYER_RIGHT_CORNER);
#else
sensor.setRemapAxes(sensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
sensor.setReampAxes(sensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
#endif
// sensor.enableFeature(sensor.FEATURE_STEP_CNTR, true);
sensor.enableFeature(sensor.FEATURE_TILT, true);
@@ -48,7 +50,7 @@ bool BMA423Sensor::init()
int32_t BMA423Sensor::runOnce()
{
if (sensor.readIrqStatus()) {
if (sensor.readIrqStatus() != DEV_WIRE_NONE) {
if (sensor.isTilt() || sensor.isDoubleTap()) {
wakeScreen();
return 500;

View File

@@ -61,6 +61,32 @@ class MotionSensor
uint32_t endCalibrationAt = 0;
};
namespace MotionSensorI2C
{
static inline int readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
{
Wire.beginTransmission(address);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom((uint8_t)address, (uint8_t)len);
uint8_t i = 0;
while (Wire.available()) {
data[i++] = Wire.read();
}
return 0; // Pass
}
static inline int writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
{
Wire.beginTransmission(address);
Wire.write(reg);
Wire.write(data, len);
return (0 != Wire.endTransmission());
}
} // namespace MotionSensorI2C
#endif
#endif

View File

@@ -786,6 +786,10 @@ bool loadConfig(const char *configPath)
}
}
if (yamlConfig["StoreAndForward"]) {
portduino_config.sfpp_stratum0 = (yamlConfig["StoreAndForward"]["Stratum0"]).as<bool>(false);
}
if (yamlConfig["General"]) {
portduino_config.MaxNodes = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
portduino_config.maxtophone = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);

View File

@@ -169,6 +169,9 @@ extern struct portduino_config_struct {
int configDisplayMode = 0;
bool has_configDisplayMode = false;
// Store and Forward++
bool sfpp_stratum0 = false;
// General
std::string mac_address = "";
bool mac_address_explicit = false;
@@ -488,6 +491,13 @@ extern struct portduino_config_struct {
out << YAML::EndMap; // Config
}
// StoreAndForward
if (sfpp_stratum0) {
out << YAML::Key << "StoreAndForward" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "Stratum0" << YAML::Value << true;
out << YAML::EndMap; // StoreAndForward
}
// General
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
if (config_directory != "")

View File

@@ -20,8 +20,8 @@ lib_deps =
${esp32_base.lib_deps}
# renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2
zinggjm/GxEPD2@1.6.5
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.2
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
lib_ignore =
m5stack-coreink
monitor_filters = esp32_exception_decoder

View File

@@ -13,6 +13,7 @@
#define LED_STATE_ON 1 // State when LED is lit
#define LED_PIN 10
#include "pcf8563.h"
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
#define HAS_RTC 1

View File

@@ -56,6 +56,10 @@
#define HAS_SCREEN 1
#define USE_SH1106 1
// PCF8563 RTC Module
// #define PCF8563_RTC 0x51
// #define PIN_RTC_INT 48 // Interrupt from the PCF8563 RTC
#define HAS_RTC 0
#define HAS_GPS 0
#define BUTTON_PIN PIN_BUTTON1

View File

@@ -18,5 +18,7 @@ build_flags =
lib_deps = ${esp32s3_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=custom.pio depName=PCA9557-arduino packageName=maxpromer/library/PCA9557-arduino
maxpromer/PCA9557-arduino@1.0.0

View File

@@ -44,6 +44,9 @@
#define PIN_SERIAL1_RX GPS_TX_PIN
#define PIN_SERIAL1_TX GPS_RX_PIN
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
#define SX126X_CS 17
#define LORA_SCK 16
#define LORA_MOSI 15

View File

@@ -19,6 +19,8 @@ lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
upload_speed = 115200
[env:heltec-vision-master-e213-inkhud]

View File

@@ -22,6 +22,8 @@ lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/448c8538129fde3d02a7cb5e6fc81971ad92547f.zip
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
upload_speed = 115200
[env:heltec-vision-master-e290-inkhud]

View File

@@ -8,6 +8,8 @@ build_flags =
-D HELTEC_VISION_MASTER_T190
lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=git-refs depName=meshtastic-st7789 packageName=https://github.com/meshtastic/st7789 gitBranch=main
https://github.com/meshtastic/st7789/archive/bd33ea58ddfe4a5e4a66d53300ccbd38d66ac21f.zip
upload_speed = 921600

View File

@@ -20,6 +20,8 @@ lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
upload_speed = 115200
[env:heltec-wireless-paper-inkhud]

View File

@@ -17,4 +17,6 @@ lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/55f618961db45a23eff0233546430f1e5a80f63a.zip
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
upload_speed = 115200

View File

@@ -9,12 +9,14 @@ upload_protocol = esptool
build_flags = ${esp32s3_base.build_flags}
-DT_WATCH_S3
-Ivariants/esp32s3/t-watch-s3
-DPCF8563_RTC=0x51
-DHAS_BMA423=1
lib_deps = ${esp32s3_base.lib_deps}
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
lovyan03/LovyanGFX@1.2.7
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.2
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=custom.pio depName=Adafruit DRV2605 packageName=adafruit/library/Adafruit DRV2605 Library
adafruit/Adafruit DRV2605 Library@1.2.4
# renovate: datasource=custom.pio depName=ESP8266Audio packageName=earlephilhower/library/ESP8266Audio

View File

@@ -41,14 +41,11 @@
#define HAS_AXP2101
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
#define HAS_RTC 1
#define I2C_SDA 10 // For QMC6310 sensors and screens
#define I2C_SCL 11 // For QMC6310 sensors and screens
#define HAS_BMA423 1
#define BMA4XX_INT 14 // Interrupt for BMA_423 axis sensor
#define HAS_GPS 0

View File

@@ -7,9 +7,10 @@ board_check = true
lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.2
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
build_flags =
${esp32s3_base.build_flags}
-I variants/esp32s3/tbeam-s3-core
-D PCF8563_RTC=0x51 ;Putting definitions in variant.h does not compile correctly

View File

@@ -53,8 +53,6 @@
// #define PMU_IRQ 40
#define HAS_AXP2101
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
#define HAS_RTC 1
// Specify the PMU as Wire1. In the t-beam-s3 core, PCF8563 and PMU share the bus
@@ -74,6 +72,9 @@
#define HAS_SDCARD // Have SPI interface SD card slot
#define SDCARD_USE_SPI1
// PCF8563 RTC Module
// #define PCF8563_RTC 0x51 //Putting definitions in variant. h does not compile correctly
// has 32768 Hz crystal
#define HAS_32768HZ 1

View File

@@ -27,8 +27,8 @@ lib_deps = ${esp32s3_base.lib_deps}
adafruit/Adafruit DRV2605 Library@1.2.4
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.2
# renovate: datasource=custom.pio depName=lewisxhe-SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.1
# renovate: datasource=github-tags depName=pschatzmann_arduino-audio-driver packageName=pschatzmann/arduino-audio-driver
https://github.com/pschatzmann/arduino-audio-driver/archive/v0.1.3.zip
# TODO renovate

View File

@@ -35,8 +35,11 @@
#define GPS_TX_PIN 12
#define PIN_GPS_PPS 13
// PCF85063 RTC Module
#define PCF85063_RTC 0x51
// PCF8563 RTC Module
#if __has_include("pcf8563.h")
#include "pcf8563.h"
#endif
#define PCF8563_RTC 0x51
#define HAS_RTC 1
// Rotary

View File

@@ -46,6 +46,7 @@ build_flags = ${native_base.build_flags} -Os -lX11 -linput -lxkbcommon -ffunctio
!pkg-config --libs openssl --silence-errors || :
!pkg-config --cflags --libs sdl2 --silence-errors || :
!pkg-config --cflags --libs libbsd-overlay --silence-errors || :
!pkg-config --cflags --libs sqlite3 --silence-errors || :
build_src_filter =
${native_base.build_src_filter}

View File

@@ -25,6 +25,8 @@ lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/33db3fa8ee6fc47d160bdb44f8f127c9a9203a10.zip
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=custom.pio depName=nRF52_PWM packageName=khoih-prog/library/nRF52_PWM
khoih-prog/nRF52_PWM@1.0.1
;upload_protocol = fs
@@ -46,3 +48,5 @@ build_src_filter =
lib_deps =
${inkhud.lib_deps} ; InkHUD libs first, so we get GFXRoot instead of AdafruitGFX
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1

View File

@@ -93,6 +93,8 @@ static const uint8_t A0 = PIN_A0;
#define TP_SER_IO (0 + 11)
#define PIN_RTC_INT (0 + 16) // Interrupt from the PCF8563 RTC
/*
External serial flash WP25R1635FZUIL0
*/
@@ -159,6 +161,9 @@ External serial flash WP25R1635FZUIL0
#define PIN_SERIAL1_TX GPS_TX_PIN
#define PIN_SERIAL1_RX GPS_RX_PIN
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
/*
* SPI Interfaces
*/
@@ -185,6 +190,7 @@ External serial flash WP25R1635FZUIL0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (2.02F)
// #define HAS_RTC 0
// #define HAS_SCREEN 0
#ifdef __cplusplus

View File

@@ -11,4 +11,6 @@ build_flags =
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/canaryone>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
;upload_protocol = fs

View File

@@ -14,6 +14,8 @@ build_src_filter =
lib_deps =
${inkhud.lib_deps} ; InkHUD libs first, so we get GFXRoot instead of AdafruitGFX
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
extra_scripts =
${env.extra_scripts}
variants/nrf52840/diy/nrf52_promicro_diy_tcxo/custom_build_tasks.py ; Add to PIO's Project Tasks pane: preset builds for common displays

View File

@@ -124,6 +124,9 @@ No longer populated on PCB
#define PIN_SERIAL1_RX GPS_RX_PIN
#define PIN_SERIAL1_TX GPS_TX_PIN
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
/*
* SPI Interfaces
*/
@@ -160,6 +163,7 @@ No longer populated on PCB
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (4.90F)
#define HAS_RTC 0
#ifdef __cplusplus
}
#endif

View File

@@ -13,5 +13,7 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_node_t114>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=git-refs depName=meshtastic-st7789 packageName=https://github.com/meshtastic/st7789 gitBranch=main
https://github.com/meshtastic/st7789/archive/bd33ea58ddfe4a5e4a66d53300ccbd38d66ac21f.zip

View File

@@ -175,6 +175,9 @@ No longer populated on PCB
#define PIN_SERIAL1_RX GPS_RX_PIN
#define PIN_SERIAL1_TX GPS_TX_PIN
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
/*
* SPI Interfaces
*/

View File

@@ -25,6 +25,8 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip
@@ -70,6 +72,8 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_pocket>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip

View File

@@ -130,6 +130,7 @@ No longer populated on PCB
#undef HAS_GPS
#define HAS_GPS 0
#define HAS_RTC 0
#ifdef __cplusplus
}
#endif

View File

@@ -15,6 +15,8 @@ lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=git-refs depName=NMIoT-meshsolar packageName=https://github.com/NMIoT/meshsolar gitBranch=main
https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
# renovate: datasource=custom.pio depName=ArduinoJson packageName=bblanchon/library/ArduinoJson
bblanchon/ArduinoJson@6.21.4

View File

@@ -142,6 +142,7 @@ No longer populated on PCB
#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

View File

@@ -10,6 +10,6 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/nano-g2-ultra>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.2
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
;upload_protocol = fs

View File

@@ -87,6 +87,8 @@ static const uint8_t A4 = PIN_A4;
#define PIN_WIRE_SDA (0 + 17)
#define PIN_WIRE_SCL (0 + 15)
#define PIN_RTC_INT (0 + 14) // Interrupt from the PCF8563 RTC
/*
External serial flash W25Q16JV_IQ
*/
@@ -139,9 +141,7 @@ External serial flash W25Q16JV_IQ
#define PIN_SERIAL1_RX PIN_GPS_RX
// PCF8563 RTC Module
#define PIN_RTC_INT (0 + 14) // Interrupt from the PCF8563 RTC
#define PCF8563_RTC 0x51
#define HAS_RTC 1
/*
* SPI Interfaces
@@ -169,6 +169,8 @@ External serial flash W25Q16JV_IQ
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (2.0F)
#define HAS_RTC 1
/**
OLED Screen Model
*/

View File

@@ -19,7 +19,7 @@
// Shared NicheGraphics components
// --------------------------------
#include "graphics/niche/Drivers/EInk/ZJY122250_0213BAAMFGN.h"
#include "graphics/niche/Inputs/TwoButtonExtended.h"
#include "graphics/niche/Inputs/TwoButton.h"
void setupNicheGraphics()
{
@@ -55,11 +55,6 @@ void setupNicheGraphics()
// Customize default settings
inkhud->persistence->settings.rotation = 1; // 90 degrees clockwise
#if HAS_TRACKBALL
inkhud->persistence->settings.joystick.enabled = true; // Device uses a joystick
inkhud->persistence->settings.joystick.alignment = 3; // 270 degrees
inkhud->persistence->settings.optionalMenuItems.nextTile = false; // Use joystick instead
#endif
inkhud->persistence->settings.optionalFeatures.batteryIcon = true; // Device definitely has a battery
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
@@ -80,33 +75,13 @@ void setupNicheGraphics()
// Buttons
// --------------------------
Inputs::TwoButtonExtended *buttons = Inputs::TwoButtonExtended::getInstance(); // Shared NicheGraphics component
Inputs::TwoButton *buttons = Inputs::TwoButton::getInstance(); // Shared NicheGraphics component
#if HAS_TRACKBALL
// #0: Exit Button
buttons->setWiring(0, Inputs::TwoButtonExtended::getUserButtonPin());
buttons->setTiming(0, 75, 500);
buttons->setHandlerShortPress(0, [inkhud]() { inkhud->exitShort(); });
buttons->setHandlerLongPress(0, [inkhud]() { inkhud->exitLong(); });
// #1: Joystick Center
buttons->setWiring(1, TB_PRESS);
buttons->setTiming(1, 75, 500);
buttons->setHandlerShortPress(1, [inkhud]() { inkhud->shortpress(); });
buttons->setHandlerLongPress(1, [inkhud]() { inkhud->longpress(); });
// Joystick Directions
buttons->setJoystickWiring(TB_UP, TB_DOWN, TB_LEFT, TB_RIGHT);
buttons->setJoystickDebounce(50);
buttons->setJoystickPressHandlers([inkhud]() { inkhud->navUp(); }, [inkhud]() { inkhud->navDown(); },
[inkhud]() { inkhud->navLeft(); }, [inkhud]() { inkhud->navRight(); });
#else
// #0: User Button
buttons->setWiring(0, Inputs::TwoButtonExtended::getUserButtonPin());
// #0: Main User Button
buttons->setWiring(0, Inputs::TwoButton::getUserButtonPin());
buttons->setTiming(0, 75, 500);
buttons->setHandlerShortPress(0, [inkhud]() { inkhud->shortpress(); });
buttons->setHandlerLongPress(0, [inkhud]() { inkhud->longpress(); });
#endif
// Begin handling button events
buttons->start();

View File

@@ -22,8 +22,8 @@ lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/55f618961db45a23eff0233546430f1e5a80f63a.zip
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.2
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1
;upload_protocol = fs
[env:t-echo-inkhud]
@@ -43,5 +43,5 @@ build_src_filter =
lib_deps =
${inkhud.lib_deps} ; InkHUD libs first, so we get GFXRoot instead of AdafruitGFX
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.2
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1

View File

@@ -108,6 +108,8 @@ No longer populated on PCB
#define TP_SER_IO (0 + 11)
#define PIN_RTC_INT (0 + 16) // Interrupt from the PCF8563 RTC
/*
External serial flash WP25R1635FZUIL0
*/
@@ -189,9 +191,7 @@ External serial flash WP25R1635FZUIL0
#define PIN_SERIAL1_TX GPS_TX_PIN
// PCF8563 RTC Module
#define PIN_RTC_INT (0 + 16) // Interrupt from the PCF8563 RTC
#define PCF8563_RTC 0x51
#define HAS_RTC 1
/*
* SPI Interfaces
@@ -219,6 +219,8 @@ External serial flash WP25R1635FZUIL0
// #define NO_EXT_GPIO 1
#define HAS_RTC 1
#ifdef __cplusplus
}
#endif