2019-12-31 13:23:34 +00:00
/*
xsns_30_mpr121 . ino - MPR121 support for Tasmota
Copyright ( C ) 2020 Rene ' Renne ' Bartsch and Theo Arends
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2018-07-06 14:41:16 +01:00
/**
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* @ file xsns_30_mpr121 . ino
2018-10-20 16:28:42 +01:00
*
2019-10-27 10:13:24 +00:00
* @ package Tasmota
2018-07-06 14:41:16 +01:00
* @ subpackage Sensors
* @ name MPR121
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* @ description Driver for up to 4 x Freescale MPR121 Proximity Capacitive Touch Sensor Controllers ( Only touch buttons ) .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* @ author Rene ' Renne ' Bartsch , B . Sc . Informatics , < rene @ bartschnet . de >
* @ copyright Rene ' Renne ' Bartsch 2018
* @ date $ Date $
* @ version $ Id $
2018-10-20 16:28:42 +01:00
*
2019-10-27 10:13:24 +00:00
* @ link https : //github.com/arendst/Tasmota/wiki/MPR121 \endlink
2018-10-20 16:28:42 +01:00
* @ link https : //www.sparkfun.com/datasheets/Components/MPR121.pdf \endlink
* @ link http : //cache.freescale.com/files/sensors/doc/app_note/AN3891.pdf \endlink
* @ link http : //cache.freescale.com/files/sensors/doc/app_note/AN3892.pdf \endlink
* @ link http : //cache.freescale.com/files/sensors/doc/app_note/AN3893.pdf \endlink
* @ link http : //cache.freescale.com/files/sensors/doc/app_note/AN3894.pdf \endlink
* @ link http : //cache.freescale.com/files/sensors/doc/app_note/AN3895.pdf \endlink
*
2018-07-06 14:41:16 +01:00
* @ license GNU GPL v .3
*/
2018-10-20 16:28:42 +01:00
2018-07-06 14:41:16 +01:00
# ifdef USE_I2C
# ifdef USE_MPR121
2018-11-06 16:33:51 +00:00
/**
* @ ingroup group1
* Assign Tasmota sensor model ID
*/
# define XSNS_30 30
2019-11-03 16:54:39 +00:00
# define XI2C_23 23 // See I2CDEVICES.md
2018-11-06 16:33:51 +00:00
2018-07-06 14:41:16 +01:00
/** @defgroup group1 MPR121
* MPR121 preprocessor directives
* @ {
*/
/** Electrodes Status Register. */
# define MPR121_ELEX_REG 0x00
/** ELEC0-11 Maximum Half Delta Rising Register. */
# define MPR121_MHDR_REG 0x2B
/** ELEC0-11 Maximum Half Delta Rising Value. */
# define MPR121_MHDR_VAL 0x01
/** ELEC0-11 Noise Half Delta Falling Register. */
# define MPR121_NHDR_REG 0x2C
/** ELEC0-11 Noise Half Delta Falling Value. */
# define MPR121_NHDR_VAL 0x01
/** ELEC0-11 Noise Count Limit Rising Register. */
# define MPR121_NCLR_REG 0x2D
/** ELEC0-11 Noise Count Limit Rising Value. */
# define MPR121_NCLR_VAL 0x0E
/** ELEC0-11 Maximum Half Delta Falling Register. */
# define MPR121_MHDF_REG 0x2F
/** ELEC0-11 Maximum Half Delta Falling Value. */
# define MPR121_MHDF_VAL 0x01
/** ELEC0-11 Noise Half Delta Falling Register. */
# define MPR121_NHDF_REG 0x30
/** ELEC0-11 Noise Half Delta Falling Value. */
# define MPR121_NHDF_VAL 0x05
/** ELEC0-11 Noise Count Limit Falling Register. */
# define MPR121_NCLF_REG 0x31
/** ELEC0-11 Noise Count Limit Falling Value. */
# define MPR121_NCLF_VAL 0x01
/** Proximity Maximum Half Delta Rising Register. */
# define MPR121_MHDPROXR_REG 0x36
/** Proximity Maximum Half Delta Rising Value. */
# define MPR121_MHDPROXR_VAL 0x3F
/** Proximity Noise Half Delta Rising Register. */
# define MPR121_NHDPROXR_REG 0x37
/** Proximity Noise Half Delta Rising Value. */
# define MPR121_NHDPROXR_VAL 0x5F
/** Proximity Noise Count Limit Rising Register. */
# define MPR121_NCLPROXR_REG 0x38
/** Proximity Noise Count Limit Rising Value. */
# define MPR121_NCLPROXR_VAL 0x04
/** Proximity Filter Delay Count Limit Rising Register. */
# define MPR121_FDLPROXR_REG 0x39
/** Proximity Filter Delay Count Limit Rising Value. */
# define MPR121_FDLPROXR_VAL 0x00
/** Proximity Maximum Half Delta Falling Register. */
# define MPR121_MHDPROXF_REG 0x3A
/** Proximity Maximum Half Delta Falling Value. */
# define MPR121_MHDPROXF_VAL 0x01
/** Proximity Noise Half Delta Falling Register. */
# define MPR121_NHDPROXF_REG 0x3B
/** Proximity Noise Half Delta Falling Value. */
# define MPR121_NHDPROXF_VAL 0x01
/** Proximity Noise Count Limit Falling Register. */
# define MPR121_NCLPROXF_REG 0x3C
/** Proximity Noise Count Limit Falling Value. */
# define MPR121_NCLPROXF_VAL 0x1F
/** Proximity Filter Delay Count Limit Falling Register. */
# define MPR121_FDLPROXF_REG 0x3D
/** Proximity Filter Delay Count Limit Falling Value. */
# define MPR121_FDLPROXF_VAL 0x04
/** First Electrode Touch Threshold Register. */
# define MPR121_E0TTH_REG 0x41
/** First Electrode Touch Threshold Value. */
# define MPR121_E0TTH_VAL 12
/** First Electrode Release Threshold Register. */
# define MPR121_E0RTH_REG 0x42
/** First Electrode Release Threshold Value. */
# define MPR121_E0RTH_VAL 6
/** Global CDC/CDT Configuration Register. */
# define MPR121_CDT_REG 0x5D
/** Global CDC/CDT Configuration Value. */
# define MPR121_CDT_VAL 0x20
/** Enable electrodes Register. */
# define MPR121_ECR_REG 0x5E
/** Enable electrodes Value. */
# define MPR121_ECR_VAL 0x8F // Start ELE0-11 with first 5 bits of baseline tracking
//#define MPR121_ECR_VAL 0xBF // Start ELE0-11 + proximity with first 5 bits of baseline tracking
/** Soft-reset Register. */
# define MPR121_SRST_REG 0x80
/** Soft-reset Value. */
# define MPR121_SRST_VAL 0x63
/** Get bit of variable 'current' of sensor at position. */
# define BITC(sensor, position) ((pS->current[sensor] >> position) & 1)
/** Get bit of variable 'previous' of sensor at position. */
# define BITP(sensor, position) ((pS->previous[sensor] >> position) & 1)
/**@}*/
/**
* MPR121 sensors status and data struct .
*
2018-10-20 16:28:42 +01:00
* 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
2018-07-06 14:41:16 +01:00
* and the indices of the arrays connected , running , current and previous to store sensor status and data of a specific sensor .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
*/
2018-07-19 20:18:20 +01:00
typedef struct mpr121 mpr121 ;
struct mpr121 {
2018-07-06 14:41:16 +01:00
const uint8_t i2c_addr [ 4 ] = { 0x5A , 0x5B , 0x5C , 0x5D } ; /** I2C addresses of MPR121 controller */
const char id [ 4 ] = { ' A ' , ' B ' , ' C ' , ' D ' } ; /** Human-readable sensor IDs*/
bool connected [ 4 ] = { false , false , false , false } ; /** Status if sensor is connected at I2C address */
bool running [ 4 ] = { false , false , false , false } ; /** Running state of sensor */
uint16_t current [ 4 ] = { 0x0000 , 0x0000 , 0x0000 , 0x0000 } ; /** Current values in electrode register of sensor */
uint16_t previous [ 4 ] = { 0x0000 , 0x0000 , 0x0000 , 0x0000 } ; /** Current values in electrode register of sensor */
} ;
2019-11-11 16:32:44 +00:00
bool mpr21_found = false ;
2018-07-06 14:41:16 +01:00
/**
* The function Mpr121Init ( ) soft - resets , detects and configures up to 4 x MPR121 sensors .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* @ param struct * pS Struct with MPR121 status and data .
2019-11-11 16:32:44 +00:00
* bool initial true - Initial call , false - next calls
2018-07-06 14:41:16 +01:00
* @ return void
* @ pre None .
* @ post None .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
*/
2019-11-11 16:32:44 +00:00
void Mpr121Init ( struct mpr121 * pS , bool initial )
2018-07-06 14:41:16 +01:00
{
// Loop through I2C addresses
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < sizeof ( pS - > i2c_addr [ i ] ) ; i + + ) {
2018-07-06 14:41:16 +01:00
2019-11-11 16:32:44 +00:00
if ( initial & & I2cActive ( pS - > i2c_addr [ i ] ) ) { continue ; }
2018-07-06 14:41:16 +01:00
// Soft reset sensor and check if connected at I2C address
pS - > connected [ i ] = ( I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_SRST_REG , MPR121_SRST_VAL )
& & ( 0x24 = = I2cRead8 ( pS - > i2c_addr [ i ] , 0x5D ) ) ) ;
if ( pS - > connected [ i ] ) {
// Log sensor found
2019-11-11 16:32:44 +00:00
mpr21_found = true ;
char device_name [ 16 ] ;
snprintf_P ( device_name , sizeof ( device_name ) , PSTR ( " MPR121(%c) " ) , pS - > id [ i ] ) ;
I2cSetActiveFound ( pS - > i2c_addr [ i ] , device_name ) ;
2018-07-06 14:41:16 +01:00
// Set thresholds for registers 0x41 - 0x5A (ExTTH and ExRTH)
2019-06-30 15:44:36 +01:00
for ( uint32_t j = 0 ; j < 13 ; j + + ) {
2018-07-06 14:41:16 +01:00
// Touch
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_E0TTH_REG + 2 * j , MPR121_E0TTH_VAL ) ;
// Release
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_E0RTH_REG + 2 * j , MPR121_E0RTH_VAL ) ;
}
// ELEC0-11 Maximum Half Delta Rising
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_MHDR_REG , MPR121_MHDR_VAL ) ;
// ELEC0-11 Noise Half Delta Rising
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NHDR_REG , MPR121_NHDR_VAL ) ;
// ELEC0-11 Noise Count Limit Rising
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NCLR_REG , MPR121_NCLR_VAL ) ;
// ELEC0-11 Maximum Half Delta Falling
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_MHDF_REG , MPR121_MHDF_VAL ) ;
// ELEC0-11 Noise Half Delta Falling
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NHDF_REG , MPR121_NHDF_VAL ) ;
// ELEC0-11 Noise Count Limit Falling
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NCLF_REG , MPR121_NCLF_VAL ) ;
// Proximity Maximum Half Delta Rising
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_MHDPROXR_REG , MPR121_MHDPROXR_VAL ) ;
// Proximity Noise Half Delta Rising
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NHDPROXR_REG , MPR121_NHDPROXR_VAL ) ;
// Proximity Noise Count Limit Rising
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NCLPROXR_REG , MPR121_NCLPROXR_VAL ) ;
// Proximity Filter Delay Count Limit Rising
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_FDLPROXR_REG , MPR121_FDLPROXR_VAL ) ;
// Proximity Maximum Half Delta Falling
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_MHDPROXF_REG , MPR121_MHDPROXF_VAL ) ;
// Proximity Noise Half Delta Falling
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NHDPROXF_REG , MPR121_NHDPROXF_VAL ) ;
// Proximity Noise Count Limit Falling
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_NCLPROXF_REG , MPR121_NCLPROXF_VAL ) ;
// Proximity Filter Delay Count Limit Falling
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_FDLPROXF_REG , MPR121_FDLPROXF_VAL ) ;
// Global CDC/CDT Configuration
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_CDT_REG , MPR121_CDT_VAL ) ;
// Enable sensor
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_ECR_REG , MPR121_ECR_VAL ) ;
// Check if sensor is running
pS - > running [ i ] = ( 0x00 ! = I2cRead8 ( pS - > i2c_addr [ i ] , MPR121_ECR_REG ) ) ;
2019-03-08 14:15:42 +00:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_LOG_I2C " MPR121%c: %sRunning " ) , pS - > id [ i ] , ( pS - > running [ i ] ) ? " " : " NOT " ) ;
2018-07-06 14:41:16 +01:00
} else {
// Make sure running is false
pS - > running [ i ] = false ;
}
} // for-loop
// Display no sensor found message
if ( ! ( pS - > connected [ 0 ] | | pS - > connected [ 1 ] | | pS - > connected [ 2 ]
| | pS - > connected [ 3 ] ) ) {
2019-03-08 14:15:42 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_I2C " MPR121: No sensors found " ) ) ;
2018-07-06 14:41:16 +01:00
}
} // void Mpr121Init(struct mpr121 *s)
/**
* Publishes the sensor information .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* The function Mpr121Show ( ) reads sensor data , checks for over - current exceptions and
* creates strings with button states for the web - interface and near real - time / telemetriy MQTT .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* @ param struct * pS Struct with MPR121 status and data .
* @ param byte function Tasmota function ID .
* @ return void
* @ pre Call Mpr121Init ( ) once .
* @ post None .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
*/
2019-01-28 13:08:33 +00:00
void Mpr121Show ( struct mpr121 * pS , uint8_t function )
2018-07-06 14:41:16 +01:00
{
// Loop through sensors
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < sizeof ( pS - > i2c_addr [ i ] ) ; i + + ) {
2018-07-06 14:41:16 +01:00
// Check if sensor is connected
if ( pS - > connected [ i ] ) {
// Read data
if ( ! I2cValidRead16LE ( & pS - > current [ i ] , pS - > i2c_addr [ i ] , MPR121_ELEX_REG ) ) {
2019-03-08 14:15:42 +00:00
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( D_LOG_I2C " MPR121%c: ERROR: Cannot read data! " ) , pS - > id [ i ] ) ;
2019-11-11 16:32:44 +00:00
Mpr121Init ( pS , false ) ;
2018-07-06 14:41:16 +01:00
return ;
}
// Check if OVCF bit is set
if ( BITC ( i , 15 ) ) {
// Clear OVCF bit
I2cWrite8 ( pS - > i2c_addr [ i ] , MPR121_ELEX_REG , 0x00 ) ;
2019-03-08 14:15:42 +00:00
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( D_LOG_I2C " MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ... " ) , pS - > id [ i ] ) ;
2019-11-11 16:32:44 +00:00
Mpr121Init ( pS , false ) ;
2018-07-06 14:41:16 +01:00
return ;
}
}
// Check if sensor is running
if ( pS - > running [ i ] ) {
// Append sensor to JSON message string
if ( FUNC_JSON_APPEND = = function ) {
2019-03-23 16:57:31 +00:00
ResponseAppend_P ( PSTR ( " , \" MPR121%c \" :{ " ) , pS - > id [ i ] ) ;
2018-07-06 14:41:16 +01:00
}
// Loop through buttons
2019-06-30 15:44:36 +01:00
for ( uint32_t j = 0 ; j < 13 ; j + + ) {
2018-07-06 14:41:16 +01:00
// Add sensor, button and state to MQTT JSON message string
if ( ( FUNC_EVERY_50_MSECOND = = function )
& & ( BITC ( i , j ) ! = BITP ( i , j ) ) ) {
2019-03-23 16:57:31 +00:00
Response_P ( PSTR ( " { \" MPR121%c \" :{ \" Button%i \" :%i}} " ) , pS - > id [ i ] , j , BITC ( i , j ) ) ;
2018-07-06 14:41:16 +01:00
MqttPublishPrefixTopic_P ( RESULT_OR_STAT , mqtt_data ) ;
}
// Add buttons to web string
# ifdef USE_WEBSERVER
2019-03-19 16:31:43 +00:00
if ( FUNC_WEB_SENSOR = = function ) {
WSContentSend_PD ( PSTR ( " {s}MPR121%c Button%d{m}%d{e} " ) , pS - > id [ i ] , j , BITC ( i , j ) ) ;
2018-07-06 14:41:16 +01:00
}
# endif // USE_WEBSERVER
// Append JSON message string
if ( FUNC_JSON_APPEND = = function ) {
2019-03-23 16:57:31 +00:00
ResponseAppend_P ( PSTR ( " %s \" Button%i \" :%i " ) , ( j > 0 ? " , " : " " ) , j , BITC ( i , j ) ) ;
2018-07-06 14:41:16 +01:00
}
} // for-loop j
// Save sensor data
pS - > previous [ i ] = pS - > current [ i ] ;
// Append JSON message string
if ( FUNC_JSON_APPEND = = function ) {
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
ResponseJsonEnd ( ) ;
2018-07-06 14:41:16 +01:00
}
} // if->running
} // for-loop i
2019-01-28 13:08:33 +00:00
} // void Mpr121Show(uint8_t function)
2018-07-06 14:41:16 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* The function Xsns30 ( ) interfaces Tasmota with the driver .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* It provides the function IDs
* FUNC_INIT to initialize a driver ,
* FUNC_EVERY_50_MSECOND for near real - time operation ,
* FUNC_JSON_APPEND for telemetry data and
2019-03-19 16:31:43 +00:00
* FUNC_WEB_SENSOR for displaying data in the Tasmota web - interface
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
* @ param byte function Tasmota function ID .
2019-01-28 13:08:33 +00:00
* @ return bool ? ? ?
2018-07-06 14:41:16 +01:00
* @ pre None .
* @ post None .
2018-10-20 16:28:42 +01:00
*
2018-07-06 14:41:16 +01:00
*/
2019-01-28 13:08:33 +00:00
bool Xsns30 ( uint8_t function )
2018-07-06 14:41:16 +01:00
{
2019-11-04 09:38:05 +00:00
if ( ! I2cEnabled ( XI2C_23 ) ) { return false ; }
2019-11-03 16:54:39 +00:00
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-07-06 14:41:16 +01:00
// Sensor state/data struct
static struct mpr121 mpr121 ;
2019-11-11 16:32:44 +00:00
if ( FUNC_INIT = = function ) {
2019-11-04 09:38:05 +00:00
// Initialize Sensors
2019-11-11 16:32:44 +00:00
Mpr121Init ( & mpr121 , true ) ;
}
else if ( mpr21_found ) {
2018-07-06 14:41:16 +01:00
2019-11-11 16:32:44 +00:00
switch ( function ) {
2018-07-06 14:41:16 +01:00
2019-11-11 16:32:44 +00:00
// Run ever 50 milliseconds (near real-time functions)
case FUNC_EVERY_50_MSECOND :
Mpr121Show ( & mpr121 , FUNC_EVERY_50_MSECOND ) ;
break ;
// Generate JSON telemetry string
case FUNC_JSON_APPEND :
Mpr121Show ( & mpr121 , FUNC_JSON_APPEND ) ;
break ;
2018-07-06 14:41:16 +01:00
# ifdef USE_WEBSERVER
2019-11-11 16:32:44 +00:00
// Show sensor data on main web page
case FUNC_WEB_SENSOR :
Mpr121Show ( & mpr121 , FUNC_WEB_SENSOR ) ;
break ;
2018-07-06 14:41:16 +01:00
# endif // USE_WEBSERVER
2019-11-11 16:32:44 +00:00
}
2018-07-06 14:41:16 +01:00
}
2019-01-28 13:08:33 +00:00
// Return bool result
2018-07-06 14:41:16 +01:00
return result ;
}
# endif // USE_MPR121
# endif // USE_I2C