Browse Source

sns: digital sensor settings and setup checks

test/dev
Maxim Prokhorov 1 month ago
parent
commit
d9a7743d04
2 changed files with 334 additions and 140 deletions
  1. +1
    -14
      code/espurna/sensor.cpp
  2. +333
    -126
      code/espurna/sensors/DigitalSensor.h

+ 1
- 14
code/espurna/sensor.cpp View File

@ -2105,20 +2105,7 @@ void load() {
#if DIGITAL_SUPPORT
{
const auto pins = gpioPins();
for (size_t index = 0; index < pins; ++index) {
const auto pin = DigitalSensor::defaultPin(index);
if (pin == GPIO_NONE) {
break;
}
auto* sensor = new DigitalSensor();
sensor->setPin(pin);
sensor->setPinMode(DigitalSensor::defaultPinMode(index));
sensor->setDefault(DigitalSensor::defaultState(index));
add(sensor);
}
sensor::driver::digital::load();
}
#endif


+ 333
- 126
code/espurna/sensors/DigitalSensor.h View File

@ -3,170 +3,377 @@
// Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
#define SENSOR_SUPPORT 1
#define DIGITAL_SUPPORT 1
#if SENSOR_SUPPORT && DIGITAL_SUPPORT
#pragma once
#include "BaseSensor.h"
class DigitalSensor : public BaseSensor {
public:
// ---------------------------------------------------------------------
static unsigned char defaultPin(unsigned char index) {
switch (index) {
case 0:
return DIGITAL1_PIN;
case 1:
return DIGITAL2_PIN;
case 2:
return DIGITAL3_PIN;
case 3:
return DIGITAL4_PIN;
case 4:
return DIGITAL5_PIN;
case 5:
return DIGITAL6_PIN;
case 6:
return DIGITAL7_PIN;
case 7:
return DIGITAL8_PIN;
}
namespace espurna {
namespace sensor {
namespace driver {
namespace digital {
return GPIO_NONE;
}
enum class State {
Low,
High,
Initial,
};
static uint8_t defaultPinMode(unsigned char index) {
switch (index) {
case 0:
return DIGITAL1_PIN_MODE;
case 1:
return DIGITAL2_PIN_MODE;
case 2:
return DIGITAL3_PIN_MODE;
case 3:
return DIGITAL4_PIN_MODE;
case 4:
return DIGITAL5_PIN_MODE;
case 5:
return DIGITAL6_PIN_MODE;
case 6:
return DIGITAL7_PIN_MODE;
case 7:
return DIGITAL8_PIN_MODE;
}
enum Mode {
Input,
PullUp,
PullDown,
};
return INPUT_PULLUP;
}
namespace {
namespace build {
constexpr size_t SensorsMax { 8 };
constexpr unsigned char pin(unsigned char index) {
return (0 == index) ? (DIGITAL1_PIN) :
(1 == index) ? (DIGITAL2_PIN) :
(2 == index) ? (DIGITAL3_PIN) :
(3 == index) ? (DIGITAL4_PIN) :
(4 == index) ? (DIGITAL5_PIN) :
(5 == index) ? (DIGITAL6_PIN) :
(6 == index) ? (DIGITAL7_PIN) :
(7 == index) ? (DIGITAL8_PIN) : (GPIO_NONE);
}
constexpr Mode mode_from_value(int value) {
return (INPUT == value) ? Mode::Input :
(INPUT_PULLUP == value) ? Mode::PullUp :
(INPUT_PULLDOWN == value) ? Mode::PullDown : Mode::Input;
}
constexpr Mode mode(unsigned char index) {
return mode_from_value(
(0 == index) ? (DIGITAL1_PIN_MODE) :
(1 == index) ? (DIGITAL2_PIN_MODE) :
(2 == index) ? (DIGITAL3_PIN_MODE) :
(3 == index) ? (DIGITAL4_PIN_MODE) :
(4 == index) ? (DIGITAL5_PIN_MODE) :
(5 == index) ? (DIGITAL6_PIN_MODE) :
(6 == index) ? (DIGITAL7_PIN_MODE) :
(7 == index) ? (DIGITAL8_PIN_MODE) : (INPUT_PULLUP));
}
constexpr State state_from_value(int value) {
return (HIGH == value) ? State::High :
(LOW == value) ? State::Low : State::Initial;
}
constexpr State state(unsigned char index) {
return state_from_value(
(0 == index) ? (DIGITAL1_DEFAULT_STATE) :
(1 == index) ? (DIGITAL2_DEFAULT_STATE) :
(2 == index) ? (DIGITAL3_DEFAULT_STATE) :
(3 == index) ? (DIGITAL4_DEFAULT_STATE) :
(4 == index) ? (DIGITAL5_DEFAULT_STATE) :
(5 == index) ? (DIGITAL6_DEFAULT_STATE) :
(6 == index) ? (DIGITAL7_DEFAULT_STATE) :
(7 == index) ? (DIGITAL8_DEFAULT_STATE) : (HIGH));
}
} // namespace build
namespace settings {
namespace options {
using espurna::settings::options::Enumeration;
PROGMEM_STRING(Low, "low");
PROGMEM_STRING(High, "high");
PROGMEM_STRING(Initial, "initial");
static constexpr std::array<Enumeration<digital::State>, 3> State PROGMEM {
{{digital::State::Low, Low},
{digital::State::High, High},
{digital::State::Initial, Initial}}
};
static int defaultState(unsigned char index) {
switch (index) {
case 0:
return DIGITAL1_DEFAULT_STATE;
case 1:
return DIGITAL2_DEFAULT_STATE;
case 2:
return DIGITAL3_DEFAULT_STATE;
case 3:
return DIGITAL4_DEFAULT_STATE;
case 4:
return DIGITAL5_DEFAULT_STATE;
case 5:
return DIGITAL6_DEFAULT_STATE;
case 6:
return DIGITAL7_DEFAULT_STATE;
case 7:
return DIGITAL8_DEFAULT_STATE;
}
PROGMEM_STRING(Input, "default");
PROGMEM_STRING(PullUp, "pull-up");
PROGMEM_STRING(PullDown, "pull-down");
return HIGH;
}
static constexpr std::array<Enumeration<digital::Mode>, 3> Mode PROGMEM {
{{digital::Mode::Input, Input},
{digital::Mode::PullUp, PullUp},
{digital::Mode::PullDown, PullDown}}
};
// ---------------------------------------------------------------------
} // namespace options
} // namespace settings
} // namespace
void setPin(unsigned char pin) {
_pin = pin;
}
} // namespace digital
} // namespace driver
} // namespace sensor
void setPinMode(uint8_t mode) {
_pin_mode = mode;
}
namespace settings {
namespace internal {
void setDefault(int value) {
_default = value;
}
using namespace espurna::sensor::driver;
// ---------------------------------------------------------------------
template<>
digital::Mode convert(const String& value) {
return convert(digital::settings::options::Mode, value, digital::Mode::PullUp);
}
unsigned char getPin() {
return _pin;
}
String serialize(digital::Mode value) {
return serialize(digital::settings::options::Mode, value);
}
unsigned char getPinMode() {
return _pin_mode;
}
template<>
digital::State convert(const String& value) {
return convert(digital::settings::options::State, value, digital::State::High);
}
int getDefault() {
return _default;
}
String serialize(digital::State value) {
return serialize(digital::settings::options::State, value);
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
} // namespace internal
} // namespace settings
unsigned char id() const override {
return SENSOR_DIGITAL_ID;
}
namespace sensor {
namespace driver {
namespace digital {
namespace {
unsigned char count() const override {
return 1;
}
namespace settings {
STRING_VIEW_INLINE(Prefix, "digital");
namespace keys {
STRING_VIEW_INLINE(Pin, "digitalPin");
STRING_VIEW_INLINE(Mode, "digitalPinMode");
STRING_VIEW_INLINE(State, "digitalDefState");
} // namespace keys
unsigned char pin(size_t index) {
return getSetting(keys::Pin, build::pin(index));
}
Mode mode(size_t index) {
return getSetting(keys::Mode, build::mode(index));
}
State state(size_t index) {
return getSetting(keys::State, build::state(index));
}
// Initialization method, must be idempotent
void begin() override {
namespace query {
#define ID_VALUE(NAME, FUNC)\
String NAME (size_t id) {\
return espurna::settings::internal::serialize(FUNC(id));\
}
ID_VALUE(pin, settings::pin)
ID_VALUE(mode, settings::mode)
ID_VALUE(state, settings::state)
#undef ID_VALUE
static constexpr espurna::settings::query::IndexedSetting IndexedSettings[] PROGMEM {
{keys::Pin, pin},
{keys::Mode, mode},
{keys::State, state},
};
bool checkSamePrefix(StringView key) {
return key.startsWith(Prefix);
}
String findValueFrom(StringView key) {
return espurna::settings::query::IndexedSetting::findValueFrom(
build::SensorsMax, IndexedSettings, key);
}
void setup() {
settingsRegisterQueryHandler({
.check = checkSamePrefix,
.get = findValueFrom,
});
}
} // namespace query
} // namespace settings
struct Config {
uint8_t pin;
int pin_mode;
State default_state;
};
class Sensor : public BaseSensor {
public:
Sensor() = delete;
explicit Sensor(Config config) :
_pin(config.pin),
_pin_mode(config.pin_mode),
_default_state(config.default_state)
{}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
unsigned char id() const override {
return SENSOR_DIGITAL_ID;
}
unsigned char count() const override {
return 1;
}
// Initialization method, must be idempotent
void begin() override {
if (!_ready) {
pinMode(_pin, _pin_mode);
_ready = true;
switch (_default_state) {
case State::Initial:
_default = digitalRead(_pin);
break;
case State::High:
_default = HIGH;
break;
case State::Low:
_default = LOW;
break;
}
}
// Descriptive name of the sensor
String description() const override {
char buffer[32];
snprintf_P(buffer, sizeof(buffer),
PSTR("DIGITAL @ GPIO%hhu"), _pin);
return String(buffer);
_ready = true;
}
// Descriptive name of the sensor
String description() const override {
char buffer[32];
snprintf_P(buffer, sizeof(buffer),
PSTR("Digital @ GPIO%hhu"), _pin);
return String(buffer);
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
return String(_pin, 10);
}
// Type for slot # index
unsigned char type(unsigned char index) const override {
if (index == 0) {
return MAGNITUDE_DIGITAL;
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
return String(_pin, 10);
return MAGNITUDE_NONE;
}
void pre() override {
_current = digitalRead(_pin);
}
// Current value for slot # index
double value(unsigned char index) override {
if (index != 0) {
return 0;
}
// Type for slot # index
unsigned char type(unsigned char index) const override {
if (index == 0) {
return MAGNITUDE_DIGITAL;
return (_current != _default) ? 1.0 : 0.0;
}
private:
unsigned char _pin;
uint8_t _pin_mode;
State _default_state;
int _current { -1 };
int _default { LOW };
};
class Init : public sensor::PreInit {
public:
Init() = default;
String description() const override {
return STRING_VIEW("DigitalSensor").toString();
}
Result find_sensors() override {
return _find_sensors();
}
private:
Result _with_error(int error) {
return Result{
.sensors = make_span(_sensors),
.error = error,
};
}
Result _find_sensors() {
std::array<uint8_t, build::SensorsMax> pins;
pins.fill(GPIO_NONE);
int err = SENSOR_ERROR_OK;
size_t index = 0;
for (; index < build::SensorsMax; ++index) {
const auto pin = settings::pin(index);
if (pin == GPIO_NONE) {
break;
}
if (!gpioLock(pin)) {
err = SENSOR_ERROR_GPIO_USED;
break;
}
return MAGNITUDE_NONE;
pins[index] = pin;
}
// Current value for slot # index
double value(unsigned char index) {
if (index == 0) {
if (digitalRead(_pin) != _default) {
return 1;
}
}
size_t until = index;
if (!until) {
err = SENSOR_ERROR_CONFIG;
}
return 0;
for (size_t index = 0; index < until; ++index) {
if (err != SENSOR_ERROR_OK) {
gpioUnlock(pins[index]);
} else {
_sensors.push_back(
new Sensor(Config{
.pin = pins[index],
.pin_mode = settings::mode(index),
.default_state = settings::state(index),
}));
}
}
private:
unsigned char _pin;
uint8_t _pin_mode;
int _default = LOW;
return _with_error(err);
}
std::vector<BaseSensor*> _sensors;
};
inline void load() {
settings::query::setup();
sensor::add_preinit(std::make_unique<Init>());
}
} // namespace
} // namespace digital
} // namespace driver
} // namespace sensor
} // namespace espurna
#endif // SENSOR_SUPPORT && DIGITAL_SUPPORT

Loading…
Cancel
Save