xsns_30_mpr121.ino 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /**
  2. *
  3. * @file xsns_30_mpr121.ino
  4. *
  5. * @package Sonoff-Tasmota
  6. * @subpackage Sensors
  7. * @name MPR121
  8. *
  9. * @description Driver for up to 4x Freescale MPR121 Proximity Capacitive Touch Sensor Controllers (Only touch buttons).
  10. *
  11. * @author Rene 'Renne' Bartsch, B.Sc. Informatics, <rene@bartschnet.de>
  12. * @copyright Rene 'Renne' Bartsch 2018
  13. * @date $Date$
  14. * @version $Id$
  15. *
  16. * @link https://github.com/arendst/Sonoff-Tasmota/wiki/MPR121 \endlink
  17. * @link https://www.sparkfun.com/datasheets/Components/MPR121.pdf \endlink
  18. * @link http://cache.freescale.com/files/sensors/doc/app_note/AN3891.pdf \endlink
  19. * @link http://cache.freescale.com/files/sensors/doc/app_note/AN3892.pdf \endlink
  20. * @link http://cache.freescale.com/files/sensors/doc/app_note/AN3893.pdf \endlink
  21. * @link http://cache.freescale.com/files/sensors/doc/app_note/AN3894.pdf \endlink
  22. * @link http://cache.freescale.com/files/sensors/doc/app_note/AN3895.pdf \endlink
  23. *
  24. * @license GNU GPL v.3
  25. */
  26. /*
  27. * This program is free software: you can redistribute it and/or modify
  28. * it under the terms of the GNU General Public License as published by
  29. * the Free Software Foundation, either version 3 of the License, or
  30. * (at your option) any later version.
  31. *
  32. * This program is distributed in the hope that it will be useful,
  33. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. * GNU General Public License for more details.
  36. *
  37. * You should have received a copy of the GNU General Public License
  38. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  39. */
  40. #ifdef USE_I2C
  41. #ifdef USE_MPR121
  42. /**
  43. * @ingroup group1
  44. * Assign Tasmota sensor model ID
  45. */
  46. #define XSNS_30 30
  47. /** @defgroup group1 MPR121
  48. * MPR121 preprocessor directives
  49. * @{
  50. */
  51. /** Electrodes Status Register. */
  52. #define MPR121_ELEX_REG 0x00
  53. /** ELEC0-11 Maximum Half Delta Rising Register. */
  54. #define MPR121_MHDR_REG 0x2B
  55. /** ELEC0-11 Maximum Half Delta Rising Value. */
  56. #define MPR121_MHDR_VAL 0x01
  57. /** ELEC0-11 Noise Half Delta Falling Register. */
  58. #define MPR121_NHDR_REG 0x2C
  59. /** ELEC0-11 Noise Half Delta Falling Value. */
  60. #define MPR121_NHDR_VAL 0x01
  61. /** ELEC0-11 Noise Count Limit Rising Register. */
  62. #define MPR121_NCLR_REG 0x2D
  63. /** ELEC0-11 Noise Count Limit Rising Value. */
  64. #define MPR121_NCLR_VAL 0x0E
  65. /** ELEC0-11 Maximum Half Delta Falling Register. */
  66. #define MPR121_MHDF_REG 0x2F
  67. /** ELEC0-11 Maximum Half Delta Falling Value. */
  68. #define MPR121_MHDF_VAL 0x01
  69. /** ELEC0-11 Noise Half Delta Falling Register. */
  70. #define MPR121_NHDF_REG 0x30
  71. /** ELEC0-11 Noise Half Delta Falling Value. */
  72. #define MPR121_NHDF_VAL 0x05
  73. /** ELEC0-11 Noise Count Limit Falling Register. */
  74. #define MPR121_NCLF_REG 0x31
  75. /** ELEC0-11 Noise Count Limit Falling Value. */
  76. #define MPR121_NCLF_VAL 0x01
  77. /** Proximity Maximum Half Delta Rising Register. */
  78. #define MPR121_MHDPROXR_REG 0x36
  79. /** Proximity Maximum Half Delta Rising Value. */
  80. #define MPR121_MHDPROXR_VAL 0x3F
  81. /** Proximity Noise Half Delta Rising Register. */
  82. #define MPR121_NHDPROXR_REG 0x37
  83. /** Proximity Noise Half Delta Rising Value. */
  84. #define MPR121_NHDPROXR_VAL 0x5F
  85. /** Proximity Noise Count Limit Rising Register. */
  86. #define MPR121_NCLPROXR_REG 0x38
  87. /** Proximity Noise Count Limit Rising Value. */
  88. #define MPR121_NCLPROXR_VAL 0x04
  89. /** Proximity Filter Delay Count Limit Rising Register. */
  90. #define MPR121_FDLPROXR_REG 0x39
  91. /** Proximity Filter Delay Count Limit Rising Value. */
  92. #define MPR121_FDLPROXR_VAL 0x00
  93. /** Proximity Maximum Half Delta Falling Register. */
  94. #define MPR121_MHDPROXF_REG 0x3A
  95. /** Proximity Maximum Half Delta Falling Value. */
  96. #define MPR121_MHDPROXF_VAL 0x01
  97. /** Proximity Noise Half Delta Falling Register. */
  98. #define MPR121_NHDPROXF_REG 0x3B
  99. /** Proximity Noise Half Delta Falling Value. */
  100. #define MPR121_NHDPROXF_VAL 0x01
  101. /** Proximity Noise Count Limit Falling Register. */
  102. #define MPR121_NCLPROXF_REG 0x3C
  103. /** Proximity Noise Count Limit Falling Value. */
  104. #define MPR121_NCLPROXF_VAL 0x1F
  105. /** Proximity Filter Delay Count Limit Falling Register. */
  106. #define MPR121_FDLPROXF_REG 0x3D
  107. /** Proximity Filter Delay Count Limit Falling Value. */
  108. #define MPR121_FDLPROXF_VAL 0x04
  109. /** First Electrode Touch Threshold Register. */
  110. #define MPR121_E0TTH_REG 0x41
  111. /** First Electrode Touch Threshold Value. */
  112. #define MPR121_E0TTH_VAL 12
  113. /** First Electrode Release Threshold Register. */
  114. #define MPR121_E0RTH_REG 0x42
  115. /** First Electrode Release Threshold Value. */
  116. #define MPR121_E0RTH_VAL 6
  117. /** Global CDC/CDT Configuration Register. */
  118. #define MPR121_CDT_REG 0x5D
  119. /** Global CDC/CDT Configuration Value. */
  120. #define MPR121_CDT_VAL 0x20
  121. /** Enable electrodes Register. */
  122. #define MPR121_ECR_REG 0x5E
  123. /** Enable electrodes Value. */
  124. #define MPR121_ECR_VAL 0x8F // Start ELE0-11 with first 5 bits of baseline tracking
  125. //#define MPR121_ECR_VAL 0xBF // Start ELE0-11 + proximity with first 5 bits of baseline tracking
  126. /** Soft-reset Register. */
  127. #define MPR121_SRST_REG 0x80
  128. /** Soft-reset Value. */
  129. #define MPR121_SRST_VAL 0x63
  130. /** Get bit of variable 'current' of sensor at position. */
  131. #define BITC(sensor, position) ((pS->current[sensor] >> position) & 1)
  132. /** Get bit of variable 'previous' of sensor at position. */
  133. #define BITP(sensor, position) ((pS->previous[sensor] >> position) & 1)
  134. /**@}*/
  135. /**
  136. * MPR121 sensors status and data struct.
  137. *
  138. * The struct mpr121 uses the indices of i2c_addr and id to link the specific sensors to an I2C address and a human-readable ID
  139. * and the indices of the arrays connected, running, current and previous to store sensor status and data of a specific sensor.
  140. *
  141. */
  142. typedef struct mpr121 mpr121;
  143. struct mpr121 {
  144. const uint8_t i2c_addr[4] = { 0x5A, 0x5B, 0x5C, 0x5D }; /** I2C addresses of MPR121 controller */
  145. const char id[4] = { 'A', 'B', 'C', 'D' }; /** Human-readable sensor IDs*/
  146. bool connected[4] = { false, false, false, false }; /** Status if sensor is connected at I2C address */
  147. bool running[4] = { false, false, false, false }; /** Running state of sensor */
  148. uint16_t current[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; /** Current values in electrode register of sensor */
  149. uint16_t previous[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; /** Current values in electrode register of sensor */
  150. };
  151. /**
  152. * The function Mpr121Init() soft-resets, detects and configures up to 4x MPR121 sensors.
  153. *
  154. * @param struct *pS Struct with MPR121 status and data.
  155. * @return void
  156. * @pre None.
  157. * @post None.
  158. *
  159. */
  160. void Mpr121Init(struct mpr121 *pS)
  161. {
  162. // Loop through I2C addresses
  163. for (uint8_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) {
  164. // Soft reset sensor and check if connected at I2C address
  165. pS->connected[i] = (I2cWrite8(pS->i2c_addr[i], MPR121_SRST_REG, MPR121_SRST_VAL)
  166. && (0x24 == I2cRead8(pS->i2c_addr[i], 0x5D)));
  167. if (pS->connected[i]) {
  168. // Log sensor found
  169. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121(%c) " D_FOUND_AT " 0x%X"), pS->id[i], pS->i2c_addr[i]);
  170. AddLog(LOG_LEVEL_INFO);
  171. // Set thresholds for registers 0x41 - 0x5A (ExTTH and ExRTH)
  172. for (uint8_t j = 0; j < 13; j++) {
  173. // Touch
  174. I2cWrite8(pS->i2c_addr[i], MPR121_E0TTH_REG + 2 * j, MPR121_E0TTH_VAL);
  175. // Release
  176. I2cWrite8(pS->i2c_addr[i], MPR121_E0RTH_REG + 2 * j, MPR121_E0RTH_VAL);
  177. }
  178. // ELEC0-11 Maximum Half Delta Rising
  179. I2cWrite8(pS->i2c_addr[i], MPR121_MHDR_REG, MPR121_MHDR_VAL);
  180. // ELEC0-11 Noise Half Delta Rising
  181. I2cWrite8(pS->i2c_addr[i], MPR121_NHDR_REG, MPR121_NHDR_VAL);
  182. // ELEC0-11 Noise Count Limit Rising
  183. I2cWrite8(pS->i2c_addr[i], MPR121_NCLR_REG, MPR121_NCLR_VAL);
  184. // ELEC0-11 Maximum Half Delta Falling
  185. I2cWrite8(pS->i2c_addr[i], MPR121_MHDF_REG, MPR121_MHDF_VAL);
  186. // ELEC0-11 Noise Half Delta Falling
  187. I2cWrite8(pS->i2c_addr[i], MPR121_NHDF_REG, MPR121_NHDF_VAL);
  188. // ELEC0-11 Noise Count Limit Falling
  189. I2cWrite8(pS->i2c_addr[i], MPR121_NCLF_REG, MPR121_NCLF_VAL);
  190. // Proximity Maximum Half Delta Rising
  191. I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXR_REG, MPR121_MHDPROXR_VAL);
  192. // Proximity Noise Half Delta Rising
  193. I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXR_REG, MPR121_NHDPROXR_VAL);
  194. // Proximity Noise Count Limit Rising
  195. I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXR_REG, MPR121_NCLPROXR_VAL);
  196. // Proximity Filter Delay Count Limit Rising
  197. I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXR_REG, MPR121_FDLPROXR_VAL);
  198. // Proximity Maximum Half Delta Falling
  199. I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXF_REG, MPR121_MHDPROXF_VAL);
  200. // Proximity Noise Half Delta Falling
  201. I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXF_REG, MPR121_NHDPROXF_VAL);
  202. // Proximity Noise Count Limit Falling
  203. I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXF_REG, MPR121_NCLPROXF_VAL);
  204. // Proximity Filter Delay Count Limit Falling
  205. I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXF_REG, MPR121_FDLPROXF_VAL);
  206. // Global CDC/CDT Configuration
  207. I2cWrite8(pS->i2c_addr[i], MPR121_CDT_REG, MPR121_CDT_VAL);
  208. // Enable sensor
  209. I2cWrite8(pS->i2c_addr[i], MPR121_ECR_REG, MPR121_ECR_VAL);
  210. // Check if sensor is running
  211. pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG));
  212. if (pS->running[i]) {
  213. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: Running"), pS->id[i]);
  214. } else {
  215. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: NOT Running"), pS->id[i]);
  216. }
  217. AddLog(LOG_LEVEL_INFO);
  218. } else {
  219. // Make sure running is false
  220. pS->running[i] = false;
  221. }
  222. } // for-loop
  223. // Display no sensor found message
  224. if (!(pS->connected[0] || pS->connected[1] || pS->connected[2]
  225. || pS->connected[3])) {
  226. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121: No sensors found"));
  227. AddLog(LOG_LEVEL_DEBUG);
  228. }
  229. } // void Mpr121Init(struct mpr121 *s)
  230. /**
  231. * Publishes the sensor information.
  232. *
  233. * The function Mpr121Show() reads sensor data, checks for over-current exceptions and
  234. * creates strings with button states for the web-interface and near real-time/ telemetriy MQTT.
  235. *
  236. * @param struct *pS Struct with MPR121 status and data.
  237. * @param byte function Tasmota function ID.
  238. * @return void
  239. * @pre Call Mpr121Init() once.
  240. * @post None.
  241. *
  242. */
  243. void Mpr121Show(struct mpr121 *pS, byte function)
  244. {
  245. // Loop through sensors
  246. for (uint8_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) {
  247. // Check if sensor is connected
  248. if (pS->connected[i]) {
  249. // Read data
  250. if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) {
  251. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]);
  252. AddLog(LOG_LEVEL_ERROR);
  253. Mpr121Init(pS);
  254. return;
  255. }
  256. // Check if OVCF bit is set
  257. if (BITC(i, 15)) {
  258. // Clear OVCF bit
  259. I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00);
  260. snprintf_P(log_data, sizeof(log_data),
  261. PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]);
  262. AddLog(LOG_LEVEL_ERROR);
  263. Mpr121Init(pS);
  264. return;
  265. }
  266. }
  267. // Check if sensor is running
  268. if (pS->running[i]) {
  269. // Append sensor to JSON message string
  270. if (FUNC_JSON_APPEND == function) {
  271. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MPR121%c\":{"), mqtt_data, pS->id[i]);
  272. }
  273. // Loop through buttons
  274. for (uint8_t j = 0; j < 13; j++) {
  275. // Add sensor, button and state to MQTT JSON message string
  276. if ((FUNC_EVERY_50_MSECOND == function)
  277. && (BITC(i, j) != BITP(i, j))) {
  278. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i, j));
  279. MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
  280. }
  281. // Add buttons to web string
  282. #ifdef USE_WEBSERVER
  283. if (FUNC_WEB_APPEND == function) {
  284. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{s}MPR121%c Button%d{m}%d{e}"), mqtt_data, pS->id[i], j, BITC(i, j));
  285. }
  286. #endif // USE_WEBSERVER
  287. // Append JSON message string
  288. if (FUNC_JSON_APPEND == function) {
  289. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"Button%i\":%i"), mqtt_data, (j > 0 ? "," : ""), j, BITC(i, j));
  290. }
  291. } // for-loop j
  292. // Save sensor data
  293. pS->previous[i] = pS->current[i];
  294. // Append JSON message string
  295. if (FUNC_JSON_APPEND == function) {
  296. snprintf_P(mqtt_data, sizeof(mqtt_data), "%s}", mqtt_data);
  297. }
  298. } // if->running
  299. } // for-loop i
  300. } // void Mpr121Show(byte function)
  301. /*********************************************************************************************\
  302. * Interface
  303. \*********************************************************************************************/
  304. /**
  305. * The function Xsns30() interfaces Tasmota with the driver.
  306. *
  307. * It provides the function IDs
  308. * FUNC_INIT to initialize a driver,
  309. * FUNC_EVERY_50_MSECOND for near real-time operation,
  310. * FUNC_JSON_APPEND for telemetry data and
  311. * FUNC_WEB_APPEND for displaying data in the Tasmota web-interface
  312. *
  313. * @param byte function Tasmota function ID.
  314. * @return boolean ???
  315. * @pre None.
  316. * @post None.
  317. *
  318. */
  319. boolean Xsns30(byte function)
  320. {
  321. // ???
  322. boolean result = false;
  323. // Sensor state/data struct
  324. static struct mpr121 mpr121;
  325. // Check if I2C is enabled
  326. if (i2c_flg) {
  327. switch (function) {
  328. // Initialize Sensors
  329. case FUNC_INIT:
  330. Mpr121Init(&mpr121);
  331. break;
  332. // Run ever 50 milliseconds (near real-time functions)
  333. case FUNC_EVERY_50_MSECOND:
  334. Mpr121Show(&mpr121, FUNC_EVERY_50_MSECOND);
  335. break;
  336. // Generate JSON telemetry string
  337. case FUNC_JSON_APPEND:
  338. Mpr121Show(&mpr121, FUNC_JSON_APPEND);
  339. break;
  340. #ifdef USE_WEBSERVER
  341. // Show sensor data on main web page
  342. case FUNC_WEB_APPEND:
  343. Mpr121Show(&mpr121, FUNC_WEB_APPEND);
  344. break;
  345. #endif // USE_WEBSERVER
  346. }
  347. }
  348. // Return boolean result
  349. return result;
  350. }
  351. #endif // USE_MPR121
  352. #endif // USE_I2C