Consistency in formatting code blocks

Michael Ingraham 2019-06-27 17:00:01 -04:00
parent 8af98cd359
commit 917e733fcf
1 changed files with 112 additions and 56 deletions

@ -30,7 +30,7 @@ Conditional compiling of a sensor driver is achieved by adding a pre-processor d
## Callback function
Any sensor driver needs a callback function following the scheme
```
```c++
// Conditional compilation of driver
#ifdef USE_<driver_name>
@ -85,13 +85,16 @@ boolean Xsns<driverID>(byte callback_id) {
## Callback IDs
### FUNC_INIT
`FUNC_INIT`
This callback ID is called when sensor drivers should be initialized.
### FUNC_EVERY_50_MSECOND
`FUNC_EVERY_50_MSECOND`
This callback ID is called every 50 milliseconds, e.g. for near real-time operation
### FUNC_EVERY_SECOND
`FUNC_EVERY_SECOND`
This callback ID is called every second.
It can be useful for anything that you need to do on a per second basis and is commonly used as an entry point to detect a driver or initialize an externally driven device such as a sensor, relay board or other forms of input/output required by your driver.
@ -99,7 +102,7 @@ It can be useful for anything that you need to do on a per second basis and is c
You would normally want to make sure you've detected and initialised before it is used by JSON_APPEND etc so that its ready to serve data.
The generally accepted way to use this would be to detect your sensor and once this is done set a sensor value accordingly so that the function does not use unnecessary resources during future calls, for example:
```
```c++
void MySensorDetect()
{
if (MySensorDetected) { return; }
@ -124,7 +127,7 @@ When writing your function responsible for detecting an externally connected I<s
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_override.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.
**I<sup>2</sup>C address auto-detection example**
```
```c++
#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 **/
@ -172,34 +175,39 @@ void mpr121_init() {
}
```
**Four advanced methods to use FUNC_EVERY_SECOND (Food for thought) :**
**Four advanced methods to use `FUNC_EVERY_SECOND` (Food for thought) :**
* If a sensor needs an action which takes a long time, like more than 100mS, the action will be started here for a future follow-up. Using the uptime variable for testing like (uptime &1) will happen every 2 seconds. An example is the DS18B20 driver where readings (conversions they call it) can take up to 800mS from the initial request.
* If a sensor needed the previous action it is now time to gather the information and store it in a safe place to be used by FUNC_JSON_APPEND and/or FUNC_WEB_APPEND. Using the else function of the previous test (uptime &1) will happen every 2 seconds too but just 1 second later than the previous action.
* If a sensor needed the previous action it is now time to gather the information and store it in a safe place to be used by `FUNC_JSON_APPEND` and/or `FUNC_WEB_APPEND`. Using the else function of the previous test (uptime &1) will happen every 2 seconds too but just 1 second later than the previous action.
* If a sensor does not respond for 10 times the sensor detection flag could be reset which will stop further processing until the sensor is re-detected. This is currently not being used actively as some users complain about disappearing sensors for whatever reason - Could be hardware related but easier to make Tasmota a little more flexible.
* Making re-detection of a sensor possible by executing this once every 100 seconds (94 == (uptime %100)) a re-attached sensor can be detected without a restart of Tasmota. The 94 given in this example should be different for every sensor driver to make sure not all sensors start detection at the same time. Using the drivers index number should be a good starting point.
### FUNC_PREP_BEFORE_TELEPERIOD
NOTE: This callback ID is deprecated as sensors should prepare for more regular updates due to "realtime" rule execution. Use FUNC_EVERY_SECOND instead. See examples used in xsns_05_ds18x20.ino and xsns_09_bmp.ino where updated sensor data is stored in preparation to calls to FUNC_JSON_APPEND and FUNC_WEB_APPEND.
`FUNC_PREP_BEFORE_TELEPERIOD`
NOTE: This callback ID is deprecated as sensors should prepare for more regular updates due to "realtime" rule execution. Use `FUNC_EVERY_SECOND` instead. See examples used in xsns_05_ds18x20.ino and xsns_09_bmp.ino where updated sensor data is stored in preparation to calls to FUNC_JSON_APPEND and FUNC_WEB_APPEND.
`FUNC_JSON_APPEND`
### FUNC_JSON_APPEND
This callback ID is called when [`TelePeriod`](Commands#teleperiod) is due to append telemetry data to the MQTT JSON string or at approximately every 2 seconds when a rule is checked, e.g.
```
```c++
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i,j));
```
### FUNC_WEB_APPEND
This callback ID is called every <WebRefresh> millisecond when HTML code should be added to the Tasmota web-interface main page, e.g.
```
`FUNC_WEB_APPEND`
This callback ID is called every <WebRefresh> millisecond when HTML code should be added to the Tasmota web-interface main page, e.g.,
```c++
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));
```
It should be wrapped in `#ifdef USE_WEBSERVER ... #endif // USE_WEBSERVER`
### FUNC_SAVE_BEFORE_RESTART
`FUNC_SAVE_BEFORE_RESTART`
This callback ID is called to allow a sensor to prepare for saving configuration changes. To be used to save volatile data just before a restart. Variables can be appended to `struct SYSCFG {} Settings` in file `sonoff/settings.h`.
### FUNC_COMMAND
`FUNC_COMMAND`
This callback ID is called when a sensor specific command ``Sensor<xx>`` or ``Driver<xx>`` is executed where xx is the sensor index.
```
```c++
case FUNC_COMMAND:
if (XSNS_<driver_ID> == XdrvMailbox.index) {
result = <driver_name>Command() { ... }; // Return true on success
@ -207,7 +215,7 @@ This callback ID is called when a sensor specific command ``Sensor<xx>`` or ``Dr
break;
```
```
```c++
// Data struct of FUNC_COMMAND ID
struct XDRVMAILBOX {
uint16_t valid; // ???
@ -225,7 +233,7 @@ struct XDRVMAILBOX {
If your driver needs to accept multiple parameters for `SensorXX` and/or DriverXX please consider using comma delimited formatting and use the already written subStr() function declared in support.ino to parse through the parameters you need.
An example of those could be
```
```c++
SensorXX reset // The reset parameter may be intercepted using:
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { // Note 1 used for param number
MyDriverName_Reset();
@ -233,7 +241,7 @@ if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { // Note 1 u
}
```
Or in the case of multiple parameters
```
```c++
SensorXX mode,1
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { // Note 1 used for param number
uint8_t mode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2); // Note 2 used for param number
@ -244,74 +252,123 @@ if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { // Note 1 u
### MQTT
___
#### void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, boolean retained)
This function publishes MQTT messages immediately, e.g.
```c++
void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, boolean retained)
```
This function publishes MQTT messages immediately, e.g.
```c++
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i,j));
MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
```
### Logging
___
#### void AddLog(byte loglevel)
This function adds log messages stored in ``log_data`` to the local logging system, e.g.
```c++
void AddLog(byte loglevel)
```
This function adds log messages stored in ``log_data`` to the local logging system, e.g.
```c++
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]);
AddLog(LOG_LEVEL_INFO);
```
#### void AddLogSerial(byte loglevel)
This function adds a log message to the local logging system dumping the serial buffer as hex information, e.g.
```c++
void AddLogSerial(byte loglevel)
```
This function adds a log message to the local logging system dumping the serial buffer as hex information, e.g.
```c++
AddLogSerial(LOG_LEVEL_INFO);
```
#### void AddLogMissed(char *sensor, uint8_t misses)
```c++
void AddLogMissed(char *sensor, uint8_t misses)
```
This function adds a log message to the local logging system about missed sensor reads.
### 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)
#### bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg)
#### bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg)
#### 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)
```c++
bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg)
```
```c++
bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg)
```
```c++
bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg)
```
```c++
bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg)
```
```c++
bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg)
```
```c++
bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg)
```
```c++
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 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.
#### uint8_t I2cRead8(uint8_t addr, uint8_t reg)
#### uint16_t I2cRead16(uint8_t addr, uint8_t reg)
#### int16_t I2cReadS16(uint8_t addr, uint8_t reg)
#### 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)
```c++
uint8_t I2cRead8(uint8_t addr, uint8_t reg)
```
```c++
uint16_t I2cRead16(uint8_t addr, uint8_t reg)
```
```c++
int16_t I2cReadS16(uint8_t addr, uint8_t reg)
```
```c++
uint16_t I2cRead16LE(uint8_t addr, uint8_t reg)
```
```c++
int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg)
```
```c++
int32_t I2cRead24(uint8_t addr, uint8_t 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.
#### 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)
```c++
bool I2cWrite8(uint8_t addr, uint8_t reg, uint8_t val)
```
```c++
bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val)
```
```c++
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 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)
```c++
int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len)
```
```c++
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 I<sup>2</sup>C address `addr` and register `reg`.
#### void I2cScan(char *devs, unsigned int devs_len)
```c++
void I2cScan(char *devs, unsigned int 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)
```c++
boolean I2cDevice(byte addr)
```
This functions checks if the I<sup>2</sup>C address `addr` is in use.
## Useful pre-processor directives
#### PSTR("string")
```c++
PSTR("string")
```
This pre-processor directive saves RAM by storing strings in flash instead of RAM.
#### const char MyTextStaticVariable[] PROGMEM = "string";
```c++
const char MyTextStaticVariable[] PROGMEM = "string";
```
This pre-processor directive saves RAM by storing strings in flash instead of RAM.
You may then reference them directly (if the type matches the parameter required) or force it to 4 byte alignment by using the variable as `FPSTR(MyTextStaticVariable)`
@ -324,7 +381,7 @@ Below are various tips and tricks to keep ESP8266 code compact and save both Fla
ESP8266 is based on [Xtensa instruction set](https://0x04.net/~mwk/doc/xtensa.pdf). Xtensa is a 32 bits RISC processor core, containing 16 x 32 bits registers. ESP8266 supports integer operations, including 32x32 multiplication. It does not contain an FPU for floating point operations, nor integer divisions.
Contraty to classical RISC processors, all instructions are 24 bits wide instead of 32 bits. To increase code compactness, some instructions have a 16 bits version used whenever possible by gcc.
Contrary to classical RISC processors, all instructions are 24 bits wide instead of 32 bits. To increase code compactness, some instructions have a 16 bits version used whenever possible by gcc.
If you want to see what assembly is generated by gcc, in file `platform.ini`, at the section used to compile (ex: `[core_2_5_2]`) in section `build_flags` add:
@ -342,7 +399,7 @@ uint32_t Example(uint32_t a, uint32_t b) {
}
```
Below is the generated assembly. Function names are mangled using satndard C++, i.e. their name derive from their arguments and return types:
Below is the generated assembly. Function names are mangled using standard C++, i.e. their name derive from their arguments and return types:
```asm
_Z7Examplejj:
@ -753,4 +810,3 @@ _Z20ExampleStringConcat3R6Stringhh:
addi sp, sp, 16 #,,
ret.n
```