Skip to content

Commit

Permalink
T1000-E Peripherals (#5141)
Browse files Browse the repository at this point in the history
* T1000-E Peripherals
- enable intelligent charge controller signals
- enable Accelerometer
- enable internal I2C bus
- provide Power to Accelerometer

* POC Accelerometer Code (wakeScreen is moot for that device, just test if the driver works)
* fix building without the sensor
  • Loading branch information
caveman99 committed Nov 3, 2024
1 parent 0cbda88 commit feeee30
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 22 deletions.
5 changes: 5 additions & 0 deletions src/Power.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,12 @@ class AnalogBatteryLevel : public HasBatteryLevel
/**
* return true if there is a battery installed in this unit
*/
// if we have a integrated device with a battery, we can assume that the battery is always connected
#ifdef BATTERY_IMMUTABLE
virtual bool isBatteryConnect() override { return true; }
#else
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
#endif

/// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power
Expand Down
1 change: 1 addition & 0 deletions src/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define LPS22HB_ADDR_ALT 0x5D
#define SHT31_4x_ADDR 0x44
#define PMSA0031_ADDR 0x12
#define QMA6100P_ADDR 0x12
#define AHT10_ADDR 0x38
#define RCWL9620_ADDR 0x57
#define VEML7700_ADDR 0x10
Expand Down
4 changes: 2 additions & 2 deletions src/detect/ScanI2C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const

ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
{
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948};
return firstOfOrNONE(7, types);
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P};
return firstOfOrNONE(8, types);
}

ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const
Expand Down
1 change: 1 addition & 0 deletions src/detect/ScanI2C.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class ScanI2C
QMC5883L,
HMC5883L,
PMSA0031,
QMA6100P,
MPU6050,
LIS3DH,
BMA423,
Expand Down
15 changes: 14 additions & 1 deletion src/detect/ScanI2CTwoWire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
#endif

for (addr.address = 1; addr.address < 127; addr.address++) {
// We only need to scan 112 addresses, the rest is reserved for special purposes
// 0x00 General Call
// 0x01 CBUS addresses
// 0x02 Reserved for different bus formats
// 0x03 Reserved for future purposes
// 0x04-0x07 High Speed Master Code
// 0x78-0x7B 10-bit slave addressing
// 0x7C-0x7F Reserved for future purposes

for (addr.address = 8; addr.address < 120; addr.address++) {
if (asize != 0) {
if (!in_array(address, asize, addr.address))
continue;
Expand Down Expand Up @@ -395,7 +404,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)

SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L Highrate 3-Axis magnetic sensor found")
SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L 3-Axis digital compass found")
#ifdef HAS_QMA6100P
SCAN_SIMPLE_CASE(QMA6100P_ADDR, QMA6100P, "QMA6100P accelerometer found")
#else
SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found")
#endif
SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found");
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535 I2C expander found");
Expand Down
4 changes: 2 additions & 2 deletions src/input/cardKbI2cImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}

void CardKbI2cImpl::init()
{
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO)
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(I2C_NO_RESCAN)
if (cardkb_found.address == 0x00) {
LOG_DEBUG("Rescanning for I2C keyboard");
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR};
Expand Down Expand Up @@ -49,7 +49,7 @@ void CardKbI2cImpl::init()
kb_model = 0x00;
}
}
LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x\n", kb_info.type, kb_model, cardkb_found.address);
LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x", kb_info.type, kb_model, cardkb_found.address);
if (cardkb_found.address == 0x00) {
disable();
return;
Expand Down
2 changes: 2 additions & 0 deletions src/modules/Telemetry/AirQualityTelemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ int32_t AirQualityTelemetryModule::runOnce()
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: Initializing");
if (!aqi.begin_I2C()) {
#ifndef I2C_NO_RESCAN
LOG_WARN("Could not establish i2c connection to AQI sensor. Rescanning...");
// rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty.
uint8_t i2caddr_scan[] = {PMSA0031_ADDR};
Expand All @@ -51,6 +52,7 @@ int32_t AirQualityTelemetryModule::runOnce()
i2cScanner->fetchI2CBus(found.address);
return 1000;
}
#endif
return disable();
}
return 1000;
Expand Down
8 changes: 8 additions & 0 deletions src/motion/AccelerometerThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#include "LSM6DS3Sensor.h"
#include "MPU6050Sensor.h"
#include "MotionSensor.h"
#ifdef HAS_QMA6100P
#include "QMA6100PSensor.h"
#endif
#include "STK8XXXSensor.h"

extern ScanI2C::DeviceAddress accelerometer_found;
Expand Down Expand Up @@ -97,6 +100,11 @@ class AccelerometerThread : public concurrency::OSThread
case ScanI2C::DeviceType::ICM20948:
sensor = new ICM20948Sensor(device);
break;
#ifdef HAS_QMA6100P
case ScanI2C::DeviceType::QMA6100P:
sensor = new QMA6100PSensor(device);
break;
#endif
default:
disable();
return;
Expand Down
183 changes: 183 additions & 0 deletions src/motion/QMA6100PSensor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#include "QMA6100PSensor.h"

#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && HAS_QMA6100P

// Flag when an interrupt has been detected
volatile static bool QMA6100P_IRQ = false;

// Interrupt service routine
void QMA6100PSetInterrupt()
{
QMA6100P_IRQ = true;
}

QMA6100PSensor::QMA6100PSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {}

bool QMA6100PSensor::init()
{
// Initialise the sensor
sensor = QMA6100PSingleton::GetInstance();
if (!sensor->init(device))
return false;

// Enable simple Wake on Motion
return sensor->setWakeOnMotion();
}

#ifdef QMA_6100P_INT_PIN

int32_t QMA6100PSensor::runOnce()
{
// Wake on motion using hardware interrupts - this is the most efficient way to check for motion
if (QMA6100P_IRQ) {
QMA6100P_IRQ = false;
wakeScreen();
}
return MOTION_SENSOR_CHECK_INTERVAL_MS;
}

#else

int32_t QMA6100PSensor::runOnce()
{
// Wake on motion using polling - this is not as efficient as using hardware interrupt pin (see above)

uint8_t tempVal;
if (!sensor->readRegisterRegion(SFE_QMA6100P_INT_ST0, &tempVal, 1)) {
LOG_DEBUG("QMA6100PSensor::isWakeOnMotion failed to read interrupts");
return MOTION_SENSOR_CHECK_INTERVAL_MS;
}

if ((tempVal & 7) != 0) {
// Wake up!
wakeScreen();
}
return MOTION_SENSOR_CHECK_INTERVAL_MS;
}

#endif

// ----------------------------------------------------------------------
// QMA6100PSingleton
// ----------------------------------------------------------------------

// Get a singleton wrapper for an Sparkfun QMA_6100P_I2C
QMA6100PSingleton *QMA6100PSingleton::GetInstance()
{
if (pinstance == nullptr) {
pinstance = new QMA6100PSingleton();
}
return pinstance;
}

QMA6100PSingleton::QMA6100PSingleton() {}

QMA6100PSingleton::~QMA6100PSingleton() {}

QMA6100PSingleton *QMA6100PSingleton::pinstance{nullptr};

// Initialise the QMA6100P Sensor
bool QMA6100PSingleton::init(ScanI2C::FoundDevice device)
{

// startup
#ifdef Wire1
bool status = begin(device.address.address, device.address.port == ScanI2C::I2CPort::WIRE1 ? &Wire1 : &Wire);
#else
// check chip id
bool status = begin(device.address.address, &Wire);
#endif
if (status != true) {
LOG_WARN("QMA6100PSensor::init begin failed\n");
return false;
}
delay(20);
// SW reset to make sure the device starts in a known state
if (softwareReset() != true) {
LOG_WARN("QMA6100PSensor::init reset failed\n");
return false;
}
delay(20);
// Set range
if (!setRange(QMA_6100P_MPU_ACCEL_SCALE)) {
LOG_WARN("QMA6100PSensor::init range failed");
return false;
}
// set active mode
if (!enableAccel()) {
LOG_WARN("ERROR :QMA6100PSensor::active mode set failed");
}
// set calibrateoffsets
if (!calibrateOffsets()) {
LOG_WARN("ERROR :QMA6100PSensor:: calibration failed");
}
#ifdef QMA_6100P_INT_PIN

// Active low & Open Drain
uint8_t tempVal;
if (!readRegisterRegion(SFE_QMA6100P_INTPINT_CONF, &tempVal, 1)) {
LOG_WARN("QMA6100PSensor::init failed to read interrupt pin config");
return false;
}

tempVal |= 0b00000010; // Active low & Open Drain

if (!writeRegisterByte(SFE_QMA6100P_INTPINT_CONF, tempVal)) {
LOG_WARN("QMA6100PSensor::init failed to write interrupt pin config");
return false;
}

// Latch until cleared, all reads clear the latch
if (!readRegisterRegion(SFE_QMA6100P_INT_CFG, &tempVal, 1)) {
LOG_WARN("QMA6100PSensor::init failed to read interrupt config");
return false;
}

tempVal |= 0b10000001; // Latch until cleared, INT_RD_CLR1

if (!writeRegisterByte(SFE_QMA6100P_INT_CFG, tempVal)) {
LOG_WARN("QMA6100PSensor::init failed to write interrupt config");
return false;
}
// Set up an interrupt pin with an internal pullup for active low
pinMode(QMA_6100P_INT_PIN, INPUT_PULLUP);

// Set up an interrupt service routine
attachInterrupt(QMA_6100P_INT_PIN, QMA6100PSetInterrupt, FALLING);

#endif
return true;
}

bool QMA6100PSingleton::setWakeOnMotion()
{
// Enable 'Any Motion' interrupt
if (!writeRegisterByte(SFE_QMA6100P_INT_EN2, 0b00000111)) {
LOG_WARN("QMA6100PSingleton::setWakeOnMotion failed to write interrupt enable");
return false;
}

// Set 'Significant Motion' interrupt map to INT1
uint8_t tempVal;

if (!readRegisterRegion(SFE_QMA6100P_INT_MAP1, &tempVal, 1)) {
LOG_WARN("QMA6100PSingleton::setWakeOnMotion failed to read interrupt map");
return false;
}

sfe_qma6100p_int_map1_bitfield_t int_map1;
int_map1.all = tempVal;
int_map1.bits.int1_any_mot = 1; // any motion interrupt to INT1
tempVal = int_map1.all;

if (!writeRegisterByte(SFE_QMA6100P_INT_MAP1, tempVal)) {
LOG_WARN("QMA6100PSingleton::setWakeOnMotion failed to write interrupt map");
return false;
}

// Clear any current interrupts
QMA6100P_IRQ = false;
return true;
}

#endif
63 changes: 63 additions & 0 deletions src/motion/QMA6100PSensor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once
#ifndef _QMA_6100P_SENSOR_H_
#define _QMA_6100P_SENSOR_H_

#include "MotionSensor.h"

#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && HAS_QMA6100P

#include <QMA6100P.h>

// Set the default accelerometer scale - gpm2, gpm4, gpm8, gpm16
#ifndef QMA_6100P_MPU_ACCEL_SCALE
#define QMA_6100P_MPU_ACCEL_SCALE SFE_QMA6100P_RANGE32G
#endif

// The I2C address of the Accelerometer (if found) from main.cpp
extern ScanI2C::DeviceAddress accelerometer_found;

// Singleton wrapper for the Sparkfun QMA_6100P_I2C class
class QMA6100PSingleton : public QMA6100P
{
private:
static QMA6100PSingleton *pinstance;

protected:
QMA6100PSingleton();
~QMA6100PSingleton();

public:
// Create a singleton instance (not thread safe)
static QMA6100PSingleton *GetInstance();

// Singletons should not be cloneable.
QMA6100PSingleton(QMA6100PSingleton &other) = delete;

// Singletons should not be assignable.
void operator=(const QMA6100PSingleton &) = delete;

// Initialise the motion sensor singleton for normal operation
bool init(ScanI2C::FoundDevice device);

// Enable Wake on Motion interrupts (sensor must be initialised first)
bool setWakeOnMotion();
};

class QMA6100PSensor : public MotionSensor
{
private:
QMA6100PSingleton *sensor = nullptr;

public:
explicit QMA6100PSensor(ScanI2C::FoundDevice foundDevice);

// Initialise the motion sensor
virtual bool init() override;

// Called each time our sensor gets a chance to run
virtual int32_t runOnce() override;
};

#endif

#endif
2 changes: 1 addition & 1 deletion src/mqtt/MQTT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ bool MQTT::isPrivateIpAddress(const char address[])
// Even if it's not a valid IP address, we will know it's not a domain.
bool hasColon = false;
int numDots = 0;
for (int i = 0; i < length; i++) {
for (size_t i = 0; i < length; i++) {
if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') {
return false;
}
Expand Down
Loading

0 comments on commit feeee30

Please sign in to comment.