kopia lustrzana https://github.com/meshtastic/firmware
Refactor display handling add Raspbian TFT display (#2998)
* Refactor display handling add Raspbian TFT display * Add missed change * Add static casts * Add missed TFT refactor for RAK14014 * Add missed GPIO configuration * Adds Native keyboard input option * Get the ifdefs right * CannedMessage send via queue, not run immediately. * Fixup systemd service file * Add display blanking for Raspberry Pi * Add a couple missed key definitions --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com>pull/3001/head^2
rodzic
d14d2c89c3
commit
2ebaea317a
|
@ -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 =
|
||||
|
@ -29,4 +29,3 @@ build_flags =
|
|||
-fPIC
|
||||
-Isrc/platform/portduino
|
||||
-DRADIOLIB_EEPROM_UNSUPPORTED
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
[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
|
||||
|
||||
|
|
|
@ -52,6 +52,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#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<AutoOLEDWire *>(dispdev)->setDetected(model);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SH1107_128_64
|
||||
dispdev.setSubtype(7);
|
||||
static_cast<SH1106Wire *>(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<TFTDisplay *>(dispdev)->getTouch);
|
||||
touchScreenImpl1->init();
|
||||
}
|
||||
#elif HAS_TOUCHSCREEN
|
||||
touchScreenImpl1 =
|
||||
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(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<EInkDisplay *>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.h>
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
TFT_eSPI *tft = nullptr;
|
||||
|
||||
#elif defined(ST7789_CS)
|
||||
#include <LovyanGFX.hpp> // 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 <TFT_eSPI.h> // 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 <LovyanGFX.hpp> // 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 <SPI.h>
|
||||
|
||||
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,22 +590,22 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
#if ARCH_RASPBERRY_PI
|
||||
#include "LinuxInput.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// 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
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "InputBroker.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_EVENTS 10
|
||||
|
||||
class LinuxInput : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
explicit LinuxInput(const char *name);
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
const char *_originName;
|
||||
bool firstTime = 1;
|
||||
int shift = 0;
|
||||
char key = 0;
|
||||
char prevkey = 0;
|
||||
|
||||
InputEvent eventqueue[50]; // The Linux API will return multiple keypresses at a time. Queue them to not miss any.
|
||||
int queue_length = 0;
|
||||
int queue_progress = 0;
|
||||
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
int fd;
|
||||
int ret;
|
||||
uint8_t report[8];
|
||||
int epollfd;
|
||||
struct epoll_event ev;
|
||||
uint8_t modifiers = 0;
|
||||
std::map<int, char> keymap{
|
||||
{KEY_A, 'a'}, {KEY_B, 'b'}, {KEY_C, 'c'}, {KEY_D, 'd'}, {KEY_E, 'e'},
|
||||
{KEY_F, 'f'}, {KEY_G, 'g'}, {KEY_H, 'h'}, {KEY_I, 'i'}, {KEY_J, 'j'},
|
||||
{KEY_K, 'k'}, {KEY_L, 'l'}, {KEY_M, 'm'}, {KEY_N, 'n'}, {KEY_O, 'o'},
|
||||
{KEY_P, 'p'}, {KEY_Q, 'q'}, {KEY_R, 'r'}, {KEY_S, 's'}, {KEY_T, 't'},
|
||||
{KEY_U, 'u'}, {KEY_V, 'v'}, {KEY_W, 'w'}, {KEY_X, 'x'}, {KEY_Y, 'y'},
|
||||
{KEY_Z, 'z'}, {KEY_BACKSPACE, 0x08}, {KEY_SPACE, ' '}, {KEY_1, '1'}, {KEY_2, '2'},
|
||||
{KEY_3, '3'}, {KEY_4, '4'}, {KEY_5, '5'}, {KEY_6, '6'}, {KEY_7, '7'},
|
||||
{KEY_8, '8'}, {KEY_9, '9'}, {KEY_0, '0'}, {KEY_DOT, '.'}, {KEY_COMMA, ','},
|
||||
{KEY_MINUS, '-'}, {KEY_EQUAL, '='}, {KEY_LEFTBRACE, '['}, {KEY_RIGHTBRACE, ']'}, {KEY_BACKSLASH, '\\'},
|
||||
{KEY_SEMICOLON, ';'}, {KEY_APOSTROPHE, '\''}, {KEY_SLASH, '/'}, {KEY_TAB, 0x09}};
|
||||
std::map<char, char> uppers{{'a', 'A'}, {'b', 'B'}, {'c', 'C'}, {'d', 'D'}, {'e', 'E'}, {'f', 'F'}, {'g', 'G'}, {'h', 'H'},
|
||||
{'i', 'I'}, {'j', 'J'}, {'k', 'K'}, {'l', 'L'}, {'m', 'M'}, {'n', 'N'}, {'o', 'O'}, {'p', 'P'},
|
||||
{'q', 'Q'}, {'r', 'R'}, {'s', 'S'}, {'t', 'T'}, {'u', 'U'}, {'v', 'V'}, {'w', 'W'}, {'x', 'X'},
|
||||
{'y', 'Y'}, {'z', 'Z'}, {'1', '!'}, {'2', '@'}, {'3', '#'}, {'4', '$'}, {'5', '%'}, {'6', '^'},
|
||||
{'7', '&'}, {'8', '*'}, {'9', '('}, {'0', ')'}, {'.', '>'}, {',', '<'}, {'-', '_'}, {'=', '+'},
|
||||
{'[', '{'}, {']', '}'}, {'\\', '|'}, {';', ':'}, {'\'', '"'}, {'/', '?'}};
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
#if ARCH_RASPBERRY_PI
|
||||
#include "LinuxInputImpl.h"
|
||||
#include "InputBroker.h"
|
||||
|
||||
LinuxInputImpl *aLinuxInputImpl;
|
||||
|
||||
LinuxInputImpl::LinuxInputImpl() : LinuxInput("LinuxInput") {}
|
||||
|
||||
void LinuxInputImpl::init()
|
||||
{
|
||||
inputBroker->registerSource(this);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
#ifdef ARCH_RASPBERRY_PI
|
||||
#pragma once
|
||||
#include "LinuxInput.h"
|
||||
#include "main.h"
|
||||
|
||||
/**
|
||||
* @brief The idea behind this class to have static methods for the event handlers.
|
||||
* Check attachInterrupt() at RotaryEncoderInteruptBase.cpp
|
||||
* Technically you can have as many rotary encoders hardver attached
|
||||
* to your device as you wish, but you always need to have separate event
|
||||
* handlers, thus you need to have a RotaryEncoderInterrupt implementation.
|
||||
*/
|
||||
|
||||
class LinuxInputImpl : public LinuxInput
|
||||
{
|
||||
public:
|
||||
LinuxInputImpl();
|
||||
void init();
|
||||
};
|
||||
extern LinuxInputImpl *aLinuxInputImpl;
|
||||
#endif
|
|
@ -4,6 +4,10 @@
|
|||
#include "configuration.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
TouchScreenImpl1 *touchScreenImpl1;
|
||||
|
||||
TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTouch)(int16_t *, int16_t *))
|
||||
|
@ -13,7 +17,14 @@ TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTo
|
|||
|
||||
void TouchScreenImpl1::init()
|
||||
{
|
||||
#if !HAS_TOUCHSCREEN
|
||||
#if ARCH_RASPBERRY_PI
|
||||
if (settingsMap[touchscreenModule]) {
|
||||
TouchScreenBase::init(true);
|
||||
inputBroker->registerSource(this);
|
||||
} else {
|
||||
TouchScreenBase::init(false);
|
||||
}
|
||||
#elif !HAS_TOUCHSCREEN
|
||||
TouchScreenBase::init(false);
|
||||
return;
|
||||
#else
|
||||
|
|
|
@ -697,6 +697,10 @@ void setup()
|
|||
// the current region name)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
screen->setup();
|
||||
#elif ARCH_RASPBERRY_PI
|
||||
if (screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) {
|
||||
screen->setup();
|
||||
}
|
||||
#else
|
||||
if (screen_found.port != ScanI2C::I2CPort::NO_I2C)
|
||||
screen->setup();
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include <nvs_flash.h>
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_RASPBERRY_PI
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_NRF52
|
||||
#include <bluefruit.h>
|
||||
#include <utility/bonding.h>
|
||||
|
@ -191,6 +195,12 @@ void NodeDB::installDefaultConfig()
|
|||
config.bluetooth.fixed_pin = defaultBLEPin;
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
bool hasScreen = true;
|
||||
#elif ARCH_RASPBERRY_PI
|
||||
bool hasScreen = false;
|
||||
if (settingsMap[displayPanel])
|
||||
hasScreen = true;
|
||||
else
|
||||
hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
||||
#else
|
||||
bool hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#include "configuration.h"
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "PortduinoGlue.h"
|
||||
#endif
|
||||
#if HAS_SCREEN
|
||||
#include "CannedMessageModule.h"
|
||||
#include "FSCommon.h"
|
||||
|
@ -163,9 +166,14 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
|||
}
|
||||
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
|
||||
LOG_DEBUG("Canned message event Cancel\n");
|
||||
// emulate a timeout. Same result
|
||||
this->lastTouchMillis = 0;
|
||||
validEvent = true;
|
||||
UIFrameEvent e = {false, true};
|
||||
e.frameChanged = true;
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = ""; // clear freetext
|
||||
this->cursor = 0;
|
||||
this->destSelect = false;
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
if ((event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK)) ||
|
||||
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
|
||||
|
@ -212,7 +220,11 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
|||
|
||||
if (validEvent) {
|
||||
// Let runOnce to be called immediately.
|
||||
setIntervalFromNow(0);
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
|
||||
setIntervalFromNow(0); // on fast keypresses, this isn't fast enough.
|
||||
} else {
|
||||
runOnce();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#include "modules/TextMessageModule.h"
|
||||
#include "modules/TraceRouteModule.h"
|
||||
#include "modules/WaypointModule.h"
|
||||
#if ARCH_RASPBERRY_PI
|
||||
#include "input/LinuxInputImpl.h"
|
||||
#endif
|
||||
#if HAS_TELEMETRY
|
||||
#include "modules/Telemetry/DeviceTelemetry.h"
|
||||
#endif
|
||||
|
@ -44,7 +47,7 @@
|
|||
void setupModules()
|
||||
{
|
||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
#if HAS_BUTTON
|
||||
#if HAS_BUTTON || ARCH_RASPBERRY_PI
|
||||
inputBroker = new InputBroker();
|
||||
#endif
|
||||
adminModule = new AdminModule();
|
||||
|
@ -61,7 +64,7 @@ void setupModules()
|
|||
|
||||
new RemoteHardwareModule();
|
||||
new ReplyModule();
|
||||
#if HAS_BUTTON
|
||||
#if HAS_BUTTON || ARCH_RASPBERRY_PI
|
||||
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
||||
if (!rotaryEncoderInterruptImpl1->init()) {
|
||||
delete rotaryEncoderInterruptImpl1;
|
||||
|
@ -79,6 +82,10 @@ void setupModules()
|
|||
kbMatrixImpl->init();
|
||||
#endif // INPUTBROKER_MATRIX_TYPE
|
||||
#endif // HAS_BUTTON
|
||||
#if ARCH_RASPBERRY_PI
|
||||
aLinuxInputImpl = new LinuxInputImpl();
|
||||
aLinuxInputImpl->init();
|
||||
#endif
|
||||
#if HAS_TRACKBALL
|
||||
trackballInterruptImpl1 = new TrackballInterruptImpl1();
|
||||
trackballInterruptImpl1->init();
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
#include <map>
|
||||
#include <unistd.h>
|
||||
|
||||
std::map<int, int> settingsMap;
|
||||
std::map<configNames, int> settingsMap;
|
||||
std::map<configNames, std::string> settingsStrings;
|
||||
|
||||
#else
|
||||
#include <linux/gpio/LinuxGPIOPin.h>
|
||||
|
@ -154,6 +155,28 @@ void portduinoSetup()
|
|||
settingsMap[has_gps] = 1;
|
||||
}
|
||||
}
|
||||
settingsMap[displayPanel] = no_screen;
|
||||
if (yamlConfig["Display"]) {
|
||||
if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7789")
|
||||
settingsMap[displayPanel] = st7789;
|
||||
settingsMap[displayHeight] = yamlConfig["Display"]["Height"].as<int>(0);
|
||||
settingsMap[displayWidth] = yamlConfig["Display"]["Width"].as<int>(0);
|
||||
settingsMap[displayDC] = yamlConfig["Display"]["DC"].as<int>(-1);
|
||||
settingsMap[displayCS] = yamlConfig["Display"]["CS"].as<int>(-1);
|
||||
settingsMap[displayBacklight] = yamlConfig["Display"]["Backlight"].as<int>(-1);
|
||||
settingsMap[displayReset] = yamlConfig["Display"]["Reset"].as<int>(-1);
|
||||
settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as<bool>(false);
|
||||
}
|
||||
settingsMap[touchscreenModule] = no_touchscreen;
|
||||
if (yamlConfig["Touchscreen"]) {
|
||||
if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "XPT2046")
|
||||
settingsMap[touchscreenModule] = xpt2046;
|
||||
settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as<int>(-1);
|
||||
settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as<int>(-1);
|
||||
}
|
||||
if (yamlConfig["Input"]) {
|
||||
settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as<std::string>("");
|
||||
}
|
||||
|
||||
} catch (YAML::Exception e) {
|
||||
std::cout << "*** Exception " << e.what() << std::endl;
|
||||
|
@ -191,6 +214,23 @@ void portduinoSetup()
|
|||
}
|
||||
}
|
||||
|
||||
if (settingsMap[displayPanel] != no_screen) {
|
||||
if (settingsMap[displayCS] > 0)
|
||||
initGPIOPin(settingsMap[displayCS], gpioChipName);
|
||||
if (settingsMap[displayDC] > 0)
|
||||
initGPIOPin(settingsMap[displayDC], gpioChipName);
|
||||
if (settingsMap[displayBacklight] > 0)
|
||||
initGPIOPin(settingsMap[displayBacklight], gpioChipName);
|
||||
if (settingsMap[displayReset] > 0)
|
||||
initGPIOPin(settingsMap[displayReset], gpioChipName);
|
||||
}
|
||||
if (settingsMap[touchscreenModule] != no_touchscreen) {
|
||||
if (settingsMap[touchscreenCS] > 0)
|
||||
initGPIOPin(settingsMap[touchscreenCS], gpioChipName);
|
||||
if (settingsMap[touchscreenIRQ] > 0)
|
||||
initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName);
|
||||
}
|
||||
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
@ -250,8 +290,9 @@ int initGPIOPin(int pinNum, std::string gpioChipName)
|
|||
csPin->setSilent();
|
||||
gpioBind(csPin);
|
||||
return ERRNO_OK;
|
||||
} catch (std::invalid_argument &e) {
|
||||
std::cout << "Warning, cannot claim pin" << gpio_name << std::endl;
|
||||
} catch (...) {
|
||||
std::exception_ptr p = std::current_exception();
|
||||
std::cout << "Warning, cannot claim pin " << gpio_name << (p ? p.__cxa_exception_type()->name() : "null") << std::endl;
|
||||
return ERRNO_DISABLED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,35 @@
|
|||
#ifdef ARCH_RASPBERRY_PI
|
||||
#include <map>
|
||||
|
||||
extern std::map<int, int> settingsMap;
|
||||
enum configNames {
|
||||
use_sx1262,
|
||||
cs,
|
||||
irq,
|
||||
busy,
|
||||
reset,
|
||||
dio2_as_rf_switch,
|
||||
use_rf95,
|
||||
user,
|
||||
gpiochip,
|
||||
has_gps,
|
||||
touchscreenModule,
|
||||
touchscreenCS,
|
||||
touchscreenIRQ,
|
||||
displayPanel,
|
||||
displayWidth,
|
||||
displayHeight,
|
||||
displayCS,
|
||||
displayDC,
|
||||
displayBacklight,
|
||||
displayReset,
|
||||
displayRotate,
|
||||
keyboardDevice
|
||||
};
|
||||
enum { no_screen, st7789 };
|
||||
enum { no_touchscreen, xpt2046 };
|
||||
|
||||
enum { use_sx1262, cs, irq, busy, reset, dio2_as_rf_switch, use_rf95, user, gpiochip, has_gps };
|
||||
extern std::map<configNames, int> settingsMap;
|
||||
extern std::map<configNames, std::string> settingsStrings;
|
||||
int initGPIOPin(int pinNum, std::string gpioChipname);
|
||||
|
||||
#endif
|
|
@ -19,4 +19,5 @@ extends = portduino_base
|
|||
build_flags = ${portduino_base.build_flags} -O0 -lgpiod -I variants/portduino -DARCH_RASPBERRY_PI -lpigpio -lyaml-cpp
|
||||
board = linux_arm
|
||||
lib_deps = ${portduino_base.lib_deps}
|
||||
https://github.com/jp-bennett/LovyanGFX.git#jp-bennett-patch-1 ; lovyan03/LovyanGFX@^1.1.9
|
||||
build_src_filter = ${portduino_base.build_src_filter}
|
|
@ -1,6 +1,7 @@
|
|||
#if defined(ARCH_RASPBERRY_PI)
|
||||
#define HAS_WIRE 1
|
||||
#define HAS_SCREEN 1
|
||||
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
||||
|
||||
#else // Pine64 mode.
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue