diff --git a/platforms/obk_main.cmake b/platforms/obk_main.cmake index 13ee7dcbc..c3baf95eb 100644 --- a/platforms/obk_main.cmake +++ b/platforms/obk_main.cmake @@ -124,6 +124,7 @@ set(OBKM_SRC ${OBK_SRCS}driver/drv_sm16703P.c ${OBK_SRCS}driver/drv_simpleEEPROM.c ${OBK_SRCS}driver/drv_leds_shared.c + ${OBK_SRCS}driver/drv_ltr_als.c ${OBK_SRCS}driver/drv_spi.c ${OBK_SRCS}driver/drv_spiLED.c ${OBK_SRCS}driver/drv_spi_flash.c diff --git a/platforms/obk_main.mk b/platforms/obk_main.mk index 7787f4915..d9c7d13e4 100644 --- a/platforms/obk_main.mk +++ b/platforms/obk_main.mk @@ -140,6 +140,7 @@ OBKM_SRC += $(OBK_SRCS)driver/drv_sm15155e.c OBKM_SRC += $(OBK_SRCS)driver/drv_sm16703P.c OBKM_SRC += $(OBK_SRCS)driver/drv_simpleEEPROM.c OBKM_SRC += $(OBK_SRCS)driver/drv_leds_shared.c +OBKM_SRC += $(OBK_SRCS)driver/drv_ltr_als.c OBKM_SRC += $(OBK_SRCS)driver/drv_sm2135.c OBKM_SRC += $(OBK_SRCS)driver/drv_sm2235.c OBKM_SRC += $(OBK_SRCS)driver/drv_soft_i2c.c diff --git a/src/driver/drv_local.h b/src/driver/drv_local.h index 05f1a8001..b556f02f6 100644 --- a/src/driver/drv_local.h +++ b/src/driver/drv_local.h @@ -287,3 +287,7 @@ void Bridge_driver_DeInit(); void Bridge_driver_QuickFrame(); void Bridge_driver_OnChannelChanged(int ch, int value); /*************************************************************/ + +void LTR_Init(); +void LTR_OnEverySecond(); +void LTR_AppendInformationToHTTPIndexPage(http_request_t* request, int bPreState); diff --git a/src/driver/drv_ltr_als.c b/src/driver/drv_ltr_als.c new file mode 100644 index 000000000..340c709e2 --- /dev/null +++ b/src/driver/drv_ltr_als.c @@ -0,0 +1,206 @@ +#include "../obk_config.h" + +#if ENABLE_DRIVER_LTR_ALS + +#include "../new_pins.h" +#include "../logging/logging.h" +#include "drv_local.h" +#include "drv_ltr_als.h" + +static softI2C_t g_softI2C; +static bool is_init = false; + +static int ch_lux = -1; + +static uint16_t als_ch0 = 0; +static uint16_t als_ch1 = 0; +static float als_lux = 0.0f; + +static byte sec_left = 0; +static byte sec_cycle = 4; + +static void LTR_WriteReg(byte reg, byte val) +{ + Soft_I2C_Start(&g_softI2C, LTR_I2C_ADDR); + Soft_I2C_WriteByte(&g_softI2C, reg); + Soft_I2C_WriteByte(&g_softI2C, val); + Soft_I2C_Stop(&g_softI2C); +} + +static byte LTR_Read8(byte reg) +{ + byte v; + Soft_I2C_Start(&g_softI2C, LTR_I2C_ADDR); + Soft_I2C_WriteByte(&g_softI2C, reg); + Soft_I2C_Stop(&g_softI2C); + Soft_I2C_Start(&g_softI2C, LTR_I2C_ADDR | 1); + v = Soft_I2C_ReadByte(&g_softI2C, true); + Soft_I2C_Stop(&g_softI2C); + return v; +} + +static void LTR_Reset() +{ + LTR_WriteReg(LTR_REG_ALS_CONTR, 0x02); + rtos_delay_milliseconds(10); +} + +static void LTR_Setup() +{ + LTR_Reset(); + byte manufac = LTR_Read8(LTR_REG_MANUFAC_ID); + if(manufac != 0x05) + { + // retry once + manufac = LTR_Read8(LTR_REG_MANUFAC_ID); + if(manufac != 0x05) + { + ADDLOG_ERROR(LOG_FEATURE_SENSOR, "LTR unknown manufacturer %i", manufac); + is_init = false; + return; + } + } + LTR_WriteReg(LTR_REG_ALS_CONTR, 0x01); + rtos_delay_milliseconds(20); + // dummy read + LTR_Read8(LTR_REG_STATUS); + is_init = true; +} + +static void LTR_ReadData() +{ + if(!(LTR_Read8(LTR_REG_STATUS) & 0x04)) return; + + byte c1l = LTR_Read8(LTR_REG_CH1_0); + byte c1h = LTR_Read8(LTR_REG_CH1_1); + byte c0l = LTR_Read8(LTR_REG_CH0_0); + byte c0h = LTR_Read8(LTR_REG_CH0_1); + + als_ch1 = ((uint16_t)c1h << 8) | c1l; + als_ch0 = ((uint16_t)c0h << 8) | c0l; +} + +static void LTR_CalcLux() +{ + if(als_ch0 == 0 && als_ch1 == 0) + { + als_lux = 0; + return; + } + + float r = (float)als_ch1 / (float)(als_ch0 + als_ch1); + + if(r < 0.45f) als_lux = 1.7743f * als_ch0 + 1.1059f * als_ch1; + else if(r < 0.64f) als_lux = 4.2785f * als_ch0 - 1.9548f * als_ch1; + else if(r < 0.85f) als_lux = 0.5926f * als_ch0 + 0.1185f * als_ch1; + else als_lux = 0.0f; +} + +static void LTR_Measure() +{ + if(!is_init) return; + + LTR_ReadData(); + LTR_CalcLux(); + + if(ch_lux >= 0) CHANNEL_Set(ch_lux, (int)(als_lux * 10), 0); +} + +commandResult_t LTR_ALS(const void* ctx, const char* cmd, const char* args, int flags) +{ + Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES | TOKENIZER_DONT_EXPAND); + + if(Tokenizer_CheckArgsCountAndPrintWarning(cmd, 3)) return CMD_RES_NOT_ENOUGH_ARGUMENTS; + + int gain = Tokenizer_GetArgInteger(0); + int integ = Tokenizer_GetArgInteger(1); + int rep = Tokenizer_GetArgInteger(2); + + LTR_WriteReg(LTR_REG_ALS_CONTR, 0x01 | (gain << 2)); + LTR_WriteReg(LTR_REG_MEAS_RATE, (rep & 0x07) | ((integ & 0x07) << 3)); + return CMD_RES_OK; +} + +commandResult_t LTR_INT(const void* ctx, const char* cmd, const char* args, int flags) +{ + Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES | TOKENIZER_DONT_EXPAND); + + if(Tokenizer_CheckArgsCountAndPrintWarning(cmd, 4)) return CMD_RES_NOT_ENOUGH_ARGUMENTS; + + int en = Tokenizer_GetArgInteger(0); + int low = Tokenizer_GetArgInteger(1); + int high = Tokenizer_GetArgInteger(2); + int persist = Tokenizer_GetArgInteger(3); + + LTR_WriteReg(LTR_REG_ALS_THRES_LOW_0, low & 0xFF); + LTR_WriteReg(LTR_REG_ALS_THRES_LOW_1, low >> 8); + LTR_WriteReg(LTR_REG_ALS_THRES_UP_0, high & 0xFF); + LTR_WriteReg(LTR_REG_ALS_THRES_UP_1, high >> 8); + LTR_WriteReg(LTR_REG_INTERRUPT_PERSIST, persist & 0x0F); + LTR_WriteReg(LTR_REG_INTERRUPT, en ? 0x02 : 0x00); + + return CMD_RES_OK; +} + +commandResult_t LTR_Cycle(const void* ctx, const char* cmd, const char* args, int flags) +{ + Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES | TOKENIZER_DONT_EXPAND); + + if(Tokenizer_CheckArgsCountAndPrintWarning(cmd, 1)) return CMD_RES_NOT_ENOUGH_ARGUMENTS; + + int s = Tokenizer_GetArgInteger(0); + if(s < 1) return CMD_RES_BAD_ARGUMENT; + + sec_cycle = s; + sec_left = s; + return CMD_RES_OK; +} + +void LTR_Init() +{ + g_softI2C.pin_clk = Tokenizer_GetPin(1, 9); + g_softI2C.pin_data = Tokenizer_GetPin(2, 14); + + ch_lux = Tokenizer_GetArgIntegerDefault(3, -1); + + Soft_I2C_PreInit(&g_softI2C); + rtos_delay_milliseconds(100); + + LTR_Setup(); + + //cmddetail:{"name":"LTR_ALS","args":"[gain] [integration] [repeat]", + //cmddetail:"descr":"ALS configuration. Gain mappings: 0-x1 1-x2 2-x4 3-x8 6-x48 7-x96. Integration mappings (ms): 0-100 1-50 2-200 3-400 4-150 5-250 6-300 7-350. Repeat mappings (ms): 0-50 1-100 2-200 3-500 4-1000 5-2000.", + //cmddetail:"fn":"LTR_ALS","file":"driver/drv_ltr_als.c","requires":"", + //cmddetail:"examples":LTR_ALS 3 0 3
gain 8x, integration 100ms, repeat 500ms"} + CMD_RegisterCommand("LTR_ALS", LTR_ALS, NULL); + //cmddetail:{"name":"LTR_INT","args":"[enable] [als_low] [als_high] [persistance]", + //cmddetail:"descr":"Interrupt configuration. Enable = 0-1, persistance (oversampling) = 0-15. als_low/als_high - raw thresholds", + //cmddetail:"fn":"LTR_INT","file":"driver/drv_ltr_als.c","requires":"", + //cmddetail:"examples":"LTR_INT 1 200 2000 4
enable interrupt, when measurement is lower than 200 or higher than 2000 4 times"} + CMD_RegisterCommand("LTR_INT", LTR_INT, NULL); + //cmddetail:{"name":"LTR_Cycle","args":"[IntervalSeconds]", + //cmddetail:"descr":"This is the interval between measurements in seconds, by default 1. Max is 255.", + //cmddetail:"fn":"LTR_Cycle","file":"driver/drv_ltr_als.c","requires":"", + //cmddetail:"examples":"LTR_Cycle 60
measurement is taken every 60 seconds"} + CMD_RegisterCommand("LTR_Cycle", LTR_Cycle, NULL); +} + +void LTR_OnEverySecond() +{ + if(sec_left-- <= 0) + { + LTR_Measure(); + sec_left = sec_cycle; + } +} + +void LTR_AppendInformationToHTTPIndexPage(http_request_t* request, int bPreState) +{ + if(bPreState) + return; + + if(!is_init) hprintf255(request, "LTR sensor not detected"); + else hprintf255(request, "

LTR Lux=%.1f

", als_lux); +} + +#endif diff --git a/src/driver/drv_ltr_als.h b/src/driver/drv_ltr_als.h new file mode 100644 index 000000000..61b0fa4f5 --- /dev/null +++ b/src/driver/drv_ltr_als.h @@ -0,0 +1,27 @@ +#define LTR_I2C_ADDR (0x29 << 1) + +#define LTR_REG_ALS_CONTR 0x80 +#define LTR_REG_PS_CONTR 0x81 +#define LTR_REG_PS_LED 0x82 +#define LTR_REG_PS_N_PULSES 0x83 +#define LTR_REG_PS_MEAS_RATE 0x84 +#define LTR_REG_MEAS_RATE 0x85 +#define LTR_REG_PART_ID 0x86 +#define LTR_REG_MANUFAC_ID 0x87 +#define LTR_REG_CH1_0 0x88 +#define LTR_REG_CH1_1 0x89 +#define LTR_REG_CH0_0 0x8A +#define LTR_REG_CH0_1 0x8B +#define LTR_REG_STATUS 0x8C +#define LTR_REG_PS_DATA_0 0x8D +#define LTR_REG_PS_DATA_1 0x8E +#define LTR_REG_INTERRUPT 0x8F +#define LTR_REG_PS_THRES_UP_0 0x90 +#define LTR_REG_PS_THRES_UP_1 0x91 +#define LTR_REG_PS_THRES_LOW_0 0x92 +#define LTR_REG_PS_THRES_LOW_1 0x93 +#define LTR_REG_ALS_THRES_UP_0 0x97 +#define LTR_REG_ALS_THRES_UP_1 0x98 +#define LTR_REG_ALS_THRES_LOW_0 0x99 +#define LTR_REG_ALS_THRES_LOW_1 0x9A +#define LTR_REG_INTERRUPT_PERSIST 0x9E diff --git a/src/driver/drv_main.c b/src/driver/drv_main.c index 5f7363a00..1cc217e8c 100644 --- a/src/driver/drv_main.c +++ b/src/driver/drv_main.c @@ -1401,6 +1401,22 @@ static driver_t g_drivers[] = { NULL, // onHassDiscovery false, // loaded }, +#endif +#if ENABLE_DRIVER_LTR_ALS + //drvdetail:{"name":"LTR_ALS", + //drvdetail:"title":"TODO", + //drvdetail:"descr":"LTR-303/329 ambient light sensor driver.", + //drvdetail:"requires":""} + { "LTR_ALS", // Driver Name + LTR_Init, // Init + LTR_OnEverySecond, // onEverySecond + LTR_AppendInformationToHTTPIndexPage, // appendInformationToHTTPIndexPage + NULL, // runQuickTick + NULL, // stopFunction + NULL, // onChannelChanged + NULL, // onHassDiscovery + false, // loaded + }, #endif //{ "", NULL, NULL, NULL, NULL, NULL, NULL, NULL, false }, }; diff --git a/src/httpserver/http_fns.c b/src/httpserver/http_fns.c index 4304f844e..86151b031 100644 --- a/src/httpserver/http_fns.c +++ b/src/httpserver/http_fns.c @@ -2386,6 +2386,11 @@ void doHomeAssistantDiscovery(const char* topic, http_request_t* request) { dev_info = hass_createEnumChannelInfo(i); } break; + case ChType_Illuminance_div10: + { + dev_info = hass_init_sensor_device_info(ILLUMINANCE_SENSOR, i, 2, 1, 1); + } + break; default: { int numOptions; diff --git a/src/new_pins.c b/src/new_pins.c index 400aa2fe0..a142b2c1d 100644 --- a/src/new_pins.c +++ b/src/new_pins.c @@ -1384,6 +1384,7 @@ int ChannelType_GetDivider(int type) { case ChType_Frequency_div10: case ChType_ReadOnly_div10: case ChType_Current_div10: + case ChType_Illuminance_div10: return 10; case ChType_Frequency_div100: case ChType_Current_div100: @@ -1453,6 +1454,7 @@ const char *ChannelType_GetUnit(int type) { case ChType_ReactivePower: return "vAr"; case ChType_Illuminance: + case ChType_Illuminance_div10: return "Lux"; case ChType_Ph: return "Ph"; @@ -1510,6 +1512,7 @@ const char *ChannelType_GetTitle(int type) { case ChType_ReactivePower: return "ReactivePower"; case ChType_Illuminance: + case ChType_Illuminance_div10: return "Illuminance"; case ChType_Ph: return "Ph Water Quality"; @@ -2422,6 +2425,7 @@ const char* g_channelTypeNames[] = { "Enum", "ReadOnlyEnum", "Current_div10", + "Illuminance_div10", "error", "error", "error", diff --git a/src/new_pins.h b/src/new_pins.h index d9d695463..05acfc632 100644 --- a/src/new_pins.h +++ b/src/new_pins.h @@ -1127,6 +1127,13 @@ typedef enum channelType_e { //chandetail:"file":"new_pins.h", //chandetail:"driver":""} ChType_Current_div10, + //chandetail:{"name":"Illuminance_div10", + //chandetail:"title":"Illuminance_div10", + //chandetail:"descr":"Illuminance in Lux", + //chandetail:"enum":"ChType_Illuminance_div10", + //chandetail:"file":"new_pins.h", + //chandetail:"driver":""} + ChType_Illuminance_div10, //chandetail:{"name":"Max", //chandetail:"title":"TODO", //chandetail:"descr":"This is the current total number of available channel types.", diff --git a/src/obk_config.h b/src/obk_config.h index f12403009..b78717b44 100644 --- a/src/obk_config.h +++ b/src/obk_config.h @@ -114,6 +114,8 @@ #define ENABLE_DRIVER_NEO6M 1 #define ENABLE_TIME_SUNRISE_SUNSET 1 #define ENABLE_TIME_DST 1 + +#define ENABLE_DRIVER_LTR_ALS 1 #define ENABLE_DRIVER_SM16703P 1 #define ENABLE_DRIVER_PIXELANIM 1