Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
62bb2c0
Add Lilygo T-Impulse-Plus
vidplace7 May 18, 2026
5025ca4
Enable small screen layout 64x32
caveman99 May 18, 2026
e8ecd0d
trunk'd
caveman99 May 18, 2026
ef29126
Haptic Feedback (short and long press)
caveman99 May 18, 2026
b9d6903
enable Charging Indicator
caveman99 May 18, 2026
fb119a9
enable nrfutil uploads
caveman99 May 18, 2026
254fc47
trunk fmt
caveman99 May 19, 2026
da34334
Add Lilygo T-Impulse-Plus
vidplace7 May 18, 2026
eda3a79
Enable small screen layout 64x32
caveman99 May 18, 2026
0c1457b
trunk'd
caveman99 May 18, 2026
8a8e4b8
Haptic Feedback (short and long press)
caveman99 May 18, 2026
8fa7ae4
enable Charging Indicator
caveman99 May 18, 2026
6fde0f9
enable nrfutil uploads
caveman99 May 18, 2026
2e343a1
trunk fmt
caveman99 May 19, 2026
c417ba8
Merge branch 't-impulse-plus' of https://github.com/meshtastic/firmwa…
caveman99 May 19, 2026
40e191a
enable proper device model
caveman99 May 19, 2026
c7c7e62
Merge branch 'master' into t-impulse-plus
caveman99 May 19, 2026
c4daf0a
Merge branch 'master' into t-impulse-plus
thebentern May 19, 2026
8a82f53
Dim the haptic duration a bit.
caveman99 May 19, 2026
ae63e9b
Merge branch 't-impulse-plus' of https://github.com/meshtastic/firmwa…
caveman99 May 19, 2026
66d4049
Fix GPS pins and speed. Module enable is active low, speed is 38400
caveman99 May 19, 2026
64eded2
Potential fix for pull request finding
caveman99 May 19, 2026
ac23497
Merge branch 'master' into t-impulse-plus
caveman99 May 19, 2026
b10d1d5
set correct geometry.
caveman99 May 19, 2026
377ba3d
Merge branch 't-impulse-plus' of https://github.com/meshtastic/firmwa…
caveman99 May 19, 2026
b06c727
Merge branch 'master' into t-impulse-plus
thebentern May 21, 2026
f037c07
Add custom_meshtastic_* metadata to t-impulse-plus platformio.ini
Copilot May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions boards/t-impulse-plus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_T_IMPULSE_PLUS -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x8029"]],
"usb_product": "T-Impulse-Plus-nRF52840",
"mcu": "nrf52840",
"variant": "t-impulse-plus",
"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"
},
"frameworks": ["arduino"],
"name": "Lilygo T-Impulse-Plus-nRF52840",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"require_upload_port": true,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
]
},
"url": "https://www.lilygo.cc/",
"vendor": "Lilygo"
}
15 changes: 15 additions & 0 deletions src/Power.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "main.h"
#include "meshUtils.h"
#include "power/PowerHAL.h"
#include "power/SGM41562.h"
#include "sleep.h"

#if defined(ARCH_PORTDUINO)
Expand Down Expand Up @@ -453,6 +454,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// source
virtual bool isVbusIn() override
{
#ifdef HAS_SGM41562
if (sgm41562 && sgm41562->refresh())
return sgm41562->isInputPowerGood();
#endif
#ifdef EXT_PWR_DETECT
#if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB)
// if external powered that pin will be pulled down
Expand Down Expand Up @@ -483,6 +488,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// we can't be smart enough to say 'full'?
virtual bool isCharging() override
{
#ifdef HAS_SGM41562
if (sgm41562 && sgm41562->refresh())
return sgm41562->isCharging();
#endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(HAS_PMU)
if (hasRAK()) {
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
Expand Down Expand Up @@ -697,6 +706,12 @@ bool Power::analogInit()
*/
bool Power::setup()
{
#ifdef HAS_SGM41562
// Initialize the charger early so AnalogBatteryLevel can read charging
// state from it. The charger does not provide battery voltage / percent —
// those still come from the platform ADC via analogInit() below.
initSGM41562(SGM41562_WIRE);
#endif
bool found = false;
if (axpChipInit()) {
found = true;
Expand Down
97 changes: 97 additions & 0 deletions src/input/HapticFeedback.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include "HapticFeedback.h"

#ifdef HAPTIC_FEEDBACK_PIN

#include <Arduino.h>

#ifdef HAPTIC_FEEDBACK_ACTIVE_LOW
#define HAPTIC_FEEDBACK_ON_STATE LOW
#define HAPTIC_FEEDBACK_OFF_STATE HIGH
#else
#define HAPTIC_FEEDBACK_ON_STATE HIGH
#define HAPTIC_FEEDBACK_OFF_STATE LOW
#endif

HapticFeedback *hapticFeedback = nullptr;

void initHapticFeedback()
{
if (!hapticFeedback)
hapticFeedback = new HapticFeedback();
}

HapticFeedback::HapticFeedback() : concurrency::OSThread("Haptic")
{
pinMode(HAPTIC_FEEDBACK_PIN, OUTPUT);
digitalWrite(HAPTIC_FEEDBACK_PIN, HAPTIC_FEEDBACK_OFF_STATE);
}

void HapticFeedback::motorWrite(bool on)
{
digitalWrite(HAPTIC_FEEDBACK_PIN, on ? HAPTIC_FEEDBACK_ON_STATE : HAPTIC_FEEDBACK_OFF_STATE);
}

void HapticFeedback::pulse(uint16_t durationMs)
{
motorWrite(true);
pulseOffAt = millis() + durationMs;
if (pulseOffAt == 0) // 0 is the "no pulse" sentinel
pulseOffAt = 1;
scheduleNext();
}

void HapticFeedback::armDelayedPulse(uint16_t delayMs, uint16_t durationMs)
{
delayedPulseAt = millis() + delayMs;
if (delayedPulseAt == 0)
delayedPulseAt = 1;
delayedPulseDuration = durationMs;
scheduleNext();
}

void HapticFeedback::cancelDelayedPulse()
{
delayedPulseAt = 0;
}

void HapticFeedback::scheduleNext()
{
uint32_t now = millis();
uint32_t next = 0;
if (pulseOffAt != 0)
next = pulseOffAt;
if (delayedPulseAt != 0 && (next == 0 || (int32_t)(delayedPulseAt - next) < 0))
next = delayedPulseAt;
if (next == 0)
return;
int32_t delay = (int32_t)(next - now);
setIntervalFromNow(delay > 0 ? (unsigned long)delay : 0);
}

int32_t HapticFeedback::runOnce()
{
uint32_t now = millis();

if (pulseOffAt != 0 && (int32_t)(now - pulseOffAt) >= 0) {
motorWrite(false);
pulseOffAt = 0;
}

if (delayedPulseAt != 0 && (int32_t)(now - delayedPulseAt) >= 0) {
uint16_t dur = delayedPulseDuration;
delayedPulseAt = 0;
pulse(dur);
}

uint32_t next = 0;
if (pulseOffAt != 0)
next = pulseOffAt;
if (delayedPulseAt != 0 && (next == 0 || (int32_t)(delayedPulseAt - next) < 0))
next = delayedPulseAt;
if (next == 0)
return 60 * 1000;
int32_t delay = (int32_t)(next - now);
return delay > 0 ? delay : 0;
}

#endif // HAPTIC_FEEDBACK_PIN
35 changes: 35 additions & 0 deletions src/input/HapticFeedback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include "configuration.h"

#ifdef HAPTIC_FEEDBACK_PIN

#include "concurrency/OSThread.h"
#include <stdint.h>

// Non-blocking pulses on a GPIO vibration motor. HAPTIC_FEEDBACK_ACTIVE_LOW inverts polarity.
class HapticFeedback : public concurrency::OSThread
{
public:
HapticFeedback();
void pulse(uint16_t durationMs = 30);
void armDelayedPulse(uint16_t delayMs, uint16_t durationMs = 30);
void cancelDelayedPulse();

protected:
int32_t runOnce() override;

private:
uint32_t pulseOffAt = 0;
uint32_t delayedPulseAt = 0;
uint16_t delayedPulseDuration = 0;

void motorWrite(bool on);
// Reschedule to the soonest pending event so later arms don't clobber earlier wakes.
void scheduleNext();
};

extern HapticFeedback *hapticFeedback;
void initHapticFeedback();

#endif // HAPTIC_FEEDBACK_PIN
11 changes: 11 additions & 0 deletions src/input/InputBroker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "PowerFSM.h" // needed for event trigger
#include "configuration.h"
#include "graphics/Screen.h"
#include "input/HapticFeedback.h"
#include "modules/ExternalNotificationModule.h"

#if ARCH_PORTDUINO
Expand Down Expand Up @@ -237,6 +238,16 @@ void InputBroker::Init()
}
touchBacklightActive = false;
};
#endif
#if defined(HAPTIC_FEEDBACK_PIN)
// Blip on touch, second blip when long-press fires (500 ms = touchConfig.longPressTime default).
touchConfig.suppressLeadUpSound = true;
initHapticFeedback();
touchConfig.onPress = []() {
hapticFeedback->pulse(80);
hapticFeedback->armDelayedPulse(500, 80);
};
touchConfig.onRelease = []() { hapticFeedback->cancelDelayedPulse(); };
#endif
TouchButtonThread->initButton(touchConfig);
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/platform/nrf52/architecture.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_CARD
#elif defined(TTGO_T_ECHO_PLUS)
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_PLUS
#elif defined(T_IMPULSE_PLUS)
#define HW_VENDOR meshtastic_HardwareModel_T_IMPULSE_PLUS
Comment thread
caveman99 marked this conversation as resolved.
#elif defined(ELECROW_ThinkNode_M1)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M1
#elif defined(ELECROW_ThinkNode_M3)
Expand Down
109 changes: 109 additions & 0 deletions src/power/SGM41562.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "SGM41562.h"

#ifdef HAS_SGM41562

#include <Arduino.h>

SGM41562 *sgm41562 = nullptr;

bool initSGM41562(TwoWire &wire)
{
if (sgm41562)
return true;
sgm41562 = new SGM41562();
if (!sgm41562->begin(wire)) {
delete sgm41562;
sgm41562 = nullptr;
return false;
}
return true;
}

bool SGM41562::readReg(uint8_t reg, uint8_t &value)
{
wire_->beginTransmission(address_);
wire_->write(reg);
if (wire_->endTransmission(false) != 0)
return false;
if (wire_->requestFrom((int)address_, 1) != 1)
return false;
value = wire_->read();
return true;
}

bool SGM41562::writeReg(uint8_t reg, uint8_t value)
{
wire_->beginTransmission(address_);
wire_->write(reg);
wire_->write(value);
return wire_->endTransmission() == 0;
}

bool SGM41562::updateReg(uint8_t reg, uint8_t mask, uint8_t value)
{
uint8_t cur;
if (!readReg(reg, cur))
return false;
cur = (cur & ~mask) | (value & mask);
return writeReg(reg, cur);
}

bool SGM41562::begin(TwoWire &wire, uint8_t address)
{
wire_ = &wire;
address_ = address;

uint8_t id;
if (!readReg(REG_DEVICE_ID, id)) {
LOG_WARN("SGM41562: I2C read failed at 0x%02X", address_);
return false;
}
if (id != DEVICE_ID_EXPECTED) {
LOG_WARN("SGM41562: unexpected device ID 0x%02X (expected 0x%02X)", id, DEVICE_ID_EXPECTED);
return false;
}
LOG_INFO("SGM41562: detected at 0x%02X (id 0x%02X)", address_, id);

// Mirror the vendor reference init sequence: PCB OTP off, NTC off,
// watchdog off, charger enabled. These match LilyGo's stock firmware
// for the T-Impulse Plus.
delay(120);
writeReg(REG_SYS_VOLTAGE_REG, 0xB7);
writeReg(REG_MISC_OP_CONTROL, 0x40);
writeReg(REG_CHARGE_TERM_TIMER, 0x1A);
writeReg(REG_POWER_ON_CFG, 0xA4);

return refresh();
}

bool SGM41562::refresh()
{
uint32_t now = millis();
if (lastRefreshMs_ != 0 && (now - lastRefreshMs_) < 250)
return true; // cached
lastRefreshMs_ = now == 0 ? 1 : now;

uint8_t status, fault;
if (!readReg(REG_SYSTEM_STATUS, status))
return false;
if (!readReg(REG_FAULT, fault))
return false;

chargeStatus_ = static_cast<ChargeStatus>((status >> SYS_STATUS_CHRG_SHIFT) & SYS_STATUS_CHRG_MASK);
inputPowerGood_ = (status & SYS_STATUS_PG) != 0;
thermalReg_ = (status & SYS_STATUS_THERM_REG) != 0;
faultMask_ = fault & 0x3F; // bits [7:6] are enter_ship_time config, not faults
return true;
}

bool SGM41562::setChargeEnable(bool enable)
{
return updateReg(REG_POWER_ON_CFG, POWER_ON_CFG_CHG_DISABLE, enable ? 0x00 : POWER_ON_CFG_CHG_DISABLE);
}

bool SGM41562::setShippingModeEnable(bool enable)
{
return updateReg(REG_MISC_OP_CONTROL, MISC_OP_SHIPPING_MODE, enable ? MISC_OP_SHIPPING_MODE : 0x00);
}

#endif // HAS_SGM41562
Loading
Loading