diff --git a/boards/heltec_mesh_node_t1.json b/boards/heltec_mesh_node_t1.json
new file mode 100644
index 000000000..96a28da69
--- /dev/null
+++ b/boards/heltec_mesh_node_t1.json
@@ -0,0 +1,54 @@
+{
+ "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"],
+ ["0x2886", "0x1667"]
+ ],
+ "usb_product": "HT-n5262",
+ "mcu": "nrf52840",
+ "variant": "heltec_mesh_node_t1",
+ "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 Mesh Node T1",
+ "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",
+ "vendor": "Heltec"
+}
diff --git a/platformio.ini b/platformio.ini
index fd25bfa59..d09d13c04 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -186,12 +186,16 @@ lib_deps =
https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/archive/refs/tags/v1.1.2.zip
# renovate: datasource=github-tags depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/SparkFun_ICM-20948_ArduinoLibrary
https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/archive/refs/tags/v1.3.2.zip
+ # renovate: datasource=github-tags depName=TDK InvenSense ICM42670P packageName=tdk-invn-oss/motion.arduino.ICM42670P
+ https://github.com/tdk-invn-oss/motion.arduino.ICM42670P/archive/refs/tags/1.0.8.zip
# renovate: datasource=github-tags depName=Adafruit LTR390 Library packageName=adafruit/Adafruit_LTR390
https://github.com/adafruit/Adafruit_LTR390/archive/refs/tags/1.1.2.zip
# renovate: datasource=github-tags depName=Adafruit PCT2075 packageName=adafruit/Adafruit_PCT2075
https://github.com/adafruit/Adafruit_PCT2075/archive/refs/tags/1.0.6.zip
# renovate: datasource=github-tags depName=DFRobot_BMM150 packageName=dfrobot/DFRobot_BMM150
https://github.com/DFRobot/DFRobot_BMM150/archive/refs/tags/V1.0.0.zip
+ # renovate: datasource=github-tags depName=SparkFun MMC5983MA Magnetometer packageName=sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library
+ https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/archive/refs/tags/v1.1.4.zip
# renovate: datasource=github-tags depName=Adafruit_TSL2561 packageName=adafruit/Adafruit_TSL2561
https://github.com/adafruit/Adafruit_TSL2561/archive/refs/tags/1.1.3.zip
# renovate: datasource=github-tags depName=BH1750_WE packageName=wollewald/BH1750_WE
diff --git a/src/configuration.h b/src/configuration.h
index a31198346..e48f346a5 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -243,6 +243,7 @@ along with this program. If not, see .
#define QMI8658_ADDR 0x6B
#define QMC5883L_ADDR 0x0D
#define HMC5883L_ADDR 0x1E
+#define MMC5983MA_ADDR 0x30
#define SHTC3_ADDR 0x70
#define LPS22HB_ADDR 0x5C
#define LPS22HB_ADDR_ALT 0x5D
@@ -292,6 +293,8 @@ along with this program. If not, see .
#define DA217_ADDR 0x26
#define BMI270_ADDR 0x68
#define BMI270_ADDR_ALT 0x69
+#define ICM42607P_ADDR 0x68
+#define ICM42607P_ADDR_ALT 0x69
// -----------------------------------------------------------------------------
// LED
diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp
index 75eabc954..2c839a38b 100644
--- a/src/detect/ScanI2C.cpp
+++ b/src/detect/ScanI2C.cpp
@@ -37,8 +37,15 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
{
- ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P, BMM150, BMI270};
- return firstOfOrNONE(10, types);
+ ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX,
+ ICM20948, QMA6100P, BMM150, BMI270, ICM42607P};
+ return firstOfOrNONE(11, types);
+}
+
+ScanI2C::FoundDevice ScanI2C::firstMagnetometer() const
+{
+ ScanI2C::DeviceType types[] = {MMC5983MA};
+ return firstOfOrNONE(1, types);
}
ScanI2C::FoundDevice ScanI2C::firstAQI() const
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 054c7854b..7c4f2f0e3 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -41,6 +41,7 @@ class ScanI2C
QMI8658,
QMC5883L,
HMC5883L,
+ MMC5983MA,
PMSA003I,
QMA6100P,
MPU6050,
@@ -65,6 +66,7 @@ class ScanI2C
FT6336U,
STK8BAXX,
ICM20948,
+ ICM42607P,
SCD4X,
MAX30102,
TPS65233,
@@ -149,6 +151,8 @@ class ScanI2C
FoundDevice firstAccelerometer() const;
+ FoundDevice firstMagnetometer() const;
+
FoundDevice firstAQI() const;
FoundDevice firstRGBLED() const;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index c1c48eac8..a3bdf1440 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -360,9 +360,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address);
#ifdef HAS_NCP5623
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623", (uint8_t)addr.address);
-#endif
-#ifdef HAS_LP5562
- SCAN_SIMPLE_CASE(LP5562_ADDR, LP5562, "LP5562", (uint8_t)addr.address);
#endif
case XPOWERS_AXP192_AXP2101_ADDRESS:
// Do we have the axp2101/192 or the TCA8418
@@ -600,6 +597,18 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#else
SCAN_SIMPLE_CASE(PMSA003I_ADDR, PMSA003I, "PMSA003I", (uint8_t)addr.address)
#endif
+ case MMC5983MA_ADDR:
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x2F), 1);
+ if (registerValue == 0x30) {
+ type = MMC5983MA;
+ logFoundDevice("MMC5983MA", (uint8_t)addr.address);
+#ifdef HAS_LP5562
+ } else {
+ type = LP5562;
+ logFoundDevice("LP5562", (uint8_t)addr.address);
+#endif
+ }
+ break;
case BMA423_ADDR: // this can also be LIS3DH_ADDR_ALT
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2);
if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333
@@ -754,8 +763,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
break;
- case ICM20948_ADDR: // same as BMX160_ADDR, BMI270_ADDR_ALT, and SEN5X_ADDR
- case ICM20948_ADDR_ALT: // same as MPU6050_ADDR, BMI270_ADDR
+ case ICM20948_ADDR: // same as BMX160_ADDR, BMI270_ADDR_ALT, ICM42607P_ADDR_ALT, and SEN5X_ADDR
+ case ICM20948_ADDR_ALT: // same as MPU6050_ADDR, BMI270_ADDR, and ICM42607P_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
#ifdef HAS_ICM20948
type = ICM20948;
@@ -775,6 +784,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
logFoundDevice("BMX160", (uint8_t)addr.address);
break;
} else {
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x75), 1); // WHO_AM_I
+ if (registerValue == 0x60) {
+ type = ICM42607P;
+ logFoundDevice("ICM-42607-P", (uint8_t)addr.address);
+ break;
+ }
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
String prod = "";
prod = readSEN5xProductName(i2cBus, addr.address);
diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp
index a28924ba6..d7f56f2d9 100644
--- a/src/graphics/TFTDisplay.cpp
+++ b/src/graphics/TFTDisplay.cpp
@@ -1458,7 +1458,8 @@ void TFTDisplay::sendCommand(uint8_t com)
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOn();
-#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096)
+#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \
+ !defined(HELTEC_MESH_NODE_T1)
tft->wakeup();
tft->powerSaveOff();
#endif
@@ -1469,7 +1470,7 @@ void TFTDisplay::sendCommand(uint8_t com)
#ifdef UNPHONE
unphone.backlight(true); // using unPhone library
#endif
-#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
+#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
#elif !defined(M5STACK) && !defined(ST7789_CS) && \
!defined(HACKADAY_COMMUNICATOR) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
tft->setBrightness(172);
@@ -1485,7 +1486,8 @@ void TFTDisplay::sendCommand(uint8_t com)
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOff();
-#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096)
+#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \
+ !defined(HELTEC_MESH_NODE_T1)
tft->sleep();
tft->powerSaveOn();
#endif
@@ -1496,7 +1498,7 @@ void TFTDisplay::sendCommand(uint8_t com)
#ifdef UNPHONE
unphone.backlight(false); // using unPhone library
#endif
-#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
+#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
tft->setBrightness(0);
#endif
@@ -1511,7 +1513,7 @@ void TFTDisplay::sendCommand(uint8_t com)
void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
{
-#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
+#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
// todo
#elif !defined(HACKADAY_COMMUNICATOR)
tft->setBrightness(_brightness);
@@ -1531,7 +1533,8 @@ bool TFTDisplay::hasTouch(void)
{
#ifdef RAK14014
return true;
-#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096)
+#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && \
+ !defined(HELTEC_MESH_NODE_T1)
return tft->touch() != nullptr;
#else
return false;
@@ -1550,7 +1553,8 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
} else {
return false;
}
-#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096)
+#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && \
+ !defined(HELTEC_MESH_NODE_T1)
return tft->getTouch(x, y);
#else
return false;
@@ -1567,7 +1571,7 @@ bool TFTDisplay::connect()
{
concurrency::LockGuard g(spiLock);
LOG_INFO("Do TFT init");
-#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
+#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
tft = new TFT_eSPI;
#elif defined(HACKADAY_COMMUNICATOR)
bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, 38 /* SCK */, 21 /* MOSI */, GFX_NOT_DEFINED /* MISO */, HSPI /* spi_num */);
diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp
index 279339acd..654796cce 100644
--- a/src/graphics/draw/MenuHandler.cpp
+++ b/src/graphics/draw/MenuHandler.cpp
@@ -1333,6 +1333,11 @@ void menuHandler::positionBaseMenu()
if (accelerometerThread) {
accelerometerThread->calibrate(30);
}
+#endif
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && !MESHTASTIC_EXCLUDE_MAGNETOMETER
+ if (magnetometerThread) {
+ magnetometerThread->calibrate(30);
+ }
#endif
break;
case PositionAction::GPSSmartPosition:
diff --git a/src/main.cpp b/src/main.cpp
index 979517f0a..1dc078c62 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -136,6 +136,10 @@ void printPartitionTable()
#include "motion/AccelerometerThread.h"
AccelerometerThread *accelerometerThread = nullptr;
#endif
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && !MESHTASTIC_EXCLUDE_MAGNETOMETER
+#include "motion/MagnetometerThread.h"
+MagnetometerThread *magnetometerThread = nullptr;
+#endif
#ifdef HAS_I2S
#include "AudioThread.h"
@@ -206,6 +210,8 @@ bool osk_found = false;
ScanI2C::DeviceAddress rtc_found = ScanI2C::ADDRESS_NONE;
// The I2C address of the Accelerometer (if found)
ScanI2C::DeviceAddress accelerometer_found = ScanI2C::ADDRESS_NONE;
+// The I2C address of the Magnetometer (if found)
+ScanI2C::DeviceAddress magnetometer_found = ScanI2C::ADDRESS_NONE;
// The I2C address of the RGB LED (if found)
ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE, ScanI2C::ADDRESS_NONE);
/// The I2C address of our Air Quality Indicator (if found)
@@ -431,6 +437,11 @@ void setup()
digitalWrite(VEXT_ENABLE, VEXT_ON_VALUE); // turn on the display power
#endif
+#if defined(PIN_SENSOR_EN)
+ pinMode(PIN_SENSOR_EN, OUTPUT);
+ digitalWrite(PIN_SENSOR_EN, PIN_SENSOR_EN_ACTIVE); // turn on sensor power
+#endif
+
#if defined(BIAS_T_ENABLE)
pinMode(BIAS_T_ENABLE, OUTPUT);
digitalWrite(BIAS_T_ENABLE, BIAS_T_VALUE); // turn on 5V for GPS Antenna
@@ -671,6 +682,11 @@ void setup()
accelerometer_found = acc_info.type != ScanI2C::DeviceType::NONE ? acc_info.address : accelerometer_found;
LOG_DEBUG("acc_info = %i", acc_info.type);
#endif
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_MAGNETOMETER
+ auto mag_info = i2cScanner->firstMagnetometer();
+ magnetometer_found = mag_info.type != ScanI2C::DeviceType::NONE ? mag_info.address : magnetometer_found;
+ LOG_DEBUG("mag_info = %i", mag_info.type);
+#endif
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226);
@@ -683,6 +699,8 @@ void setup()
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L);
+ scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MMC5983MA, meshtastic_TelemetrySensorType_MMC5983MA);
+ scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM42607P, meshtastic_TelemetrySensorType_ICM42607P);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102);
@@ -766,6 +784,11 @@ void setup()
accelerometerThread = new AccelerometerThread(acc_info.type);
}
#endif
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_MAGNETOMETER
+ if (mag_info.type != ScanI2C::DeviceType::NONE) {
+ magnetometerThread = new MagnetometerThread(mag_info.type);
+ }
+#endif
#if defined(HAS_NEOPIXEL) || defined(UNPHONE) || defined(RGBLED_RED)
ambientLightingThread = new AmbientLightingThread(ScanI2C::DeviceType::NONE);
diff --git a/src/main.h b/src/main.h
index 058a2aebc..d6439f1d1 100644
--- a/src/main.h
+++ b/src/main.h
@@ -39,6 +39,7 @@ extern bool kb_found;
extern bool osk_found;
extern ScanI2C::DeviceAddress rtc_found;
extern ScanI2C::DeviceAddress accelerometer_found;
+extern ScanI2C::DeviceAddress magnetometer_found;
extern ScanI2C::FoundDevice rgb_found;
extern ScanI2C::DeviceAddress aqi_found;
@@ -73,6 +74,10 @@ extern graphics::Screen *screen;
#include "motion/AccelerometerThread.h"
extern AccelerometerThread *accelerometerThread;
#endif
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && !MESHTASTIC_EXCLUDE_MAGNETOMETER
+#include "motion/MagnetometerThread.h"
+extern MagnetometerThread *magnetometerThread;
+#endif
extern bool isVibrating;
diff --git a/src/motion/AccelerometerThread.h b/src/motion/AccelerometerThread.h
index d2205fd2a..3d7341e58 100755
--- a/src/motion/AccelerometerThread.h
+++ b/src/motion/AccelerometerThread.h
@@ -15,6 +15,7 @@
#endif
#include "BMM150Sensor.h"
#include "BMX160Sensor.h"
+#include "ICM42607PSensor.h"
#include "ICM20948Sensor.h"
#include "LIS3DHSensor.h"
#include "LSM6DS3Sensor.h"
@@ -111,6 +112,9 @@ class AccelerometerThread : public concurrency::OSThread
case ScanI2C::DeviceType::ICM20948:
sensor = new ICM20948Sensor(device);
break;
+ case ScanI2C::DeviceType::ICM42607P:
+ sensor = new ICM42607PSensor(device);
+ break;
case ScanI2C::DeviceType::BMM150:
sensor = new BMM150Sensor(device);
break;
diff --git a/src/motion/ICM42607PSensor.cpp b/src/motion/ICM42607PSensor.cpp
new file mode 100644
index 000000000..0bace05a8
--- /dev/null
+++ b/src/motion/ICM42607PSensor.cpp
@@ -0,0 +1,98 @@
+#include "ICM42607PSensor.h"
+
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && __has_include()
+
+#include "detect/ScanI2CTwoWire.h"
+#include
+
+static constexpr uint16_t ICM42607P_ACCEL_ODR_HZ = 50;
+static constexpr uint16_t ICM42607P_ACCEL_FSR_G = 2;
+static constexpr float ICM42607P_COUNTS_PER_G = 32768.0f / ICM42607P_ACCEL_FSR_G;
+
+#ifdef ICM_42607P_INT_PIN
+volatile static bool ICM42607P_IRQ = false;
+
+void ICM42607PSetInterrupt()
+{
+ ICM42607P_IRQ = true;
+}
+#endif
+
+ICM42607PSensor::ICM42607PSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice)
+{
+ wire = ScanI2CTwoWire::fetchI2CBus(foundDevice.address);
+}
+
+ICM42607PSensor::~ICM42607PSensor() = default;
+
+bool ICM42607PSensor::init()
+{
+ bool addressLsb = deviceAddress() == ICM42607P_ADDR_ALT;
+
+ LOG_DEBUG("ICM-42607-P begin on addr 0x%02X (port=%d)", deviceAddress(), devicePort());
+ sensor.reset();
+ auto newSensor = std::make_unique(*wire, addressLsb);
+
+ int status = newSensor->begin();
+ // ICM42670P library returns -3 for ICM42607P because WHO_AM_I differs; the register map is compatible.
+ if (status != 0 && status != -3) {
+ LOG_DEBUG("ICM-42607-P init error %d", status);
+ return false;
+ }
+
+ status = newSensor->startAccel(ICM42607P_ACCEL_ODR_HZ, ICM42607P_ACCEL_FSR_G);
+ if (status != 0) {
+ LOG_DEBUG("ICM-42607-P accel start error %d", status);
+ return false;
+ }
+
+#ifdef ICM_42607P_INT_PIN
+ ICM42607P_IRQ = false;
+ status = newSensor->startWakeOnMotion(ICM_42607P_INT_PIN, ICM42607PSetInterrupt);
+ if (status != 0) {
+ LOG_DEBUG("ICM-42607-P wake-on-motion start error %d", status);
+ return false;
+ }
+ LOG_DEBUG("ICM-42607-P wake-on-motion interrupt ok pin=%d", ICM_42607P_INT_PIN);
+#endif
+
+ sensor = std::move(newSensor);
+ LOG_DEBUG("ICM-42607-P init ok");
+ return true;
+}
+
+int32_t ICM42607PSensor::runOnce()
+{
+#ifdef ICM_42607P_INT_PIN
+ if (ICM42607P_IRQ) {
+ ICM42607P_IRQ = false;
+ LOG_DEBUG("ICM-42607-P motion interrupt");
+ wakeScreen();
+ }
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+#else
+ int16_t x = 0;
+ int16_t y = 0;
+ int16_t z = 0;
+ inv_imu_sensor_event_t event = {};
+
+ if (sensor == nullptr || sensor->getDataFromRegisters(event) != 0) {
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+ }
+
+ // getDataFromRegisters() fills accel[] but does not set sensor_mask in this library version.
+ if (event.accel[0] == 0 && event.accel[1] == 0 && event.accel[2] == 0) {
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+ }
+
+ x = event.accel[0];
+ y = event.accel[1];
+ z = event.accel[2];
+ // LOG_DEBUG("ICM-42607-P accel read x=%.3fg y=%.3fg z=%.3fg", (float)x / ICM42607P_COUNTS_PER_G,
+ // (float)y / ICM42607P_COUNTS_PER_G, (float)z / ICM42607P_COUNTS_PER_G);
+
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+#endif
+}
+
+#endif
diff --git a/src/motion/ICM42607PSensor.h b/src/motion/ICM42607PSensor.h
new file mode 100644
index 000000000..370fef276
--- /dev/null
+++ b/src/motion/ICM42607PSensor.h
@@ -0,0 +1,28 @@
+#pragma once
+#ifndef _ICM42607P_SENSOR_H_
+#define _ICM42607P_SENSOR_H_
+
+#include "MotionSensor.h"
+
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && __has_include()
+
+#include
+
+class ICM42670;
+
+class ICM42607PSensor : public MotionSensor
+{
+ private:
+ std::unique_ptr sensor;
+ TwoWire *wire = nullptr;
+
+ public:
+ explicit ICM42607PSensor(ScanI2C::FoundDevice foundDevice);
+ ~ICM42607PSensor() override;
+ virtual bool init() override;
+ virtual int32_t runOnce() override;
+};
+
+#endif
+
+#endif
diff --git a/src/motion/MMC5983MASensor.cpp b/src/motion/MMC5983MASensor.cpp
new file mode 100644
index 000000000..8e77b4c0f
--- /dev/null
+++ b/src/motion/MMC5983MASensor.cpp
@@ -0,0 +1,115 @@
+#include "MMC5983MASensor.h"
+
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && __has_include()
+
+#include "detect/ScanI2CTwoWire.h"
+
+#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
+extern graphics::Screen *screen;
+#endif
+
+static constexpr float MMC5983MA_ZERO_FIELD = 131072.0f;
+static constexpr float MMC5983MA_COUNTS_PER_GAUSS = 16384.0f;
+static constexpr uint16_t MMC5983MA_CONTINUOUS_FREQUENCY_HZ = 10;
+static constexpr float MMC5983MA_HEADING_OFFSET_DEG = 180.0f;
+
+MMC5983MASensor::MMC5983MASensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {}
+
+bool MMC5983MASensor::init()
+{
+ LOG_DEBUG("MMC5983MA begin on addr 0x%02X (port=%d)", device.address.address, device.address.port);
+ TwoWire *wire = ScanI2CTwoWire::fetchI2CBus(device.address);
+
+ if (!sensor.begin(*wire)) {
+ LOG_DEBUG("MMC5983MA init error");
+ return false;
+ }
+
+ sensor.softReset();
+ sensor.setFilterBandwidth(100);
+ sensor.performSetOperation();
+ sensor.enableAutomaticSetReset();
+ continuousMode = sensor.setContinuousModeFrequency(MMC5983MA_CONTINUOUS_FREQUENCY_HZ);
+ continuousMode &= sensor.enableContinuousMode();
+
+ if (!continuousMode) {
+ LOG_DEBUG("MMC5983MA continuous mode failed, using single-shot reads");
+ sensor.disableContinuousMode();
+ }
+
+ loadMagnetometerCalibration(compassCalibrationFileName, highestX, lowestX, highestY, lowestY, highestZ, lowestZ);
+ return true;
+}
+
+bool MMC5983MASensor::readMagnetometer(float &xGauss, float &yGauss, float &zGauss)
+{
+ uint32_t rawX = 0;
+ uint32_t rawY = 0;
+ uint32_t rawZ = 0;
+
+ if (!(continuousMode ? sensor.readFieldsXYZ(&rawX, &rawY, &rawZ) : sensor.getMeasurementXYZ(&rawX, &rawY, &rawZ))) {
+ LOG_DEBUG("MMC5983MA read failed");
+ return false;
+ }
+
+ xGauss = ((float)rawX - MMC5983MA_ZERO_FIELD) / MMC5983MA_COUNTS_PER_GAUSS;
+ yGauss = ((float)rawY - MMC5983MA_ZERO_FIELD) / MMC5983MA_COUNTS_PER_GAUSS;
+ zGauss = ((float)rawZ - MMC5983MA_ZERO_FIELD) / MMC5983MA_COUNTS_PER_GAUSS;
+ return true;
+}
+
+int32_t MMC5983MASensor::runOnce()
+ {
+ float magX = 0, magY = 0, magZ = 0;
+ if (!readMagnetometer(magX, magY, magZ)) {
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+ }
+
+ #if !defined(MESHTASTIC_EXCLUDE_SCREEN)
+ if (doCalibration) {
+ beginCalibrationDisplay(showingScreen);
+ updateCalibrationExtrema(magX, magY, magZ, highestX, lowestX, highestY, lowestY, highestZ, lowestZ);
+ finishCalibrationIfExpired(showingScreen, compassCalibrationFileName, highestX, lowestX, highestY, lowestY, highestZ,
+ lowestZ);
+ }
+ #endif
+
+ magX -= (highestX + lowestX) / 2;
+ magY -= (highestY + lowestY) / 2;
+ magZ -= (highestZ + lowestZ) / 2;
+
+ #if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
+ float heading = atan2f(magY, magX) * RAD_TO_DEG + MMC5983MA_HEADING_OFFSET_DEG;
+ if (heading < 0.0f) {
+ heading += 360.0f;
+ } else if (heading >= 360.0f) {
+ heading -= 360.0f;
+ }
+
+ heading = applyCompassOrientation(heading);
+ if (screen) {
+ screen->setHeading(heading);
+ }
+ #endif
+
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+}
+
+void MMC5983MASensor::calibrate(uint16_t forSeconds)
+{
+#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
+ float xGauss = 0.0f;
+ float yGauss = 0.0f;
+ float zGauss = 0.0f;
+
+ LOG_DEBUG("MMC5983MA calibration started for %is", forSeconds);
+ if (readMagnetometer(xGauss, yGauss, zGauss)) {
+ seedCalibrationExtrema(xGauss, yGauss, zGauss, highestX, lowestX, highestY, lowestY, highestZ, lowestZ);
+ } else {
+ seedCalibrationExtrema(0.0f, 0.0f, 0.0f, highestX, lowestX, highestY, lowestY, highestZ, lowestZ);
+ }
+ startCalibrationWindow(forSeconds);
+#endif
+}
+
+#endif
diff --git a/src/motion/MMC5983MASensor.h b/src/motion/MMC5983MASensor.h
new file mode 100644
index 000000000..9d349b53a
--- /dev/null
+++ b/src/motion/MMC5983MASensor.h
@@ -0,0 +1,31 @@
+#pragma once
+#ifndef _MMC5983MA_SENSOR_H_
+#define _MMC5983MA_SENSOR_H_
+
+#include "MotionSensor.h"
+
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && __has_include()
+
+#include
+
+class MMC5983MASensor : public MotionSensor
+{
+ private:
+ SFE_MMC5983MA sensor;
+ bool continuousMode = false;
+ bool showingScreen = false;
+ static constexpr const char *compassCalibrationFileName = "/prefs/compass_mmc5983ma.dat";
+ float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
+
+ bool readMagnetometer(float &xGauss, float &yGauss, float &zGauss);
+
+ public:
+ explicit MMC5983MASensor(ScanI2C::FoundDevice foundDevice);
+ virtual bool init() override;
+ virtual int32_t runOnce() override;
+ virtual void calibrate(uint16_t forSeconds) override;
+};
+
+#endif
+
+#endif
diff --git a/src/motion/MagnetometerThread.h b/src/motion/MagnetometerThread.h
new file mode 100644
index 000000000..affd99e94
--- /dev/null
+++ b/src/motion/MagnetometerThread.h
@@ -0,0 +1,115 @@
+#pragma once
+#ifndef _MAGNETOMETER_THREAD_H_
+#define _MAGNETOMETER_THREAD_H_
+
+#include "configuration.h"
+
+#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && !MESHTASTIC_EXCLUDE_MAGNETOMETER
+
+#include "../concurrency/OSThread.h"
+#include "MMC5983MASensor.h"
+#include "MotionSensor.h"
+
+extern ScanI2C::DeviceAddress magnetometer_found;
+
+class MagnetometerThread : public concurrency::OSThread
+{
+ private:
+ MotionSensor *sensor = nullptr;
+ ScanI2C::FoundDevice device;
+ bool isInitialised = false;
+
+ public:
+ explicit MagnetometerThread(ScanI2C::FoundDevice foundDevice) : OSThread("Magnetometer")
+ {
+ device = foundDevice;
+ init();
+ }
+
+ explicit MagnetometerThread(ScanI2C::DeviceType type) : MagnetometerThread(ScanI2C::FoundDevice{type, magnetometer_found}) {}
+
+ void start()
+ {
+ init();
+ setIntervalFromNow(0);
+ };
+
+ void calibrate(uint16_t forSeconds)
+ {
+ if (sensor) {
+ sensor->calibrate(forSeconds);
+ setIntervalFromNow(0);
+ }
+ }
+
+ protected:
+ int32_t runOnce() override
+ {
+ canSleep = true;
+
+ if (isInitialised) {
+ return sensor->runOnce();
+ }
+
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+ }
+
+ private:
+ void init()
+ {
+ if (isInitialised) {
+ return;
+ }
+
+ if (device.address.port == ScanI2C::I2CPort::NO_I2C || device.address.address == 0 || device.type == ScanI2C::NONE) {
+ LOG_DEBUG("MagnetometerThread Disable due to no sensors found");
+ disable();
+ return;
+ }
+
+ switch (device.type) {
+ case ScanI2C::DeviceType::MMC5983MA:
+ sensor = new MMC5983MASensor(device);
+ break;
+ default:
+ disable();
+ return;
+ }
+
+ isInitialised = sensor->init();
+ if (!isInitialised) {
+ clean();
+ }
+ LOG_DEBUG("MagnetometerThread::init %s", isInitialised ? "ok" : "failed");
+ }
+
+ MagnetometerThread(const MagnetometerThread &other) : OSThread::OSThread("Magnetometer") { this->copy(other); }
+
+ virtual ~MagnetometerThread() { clean(); }
+
+ MagnetometerThread &operator=(const MagnetometerThread &other)
+ {
+ this->copy(other);
+ return *this;
+ }
+
+ void copy(const MagnetometerThread &other)
+ {
+ if (this != &other) {
+ clean();
+ this->device = ScanI2C::FoundDevice(other.device.type,
+ ScanI2C::DeviceAddress(other.device.address.port, other.device.address.address));
+ }
+ }
+
+ void clean()
+ {
+ isInitialised = false;
+ delete sensor;
+ sensor = nullptr;
+ }
+};
+
+#endif
+
+#endif
diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h
index eafd799fc..ab15bcde5 100644
--- a/src/platform/nrf52/architecture.h
+++ b/src/platform/nrf52/architecture.h
@@ -117,6 +117,8 @@
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
#elif defined(HELTEC_T114)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T114
+#elif defined(HELTEC_MESH_NODE_T1)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T1
#elif defined(MESHLINK)
#define HW_VENDOR meshtastic_HardwareModel_MESHLINK
#elif defined(SEEED_XIAO_NRF52840_KIT)
diff --git a/variants/nrf52840/heltec_mesh_node_t1/platformio.ini b/variants/nrf52840/heltec_mesh_node_t1/platformio.ini
new file mode 100644
index 000000000..664d1616e
--- /dev/null
+++ b/variants/nrf52840/heltec_mesh_node_t1/platformio.ini
@@ -0,0 +1,35 @@
+; Heltec Mesh Node T1 nRF52840/SX1262 device
+[env:heltec-mesh-node-t1]
+custom_meshtastic_hw_model = 133
+custom_meshtastic_hw_model_slug = HELTEC_MESH_NODE_T1
+custom_meshtastic_architecture = nrf52840
+custom_meshtastic_actively_supported = true
+custom_meshtastic_support_level = 1
+custom_meshtastic_display_name = Heltec Mesh Node T1
+custom_meshtastic_images = heltec-mesh-node-t1.svg
+custom_meshtastic_tags = Heltec
+
+extends = nrf52840_base
+board = heltec_mesh_node_t1
+board_level = pr
+debug_tool = jlink
+
+build_flags = ${nrf52840_base.build_flags}
+ -Ivariants/nrf52840/heltec_mesh_node_t1
+ -DHELTEC_MESH_NODE_T1
+ -DUSE_TFTDISPLAY
+ -DUSER_SETUP_LOADED
+ -DST7735_DRIVER
+ -DST7735_REDTAB160x80
+ -D TFT_SPI_PORT=SPI1
+ -D TFT_CS=ST7735_CS
+ -D TFT_DC=ST7735_RS
+ -D TFT_RST=ST7735_RESET
+ -D TFT_BL=ST7735_BL
+ -D TFT_BACKLIGHT_ON=LOW
+
+build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_node_t1>
+lib_deps =
+ ${nrf52840_base.lib_deps}
+ lewisxhe/PCF8563_Library@^1.0.1
+ bodmer/TFT_eSPI@2.5.43
diff --git a/variants/nrf52840/heltec_mesh_node_t1/variant.cpp b/variants/nrf52840/heltec_mesh_node_t1/variant.cpp
new file mode 100644
index 000000000..eb719f17f
--- /dev/null
+++ b/variants/nrf52840/heltec_mesh_node_t1/variant.cpp
@@ -0,0 +1,85 @@
+/*
+ Copyright (c) 2014-2015 Arduino LLC. All right reserved.
+ Copyright (c) 2016 Sandeep Mistry All right reserved.
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "variant.h"
+#include "nrf.h"
+#include "wiring_constants.h"
+#include "wiring_digital.h"
+
+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};
+
+void initVariant()
+{
+ pinMode(PIN_BUZZER_VOLTAGE_MULTIPLIER_1, OUTPUT);
+ pinMode(PIN_BUZZER_VOLTAGE_MULTIPLIER_2, OUTPUT);
+ digitalWrite(PIN_BUZZER_VOLTAGE_MULTIPLIER_1, HIGH);
+ digitalWrite(PIN_BUZZER_VOLTAGE_MULTIPLIER_2, HIGH);
+}
+
+void variant_shutdown()
+{
+ nrf_gpio_cfg_default(ST7735_CS);
+ nrf_gpio_cfg_default(ST7735_RS);
+ nrf_gpio_cfg_default(ST7735_SDA);
+ nrf_gpio_cfg_default(ST7735_SCK);
+ nrf_gpio_cfg_default(ST7735_RESET);
+ nrf_gpio_cfg_default(ST7735_BL);
+ nrf_gpio_cfg_default(VTFT_CTRL);
+
+ nrf_gpio_cfg_default(PIN_WIRE_SDA);
+ nrf_gpio_cfg_default(PIN_WIRE_SCL);
+
+ nrf_gpio_cfg_default(SX126X_CS);
+ nrf_gpio_cfg_default(SX126X_DIO1);
+ nrf_gpio_cfg_default(SX126X_BUSY);
+ nrf_gpio_cfg_default(SX126X_RESET);
+
+ nrf_gpio_cfg_default(PIN_SPI_MISO);
+ nrf_gpio_cfg_default(PIN_SPI_MOSI);
+ nrf_gpio_cfg_default(PIN_SPI_SCK);
+
+ // nrf_gpio_cfg_default(PIN_SPI1_MISO);// ST7735 doesn't support MISO, so we don't configure it at all
+ nrf_gpio_cfg_default(PIN_SPI1_MOSI);
+ nrf_gpio_cfg_default(PIN_SPI1_SCK);
+
+ nrf_gpio_cfg_default(PIN_GPS_RESET);
+ nrf_gpio_cfg_default(PIN_GPS_EN);
+ nrf_gpio_cfg_default(PIN_GPS_PPS);
+ nrf_gpio_cfg_default(GPS_TX_PIN);
+ nrf_gpio_cfg_default(GPS_RX_PIN);
+
+ nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_1);
+ nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_2);
+
+ pinMode(PIN_BUZZER, OUTPUT);
+ digitalWrite(PIN_BUZZER, LOW);
+
+ pinMode(PIN_SENSOR_EN, OUTPUT);
+ digitalWrite(PIN_SENSOR_EN, !PIN_SENSOR_EN_ACTIVE); // Turn off sensor power
+
+ pinMode(PIN_LED1, OUTPUT);
+ digitalWrite(PIN_LED1, HIGH);
+}
diff --git a/variants/nrf52840/heltec_mesh_node_t1/variant.h b/variants/nrf52840/heltec_mesh_node_t1/variant.h
new file mode 100644
index 000000000..dc84a0487
--- /dev/null
+++ b/variants/nrf52840/heltec_mesh_node_t1/variant.h
@@ -0,0 +1,177 @@
+/*
+ Copyright (c) 2014-2015 Arduino LLC. All right reserved.
+ Copyright (c) 2016 Sandeep Mistry All right reserved.
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef _VARIANT_HELTEC_MESH_NODE_T1_
+#define _VARIANT_HELTEC_MESH_NODE_T1_
+/** Master clock frequency */
+#define VARIANT_MCK (64000000ul)
+
+#define USE_LFXO // Board uses 32khz crystal for LF
+
+/*----------------------------------------------------------------------------
+ * Headers
+ *----------------------------------------------------------------------------*/
+
+#include "WVariant.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#define ST7735_CS (0 + 12)
+#define ST7735_RS (0 + 22) // DC
+#define ST7735_SDA (0 + 24)
+#define ST7735_SCK (32 + 0)
+#define ST7735_RESET (0 + 20)
+#define ST7735_MISO -1
+#define ST7735_BUSY -1
+#define ST7735_BL (0 + 15)
+#define VTFT_CTRL (0 + 13) // Active HIGH, powers the ST7735 display
+#define SPI_FREQUENCY 80000000
+#define SPI_READ_FREQUENCY 16000000
+#define SCREEN_ROTATE
+#define TFT_HEIGHT 160
+#define TFT_WIDTH 80
+#define TFT_OFFSET_X 24
+#define TFT_OFFSET_Y 0
+#define TFT_INVERT false
+#define SCREEN_TRANSITION_FRAMERATE 3 // fps
+#define DISPLAY_FORCE_SMALL_FONTS
+
+// Number of pins defined in PinDescription array
+#define PINS_COUNT (48)
+#define NUM_DIGITAL_PINS (48)
+#define NUM_ANALOG_INPUTS (1)
+#define NUM_ANALOG_OUTPUTS (0)
+
+// LEDs
+#define PIN_LED1 (0 + 16)
+#define LED_BLUE PIN_LED1 // fake for bluefruit library
+#define LED_GREEN PIN_LED1
+#define LED_STATE_ON 0 // State when LED is lit
+
+/*
+ * Buttons
+ */
+#define PIN_BUTTON1 (32 + 10)
+#define PIN_BUTTON2 (0 + 14)
+
+/*
+No longer populated on PCB
+*/
+#define PIN_SERIAL2_RX (-1)
+#define PIN_SERIAL2_TX (-1)
+
+/*
+ * I2C
+ */
+
+#define WIRE_INTERFACES_COUNT 1
+
+// I2C bus 1
+#define PIN_WIRE_SDA (32 + 3)
+#define PIN_WIRE_SCL (0 + 10)
+
+#define PIN_SENSOR_EN (32 + 6) // Power control pin for sensors
+#define PIN_SENSOR_EN_ACTIVE LOW // Power control active state
+// #define ICM_42607P_INT_PIN (32 + 1) // ICM42607P INT1, Arduino pin 33 / nRF P1.01
+// #define ICM_42607P_INT2_PIN (32 + 7) // ICM42607P INT2, Arduino pin 39 / nRF P1.07
+
+/*
+ * Lora radio
+ */
+
+#define USE_SX1262
+#define SX126X_CS (32 + 11) // FIXME - we really should define LORA_CS instead
+#define LORA_CS SX126X_CS
+#define SX126X_DIO1 (0 + 31)
+#define SX126X_BUSY (0 + 29)
+#define SX126X_RESET (0 + 2)
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+
+/*
+ * SPI Interfaces
+ */
+#define SPI_INTERFACES_COUNT 2
+
+// For LORA, spi 0
+#define PIN_SPI_MISO (0 + 3)
+#define PIN_SPI_MOSI (32 + 14)
+#define PIN_SPI_SCK (32 + 13)
+
+#define PIN_SPI1_MISO ST7735_MISO
+#define PIN_SPI1_MOSI ST7735_SDA
+#define PIN_SPI1_SCK ST7735_SCK
+
+/*
+ * GPS pins
+ */
+#define GPS_UC6580
+#define GPS_BAUDRATE 115200
+#define PIN_GPS_RESET (0 + 26)
+#define GPS_RESET_MODE LOW
+#define PIN_GPS_EN (0 + 4)
+#define GPS_EN_ACTIVE LOW
+#define PERIPHERAL_WARMUP_MS 1000 // Make sure I2C QuickLink has stable power before continuing
+#define PIN_GPS_PPS (32 + 9) // Pulse per second input from the GPS
+#define GPS_TX_PIN (0 + 7)
+#define GPS_RX_PIN (0 + 8)
+
+#define GPS_THREAD_INTERVAL 50
+
+#define PIN_SERIAL1_RX GPS_RX_PIN
+#define PIN_SERIAL1_TX GPS_TX_PIN
+
+#define PIN_BUZZER (0 + 9)
+#define PIN_BUZZER_VOLTAGE_MULTIPLIER_1 (32 + 2)
+#define PIN_BUZZER_VOLTAGE_MULTIPLIER_2 (32 + 5)
+
+#define ADC_CTRL 11
+#define ADC_CTRL_ENABLED HIGH
+#define BATTERY_PIN 5
+#define ADC_RESOLUTION 14
+
+#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 (4.916F)
+
+// nrf52840 AIN3 = Pin 5
+// commented out due to power leakage of 2.9mA in shutdown state see reported issue #8801
+#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_3
+
+// We have AIN3 with a VBAT divider so AIN3 = VBAT * (100/490)
+// We have the device going deep sleep under 3.1V, which is AIN3 = 0.63V
+// So we can wake up when VBAT>=VDD is restored to 3.3V, where AIN3 = 0.67V
+// Ratio 0.67/3.3 = 0.20, so we can pick a bit higher, 2/8 VDD, which means
+// VBAT=4.04V
+#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_2_8
+
+#define HAS_RTC 0
+#ifdef __cplusplus
+}
+#endif
+
+/*----------------------------------------------------------------------------
+ * Arduino objects - C++ only
+ *----------------------------------------------------------------------------*/
+
+#endif