diff --git a/boards/meshlink.json b/boards/meshlink.json new file mode 100644 index 000000000..a608de88a --- /dev/null +++ b/boards/meshlink.json @@ -0,0 +1,52 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DMESHLINK -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x00B3"], + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"] + ], + "usb_product": "MeshLink", + "mcu": "nrf52840", + "variant": "meshlink", + "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", + "svd_path": "nrf52840.svd" + }, + "frameworks": ["arduino"], + "name": "MeshLink", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": ["nrfutil", "jlink", "nrfjprog", "stlink"], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://www.loraitalia.it", + "vendor": "LoraItalia" +} diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index 6c85582c0..9702b0086 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -140,6 +140,15 @@ bool EInkDisplay::connect() adafruitDisplay->setRotation(3); adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); } +#elif defined(MESHLINK) + { + auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1); + + adafruitDisplay = new GxEPD2_BW(*lowLevel); + adafruitDisplay->init(); + adafruitDisplay->setRotation(3); + adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); + } #elif defined(RAK4630) || defined(MAKERPYTHON) { if (eink_found) { diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 3353a020f..de8a1a353 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -1775,4 +1775,4 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; } /* extern "C" */ #endif -#endif +#endif \ No newline at end of file diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 5e31e0dc0..b43f5b256 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -162,7 +162,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta case meshtastic_AdminMessage_set_module_config_tag: LOG_INFO("Client set module config"); - handleSetModuleConfig(r->set_module_config); + if (!handleSetModuleConfig(r->set_module_config)) { + myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp); + } break; case meshtastic_AdminMessage_set_channel_tag: @@ -651,15 +653,23 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) saveChanges(changes, requiresReboot); } -void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) +bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) { if (!hasOpenEditTransaction) disableBluetooth(); switch (c.which_payload_variant) { case meshtastic_ModuleConfig_mqtt_tag: +#if MESHTASTIC_EXCLUDE_MQTT + LOG_WARN("Set module config: MESHTASTIC_EXCLUDE_MQTT is defined. Not setting MQTT config"); + return false; +#else LOG_INFO("Set module config: MQTT"); + if (!MQTT::isValidConfig(c.payload_variant.mqtt)) { + return false; + } moduleConfig.has_mqtt = true; moduleConfig.mqtt = c.payload_variant.mqtt; +#endif break; case meshtastic_ModuleConfig_serial_tag: LOG_INFO("Set module config: Serial"); @@ -727,6 +737,7 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) break; } saveChanges(SEGMENT_MODULECONFIG); + return true; } void AdminModule::handleSetChannel(const meshtastic_Channel &cc) diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index 3ab8ed4d8..f393e116b 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -50,7 +50,7 @@ class AdminModule : public ProtobufModule, public Obser void handleSetOwner(const meshtastic_User &o); void handleSetChannel(const meshtastic_Channel &cc); void handleSetConfig(const meshtastic_Config &c); - void handleSetModuleConfig(const meshtastic_ModuleConfig &c); + bool handleSetModuleConfig(const meshtastic_ModuleConfig &c); void handleSetChannel(); void handleSetHamMode(const meshtastic_HamParameters &req); void handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg); diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index bf53b1748..c6a95912b 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -60,7 +60,7 @@ SerialModule *serialModule; SerialModuleRadio *serialModuleRadio; -#if defined(TTGO_T_ECHO) || defined(CANARYONE) +#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial; #elif defined(CONFIG_IDF_TARGET_ESP32C6) @@ -158,7 +158,7 @@ int32_t SerialModule::runOnce() Serial.begin(baud); Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT); } -#elif !defined(TTGO_T_ECHO) && !defined(CANARYONE) +#elif !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) if (moduleConfig.serial.rxd && moduleConfig.serial.txd) { #ifdef ARCH_RP2040 Serial2.setFIFOSize(RX_BUFFER); @@ -214,7 +214,7 @@ int32_t SerialModule::runOnce() } } -#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) +#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) { processWXSerial(); @@ -416,7 +416,7 @@ uint32_t SerialModule::getBaudRate() */ void SerialModule::processWXSerial() { -#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) +#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(MESHLINK) static unsigned int lastAveraged = 0; static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded. static double dir_sum_sin = 0; diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 6043daa34..67eba82a6 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -41,6 +41,7 @@ MQTT *mqtt; namespace { constexpr int reconnectMax = 5; +constexpr uint16_t mqttPort = 1883; // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid @@ -245,6 +246,11 @@ std::pair parseHostAndPort(String server, uint16_t port = 0) } return std::make_pair(std::move(server), port); } + +bool isDefaultServer(const String &host) +{ + return host.length() == 0 || host == default_mqtt_address; +} } // namespace void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) @@ -324,7 +330,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) } String host = parseHostAndPort(moduleConfig.mqtt.address).first; - isConfiguredForDefaultServer = host.length() == 0 || host == default_mqtt_address; + isConfiguredForDefaultServer = isDefaultServer(host); IPAddress ip; isMqttServerAddressPrivate = ip.fromString(host.c_str()) && isPrivateIpAddress(ip); @@ -408,7 +414,7 @@ void MQTT::reconnect() } #if HAS_NETWORKING // Defaults - int serverPort = 1883; + int serverPort = mqttPort; const char *serverAddr = default_mqtt_address; const char *mqttUsername = default_mqtt_username; const char *mqttPassword = default_mqtt_password; @@ -561,6 +567,23 @@ int32_t MQTT::runOnce() return 30000; } +bool MQTT::isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config) +{ + String host; + uint16_t port; + std::tie(host, port) = parseHostAndPort(config.address, mqttPort); + const bool defaultServer = isDefaultServer(host); + if (defaultServer && config.tls_enabled) { + LOG_ERROR("Invalid MQTT config: TLS was enabled, but the default server does not support TLS"); + return false; + } + if (defaultServer && port != mqttPort) { + LOG_ERROR("Invalid MQTT config: Unsupported port '%d' for the default MQTT server", port); + return false; + } + return true; +} + void MQTT::publishNodeInfo() { // TODO: NodeInfo broadcast over MQTT only (NODENUM_BROADCAST_NO_LORA) diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 42157fda9..f7e3864f8 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -61,6 +61,8 @@ class MQTT : private concurrency::OSThread bool isUsingDefaultServer() { return isConfiguredForDefaultServer; } + static bool isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config); + protected: struct QueueEntry { std::string topic; diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index ce99244ba..3e4397686 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -127,4 +127,4 @@ #if !defined(PIN_SERIAL_RX) && !defined(NRF52840_XXAA) // No serial ports on this board - ONLY use segger in memory console #define USE_SEGGER -#endif +#endif \ No newline at end of file diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index ad4d7a881..8483d21c6 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -304,6 +304,11 @@ void cpuDeepSleep(uint32_t msecToWake) nrf_gpio_cfg_default(WB_I2C1_SDA); #endif #endif +#ifdef MESHLINK +#ifdef PIN_WD_EN + digitalWrite(PIN_WD_EN, LOW); +#endif +#endif #ifdef HELTEC_MESH_NODE_T114 nrf_gpio_cfg_default(PIN_GPS_PPS); diff --git a/src/sleep.cpp b/src/sleep.cpp index 2c91b0014..202b8c354 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -249,6 +249,9 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN #ifdef PIN_3V3_EN digitalWrite(PIN_3V3_EN, LOW); #endif +#ifdef PIN_WD_EN + digitalWrite(PIN_WD_EN, LOW); +#endif #endif ledBlink.set(false); @@ -533,4 +536,4 @@ void enableLoraInterrupt() } #endif } -#endif +#endif \ No newline at end of file diff --git a/test/test_mqtt/MQTT.cpp b/test/test_mqtt/MQTT.cpp index 3a4625aed..c00922548 100644 --- a/test/test_mqtt/MQTT.cpp +++ b/test/test_mqtt/MQTT.cpp @@ -800,6 +800,38 @@ void test_customMqttRoot(void) [] { return pubsub->subscriptions_.count("custom/2/e/test/+") && pubsub->subscriptions_.count("custom/2/e/PKI/+"); })); } +// Empty configuration is valid. +void test_configurationEmptyIsValid(void) +{ + meshtastic_ModuleConfig_MQTTConfig config; + + TEST_ASSERT_TRUE(MQTT::isValidConfig(config)); +} + +// Configuration with the default server is valid. +void test_configWithDefaultServer(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.address = default_mqtt_address}; + + TEST_ASSERT_TRUE(MQTT::isValidConfig(config)); +} + +// Configuration with the default server and port 8888 is invalid. +void test_configWithDefaultServerAndInvalidPort(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.address = default_mqtt_address ":8888"}; + + TEST_ASSERT_FALSE(MQTT::isValidConfig(config)); +} + +// Configuration with the default server and tls_enabled = true is invalid. +void test_configWithDefaultServerAndInvalidTLSEnabled(void) +{ + meshtastic_ModuleConfig_MQTTConfig config = {.tls_enabled = true}; + + TEST_ASSERT_FALSE(MQTT::isValidConfig(config)); +} + void setup() { initializeTestEnvironment(); @@ -843,6 +875,10 @@ void setup() RUN_TEST(test_enabled); RUN_TEST(test_disabled); RUN_TEST(test_customMqttRoot); + RUN_TEST(test_configurationEmptyIsValid); + RUN_TEST(test_configWithDefaultServer); + RUN_TEST(test_configWithDefaultServerAndInvalidPort); + RUN_TEST(test_configWithDefaultServerAndInvalidTLSEnabled); exit(UNITY_END()); } #else diff --git a/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.h b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.h index d5dfc3fab..7a76727f2 100644 --- a/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.h +++ b/variants/diy/seeed-xiao-nrf52840-wio-sx1262/variant.h @@ -84,17 +84,15 @@ static const uint8_t A5 = PIN_A5; #define PIN_NFC2 (31) // RX and TX pins -#define PIN_SERIAL1_RX (6) -#define PIN_SERIAL1_TX (7) +#define PIN_SERIAL1_RX (-1) +#define PIN_SERIAL1_TX (-1) // complains if not defined #define PIN_SERIAL2_RX (-1) #define PIN_SERIAL2_TX (-1) // 4 is used as RF_SW and 5 for USR button so... -#define PIN_WIRE_SDA (-1) -#define PIN_WIRE_SCL (-1) -// #define PIN_WIRE_SDA (6) -// #define PIN_WIRE_SCL (7) +#define PIN_WIRE_SDA (6) +#define PIN_WIRE_SCL (7) static const uint8_t SDA = PIN_WIRE_SDA; static const uint8_t SCL = PIN_WIRE_SCL; diff --git a/variants/meshlink/platformio.ini b/variants/meshlink/platformio.ini new file mode 100644 index 000000000..180dddd49 --- /dev/null +++ b/variants/meshlink/platformio.ini @@ -0,0 +1,30 @@ +; MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog +; https://www.loraitalia.it +; firmware for boards with or without oled display +[env:meshlink] +extends = nrf52840_base +board = meshlink +;board_check = true +build_flags = ${nrf52840_base.build_flags} -I variants/meshlink -D MESHLINK + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -D EINK_DISPLAY_MODEL=GxEPD2_213_B74 + -D EINK_WIDTH=250 + -D EINK_HEIGHT=122 + -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk + -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted + -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates + -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates + -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated + -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. + -D EINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/meshlink> +lib_deps = + ${nrf52840_base.lib_deps} + https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a +debug_tool = jlink +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds +;upload_protocol = jlink \ No newline at end of file diff --git a/variants/meshlink/variant.cpp b/variants/meshlink/variant.cpp new file mode 100644 index 000000000..81a5097c4 --- /dev/null +++ b/variants/meshlink/variant.cpp @@ -0,0 +1,23 @@ +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 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}; + +void initVariant() +{ + pinMode(PIN_LED1, OUTPUT); + digitalWrite(PIN_LED1, HIGH); // turn off the white led while booting + // otherwise it will stay lit for several seconds (could be annoying) + +#ifdef PIN_WD_EN + pinMode(PIN_WD_EN, OUTPUT); + digitalWrite(PIN_WD_EN, HIGH); // Enable the Watchdog at boot +#endif +} \ No newline at end of file diff --git a/variants/meshlink/variant.h b/variants/meshlink/variant.h new file mode 100644 index 000000000..54df03691 --- /dev/null +++ b/variants/meshlink/variant.h @@ -0,0 +1,153 @@ +#ifndef _VARIANT_MESHLINK_ +#define _VARIANT_MESHLINK_ +#ifndef MESHLINK +#define MESHLINK +#endif +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +// #define USE_LFXO // Board uses 32khz crystal for LF +#define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (2) +#define NUM_ANALOG_OUTPUTS (0) + +#define BUTTON_PIN (-1) // If defined, this will be used for user button presses, +#define BUTTON_NEED_PULLUP + +// LEDs +#define PIN_LED1 (24) // Built in white led for status +#define LED_BLUE PIN_LED1 +#define LED_BUILTIN PIN_LED1 + +#define LED_STATE_ON 0 // State when LED is litted +#define LED_INVERTED 1 + +// Testing USB detection +// #define NRF_APM + +/* + * Analog pins + */ +#define PIN_A1 (3) // P0.03/AIN1 +#define ADC_RESOLUTION 14 + +// Other pins +// #define PIN_AREF (2) +// static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (32 + 8) +#define PIN_SERIAL1_TX (7) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (8) +#define PIN_SPI_MOSI (32 + 9) +#define PIN_SPI_SCK (11) + +#define PIN_SPI1_MISO (23) +#define PIN_SPI1_MOSI (21) +#define PIN_SPI1_SCK (19) + +static const uint8_t SS = 12; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +/* + * eink display pins + */ +// #define USE_EINK + +#define PIN_EINK_CS (15) +#define PIN_EINK_BUSY (16) +#define PIN_EINK_DC (14) +#define PIN_EINK_RES (17) +#define PIN_EINK_SCLK (19) +#define PIN_EINK_MOSI (21) // also called SDI + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (1) +#define PIN_WIRE_SCL (27) + +// QSPI Pins +#define PIN_QSPI_SCK 19 +#define PIN_QSPI_CS 22 +#define PIN_QSPI_IO0 21 +#define PIN_QSPI_IO1 23 +#define PIN_QSPI_IO2 32 +#define PIN_QSPI_IO3 20 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES W25Q16JVUXIQ +#define EXTERNAL_FLASH_USE_QSPI + +#define USE_SX1262 +#define SX126X_CS (12) +#define SX126X_DIO1 (32 + 1) +#define SX126X_BUSY (32 + 3) +#define SX126X_RESET (6) +// #define SX126X_RXEN (13) +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// pin 25 is used to enable or disable the watchdog. This pin has to be disabled when cpu is put to sleep +// otherwise the timer will expire and wd will reboot the cpu +#define PIN_WD_EN (25) + +#define PIN_GPS_PPS (26) // Pulse per second input from the GPS + +#define GPS_TX_PIN PIN_SERIAL1_RX // This is for bits going TOWARDS the CPU +#define GPS_RX_PIN PIN_SERIAL1_TX // This is for bits going TOWARDS the GPS + +// #define GPS_THREAD_INTERVAL 50 + +// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press +#define PIN_GPS_EN (0) +#define GPS_EN_ACTIVE LOW + +#define PIN_BUZZER (31) // P0.31/AIN7 + +// Battery +// The battery sense is hooked to pin A0 (2) +#define BATTERY_PIN (2) +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.42 // fine tuning of voltage + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ +#endif \ No newline at end of file diff --git a/variants/meshlink_eink/platformio.ini b/variants/meshlink_eink/platformio.ini new file mode 100644 index 000000000..db3647e73 --- /dev/null +++ b/variants/meshlink_eink/platformio.ini @@ -0,0 +1,30 @@ +; MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog +; https://www.loraitalia.it +; firmware for boards with a 250x122 e-ink display +[env:meshlink_eink] +extends = nrf52840_base +board = meshlink +;board_check = true +build_flags = ${nrf52840_base.build_flags} -I variants/meshlink_eink -D MESHLINK + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -D EINK_DISPLAY_MODEL=GxEPD2_213_B74 + -D EINK_WIDTH=250 + -D EINK_HEIGHT=122 + -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk + -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted + -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates + -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates + -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated + -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. + -D EINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/meshlink_eink> +lib_deps = + ${nrf52840_base.lib_deps} + https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a +debug_tool = jlink +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds +;upload_protocol = jlink \ No newline at end of file diff --git a/variants/meshlink_eink/variant.cpp b/variants/meshlink_eink/variant.cpp new file mode 100644 index 000000000..81a5097c4 --- /dev/null +++ b/variants/meshlink_eink/variant.cpp @@ -0,0 +1,23 @@ +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 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}; + +void initVariant() +{ + pinMode(PIN_LED1, OUTPUT); + digitalWrite(PIN_LED1, HIGH); // turn off the white led while booting + // otherwise it will stay lit for several seconds (could be annoying) + +#ifdef PIN_WD_EN + pinMode(PIN_WD_EN, OUTPUT); + digitalWrite(PIN_WD_EN, HIGH); // Enable the Watchdog at boot +#endif +} \ No newline at end of file diff --git a/variants/meshlink_eink/variant.h b/variants/meshlink_eink/variant.h new file mode 100644 index 000000000..b605d7082 --- /dev/null +++ b/variants/meshlink_eink/variant.h @@ -0,0 +1,153 @@ +#ifndef _VARIANT_MESHLINK_ +#define _VARIANT_MESHLINK_ +#ifndef MESHLINK +#define MESHLINK +#endif +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +// #define USE_LFXO // Board uses 32khz crystal for LF +#define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (2) +#define NUM_ANALOG_OUTPUTS (0) + +#define BUTTON_PIN (-1) // If defined, this will be used for user button presses, +#define BUTTON_NEED_PULLUP + +// LEDs +#define PIN_LED1 (24) // Built in white led for status +#define LED_BLUE PIN_LED1 +#define LED_BUILTIN PIN_LED1 + +#define LED_STATE_ON 0 // State when LED is litted +#define LED_INVERTED 1 + +// Testing USB detection +// #define NRF_APM + +/* + * Analog pins + */ +#define PIN_A1 (3) // P0.03/AIN1 +#define ADC_RESOLUTION 14 + +// Other pins +// #define PIN_AREF (2) +// static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (32 + 8) +#define PIN_SERIAL1_TX (7) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (8) +#define PIN_SPI_MOSI (32 + 9) +#define PIN_SPI_SCK (11) + +#define PIN_SPI1_MISO (23) +#define PIN_SPI1_MOSI (21) +#define PIN_SPI1_SCK (19) + +static const uint8_t SS = 12; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +/* + * eink display pins + */ +#define USE_EINK + +#define PIN_EINK_CS (15) +#define PIN_EINK_BUSY (16) +#define PIN_EINK_DC (14) +#define PIN_EINK_RES (17) +#define PIN_EINK_SCLK (19) +#define PIN_EINK_MOSI (21) // also called SDI + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (1) +#define PIN_WIRE_SCL (27) + +// QSPI Pins +#define PIN_QSPI_SCK 19 +#define PIN_QSPI_CS 22 +#define PIN_QSPI_IO0 21 +#define PIN_QSPI_IO1 23 +#define PIN_QSPI_IO2 32 +#define PIN_QSPI_IO3 20 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES W25Q16JVUXIQ +#define EXTERNAL_FLASH_USE_QSPI + +#define USE_SX1262 +#define SX126X_CS (12) +#define SX126X_DIO1 (32 + 1) +#define SX126X_BUSY (32 + 3) +#define SX126X_RESET (6) +// #define SX126X_RXEN (13) +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// pin 25 is used to enable or disable the watchdog. This pin has to be disabled when cpu is put to sleep +// otherwise the timer will expire and wd will reboot the cpu +#define PIN_WD_EN (25) + +#define PIN_GPS_PPS (26) // Pulse per second input from the GPS + +#define GPS_TX_PIN PIN_SERIAL1_RX // This is for bits going TOWARDS the CPU +#define GPS_RX_PIN PIN_SERIAL1_TX // This is for bits going TOWARDS the GPS + +// #define GPS_THREAD_INTERVAL 50 + +// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press +#define PIN_GPS_EN (0) +#define GPS_EN_ACTIVE LOW + +#define PIN_BUZZER (31) // P0.31/AIN7 + +// Battery +// The battery sense is hooked to pin A0 (2) +#define BATTERY_PIN (2) +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.42 // fine tuning of voltage + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ +#endif \ No newline at end of file