diff --git a/openBeken_win32_mvsc2017.vcxproj b/openBeken_win32_mvsc2017.vcxproj index de06878fa..a75fd973e 100644 --- a/openBeken_win32_mvsc2017.vcxproj +++ b/openBeken_win32_mvsc2017.vcxproj @@ -948,6 +948,7 @@ true + diff --git a/openBeken_win32_mvsc2017.vcxproj.filters b/openBeken_win32_mvsc2017.vcxproj.filters index 5c44ec298..aafa7ae68 100644 --- a/openBeken_win32_mvsc2017.vcxproj.filters +++ b/openBeken_win32_mvsc2017.vcxproj.filters @@ -866,6 +866,9 @@ SelfTest + + SelfTest + diff --git a/src/driver/drv_battery.c b/src/driver/drv_battery.c index c8c06d7aa..af0206341 100644 --- a/src/driver/drv_battery.c +++ b/src/driver/drv_battery.c @@ -22,12 +22,15 @@ static void Batt_Measure() { float batt_ref, batt_res, vref; ADDLOG_INFO(LOG_FEATURE_DRV, "DRV_BATTERY : Measure Battery volt en perc"); g_pin_adc = PIN_FindPinIndexForRole(IOR_BAT_ADC, g_pin_adc); - if (PIN_FindPinIndexForRole(IOR_BAT_Relay, -1) == -1) { + if (PIN_FindPinIndexForRole(IOR_BAT_Relay, -1) == -1 && PIN_FindPinIndexForRole(IOR_BAT_Relay_n, -1) == -1) { g_vdivider = 1; } // if divider equal to 1 then no need for relay activation if (g_vdivider > 1) { - g_pin_rel = PIN_FindPinIndexForRole(IOR_BAT_Relay, g_pin_rel); + g_pin_rel = PIN_FindPinIndexForRole(IOR_BAT_Relay, -1); + if (g_pin_rel == -1) { + g_pin_rel = PIN_FindPinIndexForRole(IOR_BAT_Relay_n, -1); + } channel_rel = g_cfg.pins.channels[g_pin_rel]; } HAL_ADC_Init(g_pin_adc); @@ -65,6 +68,9 @@ static void Batt_Measure() { g_lastbattvoltage = (int)g_battvoltage; ADDLOG_INFO(LOG_FEATURE_DRV, "DRV_BATTERY : battery voltage : %f and percentage %f%%", g_battvoltage, g_battlevel); } +void Simulator_Force_Batt_Measure() { + Batt_Measure(); +} int Battery_lastreading(int type) { diff --git a/src/driver/drv_battery.h b/src/driver/drv_battery.h index 7542d7290..033b9fb58 100644 --- a/src/driver/drv_battery.h +++ b/src/driver/drv_battery.h @@ -1,6 +1,6 @@ // battery public void -int Battery_lastreading(); +int Battery_lastreading(int type); // read last value of battery driver value enum { diff --git a/src/hal/win32/hal_pins_win32.c b/src/hal/win32/hal_pins_win32.c index 3d0cf0475..49d52fc80 100644 --- a/src/hal/win32/hal_pins_win32.c +++ b/src/hal/win32/hal_pins_win32.c @@ -80,6 +80,9 @@ bool SIM_IsPinADC(int index) { return true; return false; } +void SIM_SetIntegerValueADCPin(int index, int v) { + g_simulatedADCValues[index] = v; +} void SIM_SetVoltageOnADCPin(int index, float v) { if (g_pinModes[index] != SIM_PIN_ADC) return; @@ -90,7 +93,7 @@ void SIM_SetVoltageOnADCPin(int index, float v) { v = 0; float f = v / 3.3f; int iVal = f * 1024; - g_simulatedADCValues[index] = iVal; + SIM_SetIntegerValueADCPin(index, v); } int SIM_GetPWMValue(int index) { return g_simulatedPWMs[index]; diff --git a/src/httpserver/hass.c b/src/httpserver/hass.c index 2bc402dcc..05ff770cc 100644 --- a/src/httpserver/hass.c +++ b/src/httpserver/hass.c @@ -67,6 +67,9 @@ void hass_populate_unique_id(ENTITY_TYPE type, int index, char* uniq_id) { case CO2_SENSOR: sprintf(uniq_id, "%s_%s_%d", longDeviceName, "co2", index); break; + case SMOKE_SENSOR: + sprintf(uniq_id, "%s_%s_%d", longDeviceName, "smoke", index); + break; case TVOC_SENSOR: sprintf(uniq_id, "%s_%s_%d", longDeviceName, "tvoc", index); break; @@ -116,6 +119,7 @@ void hass_populate_device_config_channel(ENTITY_TYPE type, char* uniq_id, HassDe case BINARY_SENSOR: sprintf(info->channel, "binary_sensor/%s/config", uniq_id); break; + case SMOKE_SENSOR: case CO2_SENSOR: case TVOC_SENSOR: case POWER_SENSOR: @@ -212,6 +216,10 @@ HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, char* payload isSensor = true; sprintf(g_hassBuffer, "%s CO2", CFG_GetShortDeviceName()); break; + case SMOKE_SENSOR: + isSensor = true; + sprintf(g_hassBuffer, "%s Smoke", CFG_GetShortDeviceName()); + break; case TVOC_SENSOR: isSensor = true; sprintf(g_hassBuffer, "%s Tvoc", CFG_GetShortDeviceName()); @@ -472,6 +480,13 @@ HassDeviceInfo* hass_init_sensor_device_info(ENTITY_TYPE type, int channel, int sprintf(g_hassBuffer, "~/%d/get", channel); cJSON_AddStringToObject(info->root, "stat_t", g_hassBuffer); break; + case SMOKE_SENSOR: + // there is no "smoke" class! + //cJSON_AddStringToObject(info->root, "dev_cla", "smoke"); + cJSON_AddStringToObject(info->root, "unit_of_meas", "%"); + sprintf(g_hassBuffer, "~/%d/get", channel); + cJSON_AddStringToObject(info->root, "stat_t", g_hassBuffer); + break; case CO2_SENSOR: cJSON_AddStringToObject(info->root, "dev_cla", "carbon_dioxide"); cJSON_AddStringToObject(info->root, "unit_of_meas", "ppm"); diff --git a/src/httpserver/hass.h b/src/httpserver/hass.h index df23bac80..f6fc1a345 100644 --- a/src/httpserver/hass.h +++ b/src/httpserver/hass.h @@ -58,6 +58,8 @@ typedef enum { /// @brief CUSTOM_SENSOR, /// @brief + SMOKE_SENSOR, + /// @brief READONLYLOWMIDHIGH_SENSOR, } ENTITY_TYPE; diff --git a/src/httpserver/http_fns.c b/src/httpserver/http_fns.c index dbc1ed511..21dee98f2 100644 --- a/src/httpserver/http_fns.c +++ b/src/httpserver/http_fns.c @@ -1825,6 +1825,15 @@ void doHomeAssistantDiscovery(const char* topic, http_request_t* request) { discoveryQueued = true; } break; + case ChType_SmokePercent: + { + dev_info = hass_init_sensor_device_info(SMOKE_SENSOR, i, -1, -1); + MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); + hass_free_device_info(dev_info); + + discoveryQueued = true; + } + break; case ChType_ReadOnly: { dev_info = hass_init_sensor_device_info(CUSTOM_SENSOR, i, -1, -1); diff --git a/src/httpserver/new_http.c b/src/httpserver/new_http.c index 1a94a072d..c5b91a5b4 100644 --- a/src/httpserver/new_http.c +++ b/src/httpserver/new_http.c @@ -458,7 +458,7 @@ const char* htmlPinRoleNames[] = { "TM1638_CLK", "TM1638_DAT", "TM1638_STB", - "error", + "BAT_Relay_n", "error", "error", }; diff --git a/src/new_pins.c b/src/new_pins.c index c39bc9347..238821bfe 100644 --- a/src/new_pins.c +++ b/src/new_pins.c @@ -544,6 +544,7 @@ void CHANNEL_SetAll(int iVal, int iFlags) { case IOR_LED: case IOR_LED_n: case IOR_BAT_Relay: + case IOR_BAT_Relay_n: case IOR_Relay: case IOR_Relay_n: CHANNEL_Set(g_cfg.pins.channels[i], iVal, iFlags); @@ -657,6 +658,7 @@ void PIN_SetPinRoleForPinIndex(int index, int role) { case IOR_LED: case IOR_LED_n: case IOR_BAT_Relay: + case IOR_BAT_Relay_n: case IOR_Relay: case IOR_Relay_n: case IOR_LED_WIFI: @@ -795,6 +797,7 @@ void PIN_SetPinRoleForPinIndex(int index, int role) { case IOR_LED: case IOR_LED_n: case IOR_BAT_Relay: + case IOR_BAT_Relay_n: case IOR_Relay: case IOR_Relay_n: { @@ -805,7 +808,7 @@ void PIN_SetPinRoleForPinIndex(int index, int role) { channelValue = g_channelValues[channelIndex]; HAL_PIN_Setup_Output(index); - if (role == IOR_LED_n || role == IOR_Relay_n) { + if (role == IOR_LED_n || role == IOR_Relay_n || role == IOR_BAT_Relay_n) { HAL_PIN_SetOutputValue(index, !channelValue); } else { @@ -932,7 +935,7 @@ static void Channel_OnChanged(int ch, int prevValue, int iFlags) { if (g_cfg.pins.roles[i] == IOR_Relay || g_cfg.pins.roles[i] == IOR_BAT_Relay || g_cfg.pins.roles[i] == IOR_LED) { RAW_SetPinValue(i, bOn); } - else if (g_cfg.pins.roles[i] == IOR_Relay_n || g_cfg.pins.roles[i] == IOR_LED_n) { + else if (g_cfg.pins.roles[i] == IOR_Relay_n || g_cfg.pins.roles[i] == IOR_LED_n || g_cfg.pins.roles[i] == IOR_BAT_Relay_n) { RAW_SetPinValue(i, !bOn); } else if (g_cfg.pins.roles[i] == IOR_PWM) { @@ -1310,7 +1313,7 @@ bool CHANNEL_ShouldBePublished(int ch) { if (g_cfg.pins.channels[i] == ch) { if (role == IOR_Relay || role == IOR_Relay_n || role == IOR_LED || role == IOR_LED_n - || role == IOR_ADC || role == IOR_BAT_ADC || role == IOR_BAT_Relay + || role == IOR_ADC || role == IOR_BAT_ADC || role == IOR_CHT8305_DAT || role == IOR_SHT3X_DAT || role == IOR_SGP_DAT || role == IOR_DigitalInput || role == IOR_DigitalInput_n || role == IOR_DoorSensorWithDeepSleep || role == IOR_DoorSensorWithDeepSleep_NoPup @@ -1351,6 +1354,7 @@ int CHANNEL_GetRoleForOutputChannel(int ch) { if (g_cfg.pins.channels[i] == ch) { switch (g_cfg.pins.roles[i]) { case IOR_BAT_Relay: + case IOR_BAT_Relay_n: case IOR_Relay: case IOR_Relay_n: case IOR_LED: @@ -1790,7 +1794,7 @@ const char* g_channelTypeNames[] = { "Custom", "Power_div10", "ReadOnlyLowMidHigh", - "error", + "SmokePercent", "error", "error", "error", @@ -1917,7 +1921,6 @@ void PIN_get_Relay_PWM_Count(int* relayCount, int* pwmCount, int* dInputCount) { for (i = 0; i < PLATFORM_GPIO_MAX; i++) { int role = PIN_GetPinRoleForPinIndex(i); switch (role) { - case IOR_BAT_Relay: case IOR_Relay: case IOR_Relay_n: case IOR_LED: @@ -1983,7 +1986,7 @@ int h_isChannelRelay(int tg_ch) { if (tg_ch != ch) continue; role = PIN_GetPinRoleForPinIndex(i); - if (role == IOR_BAT_Relay || role == IOR_Relay || role == IOR_Relay_n || role == IOR_LED || role == IOR_LED_n) { + if (role == IOR_Relay || role == IOR_Relay_n || role == IOR_LED || role == IOR_LED_n) { return true; } if ((role == IOR_BridgeForward) || (role == IOR_BridgeReverse)) diff --git a/src/new_pins.h b/src/new_pins.h index 64e53bab1..b733cc29a 100644 --- a/src/new_pins.h +++ b/src/new_pins.h @@ -530,12 +530,20 @@ typedef enum ioRole_e { //iodetail:"file":"new_pins.h", //iodetail:"driver":""} IOR_TM1638_STB, + //iodetail:{"name":"BAT_Relay_n", + //iodetail:"title":"TODO", + //iodetail:"descr":"Like BAT_Relay, but inversed. See [battery driver topic here](https://www.elektroda.com/rtvforum/topic3959103.html)", + //iodetail:"enum":"IOR_BAT_Relay_n", + //iodetail:"file":"new_pins.h", + //iodetail:"driver":""} + IOR_BAT_Relay_n, //iodetail:{"name":"Total_Options", //iodetail:"title":"TODO", //iodetail:"descr":"Current total number of available IOR roles", //iodetail:"enum":"IOR_Total_Options", //iodetail:"file":"new_pins.h", //iodetail:"driver":""} + IOR_Total_Options, } ioRole_t; @@ -803,6 +811,14 @@ typedef enum channelType_e { //chandetail:"file":"new_pins.h", //chandetail:"driver":""} ChType_ReadOnlyLowMidHigh, + //chandetail:{"name":"SmokePercent", + //chandetail:"title":"TODO", + //chandetail:"descr":"Smoke percentage", + //chandetail:"enum":"ChType_SmokePercent", + //chandetail:"file":"new_pins.h", + //chandetail:"driver":""} + ChType_SmokePercent, + //chandetail:{"name":"Max", //chandetail:"title":"TODO", //chandetail:"descr":"This is the current total number of available channel types.", diff --git a/src/selftest/selftest_batteryDriver.c b/src/selftest/selftest_batteryDriver.c new file mode 100644 index 000000000..60ad6d7d4 --- /dev/null +++ b/src/selftest/selftest_batteryDriver.c @@ -0,0 +1,50 @@ +#ifdef WINDOWS + +#include "selftest_local.h" +#include "../driver/drv_battery.h" + +// P23 is ADC, it is connected to two resistors, both 1k, +// one is connected to P26, second to VDD +void Test_Battery_SmokeSensor() { + // reset whole device + SIM_ClearOBK(0); + + PIN_SetPinRoleForPinIndex(26, IOR_BAT_Relay_n); + // some random unused channel + PIN_SetPinChannelForPinIndex(26, 5); + + PIN_SetPinRoleForPinIndex(23, IOR_BAT_ADC); + PIN_SetPinChannelForPinIndex(23, 5); + + CMD_ExecuteCommand("startDriver Battery", 0); + // Battery 1.997V - raw ADC 1714 + // Battery 2.998V - raw ADC 2588 + // args minbatt and maxbatt in mv. optional V_divider(2), Vref(default 2400) and ADC bits(4096) + CMD_ExecuteCommand("Battery_Setup 2000 3000 2 2400 4096", 0); + + // Battery 2.998V - raw ADC 2588 - 100% + SIM_SetIntegerValueADCPin(23, 2588); + + Simulator_Force_Batt_Measure(); + + // assert: current ,expected, max difference, the voltage is in mV + SELFTEST_ASSERT_FLOATCOMPAREEPSILON(Battery_lastreading(OBK_BATT_VOLTAGE), 2998, 100); + // assert: current ,expected, max difference + SELFTEST_ASSERT_FLOATCOMPAREEPSILON(Battery_lastreading(OBK_BATT_LEVEL), 100, 5.0f); + + // Battery 1.997V - raw ADC 1714 - 0% + SIM_SetIntegerValueADCPin(23, 1714); + + Simulator_Force_Batt_Measure(); + + // assert: current ,expected, max difference, the voltage is in mV + SELFTEST_ASSERT_FLOATCOMPAREEPSILON(Battery_lastreading(OBK_BATT_VOLTAGE), 1997, 100); + // assert: current ,expected, max difference + SELFTEST_ASSERT_FLOATCOMPAREEPSILON(Battery_lastreading(OBK_BATT_LEVEL), 0, 5.0f); +} +void Test_Battery() { + Test_Battery_SmokeSensor(); + +} + +#endif diff --git a/src/selftest/selftest_local.h b/src/selftest/selftest_local.h index f1baba9d4..e99f11751 100644 --- a/src/selftest/selftest_local.h +++ b/src/selftest/selftest_local.h @@ -14,6 +14,7 @@ void SelfTest_Failed(const char *file, const char *function, int line, const cha SelfTest_Failed(__FILE__, __FUNCTION__, __LINE__, #expr) #define SELFTEST_ASSERT_FLOATCOMPARE(exp, res) SELFTEST_ASSERT(Float_Equals(exp, res)); +#define SELFTEST_ASSERT_FLOATCOMPAREEPSILON(exp, res, eps) SELFTEST_ASSERT(Float_EqualsEpsilon(exp, res, eps)); #define SELFTEST_ASSERT_EXPRESSION(exp, res) SELFTEST_ASSERT(Float_Equals(CMD_EvaluateExpression(exp,0), res)); #define SELFTEST_ASSERT_CHANNEL(channelIndex, res) SELFTEST_ASSERT(Float_Equals(CHANNEL_Get(channelIndex), res)); #define SELFTEST_ASSERT_CHANNELTYPE(channelIndex, res) SELFTEST_ASSERT(CHANNEL_GetType(channelIndex)==res); @@ -43,6 +44,10 @@ inline bool Float_Equals(float a, float b) { float res = fabs(a - b); return res < 0.001f; } +inline bool Float_EqualsEpsilon(float a, float b, float epsilon) { + float res = fabs(a - b); + return res < epsilon; +} #define VA_BUFFER_SIZE 4096 #define VA_COUNT 4 @@ -62,7 +67,7 @@ inline const char *va(const char *fmt, ...) { return p; } - +void Test_Battery(); void Test_Flash_Search(); void Test_JSON_Lib(); void Test_Commands_Startup(); diff --git a/src/sim/sim_import.h b/src/sim/sim_import.h index ef1ba5c9b..e85fec945 100644 --- a/src/sim/sim_import.h +++ b/src/sim/sim_import.h @@ -9,6 +9,7 @@ extern "C" { bool SIM_IsPinPWM(int index); bool SIM_IsPinADC(int index); void SIM_SetVoltageOnADCPin(int index, float v); + void SIM_SetIntegerValueADCPin(int index, int v); int SIM_GetPWMValue(int index); // flash control simulation void SIM_SetupFlashFileReading(const char *flashPath); diff --git a/src/win_main.c b/src/win_main.c index 3a303437b..3fe10048e 100644 --- a/src/win_main.c +++ b/src/win_main.c @@ -127,6 +127,8 @@ void SIM_ClearOBK(const char *flashPath) { Main_Init(); } void Win_DoUnitTests() { + + Test_Battery(); Test_TuyaMCU_BatteryPowered(); Test_JSON_Lib(); Test_MQTT_Get_LED_EnableAll();