Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
8.9 KiB

  1. // -----------------------------------------------------------------------------
  2. // Digital Sensor (maps to a digitalRead)
  3. // Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && DIGITAL_SUPPORT
  6. #pragma once
  7. #include "BaseSensor.h"
  8. namespace espurna {
  9. namespace sensor {
  10. namespace driver {
  11. namespace digital {
  12. enum class State {
  13. Low,
  14. High,
  15. Initial,
  16. };
  17. enum Mode {
  18. Input,
  19. PullUp,
  20. PullDown,
  21. };
  22. namespace {
  23. namespace build {
  24. constexpr size_t SensorsMax { 8 };
  25. constexpr unsigned char pin(unsigned char index) {
  26. return (0 == index) ? (DIGITAL1_PIN) :
  27. (1 == index) ? (DIGITAL2_PIN) :
  28. (2 == index) ? (DIGITAL3_PIN) :
  29. (3 == index) ? (DIGITAL4_PIN) :
  30. (4 == index) ? (DIGITAL5_PIN) :
  31. (5 == index) ? (DIGITAL6_PIN) :
  32. (6 == index) ? (DIGITAL7_PIN) :
  33. (7 == index) ? (DIGITAL8_PIN) : (GPIO_NONE);
  34. }
  35. constexpr Mode mode_from_value(int value) {
  36. return (INPUT == value) ? Mode::Input :
  37. (INPUT_PULLUP == value) ? Mode::PullUp :
  38. (INPUT_PULLDOWN == value) ? Mode::PullDown : Mode::Input;
  39. }
  40. constexpr Mode mode(unsigned char index) {
  41. return mode_from_value(
  42. (0 == index) ? (DIGITAL1_PIN_MODE) :
  43. (1 == index) ? (DIGITAL2_PIN_MODE) :
  44. (2 == index) ? (DIGITAL3_PIN_MODE) :
  45. (3 == index) ? (DIGITAL4_PIN_MODE) :
  46. (4 == index) ? (DIGITAL5_PIN_MODE) :
  47. (5 == index) ? (DIGITAL6_PIN_MODE) :
  48. (6 == index) ? (DIGITAL7_PIN_MODE) :
  49. (7 == index) ? (DIGITAL8_PIN_MODE) : (INPUT_PULLUP));
  50. }
  51. constexpr State state_from_value(int value) {
  52. return (HIGH == value) ? State::High :
  53. (LOW == value) ? State::Low : State::Initial;
  54. }
  55. constexpr State state(unsigned char index) {
  56. return state_from_value(
  57. (0 == index) ? (DIGITAL1_DEFAULT_STATE) :
  58. (1 == index) ? (DIGITAL2_DEFAULT_STATE) :
  59. (2 == index) ? (DIGITAL3_DEFAULT_STATE) :
  60. (3 == index) ? (DIGITAL4_DEFAULT_STATE) :
  61. (4 == index) ? (DIGITAL5_DEFAULT_STATE) :
  62. (5 == index) ? (DIGITAL6_DEFAULT_STATE) :
  63. (6 == index) ? (DIGITAL7_DEFAULT_STATE) :
  64. (7 == index) ? (DIGITAL8_DEFAULT_STATE) : (HIGH));
  65. }
  66. } // namespace build
  67. namespace settings {
  68. namespace options {
  69. using espurna::settings::options::Enumeration;
  70. PROGMEM_STRING(Low, "low");
  71. PROGMEM_STRING(High, "high");
  72. PROGMEM_STRING(Initial, "initial");
  73. static constexpr std::array<Enumeration<digital::State>, 3> State PROGMEM {
  74. {{digital::State::Low, Low},
  75. {digital::State::High, High},
  76. {digital::State::Initial, Initial}}
  77. };
  78. PROGMEM_STRING(Input, "default");
  79. PROGMEM_STRING(PullUp, "pull-up");
  80. PROGMEM_STRING(PullDown, "pull-down");
  81. static constexpr std::array<Enumeration<digital::Mode>, 3> Mode PROGMEM {
  82. {{digital::Mode::Input, Input},
  83. {digital::Mode::PullUp, PullUp},
  84. {digital::Mode::PullDown, PullDown}}
  85. };
  86. } // namespace options
  87. } // namespace settings
  88. } // namespace
  89. } // namespace digital
  90. } // namespace driver
  91. } // namespace sensor
  92. namespace settings {
  93. namespace internal {
  94. using namespace espurna::sensor::driver;
  95. template<>
  96. digital::Mode convert(const String& value) {
  97. return convert(digital::settings::options::Mode, value, digital::Mode::PullUp);
  98. }
  99. String serialize(digital::Mode value) {
  100. return serialize(digital::settings::options::Mode, value);
  101. }
  102. template<>
  103. digital::State convert(const String& value) {
  104. return convert(digital::settings::options::State, value, digital::State::High);
  105. }
  106. String serialize(digital::State value) {
  107. return serialize(digital::settings::options::State, value);
  108. }
  109. } // namespace internal
  110. } // namespace settings
  111. namespace sensor {
  112. namespace driver {
  113. namespace digital {
  114. namespace {
  115. namespace settings {
  116. STRING_VIEW_INLINE(Prefix, "digital");
  117. namespace keys {
  118. STRING_VIEW_INLINE(Pin, "digitalPin");
  119. STRING_VIEW_INLINE(Mode, "digitalPinMode");
  120. STRING_VIEW_INLINE(State, "digitalDefState");
  121. } // namespace keys
  122. unsigned char pin(size_t index) {
  123. return getSetting(keys::Pin, build::pin(index));
  124. }
  125. Mode mode(size_t index) {
  126. return getSetting(keys::Mode, build::mode(index));
  127. }
  128. State state(size_t index) {
  129. return getSetting(keys::State, build::state(index));
  130. }
  131. namespace query {
  132. #define ID_VALUE(NAME, FUNC)\
  133. String NAME (size_t id) {\
  134. return espurna::settings::internal::serialize(FUNC(id));\
  135. }
  136. ID_VALUE(pin, settings::pin)
  137. ID_VALUE(mode, settings::mode)
  138. ID_VALUE(state, settings::state)
  139. #undef ID_VALUE
  140. static constexpr espurna::settings::query::IndexedSetting IndexedSettings[] PROGMEM {
  141. {keys::Pin, pin},
  142. {keys::Mode, mode},
  143. {keys::State, state},
  144. };
  145. bool checkSamePrefix(StringView key) {
  146. return key.startsWith(Prefix);
  147. }
  148. espurna::settings::query::Result findFrom(StringView key) {
  149. return espurna::settings::query::findFrom(
  150. build::SensorsMax, IndexedSettings, key);
  151. }
  152. void setup() {
  153. settingsRegisterQueryHandler({
  154. .check = checkSamePrefix,
  155. .get = findFrom,
  156. });
  157. }
  158. } // namespace query
  159. } // namespace settings
  160. struct Config {
  161. uint8_t pin;
  162. int pin_mode;
  163. State default_state;
  164. };
  165. class Sensor : public BaseSensor {
  166. public:
  167. Sensor() = delete;
  168. explicit Sensor(Config config) :
  169. _pin(config.pin),
  170. _pin_mode(config.pin_mode),
  171. _default_state(config.default_state)
  172. {}
  173. // ---------------------------------------------------------------------
  174. // Sensor API
  175. // ---------------------------------------------------------------------
  176. unsigned char id() const override {
  177. return SENSOR_DIGITAL_ID;
  178. }
  179. unsigned char count() const override {
  180. return 1;
  181. }
  182. // Initialization method, must be idempotent
  183. void begin() override {
  184. if (!_ready) {
  185. pinMode(_pin, _pin_mode);
  186. switch (_default_state) {
  187. case State::Initial:
  188. _default = digitalRead(_pin);
  189. break;
  190. case State::High:
  191. _default = HIGH;
  192. break;
  193. case State::Low:
  194. _default = LOW;
  195. break;
  196. }
  197. }
  198. _ready = true;
  199. }
  200. // Descriptive name of the sensor
  201. String description() const override {
  202. char buffer[32];
  203. snprintf_P(buffer, sizeof(buffer),
  204. PSTR("Digital @ GPIO%hhu"), _pin);
  205. return String(buffer);
  206. }
  207. // Address of the sensor (it could be the GPIO or I2C address)
  208. String address(unsigned char) const override {
  209. return String(_pin, 10);
  210. }
  211. // Type for slot # index
  212. unsigned char type(unsigned char index) const override {
  213. if (index == 0) {
  214. return MAGNITUDE_DIGITAL;
  215. }
  216. return MAGNITUDE_NONE;
  217. }
  218. void pre() override {
  219. _current = digitalRead(_pin);
  220. }
  221. // Current value for slot # index
  222. double value(unsigned char index) override {
  223. if (index != 0) {
  224. return 0;
  225. }
  226. return (_current != _default) ? 1.0 : 0.0;
  227. }
  228. private:
  229. unsigned char _pin;
  230. uint8_t _pin_mode;
  231. State _default_state;
  232. int _current { -1 };
  233. int _default { LOW };
  234. };
  235. class Init : public sensor::PreInit {
  236. public:
  237. Init() = default;
  238. String description() const override {
  239. return STRING_VIEW("DigitalSensor").toString();
  240. }
  241. Result find_sensors() override {
  242. return _find_sensors();
  243. }
  244. private:
  245. Result _with_error(int error) {
  246. return Result{
  247. .sensors = make_span(_sensors),
  248. .error = error,
  249. };
  250. }
  251. Result _find_sensors() {
  252. std::array<uint8_t, build::SensorsMax> pins;
  253. pins.fill(GPIO_NONE);
  254. int err = SENSOR_ERROR_OK;
  255. size_t index = 0;
  256. for (; index < build::SensorsMax; ++index) {
  257. const auto pin = settings::pin(index);
  258. if (pin == GPIO_NONE) {
  259. break;
  260. }
  261. if (!gpioLock(pin)) {
  262. err = SENSOR_ERROR_GPIO_USED;
  263. break;
  264. }
  265. pins[index] = pin;
  266. }
  267. size_t until = index;
  268. if (!until) {
  269. err = SENSOR_ERROR_CONFIG;
  270. }
  271. for (size_t index = 0; index < until; ++index) {
  272. if (err != SENSOR_ERROR_OK) {
  273. gpioUnlock(pins[index]);
  274. } else {
  275. _sensors.push_back(
  276. new Sensor(Config{
  277. .pin = pins[index],
  278. .pin_mode = settings::mode(index),
  279. .default_state = settings::state(index),
  280. }));
  281. }
  282. }
  283. return _with_error(err);
  284. }
  285. std::vector<BaseSensor*> _sensors;
  286. };
  287. inline void load() {
  288. settings::query::setup();
  289. sensor::add_preinit(std::make_unique<Init>());
  290. }
  291. } // namespace
  292. } // namespace digital
  293. } // namespace driver
  294. } // namespace sensor
  295. } // namespace espurna
  296. #endif // SENSOR_SUPPORT && DIGITAL_SUPPORT