diff --git a/app/include/user_modules.h b/app/include/user_modules.h index d6d92f19..de910614 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -53,6 +53,7 @@ //#define LUA_USE_MODULES_RTCFIFO //#define LUA_USE_MODULES_RTCMEM //#define LUA_USE_MODULES_RTCTIME +//#define LUA_USE_MODULES_SI7021 //#define LUA_USE_MODULES_SIGMA_DELTA //#define LUA_USE_MODULES_SJSON //#define LUA_USE_MODULES_SNTP diff --git a/app/modules/si7021.c b/app/modules/si7021.c new file mode 100644 index 00000000..fbcbebc4 --- /dev/null +++ b/app/modules/si7021.c @@ -0,0 +1,265 @@ +//*************************************************************************** +// Si7021 module for ESP8266 with nodeMCU +// fetchbot @github +// MIT license, http://opensource.org/licenses/MIT +//*************************************************************************** + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "osapi.h" + +//*************************************************************************** +// I2C ADDRESS DEFINITON +//*************************************************************************** + +#define SI7021_I2C_ADDRESS (0x40) + +//*************************************************************************** +// COMMAND DEFINITON +//*************************************************************************** + +#define SI7021_CMD_MEASURE_RH_HOLD (0xE5) +#define SI7021_CMD_MEASURE_RH_NOHOLD (0xF5) +#define SI7021_CMD_MEASURE_TEMP_HOLD (0xE3) +#define SI7021_CMD_MEASURE_TEMP_NOHOLD (0xF3) +#define SI7021_CMD_READ_PREV_TEMP (0xE0) +#define SI7021_CMD_RESET (0xFE) +#define SI7021_CMD_WRITE_RHT_REG (0xE6) +#define SI7021_CMD_READ_RHT_REG (0xE7) +#define SI7021_CMD_WRITE_HEATER_REG (0x51) +#define SI7021_CMD_READ_HEATER_REG (0x11) +#define SI7021_CMD_ID1 (0xFA0F) +#define SI7021_CMD_ID2 (0xFCC9) +#define SI7021_CMD_FIRM_REV (0x84B8) + +//*************************************************************************** +// REGISTER DEFINITON +//*************************************************************************** + +#define SI7021_RH12_TEMP14 (0x00) +#define SI7021_RH08_TEMP12 (0x01) +#define SI7021_RH10_TEMP13 (0x80) +#define SI7021_RH11_TEMP11 (0x81) +#define SI7021_HEATER_ENABLE (0x04) +#define SI7021_HEATER_DISABLE (0x00) + +//*************************************************************************** + +static const uint32_t si7021_i2c_id = 0; +static uint8_t si7021_i2c_addr = SI7021_I2C_ADDRESS; +static uint8_t si7021_res = 0x00; +static uint8_t si7021_config = 0x3A; +static uint8_t si7021_heater = 0x00; +static uint8_t si7021_heater_setting = 0x00; + +static uint8_t write_byte(uint8_t reg) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, reg); + platform_i2c_send_stop(si7021_i2c_id); +} + +static uint8_t write_reg(uint8_t reg, uint8_t data) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, reg); + platform_i2c_send_byte(si7021_i2c_id, data); + platform_i2c_send_stop(si7021_i2c_id); +} + +static uint8_t read_reg(uint8_t reg, uint8_t *buf, uint8_t size) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, reg); + platform_i2c_send_stop(si7021_i2c_id); + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + os_delay_us(25000); + while (size-- > 0) + *buf++ = platform_i2c_recv_byte(si7021_i2c_id, size > 0); + platform_i2c_send_stop(si7021_i2c_id); + return 1; +} + +static uint8_t read_serial(uint16_t reg, uint8_t *buf, uint8_t size) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, (uint8_t)(reg >> 8)); + platform_i2c_send_byte(si7021_i2c_id, (uint8_t)(reg & 0xFF)); + // platform_i2c_send_stop(si7021_i2c_id); + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + while (size-- > 0) + *buf++ = platform_i2c_recv_byte(si7021_i2c_id, size > 0); + platform_i2c_send_stop(si7021_i2c_id); + return 1; +} + +// CRC8 +uint8_t si7021_crc8(uint8_t crc, uint8_t *buf, uint8_t size) { + while (size--) { + crc ^= *buf++; + for (uint8_t i = 0; i < 8; i++) { + if (crc & 0x80) { + crc = (crc << 1) ^ 0x31; + } else crc <<= 1; + } + } + return crc; +} + +static int si7021_lua_setup(lua_State* L) { + + write_byte(SI7021_CMD_RESET); + os_delay_us(50000); + + // check for device on i2c bus + uint8_t buf_r[1]; + read_reg(SI7021_CMD_READ_RHT_REG, buf_r, 1); + if (buf_r[0] != 0x3A) + return luaL_error(L, "found no device"); + + return 0; +} + +// Change sensor settings and returns them +// Lua: res, vdds, heater[, heater_set] = si7021.settings(RESOLUTION[,HEATER,HEATER_SETTING]) +static int si7021_lua_setting(lua_State* L) { + + // check variable + if (!lua_isnumber(L, 1)) { + return luaL_error(L, "wrong arg range"); + } + + si7021_res = luaL_checkinteger(L, 1); + if (!((si7021_res == SI7021_RH12_TEMP14) || (si7021_res == SI7021_RH08_TEMP12) || (si7021_res == SI7021_RH10_TEMP13) || (si7021_res == SI7021_RH11_TEMP11))) { + return luaL_error(L, "Invalid argument: resolution"); + } + + si7021_config = (si7021_res | 0x3A); + write_reg(SI7021_CMD_WRITE_RHT_REG,si7021_config); + + // Parse optional parameters + if (lua_isnumber(L, 2)) { + + if (!lua_isnumber(L, 2) || !lua_isnumber(L, 3)) { + return luaL_error(L, "wrong arg range"); + } + + si7021_heater = luaL_checkinteger(L, 2); + if (!((si7021_heater == SI7021_HEATER_ENABLE) || (si7021_heater == SI7021_HEATER_DISABLE))) { + return luaL_error(L, "Invalid argument: heater"); + } + + si7021_heater_setting = luaL_checkinteger(L, 3); + if ((si7021_heater_setting < 0x00) || (si7021_heater_setting > 0x0F)) { + return luaL_error(L, "Invalid argument: heater_setting"); + } + + si7021_config = (si7021_res | si7021_heater | 0x3A); + write_reg(SI7021_CMD_WRITE_RHT_REG,si7021_config); + write_reg(SI7021_CMD_WRITE_HEATER_REG,(si7021_heater_setting & 0x0F)); + } + + uint8_t buf_c[1]; + uint8_t buf_h[1]; + read_reg(SI7021_CMD_READ_RHT_REG, buf_c, 1); + read_reg(SI7021_CMD_READ_HEATER_REG, buf_h, 1); + + lua_pushinteger(L, ((buf_c[0] >> 6) + (buf_c[0] & 0x01))); + lua_pushinteger(L, ((buf_c[0] >> 6) & 0x01)); + lua_pushinteger(L, ((buf_c[0] >> 2) & 0x01)); + lua_pushinteger(L, (buf_h[0] & 0x0F)); + + return 4; +} + +// Reads sensor values from device and returns them +// Lua: hum, temp, humdec, tempdec = si7021.read() +static int si7021_lua_read(lua_State* L) { + + uint8_t buf_h[3]; // first two byte data, third byte crc + read_reg(SI7021_CMD_MEASURE_RH_HOLD, buf_h, 3); + if (buf_h[2] != si7021_crc8(0, buf_h, 2)) //crc check + return luaL_error(L, "crc error"); + double hum = (uint16_t)((buf_h[0] << 8) | buf_h[1]); + hum = ((hum * 125) / 65536 - 6); + int humdec = (int)((hum - (int)hum) * 1000); + + uint8_t buf_t[2]; // two byte data, no crc on combined temp measurement + read_reg(SI7021_CMD_READ_PREV_TEMP, buf_t, 2); + double temp = (uint16_t)((buf_t[0] << 8) | buf_t[1]); + temp = ((temp * 175.72) / 65536 - 46.85); + int tempdec = (int)((temp - (int)temp) * 1000); + + lua_pushnumber(L, hum); + lua_pushnumber(L, temp); + lua_pushinteger(L, humdec); + lua_pushinteger(L, tempdec); + + return 4; +} + +// Reads electronic serial number from device and returns them +// Lua: serial = si7021.serial() +static int si7021_lua_serial(lua_State* L) { + + uint32_t serial_a; + uint8_t crc = 0; + uint8_t buf_s_1[8]; // every second byte contains crc + read_serial(SI7021_CMD_ID1, buf_s_1, 8); + for(uint8_t i = 0; i <= 6; i+=2) { + crc = si7021_crc8(crc, buf_s_1+i, 1); + if (buf_s_1[i+1] != crc) + return luaL_error(L, "crc error"); + serial_a = (serial_a << 8) + buf_s_1[i]; + } + + uint32_t serial_b; + crc = 0; + uint8_t buf_s_2[6]; // every third byte contains crc + read_serial(SI7021_CMD_ID2, buf_s_2, 6); + for(uint8_t i = 0; i <=3; i+=3) { + crc = si7021_crc8(crc, buf_s_2+i, 2); + if (buf_s_2[i+2] != crc) + return luaL_error(L, "crc error"); + serial_b = (serial_b << 16) + (buf_s_2[i] << 8) + buf_s_2[i+1]; + } + + lua_pushinteger(L, serial_a); + lua_pushinteger(L, serial_b); + + return 2; +} + +// Reads electronic firmware revision from device and returns them +// Lua: firmware = si7021.firmware() +static int si7021_lua_firmware(lua_State* L) { + + uint8_t firmware; + uint8_t buf_f[1]; + read_serial(SI7021_CMD_FIRM_REV, buf_f, 1); + firmware = buf_f[0]; + + lua_pushinteger(L, firmware); + + return 1; +} + +static const LUA_REG_TYPE si7021_map[] = { + { LSTRKEY( "setup" ), LFUNCVAL(si7021_lua_setup) }, + { LSTRKEY( "setting" ), LFUNCVAL(si7021_lua_setting) }, + { LSTRKEY( "read" ), LFUNCVAL(si7021_lua_read) }, + { LSTRKEY( "serial" ), LFUNCVAL(si7021_lua_serial) }, + { LSTRKEY( "firmware" ), LFUNCVAL(si7021_lua_firmware) }, + { LSTRKEY( "RH12_TEMP14" ), LNUMVAL(SI7021_RH12_TEMP14) }, + { LSTRKEY( "RH08_TEMP12" ), LNUMVAL(SI7021_RH08_TEMP12) }, + { LSTRKEY( "RH10_TEMP13" ), LNUMVAL(SI7021_RH10_TEMP13) }, + { LSTRKEY( "RH11_TEMP11" ), LNUMVAL(SI7021_RH11_TEMP11) }, + { LSTRKEY( "HEATER_ENABLE" ), LNUMVAL(SI7021_HEATER_ENABLE) }, + { LSTRKEY( "HEATER_DISABLE" ), LNUMVAL(SI7021_HEATER_DISABLE) }, + { LNILKEY, LNILVAL } +}; + +NODEMCU_MODULE(SI7021, "si7021", si7021_map, NULL); diff --git a/docs/en/modules/si7021.md b/docs/en/modules/si7021.md new file mode 100644 index 00000000..fc406bf6 --- /dev/null +++ b/docs/en/modules/si7021.md @@ -0,0 +1,152 @@ +# Si7021 Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-04-19 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [si7021.c](../../../app/modules/si7021.c)| + +This module provides access to the Si7021 humidity and temperature sensor. + +## si7021.firmware() +Read the internal firmware revision of the Si7021 sensor. + +#### Syntax +`si7021.firmware()` + +#### Parameters +none + +#### Returns +`fwrev` Firmware version +* `0xFF` Firmware version 1.0 +* `0x20` Firmware version 2.0 + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +fwrev = si7021.firmware() +print(string.format("FW: %X\r\n", fwrev)) +``` + +## si7021.setting() +Settings for the sensors configuration register to adjust measurement resolution, on-chip heater and read the supply voltage status. + +#### Syntax +`si7021.setting(RESOLUTION[, HEATER, HEATER_SETTING])` + +#### Parameters +- `RESOLUTION` + * `si7021.RH12_TEMP14` Relative Humidity 12 bit - Temperature 14 bit (default) + * `si7021.RH08_TEMP12` Relative Humidity 8 bit - Temperature 12 bit + * `si7021.RH10_TEMP13` Relative Humidity 10 bit - Temperature 13 bit + * `si7021.RH11_TEMP11` Relative Humidity 11 bit - Temperature 11 bit +- `HEATER` optional + * `si7021.HEATER_ENABLE` On-chip Heater Enable + * `si7021.HEATER_DISABLE` On-chip Heater Disable (default) +- `HEATER_SETTING` optional + * `0x00` - `0x0F` 3.09 mA - 94.20 mA + +#### Returns +- `resolution` + * `0` Relative Humidity 12 bit - Temperature 14 bit + * `1` Relative Humidity 8 bit - Temperature 12 bit + * `2` Relative Humidity 10 bit - Temperature 13 bit + * `3` Relative Humidity 11 bit - Temperature 11 bit +- `vdds` + * `0` VDD OK (1.9V - 3.6V) + * `1` VDD LOW (1.8V - 1.9V) +- `heater` + * `0` Disabled + * `1` Enabled +- `heater_setting` + * `0` - `15` + +#### Example +```lua +local id, sda, scl = 0, 6, 5 +i2c.setup(id, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +res, vdds, heater, heater_set = si7021.setting(si7021.RH12_TEMP14) +res, vdds, heater, heater_set = si7021.setting(si7021.RH12_TEMP14, si7021.HEATER_ENABLE, 0x01) +``` + +## si7021.setup() +Initializes the device on fixed I²C device address (0x40). + +#### Syntax +`si7021.setup()` + +#### Parameters +none + +#### Returns +`nil` + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() +``` + +## si7021.read() + +#### Syntax +`si7021.read()` + +#### Parameters +none + +#### Returns +- `hum` humidity (see note below) +- `temp` temperature (see note below) +- `hum_dec` humidity decimal +- `temp_dec` temperature decimal + +!!! note + + If using float firmware then `hum` and `temp` are floating point numbers. On an integer firmware, the final values have to be concatenated from `hum` and `hum_dec` / `temp` and `temp_dec`. + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +hum, temp, hum_dec, temp_dec = si7021.read() + +-- Integer firmware using this example +print(string.format("Humidity:\t\t%d.%03d\nTemperature:\t%d.%03d\n", hum, hum_dec, temp, temp_dec)) + +-- Float firmware using this example +print("Humidity: "..hum.."\n".."Temperature: "..temp) +``` + +## si7021.serial() +Read the individualized 64-bit electronic serial number of the Si7021 sensor. + +#### Syntax +`si7021.serial()` + +#### Parameters +none + +#### Returns +- `sna` 32-bit serial number part a +- `snb` 32-bit serial number part b, upper byte contains the device identification + * `0x00` or `0xFF` engineering samples + * `0x0D` `13` Si7013 + * `0x14` `20` Si7020 + * `0x15` `21` Si7021 + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +sna, snb = si7021.serial() +print(string.format("SN:\t\t%X%X\nDevice:\tSi70%d", sna, snb, bit.rshift(snb,24))) +``` diff --git a/mkdocs.yml b/mkdocs.yml index c4d69c0c..c9909ae9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,7 @@ pages: - 'rtcfifo': 'en/modules/rtcfifo.md' - 'rtcmem': 'en/modules/rtcmem.md' - 'rtctime': 'en/modules/rtctime.md' + - 'si7021' : 'en/modules/si7021' - 'sigma delta': 'en/modules/sigma-delta.md' - 'sjson': 'en/modules/sjson.md' - 'sntp': 'en/modules/sntp.md'