mirror of https://github.com/arendst/Tasmota.git
Updated Sensor API (markdown)
parent
d4ff2993c8
commit
c5786d1751
|
@ -1,7 +1,7 @@
|
|||
This wiki page is an attempt to document the Tasmota sensor API for sensor driver development.
|
||||
|
||||
# Important things to consider
|
||||
* There are several I2C sensor examples you can take from the development codebase when writing your own and you are encouraged to do this as it is a quick and easy way to see how things fit together.
|
||||
* There are several I<sup>2</sup>C sensor examples you can take from the development codebase when writing your own and you are encouraged to do this as it is a quick and easy way to see how things fit together.
|
||||
* The Tasmota firmware is essentially intended for Sonoff devices and commits to the main development branch will be subject to review based on whether or not what you intend to develop or add to the existing code is relevant to the general Sonoff device users.
|
||||
* That being said, there is a lot of development going into the firmware which extends the functionality of standard off the shelf Sonoff devices, the firmware in itself is also useful to other ESP8266 based boards such as the WeMos ESP8266 boards and more technically inclined individuals who use generic ESP8266 modules in their own circuits which provides more access to pins and the ability to add more sensors and hardware external to the Sonoff device or the generic ESP8266 module circuits.
|
||||
* The resources on the ESP8266 are finite. Most of the Sonoff devices ship with 1Mbyte SPI flash chips which means for the generic Sonoff device users the code generally needs to be less than 502Kbytes to ensure that OTA (Over The Air) flash functionality (which is the main reason why people use this firmware) remains available. RAM is also limited to an absolute maximum of 80Kbytes. This memory is divided into heap (used by global variables and Strings) and stack (used by local variables) where stack space is just 4Kbytes.
|
||||
|
@ -9,7 +9,7 @@ This wiki page is an attempt to document the Tasmota sensor API for sensor drive
|
|||
* You need to think about these resource constraints all the time whilst doing any development you wish to add to the firmware functionality - Face the fact that microcontroller development isn't as close a relative to standard computer programming as you'd expect.
|
||||
* You will be adding code to an existing framework which requires you to adhere to some simple but strict rules such as not having any infinite loops like you would have in your generic Arduino code and try to avoid using the delay() functions when writing your code as this will cause the entire firmware to be subjected to these delay()'s you have added - Infinite loops will cause the firmware to lock up completely!
|
||||
* If your sensor has configuration options please make these available by using the SensorXX framework which is already incorporated in the base code - This may not stop you from using a web-based configuration interface but since web-based configuration takes up a lot of code space in flash it is very important to make this optional by means of a compiler directive or a #define in the configuration file and as such something you need to keep in mind during your development and debugging - The more progressively optional additional features are in your driver the smaller the basic codebase can be for minimalist implementations.
|
||||
* Whilst developing drivers for devices that use the I2C bus always consider other devices already supported in the codebase which may use the same address range. This could mean you need to find a unique way of differentiating your device detection from other devices on the same address range (e.g. querying a model-specific register) and/or disabling by #undef existing devices if yours is selected with a #define statement and in such cases always provide a warning to the user during compile time using the #warning pragma such as including `#warning **** Turned off conflicting drivers SHT and VEML6070 ****` in your code.
|
||||
* Whilst developing drivers for devices that use the I<sup>2</sup>C bus always consider other devices already supported in the codebase which may use the same address range. This could mean you need to find a unique way of differentiating your device detection from other devices on the same address range (e.g. querying a model-specific register) and/or disabling by #undef existing devices if yours is selected with a #define statement and in such cases always provide a warning to the user during compile time using the #warning pragma such as including `#warning **** Turned off conflicting drivers SHT and VEML6070 ****` in your code.
|
||||
* DO NOT ADD WEB INTERFACE FOR SENSOR CONFIGURATION if your sensor requires additional user configuration. The reason for this is the additional program memory required but most importantly the amount of RAM required to even create minimal user interfaces. Running out of RAM during runtime will lead to abnormal behaviour of your driver and/or other drivers or the entire firmware! See sensors such as the MCP23008/MCP23017 driver on more information on how to implement sensorXX commands instead!
|
||||
* While developing you might want to enable additional debugging provided by file ``xdrv_95_debug.ino`` using define USE_DEBUG_DRIVER which provides some commands for managing configuration settings and CPU timing. In addition you can enable define PROFILE_XSNS_SENSOR_EVERY_SECOND to profile your drivers duration.
|
||||
* Do not assume others will know immediately how to use your addition and know that you will need to write a Wiki for it in the end.
|
||||
|
@ -49,7 +49,7 @@ boolean Xsns<driverID>(byte callback_id) {
|
|||
// Set return value to `false`
|
||||
boolean result = false;
|
||||
|
||||
// Check if I2C interface mode
|
||||
// Check if I<sup>2</sup>C interface mode
|
||||
// if(i2c_flg) {
|
||||
|
||||
// Check which callback ID is called by Tasmota
|
||||
|
@ -113,22 +113,22 @@ void MySensorDetect()
|
|||
}
|
||||
```
|
||||
|
||||
Setting a flag that the driver was successful in detecting the attached chip/board via I2C or SPI will prevent it from continuously trying to initialize an already initialized device.
|
||||
Setting a flag that the driver was successful in detecting the attached chip/board via I<sup>2</sup>C or SPI will prevent it from continuously trying to initialize an already initialized device.
|
||||
|
||||
When writing your function responsible for detecting an externally connected I2C device try to create a method by which you read or write to specific registers that would be applicable to that specific I2C device only as to confirm a positive detect for the device. If this is not done extensively it will lead to some drivers getting false detects for a different device type simply because it shares the same I2C address.
|
||||
When writing your function responsible for detecting an externally connected I<sup>2</sup>C device try to create a method by which you read or write to specific registers that would be applicable to that specific I<sup>2</sup>C device only as to confirm a positive detect for the device. If this is not done extensively it will lead to some drivers getting false detects for a different device type simply because it shares the same I<sup>2</sup>C address.
|
||||
|
||||
Unless your driver is specifically going to use the entire array of addresses provisioned by the manufacturer please consider using a #define USE_MYCHIPNAME_ADDR in the user_config.h so that the user may specify the address on which to expect the device. This is of course only applicable to drivers that are not enabled by default in any of the pre-built binaries.
|
||||
|
||||
**I2C address auto-detection example**
|
||||
**I<sup>2</sup>C address auto-detection example**
|
||||
```
|
||||
#define MPR121_I2C_ADDR_1ST 0x5A /** 1st I2C address of sensor model **/
|
||||
#define MPR121_I2C_ADDR_NUM 4 /** Number of sensors/I2C addresses **/
|
||||
#define MPR121_I2C_ID_REG 0x5D /** Sensor model specific ID register **/
|
||||
#define MPR121_I2C_ID_VAL 0x24 /** Sensor model specific ID register value **/
|
||||
#define MPR121_I<sup>2</sup>C_ADDR_1ST 0x5A /** 1st I<sup>2</sup>C address of sensor model **/
|
||||
#define MPR121_I<sup>2</sup>C_ADDR_NUM 4 /** Number of sensors/I<sup>2</sup>C addresses **/
|
||||
#define MPR121_I<sup>2</sup>C_ID_REG 0x5D /** Sensor model specific ID register **/
|
||||
#define MPR121_I<sup>2</sup>C_ID_VAL 0x24 /** Sensor model specific ID register value **/
|
||||
|
||||
/* Sensor data struct type declaration/default definition */
|
||||
typedef struct {
|
||||
bool connected = false; /** Status if sensor is connected at I2C address */
|
||||
bool connected = false; /** Status if sensor is connected at I<sup>2</sup>C address */
|
||||
bool running = false; /** Running state of sensor */
|
||||
.
|
||||
.
|
||||
|
@ -136,20 +136,20 @@ typedef struct {
|
|||
} mpr121;
|
||||
|
||||
// Declare array of sensor data structs
|
||||
mpr121 mpr121[MPR121_I2C_ADDR_NUM];
|
||||
mpr121 mpr121[MPR121_I<sup>2</sup>C_ADDR_NUM];
|
||||
|
||||
// Sensor specific init function
|
||||
void mpr121_init() {
|
||||
|
||||
// Loop through I2C addresses
|
||||
for (uint8_t i = 0; i < MPR121_I2C_ADDR_NUM); i++) {
|
||||
// Loop through I<sup>2</sup>C addresses
|
||||
for (uint8_t i = 0; i < MPR121_I<sup>2</sup>C_ADDR_NUM); i++) {
|
||||
|
||||
// Check if sensor is connected on I2C address
|
||||
mpr121[i].connected = (MPR121_I2C_ID_VAL == I2cRead8(MPR121_I2C_ADDR_1ST + i, MPR121_I2C_ID_REG);
|
||||
// Check if sensor is connected on I<sup>2</sup>C address
|
||||
mpr121[i].connected = (MPR121_I<sup>2</sup>C_ID_VAL == I2cRead8(MPR121_I<sup>2</sup>C_ADDR_1ST + i, MPR121_I<sup>2</sup>C_ID_REG);
|
||||
if(mpr121[i].connected) {
|
||||
|
||||
// Log sensor found
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121-%d " D_FOUND_AT " 0x%X"), i, MPR121_I2C_ADDR_1ST + i);
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I<sup>2</sup>C "MPR121-%d " D_FOUND_AT " 0x%X"), i, MPR121_I<sup>2</sup>C_ADDR_1ST + i);
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
|
||||
// Initialize sensor
|
||||
|
@ -162,7 +162,7 @@ void mpr121_init() {
|
|||
}
|
||||
}
|
||||
if(!(mpr121[0].connected || mpr121[1].connected || mpr121[2].connected || mpr121[3].connected)){
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121: No sensors found"));
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I<sup>2</sup>C "MPR121: No sensors found"));
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ ___
|
|||
#### void AddLog(byte loglevel)
|
||||
This function adds log messages stored in ``log_data`` to the local logging system, e.g.
|
||||
```
|
||||
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]);
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I<sup>2</sup>C "MPR121(%c) " D_FOUND_AT " 0x%X"), pS->id[i], pS->i2c_addr[i]);
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
```
|
||||
#### void AddLogSerial(byte loglevel)
|
||||
|
@ -262,7 +262,7 @@ AddLogSerial(LOG_LEVEL_INFO);
|
|||
#### void AddLogMissed(char *sensor, uint8_t misses)
|
||||
This function adds a log message to the local logging system about missed sensor reads.
|
||||
|
||||
### I2C interface
|
||||
### I<sup>2</sup>C interface
|
||||
___
|
||||
#### bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg)
|
||||
#### bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg)
|
||||
|
@ -271,7 +271,7 @@ ___
|
|||
#### bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg)
|
||||
#### bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg)
|
||||
#### bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size)
|
||||
These functions return `true` if 1, 2, 3 or `size` bytes can be read from the I2C address `addr` and register `reg` into `*data`.
|
||||
These functions return `true` if 1, 2, 3 or `size` bytes can be read from the I<sup>2</sup>C address `addr` and register `reg` into `*data`.
|
||||
Functions with a `S` read signed data types while functions without a `S` read unsigned data types.
|
||||
Functions with LE read little-endian byte order while functions without LE read machine byte order.
|
||||
|
||||
|
@ -281,7 +281,7 @@ Functions with LE read little-endian byte order while functions without LE read
|
|||
#### uint16_t I2cRead16LE(uint8_t addr, uint8_t reg)
|
||||
#### int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg)
|
||||
#### int32_t I2cRead24(uint8_t addr, uint8_t reg)
|
||||
These functions return 1, 2 or 3 bytes from the I2C address `addr` and register `reg`.
|
||||
These functions return 1, 2 or 3 bytes from the I<sup>2</sup>C address `addr` and register `reg`.
|
||||
Functions with a `S` read signed data types while functions without a `S` read unsigned data types.
|
||||
Functions with LE read little endian byte order while functions without LE read machine byte order.
|
||||
|
||||
|
@ -289,17 +289,17 @@ Functions with LE read little endian byte order while functions without LE read
|
|||
#### bool I2cWrite8(uint8_t addr, uint8_t reg, uint8_t val)
|
||||
#### bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val)
|
||||
#### bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size)
|
||||
These functions return true after successfully writing 1, 2 or `size` bytes to the I2C address `addr` and register `reg`.
|
||||
These functions return true after successfully writing 1, 2 or `size` bytes to the I<sup>2</sup>C address `addr` and register `reg`.
|
||||
|
||||
#### int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len)
|
||||
#### int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len)
|
||||
These functions copy `len` bytes from/to `*reg_data` starting at I2C address `addr` and register `reg`.
|
||||
These functions copy `len` bytes from/to `*reg_data` starting at I<sup>2</sup>C address `addr` and register `reg`.
|
||||
|
||||
#### void I2cScan(char *devs, unsigned int devs_len)
|
||||
This functions writes a list of I2C addresses in use into the string `*dev` with maximum length `devs_len`.
|
||||
This functions writes a list of I<sup>2</sup>C addresses in use into the string `*dev` with maximum length `devs_len`.
|
||||
|
||||
#### boolean I2cDevice(byte addr)
|
||||
This functions checks if the I2C address `addr` is in use.
|
||||
This functions checks if the I<sup>2</sup>C address `addr` is in use.
|
||||
|
||||
|
||||
## Useful pre-processor directives
|
||||
|
|
Loading…
Reference in New Issue