Skip to content

Flex Sensor Implementation #409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion lib/bno080/BNO080.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
#include <Wire.h>
#include <SPI.h>
#include <memory>
#include "PinInterface.h"
#include <PinInterface.h>

//The default I2C address for the BNO080 on the SparkX breakout is 0x4B. 0x4A is also possible.
#define BNO080_DEFAULT_ADDRESS 0x4B
Expand Down
38 changes: 19 additions & 19 deletions lib/bno080/PinInterface.h
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
/*
SlimeVR Code is placed under the MIT license
Copyright (c) 2024 Eiren Rain & SlimeVR contributors
SlimeVR Code is placed under the MIT license
Copyright (c) 2024 Eiren Rain & SlimeVR contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once

#include <cstdint>

class PinInterface
{
class PinInterface {
public:
virtual bool init() { return true; };
virtual int digitalRead() = 0;
virtual void pinMode(uint8_t mode) = 0;
virtual void digitalWrite(uint8_t val) = 0;
virtual float analogRead() = 0;
};
12 changes: 1 addition & 11 deletions src/batterymonitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,10 @@
#include <I2Cdev.h>
#include <i2cscan.h>

#include "consts.h"
#include "globals.h"
#include "logging/Logger.h"

#if ESP8266
#define ADCResolution 1023.0 // ESP8266 has 10bit ADC
#define ADCVoltageMax 1.0 // ESP8266 input is 1.0 V = 1023.0
#endif
#ifndef ADCResolution
#define ADCResolution 1023.0
#endif
#ifndef ADCVoltageMax
#define ADCVoltageMax 1.0
#endif

#ifndef BATTERY_SHIELD_RESISTANCE
#define BATTERY_SHIELD_RESISTANCE 180.0
#endif
Expand Down
16 changes: 16 additions & 0 deletions src/consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ enum class SensorTypeID : uint8_t {
#define IMU_MPU6050_SF SoftFusionMPU6050
#define IMU_ICM45686 SoftFusionICM45686
#define IMU_ICM45605 SoftFusionICM45605
#define SENSOR_ADC ADCResistanceSensor

#define IMU_DEV_RESERVED 250 // Reserved, should not be used in any release firmware

Expand Down Expand Up @@ -173,6 +174,21 @@ enum class TrackerType : uint8_t {

#define CURRENT_CONFIGURATION_VERSION 1

#if ESP8266
#define ADCResolution 1023.0 // ESP8266 has 10bit ADC
#define ADCVoltageMax 1.0 // ESP8266 input is 1.0 V = 1023.0
#elif ESP32
#define ADCResolution 4095.0 // ESP32 has 12bit ADC
#define ADCVoltageMax 2.5 // ESP32 input is 2.5 V = 4095.0 by default
#endif

#ifndef ADCResolution
#define ADCResolution 1023.0
#endif
#ifndef ADCVoltageMax
#define ADCVoltageMax 1.0
#endif

#include "sensors/sensorposition.h"

#endif // SLIMEVR_CONSTS_H_
131 changes: 131 additions & 0 deletions src/sensorinterface/ADS111xInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
SlimeVR Code is placed under the MIT license
Copyright (c) 2025 Gorbit99 & SlimeVR Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "ADS111xInterface.h"

#include <cstring>

namespace SlimeVR {

ADS111xInterface::ADS111xInterface(
SensorInterface* interface,
PinInterface* drdy,
uint8_t address
)
: interface{interface}
, drdy{drdy}
, address{address} {}

bool ADS111xInterface::init() {
Registers::Config config{
.compQue = 0b11, // Disable comparator
.compLat = 0b0, // Doesn't matter
.compPol = 0b0, // Doesn't matter
.compMode = 0b0, // Doesn't matter
.dr = 0b100, // Doesn't matter
.mode = 0b1, // Single-shot
.pga = 0b010, // Gain (FSR): 2.048V
.mux = 0b000, // Doesn't matter
.os = 0b0, // Don't start read
};

if (!writeRegister(Registers::Addresses::Config, config)) {
logger.error("Couldn't initialize ADS interface!\n");
return false;
}

// Enable conversion ready functionality
writeRegister(Registers::Addresses::HiThresh, static_cast<uint16_t>(0x8000));
writeRegister(Registers::Addresses::LoThresh, static_cast<uint16_t>(0x0000));

drdy->pinMode(INPUT);

return true;
}

float ADS111xInterface::read(uint8_t channel) {
if (__builtin_popcount(usedPins) == 1) {
// Just read the last sample
uint16_t value = readRegister(Registers::Addresses::Conversion);

return static_cast<float>(value) / maxValue;
}

Registers::Config config{
.compQue = 0b00, // Alert after every sample
.compLat = 0b1, // Latch alert
.compPol = 0b0, // Doesn't matter
.compMode = 0b0, // Doesn't matter
.dr = 0b111, // 860 samples per second
.mode = 0b1, // Single-shot
.pga = 0b010, // Gain (FSR): 2.048V
.mux = static_cast<uint8_t>(0b100 | channel), // Current channel
.os = 0b1, // Start read
};

writeRegister(Registers::Addresses::Config, config);

while (drdy->digitalRead())
;

uint16_t value = readRegister(Registers::Addresses::Conversion);

return static_cast<float>(value) / maxValue;
}

uint16_t ADS111xInterface::readRegister(Registers::Addresses reg) {
Wire.beginTransmission(address);
Wire.write(static_cast<uint8_t>(Registers::Addresses::Conversion));
Wire.endTransmission();

Wire.beginTransmission(address);
Wire.requestFrom(address, 2);
uint8_t msb = Wire.read();
uint8_t lsb = Wire.read();
Wire.endTransmission();

return (msb << 8) | lsb;
}

void ADS111xInterface::registerChannel(uint8_t channel) {
usedPins |= 1 << channel;
if (__builtin_popcount(usedPins) != 1) {
return;
}

// If we have only one channel used, just set up continuous reads
Registers::Config config{
.compQue = 0b11, // Disable comparator
.compLat = 0b0, // Doesn't matter
.compPol = 0b0, // Doesn't matter
.compMode = 0b0, // Doesn't matter
.dr = 0b100, // 128 samples per second
.mode = 0b0, // Continuous mode
.pga = 0b010, // Gain (FSR): 2.048V
.mux = static_cast<uint8_t>(0b100 | channel), // Use the channel
.os = 0b1, // Start reads
};

writeRegister(Registers::Addresses::Config, config);
}

} // namespace SlimeVR
97 changes: 97 additions & 0 deletions src/sensorinterface/ADS111xInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
SlimeVR Code is placed under the MIT license
Copyright (c) 2025 Gorbit99 & SlimeVR Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#pragma once

#include <PinInterface.h>

#include <cstdint>

#include "../logging/Logger.h"
#include "DirectPinInterface.h"
#include "I2CWireSensorInterface.h"
#include "SensorInterface.h"

namespace SlimeVR {

class ADS111xInterface {
public:
ADS111xInterface(SensorInterface* interface, PinInterface* drdy, uint8_t address);
bool init();

float read(uint8_t channel);

private:
static constexpr uint32_t maxValue = 0x7fff;

struct Registers {
enum class Addresses : uint8_t {
Conversion = 0b00,
Config = 0b01,
LoThresh = 0b10,
HiThresh = 0b11,
};
struct Config {
uint8_t compQue : 2;
uint8_t compLat : 1;
uint8_t compPol : 1;
uint8_t compMode : 1;
uint8_t dr : 3;
uint8_t mode : 1;
uint8_t pga : 3;
uint8_t mux : 3;
uint8_t os : 1;
};
};
static_assert(sizeof(Registers::Config) == 2);

template <typename T>
bool writeRegister(Registers::Addresses reg, T value) {
static_assert(sizeof(T) == 2);

interface->swapIn();
Wire.beginTransmission(address);
Wire.write(static_cast<uint8_t>(reg));
auto* bytes = reinterpret_cast<uint8_t*>(&value);
Wire.write(bytes[1]);
Wire.write(bytes[0]);
auto result = Wire.endTransmission();
return result == 0;
}

void registerChannel(uint8_t channel);

uint16_t readRegister(Registers::Addresses red);

SensorInterface* interface;
PinInterface* drdy;
uint8_t address;
uint8_t counter = 0;
uint8_t usedPins = 0x0;

Logging::Logger logger = Logging::Logger("ADS111x");

friend class ADS111xPin;
};

} // namespace SlimeVR
43 changes: 43 additions & 0 deletions src/sensorinterface/ADS111xPin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
SlimeVR Code is placed under the MIT license
Copyright (c) 2025 Gorbit99 & SlimeVR Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "ADS111xPin.h"

#include <cassert>

namespace SlimeVR {

ADS111xPin::ADS111xPin(ADS111xInterface* interface, uint8_t channel)
: ads111x{interface}
, channel{channel} {
assert(channel < 4);

interface->registerChannel(channel);
}

int ADS111xPin::digitalRead() { return analogRead() >= 0.5f; }
void ADS111xPin::pinMode(uint8_t mode) {}
void ADS111xPin::digitalWrite(uint8_t val) {}

float ADS111xPin::analogRead() { return ads111x->read(channel); }

}; // namespace SlimeVR
Loading