2024-05-16 22:27:36 +00:00
|
|
|
#pragma once
|
2024-04-29 10:10:49 +00:00
|
|
|
#include "configuration.h"
|
|
|
|
|
2024-04-27 06:05:39 +00:00
|
|
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
|
|
|
|
2023-03-23 16:32:04 +00:00
|
|
|
#include "PowerFSM.h"
|
|
|
|
#include "concurrency/OSThread.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "power.h"
|
|
|
|
|
|
|
|
#include <Adafruit_LIS3DH.h>
|
2024-04-12 00:40:14 +00:00
|
|
|
#include <Adafruit_LSM6DS3TRC.h>
|
2023-03-23 16:32:04 +00:00
|
|
|
#include <Adafruit_MPU6050.h>
|
2023-07-22 14:26:54 +00:00
|
|
|
#include <Arduino.h>
|
2024-03-15 15:45:14 +00:00
|
|
|
#include <SensorBMA423.hpp>
|
2023-07-22 14:26:54 +00:00
|
|
|
#include <Wire.h>
|
2024-06-11 22:47:45 +00:00
|
|
|
#ifdef RAK_4631
|
|
|
|
#include "Fusion/Fusion.h"
|
2024-06-25 16:26:02 +00:00
|
|
|
#include "graphics/Screen.h"
|
|
|
|
#include "graphics/ScreenFonts.h"
|
2024-06-11 22:47:45 +00:00
|
|
|
#include <Rak_BMX160.h>
|
|
|
|
#endif
|
2023-07-22 14:26:54 +00:00
|
|
|
|
2023-03-29 18:04:02 +00:00
|
|
|
#define ACCELEROMETER_CHECK_INTERVAL_MS 100
|
|
|
|
#define ACCELEROMETER_CLICK_THRESHOLD 40
|
|
|
|
|
2024-05-16 22:27:36 +00:00
|
|
|
static inline int readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
|
2023-07-22 14:26:54 +00:00
|
|
|
{
|
|
|
|
Wire.beginTransmission(address);
|
|
|
|
Wire.write(reg);
|
|
|
|
Wire.endTransmission();
|
|
|
|
Wire.requestFrom((uint8_t)address, (uint8_t)len);
|
|
|
|
uint8_t i = 0;
|
|
|
|
while (Wire.available()) {
|
|
|
|
data[i++] = Wire.read();
|
|
|
|
}
|
|
|
|
return 0; // Pass
|
|
|
|
}
|
|
|
|
|
2024-05-16 22:27:36 +00:00
|
|
|
static inline int writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
|
2023-07-22 14:26:54 +00:00
|
|
|
{
|
|
|
|
Wire.beginTransmission(address);
|
|
|
|
Wire.write(reg);
|
|
|
|
Wire.write(data, len);
|
|
|
|
return (0 != Wire.endTransmission());
|
|
|
|
}
|
|
|
|
|
2023-03-23 16:32:04 +00:00
|
|
|
class AccelerometerThread : public concurrency::OSThread
|
|
|
|
{
|
|
|
|
public:
|
2023-12-14 13:35:46 +00:00
|
|
|
explicit AccelerometerThread(ScanI2C::DeviceType type) : OSThread("AccelerometerThread")
|
2023-03-23 16:32:04 +00:00
|
|
|
{
|
2023-03-29 18:04:02 +00:00
|
|
|
if (accelerometer_found.port == ScanI2C::I2CPort::NO_I2C) {
|
|
|
|
LOG_DEBUG("AccelerometerThread disabling due to no sensors found\n");
|
2023-03-23 16:32:04 +00:00
|
|
|
disable();
|
|
|
|
return;
|
|
|
|
}
|
2024-05-16 22:27:36 +00:00
|
|
|
acceleremoter_type = type;
|
2024-06-11 22:47:45 +00:00
|
|
|
#ifndef RAK_4631
|
2023-03-29 18:04:02 +00:00
|
|
|
if (!config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) {
|
|
|
|
LOG_DEBUG("AccelerometerThread disabling due to no interested configurations\n");
|
|
|
|
disable();
|
|
|
|
return;
|
|
|
|
}
|
2024-06-11 22:47:45 +00:00
|
|
|
#endif
|
2024-05-16 22:27:36 +00:00
|
|
|
init();
|
|
|
|
}
|
2023-03-29 18:04:02 +00:00
|
|
|
|
2024-05-16 22:27:36 +00:00
|
|
|
void start()
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
setIntervalFromNow(0);
|
|
|
|
};
|
|
|
|
|
|
|
|
protected:
|
|
|
|
int32_t runOnce() override
|
|
|
|
{
|
|
|
|
canSleep = true; // Assume we should not keep the board awake
|
|
|
|
|
|
|
|
if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.getMotionInterruptStatus()) {
|
|
|
|
wakeScreen();
|
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.getClick() > 0) {
|
|
|
|
uint8_t click = lis.getClick();
|
|
|
|
if (!config.device.double_tap_as_button_press) {
|
|
|
|
wakeScreen();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config.device.double_tap_as_button_press && (click & 0x20)) {
|
|
|
|
buttonPress();
|
|
|
|
return 500;
|
|
|
|
}
|
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.readIrqStatus() != DEV_WIRE_NONE) {
|
|
|
|
if (bmaSensor.isTilt() || bmaSensor.isDoubleTap()) {
|
|
|
|
wakeScreen();
|
|
|
|
return 500;
|
|
|
|
}
|
2024-06-11 22:47:45 +00:00
|
|
|
#ifdef RAK_4631
|
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160) {
|
|
|
|
sBmx160SensorData_t magAccel;
|
|
|
|
sBmx160SensorData_t gAccel;
|
|
|
|
|
|
|
|
/* Get a new sensor event */
|
|
|
|
bmx160.getAllData(&magAccel, NULL, &gAccel);
|
|
|
|
|
|
|
|
// expirimental calibrate routine. Limited to between 10 and 30 seconds after boot
|
2024-06-25 16:26:02 +00:00
|
|
|
if (millis() > 12 * 1000 && millis() < 30 * 1000) {
|
|
|
|
if (!showingScreen) {
|
|
|
|
showingScreen = true;
|
|
|
|
screen->startAlert((FrameCallback)drawFrameCalibration);
|
|
|
|
}
|
2024-06-11 22:47:45 +00:00
|
|
|
if (magAccel.x > highestX)
|
|
|
|
highestX = magAccel.x;
|
|
|
|
if (magAccel.x < lowestX)
|
|
|
|
lowestX = magAccel.x;
|
|
|
|
if (magAccel.y > highestY)
|
|
|
|
highestY = magAccel.y;
|
|
|
|
if (magAccel.y < lowestY)
|
|
|
|
lowestY = magAccel.y;
|
|
|
|
if (magAccel.z > highestZ)
|
|
|
|
highestZ = magAccel.z;
|
|
|
|
if (magAccel.z < lowestZ)
|
|
|
|
lowestZ = magAccel.z;
|
2024-06-25 16:26:02 +00:00
|
|
|
} else if (showingScreen && millis() >= 30 * 1000) {
|
|
|
|
showingScreen = false;
|
|
|
|
screen->endAlert();
|
2024-06-11 22:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int highestRealX = highestX - (highestX + lowestX) / 2;
|
|
|
|
|
|
|
|
magAccel.x -= (highestX + lowestX) / 2;
|
|
|
|
magAccel.y -= (highestY + lowestY) / 2;
|
|
|
|
magAccel.z -= (highestZ + lowestZ) / 2;
|
|
|
|
FusionVector ga, ma;
|
|
|
|
ga.axis.x = -gAccel.x; // default location for the BMX160 is on the rear of the board
|
|
|
|
ga.axis.y = -gAccel.y;
|
|
|
|
ga.axis.z = gAccel.z;
|
|
|
|
ma.axis.x = -magAccel.x;
|
|
|
|
ma.axis.y = -magAccel.y;
|
|
|
|
ma.axis.z = magAccel.z * 3;
|
|
|
|
|
|
|
|
// If we're set to one of the inverted positions
|
|
|
|
if (config.display.compass_orientation > meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270) {
|
|
|
|
ma = FusionAxesSwap(ma, FusionAxesAlignmentNXNYPZ);
|
|
|
|
ga = FusionAxesSwap(ga, FusionAxesAlignmentNXNYPZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
float heading = FusionCompassCalculateHeading(FusionConventionNed, ga, ma);
|
|
|
|
|
|
|
|
switch (config.display.compass_orientation) {
|
2024-06-15 14:46:15 +00:00
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0_INVERTED:
|
2024-06-11 22:47:45 +00:00
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0:
|
|
|
|
break;
|
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90:
|
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90_INVERTED:
|
|
|
|
heading += 90;
|
|
|
|
break;
|
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180:
|
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180_INVERTED:
|
|
|
|
heading += 180;
|
|
|
|
break;
|
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270:
|
|
|
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED:
|
|
|
|
heading += 270;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
screen->setHeading(heading);
|
|
|
|
|
|
|
|
#endif
|
2024-05-16 22:27:36 +00:00
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.shake()) {
|
|
|
|
wakeScreen();
|
|
|
|
return 500;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ACCELEROMETER_CHECK_INTERVAL_MS;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void init()
|
|
|
|
{
|
2023-03-23 16:32:04 +00:00
|
|
|
LOG_DEBUG("AccelerometerThread initializing\n");
|
|
|
|
|
2023-07-22 14:26:54 +00:00
|
|
|
if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.begin(accelerometer_found.address)) {
|
2023-03-23 16:32:04 +00:00
|
|
|
LOG_DEBUG("MPU6050 initializing\n");
|
|
|
|
// setup motion detection
|
|
|
|
mpu.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ);
|
|
|
|
mpu.setMotionDetectionThreshold(1);
|
|
|
|
mpu.setMotionDetectionDuration(20);
|
|
|
|
mpu.setInterruptPinLatch(true); // Keep it latched. Will turn off when reinitialized.
|
|
|
|
mpu.setInterruptPinPolarity(true);
|
2023-07-22 14:26:54 +00:00
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.begin(accelerometer_found.address)) {
|
2023-03-23 16:32:04 +00:00
|
|
|
LOG_DEBUG("LIS3DH initializing\n");
|
|
|
|
lis.setRange(LIS3DH_RANGE_2_G);
|
2023-07-14 21:25:20 +00:00
|
|
|
// Adjust threshold, higher numbers are less sensitive
|
2023-03-29 18:04:02 +00:00
|
|
|
lis.setClick(config.device.double_tap_as_button_press ? 2 : 1, ACCELEROMETER_CLICK_THRESHOLD);
|
2024-03-15 15:45:14 +00:00
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 &&
|
|
|
|
bmaSensor.begin(accelerometer_found.address, &readRegister, &writeRegister)) {
|
2023-07-22 14:26:54 +00:00
|
|
|
LOG_DEBUG("BMA423 initializing\n");
|
2024-03-15 15:45:14 +00:00
|
|
|
bmaSensor.configAccelerometer(bmaSensor.RANGE_2G, bmaSensor.ODR_100HZ, bmaSensor.BW_NORMAL_AVG4,
|
|
|
|
bmaSensor.PERF_CONTINUOUS_MODE);
|
|
|
|
bmaSensor.enableAccelerometer();
|
|
|
|
bmaSensor.configInterrupt(BMA4_LEVEL_TRIGGER, BMA4_ACTIVE_HIGH, BMA4_PUSH_PULL, BMA4_OUTPUT_ENABLE,
|
|
|
|
BMA4_INPUT_DISABLE);
|
2023-07-22 14:26:54 +00:00
|
|
|
|
|
|
|
#ifdef BMA423_INT
|
|
|
|
pinMode(BMA4XX_INT, INPUT);
|
|
|
|
attachInterrupt(
|
|
|
|
BMA4XX_INT,
|
|
|
|
[] {
|
|
|
|
// Set interrupt to set irq value to true
|
|
|
|
BMA_IRQ = true;
|
|
|
|
},
|
|
|
|
RISING); // Select the interrupt mode according to the actual circuit
|
|
|
|
#endif
|
|
|
|
|
2024-03-10 22:24:32 +00:00
|
|
|
#ifdef T_WATCH_S3
|
2024-03-15 15:45:14 +00:00
|
|
|
// Need to raise the wrist function, need to set the correct axis
|
|
|
|
bmaSensor.setReampAxes(bmaSensor.REMAP_TOP_LAYER_RIGHT_CORNER);
|
2024-03-10 22:24:32 +00:00
|
|
|
#else
|
2024-03-15 15:45:14 +00:00
|
|
|
bmaSensor.setReampAxes(bmaSensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
|
2024-03-10 22:24:32 +00:00
|
|
|
#endif
|
2024-03-15 15:45:14 +00:00
|
|
|
// bmaSensor.enableFeature(bmaSensor.FEATURE_STEP_CNTR, true);
|
|
|
|
bmaSensor.enableFeature(bmaSensor.FEATURE_TILT, true);
|
|
|
|
bmaSensor.enableFeature(bmaSensor.FEATURE_WAKEUP, true);
|
|
|
|
// bmaSensor.resetPedometer();
|
2023-07-22 14:26:54 +00:00
|
|
|
|
|
|
|
// Turn on feature interrupt
|
2024-03-15 15:45:14 +00:00
|
|
|
bmaSensor.enablePedometerIRQ();
|
|
|
|
bmaSensor.enableTiltIRQ();
|
2023-07-22 14:26:54 +00:00
|
|
|
// It corresponds to isDoubleClick interrupt
|
2024-03-15 15:45:14 +00:00
|
|
|
bmaSensor.enableWakeupIRQ();
|
2024-06-11 22:47:45 +00:00
|
|
|
#ifdef RAK_4631
|
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160 && bmx160.begin()) {
|
|
|
|
bmx160.ODR_Config(BMX160_ACCEL_ODR_100HZ, BMX160_GYRO_ODR_100HZ); // set output data rate
|
|
|
|
|
|
|
|
#endif
|
2024-04-12 00:40:14 +00:00
|
|
|
} else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.begin_I2C(accelerometer_found.address)) {
|
|
|
|
LOG_DEBUG("LSM6DS3 initializing\n");
|
|
|
|
// Default threshold of 2G, less sensitive options are 4, 8 or 16G
|
|
|
|
lsm.setAccelRange(LSM6DS_ACCEL_RANGE_2_G);
|
|
|
|
#ifndef LSM6DS3_WAKE_THRESH
|
|
|
|
#define LSM6DS3_WAKE_THRESH 20
|
|
|
|
#endif
|
|
|
|
lsm.enableWakeup(config.display.wake_on_tap_or_motion, 1, LSM6DS3_WAKE_THRESH);
|
|
|
|
// Duration is number of occurances needed to trigger, higher threshold is less sensitive
|
2023-03-23 16:32:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
void wakeScreen()
|
|
|
|
{
|
|
|
|
if (powerFSM.getState() == &stateDARK) {
|
2023-03-29 18:04:02 +00:00
|
|
|
LOG_INFO("Tap or motion detected. Turning on screen\n");
|
2023-03-23 16:32:04 +00:00
|
|
|
powerFSM.trigger(EVENT_INPUT);
|
|
|
|
}
|
|
|
|
}
|
2023-03-29 18:04:02 +00:00
|
|
|
|
|
|
|
void buttonPress()
|
|
|
|
{
|
|
|
|
LOG_DEBUG("Double-tap detected. Firing button press\n");
|
|
|
|
powerFSM.trigger(EVENT_PRESS);
|
|
|
|
}
|
|
|
|
|
2023-07-22 14:26:54 +00:00
|
|
|
ScanI2C::DeviceType acceleremoter_type;
|
2023-03-23 16:32:04 +00:00
|
|
|
Adafruit_MPU6050 mpu;
|
|
|
|
Adafruit_LIS3DH lis;
|
2024-04-12 00:40:14 +00:00
|
|
|
Adafruit_LSM6DS3TRC lsm;
|
2024-05-16 22:27:36 +00:00
|
|
|
SensorBMA423 bmaSensor;
|
2024-06-25 16:26:02 +00:00
|
|
|
bool BMA_IRQ = false;
|
2024-06-11 22:47:45 +00:00
|
|
|
#ifdef RAK_4631
|
2024-06-25 16:26:02 +00:00
|
|
|
bool showingScreen = false;
|
2024-06-11 22:47:45 +00:00
|
|
|
RAK_BMX160 bmx160;
|
|
|
|
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
2024-06-25 16:26:02 +00:00
|
|
|
|
|
|
|
static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
|
|
{
|
|
|
|
int x_offset = display->width() / 2;
|
|
|
|
int y_offset = display->height() <= 80 ? 0 : 32;
|
|
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
|
|
display->setFont(FONT_MEDIUM);
|
|
|
|
display->drawString(x, y, "Calibrating\nCompass");
|
|
|
|
int16_t compassX = 0, compassY = 0;
|
|
|
|
|
|
|
|
// coordinates for the center of the compass/circle
|
|
|
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
|
|
|
compassX = x + display->getWidth() - getCompassDiam(display) / 2 - 5;
|
|
|
|
compassY = y + display->getHeight() / 2;
|
|
|
|
} else {
|
|
|
|
compassX = x + display->getWidth() - getCompassDiam(display) / 2 - 5;
|
|
|
|
compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
|
|
|
|
}
|
|
|
|
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
|
2024-06-29 02:28:18 +00:00
|
|
|
screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180);
|
2024-06-25 16:26:02 +00:00
|
|
|
}
|
2024-06-11 22:47:45 +00:00
|
|
|
#endif
|
2023-03-23 16:32:04 +00:00
|
|
|
};
|
|
|
|
|
2024-04-27 06:05:39 +00:00
|
|
|
#endif
|