diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index 5933850e7..e739d7066 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -1,6 +1,6 @@
; The Portduino based sim environment on top of any host OS, all hardware will be simulated
[portduino_base]
-platform = https://github.com/meshtastic/platform-native.git#05255283879a0c65a7d3eba6c468b9186438bb14
+platform = https://github.com/meshtastic/platform-native.git#ff5da1d203b5c1163cfcda858d5f84920187f030
framework = arduino
build_src_filter =
@@ -28,5 +28,4 @@ build_flags =
${arduino_base.build_flags}
-fPIC
-Isrc/platform/portduino
- -DRADIOLIB_EEPROM_UNSUPPORTED
-
+ -DRADIOLIB_EEPROM_UNSUPPORTED
\ No newline at end of file
diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml
index cde45d1f8..266a9ae20 100644
--- a/bin/config-dist.yaml
+++ b/bin/config-dist.yaml
@@ -1,5 +1,5 @@
-# Define your devices here using Broadcom pin numbering
-# Uncomment the block that corresponds to your hardware
+### Define your devices here using Broadcom pin numbering
+### Uncomment the block that corresponds to your hardware
---
Lora:
# Module: sx1262 # Waveshare SX126X XXXM
@@ -25,16 +25,40 @@ Lora:
# CS: 7
# IRQ: 25
-# Set gpio chip to use in /dev/. Defaults to 0.
-# Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4
+### Set gpio chip to use in /dev/. Defaults to 0.
+### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4
# gpiochip: 4
-# Define GPIO buttons here:
+### Define GPIO buttons here:
GPIO:
# User: 6
-# Define GPS
+### Define GPS
GPS:
# SerialPath: /dev/ttyS0
+
+### Set up SPI displays here. Note that I2C displays are generally auto-detected.
+
+Display:
+
+### Waveshare 2.8inch RPi LCD
+# Panel: ST7789
+# CS: 8
+# DC: 22 # Data/Command pin
+# Backlight: 18
+# Width: 240
+# Height: 320
+# Reset: 27
+# Rotate: true
+
+Touchscreen:
+# Module: XPT2046
+# CS: 7
+# IRQ: 17
+
+### Configure device for direct keyboard input
+
+Input:
+# KeyboardDevice: /dev/input/event0
diff --git a/bin/meshtasticd.service b/bin/meshtasticd.service
index 4ed1bfd8f..f15fdc871 100644
--- a/bin/meshtasticd.service
+++ b/bin/meshtasticd.service
@@ -1,9 +1,12 @@
-[unit]
-description=Meshtastic Native Daemon
+[Unit]
+Description=Meshtastic Native Daemon
+After=network-online.target
[Service]
+User=root
+Group=root
Type=simple
ExecStart=/usr/sbin/meshtasticd
[Install]
-WantedBy=multi-user.target
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 417a6e454..a7fcd0c34 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -52,6 +52,10 @@ along with this program. If not, see .
#include "modules/esp32/StoreForwardModule.h"
#endif
+#if ARCH_RASPBERRY_PI
+#include "platform/portduino/PortduinoGlue.h"
+#endif
+
#ifdef OLED_RU
#include "fonts/OLEDDisplayFontsRU.h"
#endif
@@ -909,10 +913,40 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
}
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
- : concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32),
- dispdev(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE),
- ui(&dispdev)
+ : concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
{
+#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
+ dispdev = new SH1106Wire(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+#elif defined(USE_SSD1306)
+ dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014)
+ dispdev = new TFTDisplay(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+#elif defined(USE_EINK)
+ dispdev = new EInkDisplay(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+#elif defined(USE_ST7567)
+ dispdev = new ST7567Wire(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+#elif ARCH_RASPBERRY_PI
+ if (settingsMap[displayPanel] == st7789) {
+ LOG_DEBUG("Making TFTDisplay!\n");
+ dispdev = new TFTDisplay(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+ } else {
+ dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+ isAUTOOled = true;
+ }
+#else
+ dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+ isAUTOOled = true;
+#endif
+
+ ui = new OLEDDisplayUi(dispdev);
cmdQueue.setReader(this);
}
@@ -925,8 +959,8 @@ void Screen::doDeepSleep()
#ifdef USE_EINK
static FrameCallback sleepFrames[] = {drawSleepScreen};
static const int sleepFrameCount = sizeof(sleepFrames) / sizeof(sleepFrames[0]);
- ui.setFrames(sleepFrames, sleepFrameCount);
- ui.update();
+ ui->setFrames(sleepFrames, sleepFrameCount);
+ ui->update();
#endif
setOn(false);
}
@@ -942,14 +976,16 @@ void Screen::handleSetOn(bool on)
#ifdef T_WATCH_S3
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
- dispdev.displayOn();
- dispdev.displayOn();
+#if !ARCH_RASPBERRY_PI
+ dispdev->displayOn();
+#endif
+ dispdev->displayOn();
enabled = true;
setInterval(0); // Draw ASAP
runASAP = true;
} else {
LOG_INFO("Turning off screen\n");
- dispdev.displayOff();
+ dispdev->displayOff();
#ifdef T_WATCH_S3
PMU->disablePowerOutput(XPOWERS_ALDO2);
#endif
@@ -966,32 +1002,33 @@ void Screen::setup()
useDisplay = true;
#ifdef AutoOLEDWire_h
- dispdev.setDetected(model);
+ if (isAUTOOled)
+ static_cast(dispdev)->setDetected(model);
#endif
#ifdef USE_SH1107_128_64
- dispdev.setSubtype(7);
+ static_cast(dispdev)->setSubtype(7);
#endif
// Initialising the UI will init the display too.
- ui.init();
+ ui->init();
- displayWidth = dispdev.width();
- displayHeight = dispdev.height();
+ displayWidth = dispdev->width();
+ displayHeight = dispdev->height();
- ui.setTimePerTransition(0);
+ ui->setTimePerTransition(0);
- ui.setIndicatorPosition(BOTTOM);
+ ui->setIndicatorPosition(BOTTOM);
// Defines where the first frame is located in the bar.
- ui.setIndicatorDirection(LEFT_RIGHT);
- ui.setFrameAnimation(SLIDE_LEFT);
+ ui->setIndicatorDirection(LEFT_RIGHT);
+ ui->setFrameAnimation(SLIDE_LEFT);
// Don't show the page swipe dots while in boot screen.
- ui.disableAllIndicators();
+ ui->disableAllIndicators();
// Store a pointer to Screen so we can get to it from static functions.
- ui.getUiState()->userData = this;
+ ui->getUiState()->userData = this;
// Set the utf8 conversion function
- dispdev.setFontTableLookupFunction(customFontTableLookup);
+ dispdev->setFontTableLookupFunction(customFontTableLookup);
if (strlen(oemStore.oem_text) > 0)
logo_timeout *= 2;
@@ -999,23 +1036,23 @@ void Screen::setup()
// Add frames.
static FrameCallback bootFrames[] = {drawBootScreen};
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
- ui.setFrames(bootFrames, bootFrameCount);
+ ui->setFrames(bootFrames, bootFrameCount);
// No overlays.
- ui.setOverlays(nullptr, 0);
+ ui->setOverlays(nullptr, 0);
// Require presses to switch between frames.
- ui.disableAutoTransition();
+ ui->disableAutoTransition();
// Set up a log buffer with 3 lines, 32 chars each.
- dispdev.setLogBuffer(3, 32);
+ dispdev->setLogBuffer(3, 32);
#ifdef SCREEN_MIRROR
- dispdev.mirrorScreen();
+ dispdev->mirrorScreen();
#else
// Standard behaviour is to FLIP the screen (needed on T-Beam). If this config item is set, unflip it, and thereby logically
// flip it. If you have a headache now, you're welcome.
if (!config.display.flip_screen) {
- dispdev.flipScreenVertically();
+ dispdev->flipScreenVertically();
}
#endif
@@ -1023,20 +1060,30 @@ void Screen::setup()
uint8_t dmac[6];
getMacAddr(dmac);
snprintf(ourId, sizeof(ourId), "%02x%02x", dmac[4], dmac[5]);
+#if ARCH_RASPBERRY_PI
+ handleSetOn(false); // force clean init
+#endif
// Turn on the display.
handleSetOn(true);
// On some ssd1306 clones, the first draw command is discarded, so draw it
// twice initially. Skip this for EINK Displays to save a few seconds during boot
- ui.update();
+ ui->update();
#ifndef USE_EINK
- ui.update();
+ ui->update();
#endif
serialSinceMsec = millis();
-#if HAS_TOUCHSCREEN
- touchScreenImpl1 = new TouchScreenImpl1(dispdev.getWidth(), dispdev.getHeight(), dispdev.getTouch);
+#if ARCH_RASPBERRY_PI
+ if (settingsMap[touchscreenModule]) {
+ touchScreenImpl1 =
+ new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast(dispdev)->getTouch);
+ touchScreenImpl1->init();
+ }
+#elif HAS_TOUCHSCREEN
+ touchScreenImpl1 =
+ new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast(dispdev)->getTouch);
touchScreenImpl1->init();
#endif
@@ -1057,7 +1104,7 @@ void Screen::forceDisplay()
{
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
#ifdef USE_EINK
- dispdev.forceDisplay();
+ static_cast(dispdev)->forceDisplay();
#endif
}
@@ -1088,10 +1135,10 @@ int32_t Screen::runOnce()
// Change frames.
static FrameCallback bootOEMFrames[] = {drawOEMBootScreen};
static const int bootOEMFrameCount = sizeof(bootOEMFrames) / sizeof(bootOEMFrames[0]);
- ui.setFrames(bootOEMFrames, bootOEMFrameCount);
- ui.update();
+ ui->setFrames(bootOEMFrames, bootOEMFrameCount);
+ ui->update();
#ifndef USE_EINK
- ui.update();
+ ui->update();
#endif
showingOEMBootScreen = false;
}
@@ -1164,16 +1211,16 @@ int32_t Screen::runOnce()
// this must be before the frameState == FIXED check, because we always
// want to draw at least one FIXED frame before doing forceDisplay
- ui.update();
+ ui->update();
// Switch to a low framerate (to save CPU) when we are not in transition
// but we should only call setTargetFPS when framestate changes, because
// otherwise that breaks animations.
- if (targetFramerate != IDLE_FRAMERATE && ui.getUiState()->frameState == FIXED) {
- // oldFrameState = ui.getUiState()->frameState;
+ if (targetFramerate != IDLE_FRAMERATE && ui->getUiState()->frameState == FIXED) {
+ // oldFrameState = ui->getUiState()->frameState;
targetFramerate = IDLE_FRAMERATE;
- ui.setTargetFPS(targetFramerate);
+ ui->setTargetFPS(targetFramerate);
forceDisplay();
}
@@ -1189,7 +1236,7 @@ int32_t Screen::runOnce()
}
// LOG_DEBUG("want fps %d, fixed=%d\n", targetFramerate,
- // ui.getUiState()->frameState); If we are scrolling we need to be called
+ // ui->getUiState()->frameState); If we are scrolling we need to be called
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
// as fast as we really need so that any rounding errors still result with
// the correct framerate
@@ -1221,8 +1268,8 @@ void Screen::setSSLFrames()
if (address_found.address) {
// LOG_DEBUG("showing SSL frames\n");
static FrameCallback sslFrames[] = {drawSSLScreen};
- ui.setFrames(sslFrames, 1);
- ui.update();
+ ui->setFrames(sslFrames, 1);
+ ui->update();
}
}
@@ -1306,8 +1353,8 @@ void Screen::setFrames()
LOG_DEBUG("Finished building frames. numframes: %d\n", numframes);
- ui.setFrames(normalFrames, numframes);
- ui.enableAllIndicators();
+ ui->setFrames(normalFrames, numframes);
+ ui->enableAllIndicators();
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed)
@@ -1327,8 +1374,8 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
{
- ui.disableAllIndicators();
- ui.setFrames(drawFrames, 1);
+ ui->disableAllIndicators();
+ ui->setFrames(drawFrames, 1);
setFastFramerate();
}
@@ -1370,17 +1417,17 @@ void Screen::blink()
{
setFastFramerate();
uint8_t count = 10;
- dispdev.setBrightness(254);
+ dispdev->setBrightness(254);
while (count > 0) {
- dispdev.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- dispdev.display();
+ dispdev->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ dispdev->display();
delay(50);
- dispdev.clear();
- dispdev.display();
+ dispdev->clear();
+ dispdev->display();
delay(50);
count = count - 1;
}
- dispdev.setBrightness(brightness);
+ dispdev->setBrightness(brightness);
}
std::string Screen::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds)
@@ -1408,15 +1455,15 @@ void Screen::handlePrint(const char *text)
if (!useDisplay || !showingNormalScreen)
return;
- dispdev.print(text);
+ dispdev->print(text);
}
void Screen::handleOnPress()
{
// If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it.
- if (ui.getUiState()->frameState == FIXED) {
- ui.nextFrame();
+ if (ui->getUiState()->frameState == FIXED) {
+ ui->nextFrame();
lastScreenTransition = millis();
setFastFramerate();
}
@@ -1426,8 +1473,8 @@ void Screen::handleShowPrevFrame()
{
// If screen was off, just wake it, otherwise go back to previous frame
// If we are in a transition, the press must have bounced, drop it.
- if (ui.getUiState()->frameState == FIXED) {
- ui.previousFrame();
+ if (ui->getUiState()->frameState == FIXED) {
+ ui->previousFrame();
lastScreenTransition = millis();
setFastFramerate();
}
@@ -1437,8 +1484,8 @@ void Screen::handleShowNextFrame()
{
// If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it.
- if (ui.getUiState()->frameState == FIXED) {
- ui.nextFrame();
+ if (ui->getUiState()->frameState == FIXED) {
+ ui->nextFrame();
lastScreenTransition = millis();
setFastFramerate();
}
@@ -1453,7 +1500,7 @@ void Screen::setFastFramerate()
// We are about to start a transition so speed up fps
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
- ui.setTargetFPS(targetFramerate);
+ ui->setTargetFPS(targetFramerate);
setInterval(0); // redraw ASAP
runASAP = true;
}
@@ -1540,7 +1587,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
}
#endif
} else {
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
+ // TODO: Raspberry Pi supports more than just the one screen size
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || ARCH_RASPBERRY_PI) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1);
@@ -1780,7 +1828,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
setFastFramerate();
// TODO: We might also want switch to corresponding frame,
// but we don't know the exact frame number.
- // ui.switchToFrame(0);
+ // ui->switchToFrame(0);
}
}
diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h
index 554fa0aeb..baee4b140 100644
--- a/src/graphics/Screen.h
+++ b/src/graphics/Screen.h
@@ -324,6 +324,8 @@ class Screen : public concurrency::OSThread
// Called periodically from the main loop.
int32_t runOnce() final;
+ bool isAUTOOled = false;
+
private:
struct ScreenCmd {
Cmd cmd;
@@ -385,22 +387,10 @@ class Screen : public concurrency::OSThread
DebugInfo debugInfo;
/// Display device
+ OLEDDisplay *dispdev;
-#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
- SH1106Wire dispdev;
-#elif defined(USE_SSD1306)
- SSD1306Wire dispdev;
-#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014)
- TFTDisplay dispdev;
-#elif defined(USE_EINK)
- EInkDisplay dispdev;
-#elif defined(USE_ST7567)
- ST7567Wire dispdev;
-#else
- AutoOLEDWire dispdev;
-#endif
/// UI helper for rendering to frames and switching between them
- OLEDDisplayUi ui;
+ OLEDDisplayUi *ui;
};
} // namespace graphics
diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp
index 618880a5c..fe98882b4 100644
--- a/src/graphics/TFTDisplay.cpp
+++ b/src/graphics/TFTDisplay.cpp
@@ -1,5 +1,8 @@
#include "configuration.h"
#include "main.h"
+#if ARCH_RASPBERRY_PI
+#include "platform/portduino/PortduinoGlue.h"
+#endif
#ifndef TFT_BACKLIGHT_ON
#define TFT_BACKLIGHT_ON HIGH
@@ -103,11 +106,11 @@ class LGFX : public lgfx::LGFX_Device
}
};
-static LGFX tft;
+static LGFX *tft = nullptr;
#elif defined(RAK14014)
#include
-TFT_eSPI tft = TFT_eSPI();
+TFT_eSPI *tft = nullptr;
#elif defined(ST7789_CS)
#include // Graphics and font library for ST7735 driver chip
@@ -233,7 +236,7 @@ class LGFX : public lgfx::LGFX_Device
}
};
-static LGFX tft;
+static LGFX *tft = nullptr;
#elif defined(ILI9341_DRIVER)
@@ -322,23 +325,96 @@ class LGFX : public lgfx::LGFX_Device
}
};
-static LGFX tft;
+static LGFX *tft = nullptr;
#elif defined(ST7735_CS)
#include // Graphics and font library for ILI9341 driver chip
-static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
+static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
+#elif ARCH_RASPBERRY_PI
+#include // Graphics and font library for ST7735 driver chip
+class LGFX : public lgfx::LGFX_Device
+{
+ lgfx::Panel_LCD *_panel_instance;
+ lgfx::Bus_SPI _bus_instance;
+
+ lgfx::ITouch *_touch_instance;
+
+ public:
+ LGFX(void)
+ {
+
+ _panel_instance = new lgfx::Panel_ST7789;
+ auto buscfg = _bus_instance.config();
+ buscfg.spi_mode = 0;
+
+ buscfg.pin_dc = settingsMap[displayDC]; // Set SPI DC pin number (-1 = disable)
+
+ _bus_instance.config(buscfg); // applies the set value to the bus.
+ _panel_instance->setBus(&_bus_instance); // set the bus on the panel.
+
+ auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
+ LOG_DEBUG("Height: %d, Width: %d \n", settingsMap[displayHeight], settingsMap[displayWidth]);
+ cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable)
+ cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
+ cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
+ cfg.offset_x = 0; // Panel offset amount in X direction
+ cfg.offset_y = 0; // Panel offset amount in Y direction
+ cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is mirrored)
+ cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
+ cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
+ cfg.readable = true; // Set to true if data can be read
+ cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
+ cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
+ cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
+ cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
+
+ _panel_instance->config(cfg);
+
+ // Configure settings for touch control.
+ if (settingsMap[touchscreenModule]) {
+ if (settingsMap[touchscreenModule] == xpt2046) {
+ _touch_instance = new lgfx::Touch_XPT2046;
+ }
+ auto touch_cfg = _touch_instance->config();
+
+ touch_cfg.pin_cs = settingsMap[touchscreenCS];
+ touch_cfg.x_min = 0;
+ touch_cfg.x_max = settingsMap[displayHeight] - 1;
+ touch_cfg.y_min = 0;
+ touch_cfg.y_max = settingsMap[displayWidth] - 1;
+ touch_cfg.pin_int = settingsMap[touchscreenIRQ];
+ touch_cfg.bus_shared = true;
+ touch_cfg.offset_rotation = 1;
+
+ _touch_instance->config(touch_cfg);
+ _panel_instance->setTouch(_touch_instance);
+ }
+
+ setPanel(_panel_instance); // Sets the panel to use.
+ }
+};
+
+static LGFX *tft = nullptr;
#endif
-#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(RAK14014)
+#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(RAK14014) || ARCH_RASPBERRY_PI
#include "SPILock.h"
#include "TFTDisplay.h"
#include
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
{
-#ifdef SCREEN_ROTATE
+ LOG_DEBUG("TFTDisplay!\n");
+#if ARCH_RASPBERRY_PI
+ if (settingsMap[displayRotate]) {
+ setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]);
+ } else {
+ setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayWidth], settingsMap[configNames::displayHeight]);
+ }
+
+#elif defined(SCREEN_ROTATE)
setGeometry(GEOMETRY_RAWMODE, TFT_HEIGHT, TFT_WIDTH);
#else
setGeometry(GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
@@ -346,19 +422,25 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
}
// Write the buffer to the display memory
-void TFTDisplay::display(void)
+void TFTDisplay::display(bool fromBlank)
{
+ if (fromBlank)
+ tft->clear();
concurrency::LockGuard g(spiLock);
uint16_t x, y;
for (y = 0; y < displayHeight; y++) {
for (x = 0; x < displayWidth; x++) {
- // get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
auto isset = buffer[x + (y / 8) * displayWidth] & (1 << (y & 7));
- auto dblbuf_isset = buffer_back[x + (y / 8) * displayWidth] & (1 << (y & 7));
- if (isset != dblbuf_isset) {
- tft.drawPixel(x, y, isset ? TFT_MESH : TFT_BLACK);
+ if (!fromBlank) {
+ // get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
+ auto dblbuf_isset = buffer_back[x + (y / 8) * displayWidth] & (1 << (y & 7));
+ if (isset != dblbuf_isset) {
+ tft->drawPixel(x, y, isset ? TFT_MESH : TFT_BLACK);
+ }
+ } else if (isset) {
+ tft->drawPixel(x, y, TFT_MESH);
}
}
}
@@ -377,7 +459,11 @@ void TFTDisplay::sendCommand(uint8_t com)
// handle display on/off directly
switch (com) {
case DISPLAYON: {
-#if defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
+#if ARCH_RASPBERRY_PI
+ display(true);
+ if (settingsMap[displayBacklight] > 0)
+ digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON);
+#elif defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
if (heltec_version == 3) {
digitalWrite(ST7735_BACKLIGHT_EN_V03, TFT_BACKLIGHT_ON);
} else {
@@ -400,12 +486,16 @@ void TFTDisplay::sendCommand(uint8_t com)
#ifdef RAK14014
#elif !defined(M5STACK)
- tft.setBrightness(128);
+ tft->setBrightness(128);
#endif
break;
}
case DISPLAYOFF: {
-#if defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
+#if ARCH_RASPBERRY_PI
+ tft->clear();
+ if (settingsMap[displayBacklight] > 0)
+ digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON);
+#elif defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
if (heltec_version == 3) {
digitalWrite(ST7735_BACKLIGHT_EN_V03, !TFT_BACKLIGHT_ON);
} else {
@@ -427,7 +517,7 @@ void TFTDisplay::sendCommand(uint8_t com)
#endif
#ifdef RAK14014
#elif !defined(M5STACK)
- tft.setBrightness(0);
+ tft->setBrightness(0);
#endif
break;
}
@@ -442,7 +532,7 @@ void TFTDisplay::flipScreenVertically()
{
#if defined(T_WATCH_S3)
LOG_DEBUG("Flip TFT vertically\n"); // T-Watch S3 right-handed orientation
- tft.setRotation(0);
+ tft->setRotation(0);
#endif
}
@@ -450,7 +540,7 @@ bool TFTDisplay::hasTouch(void)
{
#ifdef RAK14014
#elif !defined(M5STACK)
- return tft.touch() != nullptr;
+ return tft->touch() != nullptr;
#else
return false;
#endif
@@ -460,7 +550,7 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
{
#ifdef RAK14014
#elif !defined(M5STACK)
- return tft.getTouch(x, y);
+ return tft->getTouch(x, y);
#else
return false;
#endif
@@ -476,6 +566,11 @@ bool TFTDisplay::connect()
{
concurrency::LockGuard g(spiLock);
LOG_INFO("Doing TFT init\n");
+#ifdef RAK14014
+ tft = new TFT_eSPI;
+#else
+ tft = new LGFX;
+#endif
#ifdef TFT_BL
pinMode(TFT_BL, OUTPUT);
@@ -495,24 +590,24 @@ bool TFTDisplay::connect()
}
#endif
- tft.init();
+ tft->init();
#if defined(M5STACK)
- tft.setRotation(0);
+ tft->setRotation(0);
#elif defined(RAK14014)
- tft.setRotation(1);
- tft.setSwapBytes(true);
-// tft.fillScreen(TFT_BLACK);
+ tft->setRotation(1);
+ tft->setSwapBytes(true);
+// tft->fillScreen(TFT_BLACK);
#elif defined(T_DECK) || defined(PICOMPUTER_S3)
- tft.setRotation(1); // T-Deck has the TFT in landscape
+ tft->setRotation(1); // T-Deck has the TFT in landscape
#elif defined(T_WATCH_S3)
- tft.setRotation(2); // T-Watch S3 left-handed orientation
+ tft->setRotation(2); // T-Watch S3 left-handed orientation
#else
- tft.setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
+ tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
#endif
- tft.fillScreen(TFT_BLACK);
+ tft->fillScreen(TFT_BLACK);
return true;
}
-#endif
+#endif
\ No newline at end of file
diff --git a/src/graphics/TFTDisplay.h b/src/graphics/TFTDisplay.h
index 8c9a9b62e..3d6ea6cc6 100644
--- a/src/graphics/TFTDisplay.h
+++ b/src/graphics/TFTDisplay.h
@@ -20,7 +20,8 @@ class TFTDisplay : public OLEDDisplay
TFTDisplay(uint8_t, int, int, OLEDDISPLAY_GEOMETRY, HW_I2C);
// Write the buffer to the display memory
- virtual void display(void) override;
+ virtual void display() override { display(false); };
+ virtual void display(bool fromBlank);
// Turn the display upside down
virtual void flipScreenVertically();
diff --git a/src/graphics/images.h b/src/graphics/images.h
index a1191076b..7f3cd46fc 100644
--- a/src/graphics/images.h
+++ b/src/graphics/images.h
@@ -14,7 +14,7 @@ const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3
const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF};
const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF};
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || ARCH_RASPBERRY_PI) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
@@ -30,4 +30,4 @@ const uint8_t imgQuestion[] PROGMEM = {0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1,
const uint8_t imgSF[] PROGMEM = {0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5};
#endif
-#include "img/icon.xbm"
+#include "img/icon.xbm"
\ No newline at end of file
diff --git a/src/input/LinuxInput.cpp b/src/input/LinuxInput.cpp
new file mode 100644
index 000000000..4b6150949
--- /dev/null
+++ b/src/input/LinuxInput.cpp
@@ -0,0 +1,179 @@
+#if ARCH_RASPBERRY_PI
+#include "LinuxInput.h"
+#include "configuration.h"
+
+#include "platform/portduino/PortduinoGlue.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Inspired by https://github.com/librerpi/rpi-tools/blob/master/keyboard-proxy/main.c which is GPL-v2
+
+LinuxInput::LinuxInput(const char *name) : concurrency::OSThread(name)
+{
+ this->_originName = name;
+}
+
+int32_t LinuxInput::runOnce()
+{
+
+ if (firstTime) {
+ if (settingsStrings[keyboardDevice] == "")
+ return disable();
+ fd = open(settingsStrings[keyboardDevice].c_str(), O_RDWR);
+ if (fd < 0)
+ return disable();
+ ret = ioctl(fd, EVIOCGRAB, (void *)1);
+ if (ret != 0)
+ return disable();
+
+ epollfd = epoll_create1(0);
+ assert(epollfd >= 0);
+
+ ev.events = EPOLLIN;
+ ev.data.fd = fd;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
+ perror("unable to epoll add");
+ return disable();
+ }
+ // This is the first time the OSThread library has called this function, so do port setup
+ firstTime = 0;
+ }
+
+ int nfds = epoll_wait(epollfd, events, MAX_EVENTS, 1);
+ if (nfds < 0) {
+ printf("%d ", nfds);
+ perror("epoll_wait failed");
+ return disable();
+ } else if (nfds == 0) {
+ return 50;
+ }
+
+ int keys = 0;
+ memset(report, 0, 8);
+ for (int i = 0; i < nfds; i++) {
+
+ struct input_event ev[64];
+ int rd = read(events[i].data.fd, ev, sizeof(ev));
+ assert(rd > ((signed int)sizeof(struct input_event)));
+ for (int j = 0; j < rd / ((signed int)sizeof(struct input_event)); j++) {
+ InputEvent e;
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
+ e.source = this->_originName;
+ e.kbchar = 0;
+ unsigned int type, code;
+ type = ev[j].type;
+ code = ev[j].code;
+ int value = ev[j].value;
+ // printf("Event: time %ld.%06ld, ", ev[j].time.tv_sec, ev[j].time.tv_usec);
+
+ if (type == EV_KEY) {
+ uint8_t mod = 0;
+
+ switch (code) {
+ case KEY_LEFTCTRL:
+ mod = 0x01;
+ break;
+ case KEY_RIGHTCTRL:
+ mod = 0x10;
+ break;
+ case KEY_LEFTSHIFT:
+ mod = 0x02;
+ break;
+ case KEY_RIGHTSHIFT:
+ mod = 0x20;
+ break;
+ case KEY_LEFTALT:
+ mod = 0x04;
+ break;
+ case KEY_RIGHTALT:
+ mod = 0x40;
+ break;
+ case KEY_LEFTMETA:
+ mod = 0x08;
+ break;
+ }
+ if (value == 1) {
+ switch (code) {
+ case KEY_LEFTCTRL:
+ mod = 0x01;
+ break;
+ case KEY_RIGHTCTRL:
+ mod = 0x10;
+ break;
+ case KEY_LEFTSHIFT:
+ mod = 0x02;
+ break;
+ case KEY_RIGHTSHIFT:
+ mod = 0x20;
+ break;
+ case KEY_LEFTALT:
+ mod = 0x04;
+ break;
+ case KEY_RIGHTALT:
+ mod = 0x40;
+ break;
+ case KEY_LEFTMETA:
+ mod = 0x08;
+ break;
+ case KEY_ESC: // ESC
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
+ break;
+ case KEY_BACK: // Back
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
+ // e.kbchar = key;
+ break;
+
+ case KEY_UP: // Up
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
+ break;
+ case KEY_DOWN: // Down
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
+ break;
+ case KEY_LEFT: // Left
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
+ break;
+ e.kbchar = 0xb4;
+ case KEY_RIGHT: // Right
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
+ break;
+ e.kbchar = 0xb7;
+ case KEY_ENTER: // Enter
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
+ break;
+ default: // all other keys
+ if (keymap[code]) {
+ e.inputEvent = ANYKEY;
+ e.kbchar = keymap[code];
+ }
+ break;
+ }
+ }
+ if (ev[j].value) {
+ modifiers |= mod;
+ } else {
+ modifiers &= ~mod;
+ }
+ report[0] = modifiers;
+ }
+ if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
+ if (e.inputEvent == ANYKEY && (modifiers && 0x22))
+ e.kbchar = uppers[e.kbchar]; // doesn't get punctuation. Meh.
+ this->notifyObservers(&e);
+ }
+ }
+ }
+
+ return 50; // Keyscan every 50msec to avoid key bounce
+}
+
+#endif
\ No newline at end of file
diff --git a/src/input/LinuxInput.h b/src/input/LinuxInput.h
new file mode 100644
index 000000000..c21fb4c36
--- /dev/null
+++ b/src/input/LinuxInput.h
@@ -0,0 +1,64 @@
+#pragma once
+#if ARCH_RASPBERRY_PI
+#include "InputBroker.h"
+#include "concurrency/OSThread.h"
+#include
+#include
+#include
+#include
+#include