Преглед на файлове

add heltec meshpocket board.

Quency-D преди 11 месеца
родител
ревизия
c9671d7d8d

+ 53 - 0
boards/heltec_mesh_pocket.json

@@ -0,0 +1,53 @@
+{
+    "build": {
+      "arduino": {
+        "ldscript": "nrf52840_s140_v6.ld"
+      },
+      "core": "nRF5",
+      "cpu": "cortex-m4",
+      "extra_flags": "-DNRF52840_XXAA",
+      "f_cpu": "64000000L",
+      "hwids": [
+        ["0x239A", "0x4405"],
+        ["0x239A", "0x0029"],
+        ["0x239A", "0x002A"]
+      ],
+      "usb_product": "HT-n5262",
+      "mcu": "nrf52840",
+      "variant": "heltec_mesh_pocket",
+      "variants_dir": "variants",
+      "bsp": {
+        "name": "adafruit"
+      },
+      "softdevice": {
+        "sd_flags": "-DS140",
+        "sd_name": "s140",
+        "sd_version": "6.1.1",
+        "sd_fwid": "0x00B6"
+      },
+      "bootloader": {
+        "settings_addr": "0xFF000"
+      }
+    },
+    "connectivity": ["bluetooth"],
+    "debug": {
+      "jlink_device": "nRF52840_xxAA",
+      "onboard_tools": ["jlink"],
+      "svd_path": "nrf52840.svd",
+      "openocd_target": "nrf52840-mdk-rs"
+    },
+    "frameworks": ["arduino"],
+    "name": "Heltec nrf (Adafruit BSP)",
+    "upload": {
+      "maximum_ram_size": 248832,
+      "maximum_size": 815104,
+      "speed": 115200,
+      "protocol": "nrfutil",
+      "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
+      "use_1200bps_touch": true,
+      "require_upload_port": true,
+      "wait_for_upload_port": true
+    },
+    "url": "https://heltec.org/project/meshpocket/",
+    "vendor": "Heltec"
+  }

+ 1 - 0
examples/simple_repeater/main.cpp

@@ -818,6 +818,7 @@ void setup() {
 #ifdef DISPLAY_CLASS
   if (display.begin()) {
     display.startFrame();
+    display.setCursor(0, 0);
     display.print("Please wait...");
     display.endFrame();
   }

+ 1 - 0
examples/simple_room_server/main.cpp

@@ -993,6 +993,7 @@ void setup() {
 #ifdef DISPLAY_CLASS
   if (display.begin()) {
     display.startFrame();
+    display.setCursor(0, 0);
     display.print("Please wait...");
     display.endFrame();
   }

+ 10 - 13
src/helpers/ui/GxEPDDisplay.cpp

@@ -5,9 +5,6 @@
   #define DISPLAY_ROTATION 3
 #endif
 
-#define SCALE_X   1.5625f   //  200 / 128
-#define SCALE_Y   1.5625f   //  200 / 128
-
 bool GxEPDDisplay::begin() {
   display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
   SPI1.begin();
@@ -77,7 +74,7 @@ void GxEPDDisplay::setColor(Color c) {
 }
 
 void GxEPDDisplay::setCursor(int x, int y) {
-  display.setCursor(x*SCALE_X, (y+10)*SCALE_Y);
+  display.setCursor((x+offset_x)*scale_x, (y+offset_y)*scale_y);
 }
 
 void GxEPDDisplay::print(const char* str) {
@@ -85,17 +82,17 @@ void GxEPDDisplay::print(const char* str) {
 }
 
 void GxEPDDisplay::fillRect(int x, int y, int w, int h) {
-  display.fillRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
+  display.fillRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
 }
 
 void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
-  display.drawRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color);
+  display.drawRect(x*scale_x, y*scale_y, w*scale_x, h*scale_y, _curr_color);
 }
 
 void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
   // Calculate the base position in display coordinates
-  uint16_t startX = x * SCALE_X;
-  uint16_t startY = y * SCALE_Y;
+  uint16_t startX = x * scale_x;
+  uint16_t startY = y * scale_y;
   
   // Width in bytes for bitmap processing
   uint16_t widthInBytes = (w + 7) / 8;
@@ -103,15 +100,15 @@ void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
   // Process the bitmap row by row
   for (uint16_t by = 0; by < h; by++) {
     // Calculate the target y-coordinates for this logical row
-    int y1 = startY + (int)(by * SCALE_Y);
-    int y2 = startY + (int)((by + 1) * SCALE_Y);
+    int y1 = startY + (int)(by * scale_y);
+    int y2 = startY + (int)((by + 1) * scale_y);
     int block_h = y2 - y1;
     
     // Scan across the row bit by bit
     for (uint16_t bx = 0; bx < w; bx++) {
       // Calculate the target x-coordinates for this logical column
-      int x1 = startX + (int)(bx * SCALE_X);
-      int x2 = startX + (int)((bx + 1) * SCALE_X);
+      int x1 = startX + (int)(bx * scale_x);
+      int x2 = startX + (int)((bx + 1) * scale_x);
       int block_w = x2 - x1;
       
       // Get the current bit
@@ -132,7 +129,7 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) {
   int16_t x1, y1;
   uint16_t w, h;
   display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h);
-  return ceil((w + 1) / SCALE_X);
+  return ceil((w + 1) / scale_x);
 }
 
 void GxEPDDisplay::endFrame() {

+ 18 - 6
src/helpers/ui/GxEPDDisplay.h

@@ -13,11 +13,8 @@
 #include <Fonts/FreeSansBold12pt7b.h>
 #include <Fonts/FreeSans18pt7b.h>
 
-#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
-#define GxEPD2_DRIVER_CLASS GxEPD2_150_BN  // DEPG0150BN 200x200, SSD1681, (FPC8101), TTGO T5 V2.4.1
-
 #include <epd/GxEPD2_150_BN.h>  // 1.54" b/w
-
+#include <epd/GxEPD2_213_B74.h> // 2.13" b/w
 #include "DisplayDriver.h"
 
 //GxEPD2_BW<GxEPD2_150_BN, 200> display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)); // DEPG0150BN 200x200, SSD1681, TTGO T5 V2.4.1
@@ -25,15 +22,30 @@
 
 class GxEPDDisplay : public DisplayDriver {
 
+#if defined(HELTEC_MESH_POCKET)
+  GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> display;
+  const float scale_x  = EINK_SCALE_X; 
+  const float scale_y  = EINK_SCALE_Y;
+  const float offset_x = EINK_X_OFFSET;
+  const float offset_y = EINK_Y_OFFSET;
+#else
   GxEPD2_BW<GxEPD2_150_BN, 200> display;
+  const float scale_x  = 1.5625f;
+  const float scale_y  = 1.5625f;
+  const float offset_x = 0;
+  const float offset_y = 10;
+#endif
   bool _init = false;
   bool _isOn = false;
   uint16_t _curr_color;
 
 public:
   // there is a margin in y...
-  GxEPDDisplay() : DisplayDriver(128, 128), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {
-  }
+#if defined(HELTEC_MESH_POCKET)
+  GxEPDDisplay() : DisplayDriver(128, 128), display(EINK_DISPLAY_MODEL(PIN_DISPLAY_CS, PIN_DISPLAY_DC, PIN_DISPLAY_RST, PIN_DISPLAY_BUSY)) {}
+#else
+  GxEPDDisplay() : DisplayDriver(128, 128), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {}
+#endif
 
   bool begin();
 

+ 72 - 0
variants/mesh_pocket/MeshPocket.cpp

@@ -0,0 +1,72 @@
+#include <Arduino.h>
+#include "MeshPocket.h"
+#include <bluefruit.h>
+#include <Wire.h>
+
+static BLEDfu bledfu;
+
+static void connect_callback(uint16_t conn_handle)
+{
+  (void)conn_handle;
+  MESH_DEBUG_PRINTLN("BLE client connected");
+}
+
+static void disconnect_callback(uint16_t conn_handle, uint8_t reason)
+{
+  (void)conn_handle;
+  (void)reason;
+
+  MESH_DEBUG_PRINTLN("BLE client disconnected");
+}
+
+void HeltecMeshPocket::begin() {
+  // for future use, sub-classes SHOULD call this from their begin()
+  startup_reason = BD_STARTUP_NORMAL;
+  Serial.begin(115200);
+  pinMode(PIN_VBAT_READ, INPUT);
+
+  pinMode(PIN_USER_BTN, INPUT);
+}
+
+bool HeltecMeshPocket::startOTAUpdate(const char* id, char reply[]) {
+  // Config the peripheral connection with maximum bandwidth
+  // more SRAM required by SoftDevice
+  // Note: All config***() function must be called before begin()
+  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
+  Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16);
+
+  Bluefruit.begin(1, 0);
+  // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
+  Bluefruit.setTxPower(4);
+  // Set the BLE device name
+  Bluefruit.setName("MESH_POCKET_OTA");
+
+  Bluefruit.Periph.setConnectCallback(connect_callback);
+  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
+
+  // To be consistent OTA DFU should be added first if it exists
+  bledfu.begin();
+
+  // Set up and start advertising
+  // Advertising packet
+  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
+  Bluefruit.Advertising.addTxPower();
+  Bluefruit.Advertising.addName();
+
+  /* Start Advertising
+    - Enable auto advertising if disconnected
+    - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
+    - Timeout for fast mode is 30 seconds
+    - Start(timeout) with timeout = 0 will advertise forever (until connected)
+
+    For recommended advertising interval
+    https://developer.apple.com/library/content/qa/qa1931/_index.html
+  */
+  Bluefruit.Advertising.restartOnDisconnect(true);
+  Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
+  Bluefruit.Advertising.setFastTimeout(30);   // number of seconds in fast mode
+  Bluefruit.Advertising.start(0);             // 0 = Don't stop advertising after n seconds
+
+  strcpy(reply, "OK - started");
+  return true;
+}

+ 45 - 0
variants/mesh_pocket/MeshPocket.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <Arduino.h>
+#include <MeshCore.h>
+
+// built-ins
+#define  PIN_VBAT_READ    29
+#define  PIN_BAT_CTL      34
+#define  MV_LSB   (3000.0F / 4096.0F) // 12-bit ADC with 3.0V input range
+
+class HeltecMeshPocket : public mesh::MainBoard {
+protected:
+  uint8_t startup_reason;
+
+public:
+  void begin();
+  uint8_t getStartupReason() const override { return startup_reason; }
+
+
+
+  uint16_t getBattMilliVolts() override {
+    int adcvalue = 0;
+    analogReadResolution(12);
+    analogReference(AR_INTERNAL_3_0);
+    pinMode(PIN_BAT_CTL, OUTPUT);          // battery adc can be read only ctrl pin set to high
+    pinMode(PIN_VBAT_READ, INPUT);
+    digitalWrite(PIN_BAT_CTL, HIGH);
+
+    delay(10);
+    adcvalue = analogRead(PIN_VBAT_READ);
+    digitalWrite(PIN_BAT_CTL, LOW);
+
+    return (uint16_t)((float)adcvalue * MV_LSB * 4.9);
+  }
+
+  const char* getManufacturerName() const override {
+    return "Heltec MeshPocket";
+  }
+
+  void reboot() override {
+    NVIC_SystemReset();
+  }
+
+  bool startOTAUpdate(const char* id, char reply[]) override;
+};

+ 104 - 0
variants/mesh_pocket/platformio.ini

@@ -0,0 +1,104 @@
+[Mesh_pocket]
+extends = nrf52_base
+board = heltec_mesh_pocket
+platform_packages = framework-arduinoadafruitnrf52
+board_build.ldscript = boards/nrf52840_s140_v6.ld
+build_flags =  ${nrf52_base.build_flags}
+  -I src/helpers/nrf52
+  -I lib/nrf52/s140_nrf52_6.1.1_API/include
+  -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
+  -I variants/mesh_pocket
+  -D HELTEC_MESH_POCKET
+  -D RADIO_CLASS=CustomSX1262
+  -D WRAPPER_CLASS=CustomSX1262Wrapper
+  -D LORA_TX_POWER=22
+  -D SX126X_CURRENT_LIMIT=140
+  -D SX126X_RX_BOOSTED_GAIN=1
+  -D EINK_DISPLAY_MODEL=GxEPD2_213_B74
+  -D EINK_SCALE_X=1.953125f
+  -D EINK_SCALE_Y=1.28f
+  -D EINK_X_OFFSET=0
+  -D EINK_Y_OFFSET=10
+  -D DISPLAY_CLASS=GxEPDDisplay
+build_src_filter = ${nrf52_base.build_src_filter}
+  +<helpers/*.cpp>
+  +<../variants/mesh_pocket>
+  +<helpers/ui/GxEPDDisplay.cpp>
+lib_deps =
+  ${nrf52_base.lib_deps}
+  adafruit/Adafruit EPD @ 4.6.1
+  rweather/Crypto @ ^0.4.0
+  stevemarple/MicroNMEA @ ^2.0.6
+  zinggjm/GxEPD2 @ 1.6.2
+
+debug_tool = jlink
+upload_protocol = nrfutil
+
+[env:Mesh_pocket_repeater]
+extends = Mesh_pocket
+build_src_filter = ${Mesh_pocket.build_src_filter}
+  +<../examples/simple_repeater>
+
+build_flags =
+  ${Mesh_pocket.build_flags}
+  -D ADVERT_NAME='"Heltec_Mesh_Pocket Repeater"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D MAX_NEIGHBOURS=8
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:Mesh_pocket_room_server]
+extends = Mesh_pocket
+build_src_filter = ${Mesh_pocket.build_src_filter}
+  +<../examples/simple_room_server>
+build_flags =
+  ${Mesh_pocket.build_flags}
+  -D ADVERT_NAME='"Heltec_Mesh_Pocket Room"'
+  -D ADVERT_LAT=0.0
+  -D ADVERT_LON=0.0
+  -D ADMIN_PASSWORD='"password"'
+  -D ROOM_PASSWORD='"hello"'
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+[env:Mesh_pocket_companion_radio_ble]
+extends = Mesh_pocket
+build_flags =
+  ${Mesh_pocket.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=100
+  -D MAX_GROUP_CHANNELS=8
+  -D BLE_PIN_CODE=123456
+  -D OFFLINE_QUEUE_SIZE=256
+;  -D BLE_DEBUG_LOGGING=1
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+
+build_src_filter = ${Mesh_pocket.build_src_filter}
+  +<helpers/nrf52/SerialBLEInterface.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Mesh_pocket.lib_deps}
+  densaugeo/base64 @ ~1.4.0
+
+[env:Mesh_pocket_companion_radio_usb]
+extends = Mesh_pocket
+build_flags =
+  ${Mesh_pocket.build_flags}
+  -I examples/companion_radio/ui-new
+  -D MAX_CONTACTS=100
+  -D MAX_GROUP_CHANNELS=8
+;  -D BLE_PIN_CODE=123456
+;  -D BLE_DEBUG_LOGGING=1
+;  -D MESH_PACKET_LOGGING=1
+;  -D MESH_DEBUG=1
+build_src_filter = ${Mesh_pocket.build_src_filter}
+  +<helpers/nrf52/*.cpp>
+  +<../examples/companion_radio/*.cpp>
+  +<../examples/companion_radio/ui-new/*.cpp>
+lib_deps =
+  ${Mesh_pocket.lib_deps}
+  densaugeo/base64 @ ~1.4.0

+ 72 - 0
variants/mesh_pocket/target.cpp

@@ -0,0 +1,72 @@
+#include <Arduino.h>
+#include "target.h"
+#include <helpers/ArduinoHelpers.h>
+#include <helpers/sensors/MicroNMEALocationProvider.h>
+
+HeltecMeshPocket board;
+
+RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
+
+WRAPPER_CLASS radio_driver(radio, board);
+
+MeshPocketSensorManager sensors = MeshPocketSensorManager();
+
+VolatileRTCClock fallback_clock;
+AutoDiscoverRTCClock rtc_clock(fallback_clock);
+
+#ifdef DISPLAY_CLASS
+  DISPLAY_CLASS display;
+  MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
+#endif
+
+bool radio_init() {
+  return radio.std_init(&SPI);
+}
+
+uint32_t radio_get_rng_seed() {
+  return radio.random(0x7FFFFFFF);
+}
+
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
+  radio.setFrequency(freq);
+  radio.setSpreadingFactor(sf);
+  radio.setBandwidth(bw);
+  radio.setCodingRate(cr);
+}
+
+void radio_set_tx_power(uint8_t dbm) {
+  radio.setOutputPower(dbm);
+}
+
+mesh::LocalIdentity radio_new_identity() {
+  RadioNoiseListener rng(radio);
+  return mesh::LocalIdentity(&rng);  // create new random identity
+}
+
+bool MeshPocketSensorManager::begin() {
+  return true;
+}
+
+void MeshPocketSensorManager::loop() {
+
+}
+
+bool MeshPocketSensorManager::querySensors(uint8_t requester_permission, CayenneLPP& telemetry) {
+  return true;
+}
+
+int MeshPocketSensorManager::getNumSettings() const {
+  return 0;
+}
+
+const char* MeshPocketSensorManager::getSettingName(int i) const {
+  return NULL;
+}
+
+const char* MeshPocketSensorManager::getSettingValue(int i) const {
+  return NULL;
+}
+
+bool MeshPocketSensorManager::setSettingValue(const char* name, const char* value) {
+  return false;
+}

+ 45 - 0
variants/mesh_pocket/target.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#define RADIOLIB_STATIC_ONLY 1
+#include <RadioLib.h>
+#include <helpers/radiolib/RadioLibWrappers.h>
+#include <helpers/radiolib/CustomSX1262Wrapper.h>
+#include <helpers/AutoDiscoverRTCClock.h>
+#include <helpers/SensorManager.h>
+#include <helpers/sensors/LocationProvider.h>
+#include "MeshPocket.h"
+
+#ifdef DISPLAY_CLASS
+#include <helpers/ui/GxEPDDisplay.h>
+#include <helpers/ui/MomentaryButton.h>
+#endif
+
+extern HeltecMeshPocket board;
+extern WRAPPER_CLASS radio_driver;
+extern AutoDiscoverRTCClock rtc_clock;
+
+#ifdef DISPLAY_CLASS
+  extern DISPLAY_CLASS display;
+  extern MomentaryButton user_btn;
+#endif
+
+bool radio_init();
+uint32_t radio_get_rng_seed();
+void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
+void radio_set_tx_power(uint8_t dbm);
+mesh::LocalIdentity radio_new_identity();
+
+class MeshPocketSensorManager : public SensorManager {
+public:
+  MeshPocketSensorManager() {};
+  bool begin() override;
+  bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry);
+  void loop();
+  int getNumSettings() const override;
+  const char* getSettingName(int i) const override;
+  const char* getSettingValue(int i) const override;
+  bool setSettingValue(const char* name, const char* value) override;
+};
+
+extern MeshPocketSensorManager sensors;
+

+ 15 - 0
variants/mesh_pocket/variant.cpp

@@ -0,0 +1,15 @@
+#include "variant.h"
+#include "nrf.h"
+#include "wiring_constants.h"
+#include "wiring_digital.h"
+
+const int MISO = PIN_SPI1_MISO;
+const int MOSI = PIN_SPI1_MOSI;
+const int SCK = PIN_SPI1_SCK;
+
+const uint32_t g_ADigitalPinMap[] = {
+    // P0 - pins 0 and 1 are hardwired for xtal and should never be enabled
+    0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+
+    // P1
+    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};

+ 124 - 0
variants/mesh_pocket/variant.h

@@ -0,0 +1,124 @@
+/*
+ * variant.h
+ * MIT License
+ */
+
+#pragma once
+
+#include "WVariant.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Low frequency clock source
+
+#define USE_LFXO    // 32.768 kHz crystal oscillator
+#define VARIANT_MCK (64000000ul)
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Power
+
+#define BATTERY_PIN             (0 + 29)
+#define PIN_BAT_CTRL            (32 + 2)
+#define ADC_MULTIPLIER          (4.90F)
+
+#define ADC_RESOLUTION          (14)
+#define BATTERY_SENSE_RES       (12)
+
+#define AREF_VOLTAGE            (3.0)
+
+////////////////////////////////////////////////////////////////////////////////
+// Number of pins
+
+#define PINS_COUNT              (48)
+#define NUM_DIGITAL_PINS        (48)
+#define NUM_ANALOG_INPUTS       (1)
+#define NUM_ANALOG_OUTPUTS      (0)
+
+////////////////////////////////////////////////////////////////////////////////
+// UART pin definition
+
+#define PIN_SERIAL1_RX          (37)
+#define PIN_SERIAL1_TX          (39)
+
+#define PIN_SERIAL2_RX          (7)
+#define PIN_SERIAL2_TX          (8)
+
+////////////////////////////////////////////////////////////////////////////////
+// I2C pin definition
+#define WIRE_INTERFACES_COUNT 	(1)
+
+#define PIN_WIRE_SDA (32+15)
+#define PIN_WIRE_SCL (32+13)
+
+////////////////////////////////////////////////////////////////////////////////
+// Builtin LEDs
+
+#define LED_BUILTIN             (13)
+#define PIN_LED                 LED_BUILTIN
+#define LED_RED                 LED_BUILTIN
+#define LED_BLUE                (-1)            // No blue led, prevents Bluefruit flashing the green LED during advertising
+#define PIN_STATUS_LED          LED_BUILTIN
+
+#define LED_STATE_ON            LOW
+
+////////////////////////////////////////////////////////////////////////////////
+// Builtin buttons
+
+#define PIN_BUTTON1             (32 + 10)
+#define BUTTON_PIN              PIN_BUTTON1
+
+// #define PIN_BUTTON2             (0 + 18)
+// #define BUTTON_PIN2             PIN_BUTTON2
+
+#define PIN_USER_BTN            BUTTON_PIN
+
+////////////////////////////////////////////////////////////////////////////////
+
+// SPI pin definition
+#define SPI_INTERFACES_COUNT    (2)
+
+// Lora
+#define USE_SX1262
+#define SX126X_CS               (0 + 26)
+#define SX126X_DIO1             (0 + 16)
+#define SX126X_BUSY             (0 + 15)
+#define SX126X_RESET            (0 + 12)
+#define SX126X_DIO2_AS_RF_SWITCH true
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+
+#define PIN_SPI_MISO            (32 + 9)
+#define PIN_SPI_MOSI            (0 + 5)
+#define PIN_SPI_SCK             (0 + 4)
+
+#define LORA_CS       SX126X_CS
+#define P_LORA_DIO_1  SX126X_DIO1
+#define P_LORA_NSS    SX126X_CS
+#define P_LORA_RESET  SX126X_RESET
+#define P_LORA_BUSY   SX126X_BUSY
+#define P_LORA_SCLK   PIN_SPI_SCK
+#define P_LORA_MISO   PIN_SPI_MISO
+#define P_LORA_MOSI   PIN_SPI_MOSI
+
+
+////////////////////////////////////////////////////////////////////////////////
+// EInk
+
+#define PIN_DISPLAY_CS                 (24)
+#define PIN_DISPLAY_BUSY               (32 + 6)
+#define PIN_DISPLAY_DC                 (31)
+#define PIN_DISPLAY_RST                (32 + 4)
+
+#define PIN_SPI1_MISO           (-1)
+#define PIN_SPI1_MOSI           (20)
+#define PIN_SPI1_SCK            (22)
+
+
+// GxEPD2 needs that for a panel that is not even used !
+extern const int MISO;
+extern const int MOSI;
+extern const int SCK;
+
+
+#undef HAS_GPS
+#define HAS_GPS 0
+#define HAS_RTC 0