diff --git a/boards/bpi_picow_esp32_s3.json b/boards/bpi_picow_esp32_s3.json index 75983d845..9a20dd57f 100644 --- a/boards/bpi_picow_esp32_s3.json +++ b/boards/bpi_picow_esp32_s3.json @@ -28,8 +28,6 @@ "flash_size": "8MB", "maximum_ram_size": 327680, "maximum_size": 8388608, - "use_1200bps_touch": true, - "wait_for_upload_port": true, "require_upload_port": true, "speed": 921600 }, diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 186887457..b42936c44 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -108,10 +108,12 @@ class LGFX : public lgfx::LGFX_Device lgfx::Panel_ST7789 _panel_instance; lgfx::Bus_SPI _bus_instance; lgfx::Light_PWM _light_instance; +#if HAS_TOUCHSCREEN #ifdef T_WATCH_S3 lgfx::Touch_FT5x06 _touch_instance; #else lgfx::Touch_GT911 _touch_instance; +#endif #endif public: @@ -126,13 +128,14 @@ class LGFX : public lgfx::LGFX_Device cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing // 80MHz by an integer) cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving - cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin - cfg.use_lock = true; // Set to true to use transaction locking - cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch / - cfg.pin_sclk = ST7789_SCK; // Set SPI SCLK pin number - cfg.pin_mosi = ST7789_SDA; // Set SPI MOSI pin number - cfg.pin_miso = ST7789_MISO; // Set SPI MISO pin number (-1 = disable) - cfg.pin_dc = ST7789_RS; // Set SPI DC pin number (-1 = disable) + cfg.spi_3wire = false; + cfg.use_lock = true; // Set to true to use transaction locking + cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch / + // SPI_DMA_CH_AUTO=auto setting) + cfg.pin_sclk = ST7789_SCK; // Set SPI SCLK pin number + cfg.pin_mosi = ST7789_SDA; // Set SPI MOSI pin number + cfg.pin_miso = ST7789_MISO; // Set SPI MISO pin number (-1 = disable) + cfg.pin_dc = ST7789_RS; // Set SPI DC pin number (-1 = disable) _bus_instance.config(cfg); // applies the set value to the bus. _panel_instance.setBus(&_bus_instance); // set the bus on the panel. @@ -181,6 +184,7 @@ class LGFX : public lgfx::LGFX_Device _panel_instance.setLight(&_light_instance); // Set the backlight on the panel. } +#if HAS_TOUCHSCREEN // Configure settings for touch screen control. { auto cfg = _touch_instance.config(); @@ -210,6 +214,7 @@ class LGFX : public lgfx::LGFX_Device _touch_instance.config(cfg); _panel_instance.setTouch(&_touch_instance); } +#endif setPanel(&_panel_instance); // Sets the panel to use. } @@ -424,7 +429,7 @@ bool TFTDisplay::connect() #endif tft.init(); -#if defined(T_DECK) +#if defined(T_DECK) || defined(PICOMPUTER_S3) tft.setRotation(1); // M5Stack/T-Deck have the TFT in landscape #elif defined(M5STACK) || defined(T_WATCH_S3) tft.setRotation(0); // T-Watch S3 has the TFT in portrait diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index cdffbaf7e..f7fd62645 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -28,17 +28,6 @@ uint8_t read_from_14004(TwoWire *i2cBus, uint8_t reg, uint8_t *data, uint8_t len return readflag; } -// Unused for now - flagging it off -#if 0 -void write_to_14004(const TwoWire * i2cBus, uint8_t reg, uint8_t data) -{ - i2cBus->beginTransmission(CARDKB_ADDR); - i2cBus->write(reg); - i2cBus->write(data); - i2cBus->endTransmission(); // stop transmitting -} -#endif - int32_t KbI2cBase::runOnce() { if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) { diff --git a/src/input/kbMatrixBase.cpp b/src/input/kbMatrixBase.cpp new file mode 100644 index 000000000..6e201b97f --- /dev/null +++ b/src/input/kbMatrixBase.cpp @@ -0,0 +1,127 @@ +#include "kbMatrixBase.h" +#include "configuration.h" + +const byte keys_cols[] = KEYS_COLS; +const byte keys_rows[] = KEYS_ROWS; + +#if INPUTBROKER_MATRIX_TYPE == 1 + +unsigned char KeyMap[3][sizeof(keys_rows)][sizeof(keys_cols)] = {{{' ', '.', 'm', 'n', 'b', 0xb6}, + {0x0d, 'l', 'k', 'j', 'h', 0xb4}, + {'p', 'o', 'i', 'u', 'y', 0xb5}, + {0x08, 'z', 'x', 'c', 'v', 0xb7}, + {'a', 's', 'd', 'f', 'g', 0x09}, + {'q', 'w', 'e', 'r', 't', 0x1a}}, + {// SHIFT + {':', ';', 'M', 'N', 'B', 0xb6}, + {0x0d, 'L', 'K', 'J', 'H', 0xb4}, + {'P', 'O', 'I', 'U', 'Y', 0xb5}, + {0x08, 'Z', 'X', 'C', 'V', 0xb7}, + {'A', 'S', 'D', 'F', 'G', 0x09}, + {'Q', 'W', 'E', 'R', 'T', 0x1a}}, + {// SHIFT-SHIFT + {'_', ',', '>', '<', '"', '{'}, + {'~', '-', '*', '&', '+', '['}, + {'0', '9', '8', '7', '6', '}'}, + {'=', '(', ')', '?', '/', ']'}, + {'!', '@', '#', '$', '%', '\\'}, + {'1', '2', '3', '4', '5', 0x1a}}}; +#endif + +KbMatrixBase::KbMatrixBase(const char *name) : concurrency::OSThread(name) +{ + this->_originName = name; +} + +int32_t KbMatrixBase::runOnce() +{ + if (!INPUTBROKER_MATRIX_TYPE) { + // Input device is not requested. + return disable(); + } + + if (firstTime) { + // This is the first time the OSThread library has called this function, so do port setup + firstTime = 0; + for (byte i = 0; i < sizeof(keys_rows); i++) { + pinMode(keys_rows[i], OUTPUT); + digitalWrite(keys_rows[i], HIGH); + } + for (byte i = 0; i < sizeof(keys_cols); i++) { + pinMode(keys_cols[i], INPUT_PULLUP); + } + } + + key = 0; + + if (INPUTBROKER_MATRIX_TYPE == 1) { + // scan for keypresses + for (byte i = 0; i < sizeof(keys_rows); i++) { + digitalWrite(keys_rows[i], LOW); + for (byte j = 0; j < sizeof(keys_cols); j++) { + if (digitalRead(keys_cols[j]) == LOW) { + key = KeyMap[shift][i][j]; + } + } + digitalWrite(keys_rows[i], HIGH); + } + // debounce + if (key != prevkey) { + if (key != 0) { + LOG_DEBUG("Key 0x%x pressed\n", key); + // reset shift now that we have a keypress + InputEvent e; + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + e.source = this->_originName; + switch (key) { + case 0x1b: // ESC + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; + break; + case 0x08: // Back + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = key; + break; + case 0xb5: // Up + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; + break; + case 0xb6: // Down + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN; + break; + case 0xb4: // Left + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT; + e.kbchar = key; + break; + case 0xb7: // Right + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; + e.kbchar = key; + break; + case 0x0d: // Enter + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; + break; + case 0x00: // nopress + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + break; + case 0x1a: // Shift + shift++; + if (shift > 2) { + shift = 0; + } + break; + default: // all other keys + e.inputEvent = ANYKEY; + e.kbchar = key; + break; + } + if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { + this->notifyObservers(&e); + } + } + prevkey = key; + } + + } else { + LOG_WARN("Unknown kb_model 0x%02x\n", INPUTBROKER_MATRIX_TYPE); + return disable(); + } + return 50; // Keyscan every 50msec to avoid key bounce +} diff --git a/src/input/kbMatrixBase.h b/src/input/kbMatrixBase.h new file mode 100644 index 000000000..8259fc07c --- /dev/null +++ b/src/input/kbMatrixBase.h @@ -0,0 +1,20 @@ +#pragma once + +#include "InputBroker.h" +#include "concurrency/OSThread.h" + +class KbMatrixBase : public Observable, public concurrency::OSThread +{ + public: + explicit KbMatrixBase(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; +}; \ No newline at end of file diff --git a/src/input/kbMatrixImpl.cpp b/src/input/kbMatrixImpl.cpp new file mode 100644 index 000000000..8f02c3ae3 --- /dev/null +++ b/src/input/kbMatrixImpl.cpp @@ -0,0 +1,16 @@ +#include "kbMatrixImpl.h" +#include "InputBroker.h" + +KbMatrixImpl *kbMatrixImpl; + +KbMatrixImpl::KbMatrixImpl() : KbMatrixBase("matrixKB") {} + +void KbMatrixImpl::init() +{ + if (!INPUTBROKER_MATRIX_TYPE) { + disable(); + return; + } + + inputBroker->registerSource(this); +} \ No newline at end of file diff --git a/src/input/kbMatrixImpl.h b/src/input/kbMatrixImpl.h new file mode 100644 index 000000000..ead4a2d57 --- /dev/null +++ b/src/input/kbMatrixImpl.h @@ -0,0 +1,19 @@ +#pragma once +#include "kbMatrixBase.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 KbMatrixImpl : public KbMatrixBase +{ + public: + KbMatrixImpl(); + void init(); +}; + +extern KbMatrixImpl *kbMatrixImpl; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 12ee157a5..5f0eaca30 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -480,7 +480,7 @@ void setup() #endif // We do this as early as possible because this loads preferences from flash - // but we need to do this after main cpu iniot (esp32setup), because we need the random seed set + // but we need to do this after main cpu init (esp32setup), because we need the random seed set nodeDB.init(); // If we're taking on the repeater role, use flood router @@ -545,6 +545,7 @@ void setup() #else // ESP32 SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS); + LOG_WARN("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS); SPI.setFrequency(4000000); #endif diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 7fffe9ea7..8c2a645ad 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -107,6 +107,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_T_DECK = 50, /* LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display */ meshtastic_HardwareModel_T_WATCH_S3 = 51, + /* LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display */ + meshtastic_HardwareModel_PICOMPUTER_S3 = 52, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 3ca718e99..43b9e2035 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -62,7 +62,7 @@ CannedMessageModule::CannedMessageModule() if (moduleConfig.canned_message.enabled) { this->loadProtoForModule(); if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address != CARDKB_ADDR) && - (cardkb_found.address != TDECK_KB_ADDR)) { + (cardkb_found.address != TDECK_KB_ADDR) && !INPUTBROKER_MATRIX_TYPE) { LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled\n"); this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED; disable(); diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 38fb7a4ae..ed1dd999a 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -4,6 +4,7 @@ #include "input/TrackballInterruptImpl1.h" #include "input/UpDownInterruptImpl1.h" #include "input/cardKbI2cImpl.h" +#include "input/kbMatrixImpl.h" #include "modules/AdminModule.h" #include "modules/CannedMessageModule.h" #include "modules/NodeInfoModule.h" @@ -60,6 +61,8 @@ void setupModules() upDownInterruptImpl1->init(); cardKbI2cImpl = new CardKbI2cImpl(); cardKbI2cImpl->init(); + kbMatrixImpl = new KbMatrixImpl(); + kbMatrixImpl->init(); #endif #if HAS_TRACKBALL trackballInterruptImpl1 = new TrackballInterruptImpl1(); diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 6e80b50e6..8f71bace6 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -117,6 +117,8 @@ #define HW_VENDOR meshtastic_HardwareModel_NANO_G1_EXPLORER #elif defined(BETAFPV_900_TX_NANO) #define HW_VENDOR meshtastic_HardwareModel_BETAFPV_900_NANO_TX +#elif defined(PICOMPUTER_S3) +#define HW_VENDOR meshtastic_HardwareModel_PICOMPUTER_S3 #endif // diff --git a/variants/picomputer-s3/pins_arduino.h b/variants/picomputer-s3/pins_arduino.h index ee0e34ebf..c84601b1e 100644 --- a/variants/picomputer-s3/pins_arduino.h +++ b/variants/picomputer-s3/pins_arduino.h @@ -18,20 +18,13 @@ static const uint8_t TX = 43; static const uint8_t RX = 44; // The default Wire will be mapped to PMU and RTC -static const uint8_t SDA = 12; -static const uint8_t SCL = 14; +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; -// Default SPI will be mapped to Radio +// Default SPI static const uint8_t MISO = 39; static const uint8_t SCK = 21; static const uint8_t MOSI = 38; -static const uint8_t SS = 17; - -//#define SPI_MOSI (11) -//#define SPI_SCK (14) -//#define SPI_MISO (2) -//#define SPI_CS (13) - -//#define SDCARD_CS SPI_CS +static const uint8_t SS = 40; #endif /* Pins_Arduino_h */ \ No newline at end of file diff --git a/variants/picomputer-s3/platformio.ini b/variants/picomputer-s3/platformio.ini index 0cb58f23c..c926ad382 100644 --- a/variants/picomputer-s3/platformio.ini +++ b/variants/picomputer-s3/platformio.ini @@ -8,20 +8,10 @@ board_level = extra upload_protocol = esptool build_flags = - ${esp32_base.build_flags} - -D PRIVATE_HW + ${esp32s3_base.build_flags} + -DPICOMPUTER_S3 -I variants/picomputer-s3 - -DST7789_DRIVER - -DUSER_SETUP_LOADED - -DTFT_SDA_READ - -DTFT_MOSI=19 - -DTFT_MISO=-1 - -DTFT_SCLK=18 - -DTFT_CS=21 - -DTFT_DC=16 - -DTFT_BL=20 - -DDISABLE_ALL_LIBRARY_WARNINGS lib_deps = - ${esp32_base.lib_deps} - bodmer/TFT_eSPI@^2.4.76 \ No newline at end of file + ${esp32s3_base.lib_deps} + lovyan03/LovyanGFX@^1.1.8 diff --git a/variants/picomputer-s3/variant.h b/variants/picomputer-s3/variant.h index cbb8fe65e..0f416efe0 100644 --- a/variants/picomputer-s3/variant.h +++ b/variants/picomputer-s3/variant.h @@ -1,24 +1,54 @@ -#define HAS_GPS 0 #undef GPS_RX_PIN #undef GPS_TX_PIN -#define USE_ST7789 +#define BUTTON_PIN 0 -#define LED_PIN 48 // If defined we will blink this LED +#define PIN_BUZZER 43 -#define BUTTON_PIN 0 // If defined, this will be used for user button presses, - -#define BUTTON_NEED_PULLUP +#define HAS_GPS 0 +#define HAS_WIRE 0 #define USE_RF95 // RFM95/SX127x -#define RF95_SCK 10 -#define RF95_MISO 12 -#define RF95_MOSI 11 -#define RF95_NSS 13 +#define RF95_SCK SCK // 21 +#define RF95_MISO MISO // 39 +#define RF95_MOSI MOSI // 38 +#define RF95_NSS SS // 40 #define LORA_RESET RADIOLIB_NC // per SX1276_Receive_Interrupt/utilities.h -#define LORA_DIO0 28 +#define LORA_DIO0 10 #define LORA_DIO1 RADIOLIB_NC -#define LORA_DIO2 RADIOLIB_NC \ No newline at end of file +#define LORA_DIO2 RADIOLIB_NC + +// Default SPI1 will be mapped to the display +#define ST7789_SDA 4 +#define ST7789_SCK 3 +#define ST7789_CS 6 +#define ST7789_RS 1 +#define ST7789_BL 5 + +#define ST7789_RESET -1 +#define ST7789_MISO -1 +#define ST7789_BUSY -1 +#define ST7789_SPI_HOST SPI3_HOST +#define ST7789_BACKLIGHT_EN 5 +#define SPI_FREQUENCY 40000000 +#define SPI_READ_FREQUENCY 16000000 +#define TFT_HEIGHT 320 +#define TFT_WIDTH 240 +#define TFT_OFFSET_X 0 +#define TFT_OFFSET_Y 0 +#define SCREEN_ROTATE +#define SCREEN_TRANSITION_FRAMERATE 5 + +#define INPUTBROKER_MATRIX_TYPE 1 + +#define KEYS_COLS \ + { \ + 44, 47, 17, 15, 13, 41 \ + } +#define KEYS_ROWS \ + { \ + 12, 16, 42, 18, 14, 7 \ + }