Kept branch version of /esp32test/Esp32DeepSleepTest/src/main.cpp
This commit is contained in:
commit
7eda866a91
@ -25,6 +25,7 @@
|
||||
"fstream": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp"
|
||||
"system_error": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,51 @@
|
||||
/**
|
||||
* @file ControllerConfiguration.h
|
||||
* @author your name (you@domain.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2020-05-30
|
||||
*
|
||||
* @copyright Copyright (c) 2020
|
||||
*
|
||||
* \mainpage Configuration of the controller
|
||||
* @{
|
||||
* Describe the used PINs of the controller
|
||||
*
|
||||
* @subpage Controller
|
||||
*
|
||||
* @subpage Homie
|
||||
*
|
||||
* @subpage Configuration
|
||||
*
|
||||
* There are several modes in the controller
|
||||
* \dot
|
||||
* digraph Operationmode {
|
||||
* ranksep=.75;
|
||||
* poweroff [ label="off" ];
|
||||
* mode1 [ label="Mode 1 - Sensor only", shape=box, width=2 ];
|
||||
* mode2 [ label="Mode 2 - Wifi enabled", shape=box ];
|
||||
* mode3 [ label="Mode 3 - Stay alive", shape=box ];
|
||||
* mode1 -> mode2 [ label="wakeup reason", fontsize=10 ];
|
||||
* mode1 -> mode2 [ label="Time duration", fontsize=10 ];
|
||||
* mode2 -> mode3 [ label="Over the Air Update", fontsize=10 ];
|
||||
* mode3 -> mode2 [ label="Over the Air Finished", fontsize=10 ];
|
||||
* mode3 -> mode2 [ label="Mqtt Command", fontsize=10 ];
|
||||
* mode2 -> mode3 [ label="Mqtt Command", fontsize=10 ];
|
||||
* poweroff -> mode1 [ label="deep sleep wakeup", fontsize=10 ];
|
||||
* mode1 -> poweroff [ label="enter deep sleep", fontsize=10 ];
|
||||
* mode2 -> poweroff [ label="Mqtt queue empty", fontsize=10 ];
|
||||
* }
|
||||
* \enddot
|
||||
*
|
||||
* Before entering Deep sleep the controller is configured with an wakeup time.
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
#ifndef CONTROLLER_CONFIG_H
|
||||
#define CONTROLLER_CONFIG_H
|
||||
|
||||
/** \addtogroup GPIO Settings
|
||||
* @{
|
||||
*/
|
||||
#define SENSOR_PLANT0 GPIO_NUM_32 /**< GPIO 32 (ADC1) */
|
||||
#define SENSOR_PLANT1 GPIO_NUM_33 /**< GPIO 33 (ADC1) */
|
||||
#define SENSOR_PLANT2 GPIO_NUM_25 /**< GPIO 25 (ADC2) */
|
||||
@ -21,8 +66,8 @@
|
||||
#define OUTPUT_ENABLE_PUMP GPIO_NUM_13 /**< GPIO 13 - Enable Pumps */
|
||||
|
||||
#define SENSOR_ONEWIRE GPIO_NUM_4 /**< GPIO 12 - Temperatur sensor, Battery and other cool onewire stuff */
|
||||
#define SHARED_SCL GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */
|
||||
#define SENSOR_TANK_SDA GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */
|
||||
#define SENSOR_TANK_SDA GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */
|
||||
#define SENSOR_TANK_SCL GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */
|
||||
#define BUTTON GPIO_NUM_0 /**< GPIO 0 - Fix button of NodeMCU */
|
||||
|
||||
#define CUSTOM1_PIN1 GPIO_NUM_34 /** direct gpio */
|
||||
@ -32,20 +77,28 @@
|
||||
|
||||
#define I2C1_SDA GPIO_NUM_34 /**< GPIO 34 - I2C */
|
||||
#define I2C1_SCL GPIO_NUM_35 /**< GPIO 35 - I2C */
|
||||
/* @} */
|
||||
|
||||
#define FIRMWARE_VERSION "sw2.201 hw0.10b"
|
||||
/** \addtogroup Configuration
|
||||
* @{
|
||||
*/
|
||||
#define CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
|
||||
#define CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
|
||||
|
||||
#define FIRMWARE_VERSION "2.4 HW0.10b"
|
||||
|
||||
#define TIMED_LIGHT_PIN CUSTOM1_PIN5
|
||||
#define FLOWMETER_PIN CUSTOM1_PIN1
|
||||
#ifdef FLOWMETER_PIN
|
||||
#define FLOWMETER_FLOWFACTOR 23 /** F = 22 * Q;Q = L/min */
|
||||
#define FLOWMETER_PULSES_PER_ML 2.2
|
||||
#endif
|
||||
|
||||
#define MOIST_SENSOR_MAX_FRQ 10000 // 10kHz (as never more then 3000 was measured)
|
||||
#define MOIST_SENSOR_MIN_FRQ 1000 // 1kHz (500Hz margin)
|
||||
#define MOIST_SENSOR_MAX_FRQ 60000 // 60kHz (500Hz margin)
|
||||
#define MOIST_SENSOR_MIN_FRQ 500 // 0.5kHz (500Hz margin)
|
||||
|
||||
#define ANALOG_SENSOR_MAX_MV 4095 /**< Maximum according https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html */
|
||||
#define ANALOG_SENSOR_MIN_MV 100
|
||||
#define ANALOG_SENSOR_MAX_MV 1300 //successive approximation of good range
|
||||
#define ANALOG_SENSOR_MIN_MV 300 //successive approximation of good range
|
||||
|
||||
#define SOLAR_VOLT_FACTOR 11
|
||||
#define BATTSENSOR_INDEX_SOLAR 0
|
||||
@ -65,11 +118,9 @@
|
||||
#define TEMPERATUR_TIMEOUT 3000 /**< 3 Seconds timeout for the temperatur sensors */
|
||||
#define DS18B20_RESOLUTION 9 /**< 9bit temperature resolution -> 0.5°C steps */
|
||||
|
||||
#define PLANT_WITHOUT_TEMPSENSOR 100
|
||||
|
||||
#define UTC_OFFSET_DE 3600 /* UTC offset in seconds for Germany */
|
||||
#define UTF_OFFSET_DE_DST 3600 /* offset in seconds if daylight saving time is active */
|
||||
|
||||
/* @} */
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -88,6 +88,7 @@ HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as d
|
||||
HomieSetting<long> timedLightStart("LightStart", "hour to start light");
|
||||
HomieSetting<long> timedLightEnd("LightEnd", "hour to end light");
|
||||
HomieSetting<bool> timedLightOnlyWhenDark("LightOnlyDark", "only enable light, if solar is low");
|
||||
HomieSetting<long> timedLightPowerLevel("LightPowerLevel", "0-255 power level");
|
||||
#endif // TIMED_LIGHT_PIN
|
||||
|
||||
|
||||
@ -106,11 +107,11 @@ HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as d
|
||||
HomieSetting<long> mPumpAllowedHourRangeStart##plant = HomieSetting<long>("hourstart" strplant, "Plant" strplant " - Range pump allowed hour start (0-23)"); \
|
||||
HomieSetting<long> mPumpAllowedHourRangeEnd##plant = HomieSetting<long>("hourend" strplant, "Plant" strplant " - Range pump allowed hour end (0-23)"); \
|
||||
HomieSetting<bool> mPumpOnlyWhenLowLight##plant = HomieSetting<bool>("lowLight" strplant, "Plant" strplant " - Enable the Pump only, when there is no sunlight"); \
|
||||
HomieSetting<long> mPumpCooldownInMinutes##plant = HomieSetting<long>("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (minutes)"); \
|
||||
HomieSetting<long> mPumpCooldownInSeconds##plant = HomieSetting<long>("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (minutes)"); \
|
||||
HomieSetting<long> pPumpDuration##plant = HomieSetting<long>("pumpDuration" strplant, "Plant" strplant " - time seconds to water when pump is active"); \
|
||||
HomieSetting<long> pPumpMl##plant = HomieSetting<long>("pumpAmount" strplant, "Plant" strplant " - ml (if using flowmeter) to water when pump is active"); \
|
||||
HomieSetting<long> pPowerLevel##plant = HomieSetting<long>("powerLevel" strplant, "Plant" strplant " - pwm duty cycle in percent"); \
|
||||
PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInMinutes##plant, &pPumpDuration##plant, &pPowerLevel##plant, &pPumpMl##plant}; \
|
||||
PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInSeconds##plant, &pPumpDuration##plant, &pPowerLevel##plant, &pPumpMl##plant}; \
|
||||
/**< Generate all settings for one plant \
|
||||
* \
|
||||
* Feature to start pumping only at morning: @link{SOLAR_CHARGE_MIN_VOLTAGE} and @link{SOLAR_CHARGE_MAX_VOLTAGE} \
|
||||
|
@ -22,8 +22,7 @@
|
||||
#define FOREACH_SENSOR(SENSOR) \
|
||||
SENSOR(NONE) \
|
||||
SENSOR(CAPACITIVE_FREQUENCY) \
|
||||
SENSOR(ANALOG_RESISTANCE_PROBE) \
|
||||
SENSOR(SHT20)
|
||||
SENSOR(ANALOG_RESISTANCE_PROBE)
|
||||
|
||||
/**
|
||||
* @}
|
||||
@ -46,6 +45,8 @@ static const char *SENSOR_STRING[] = {
|
||||
#define MISSING_SENSOR -2
|
||||
//plant uses only cooldown and duration, moisture is measured but ignored, allowedHours is ignored (eg. make a 30min on 30min off cycle)
|
||||
#define HYDROPONIC_MODE -3
|
||||
//plant uses cooldown and duration and workhours, moisture is measured but ignored
|
||||
#define TIMER_ONLY -4
|
||||
|
||||
typedef struct PlantSettings_t
|
||||
{
|
||||
|
@ -36,4 +36,5 @@
|
||||
#define LOG_SLEEP_NIGHT 100
|
||||
#define LOG_SLEEP_DAY 101
|
||||
#define LOG_SLEEP_CYCLE 102
|
||||
#define LOG_MISSING_PUMP -4
|
||||
#define LOG_MISSING_PUMP -4
|
||||
#define LOG_BOOT_ERROR_DETECTION 10000
|
@ -75,6 +75,12 @@ public:
|
||||
return SENSOR_STRING[mode];
|
||||
}
|
||||
|
||||
bool isTimerOnly()
|
||||
{
|
||||
long current = this->mSetting->pSensorDry->get();
|
||||
return equalish(current, TIMER_ONLY);
|
||||
}
|
||||
|
||||
bool isHydroponic()
|
||||
{
|
||||
long current = this->mSetting->pSensorDry->get();
|
||||
@ -94,7 +100,7 @@ public:
|
||||
*/
|
||||
bool isPumpRequired()
|
||||
{
|
||||
if (isHydroponic())
|
||||
if (isHydroponic() || isTimerOnly())
|
||||
{
|
||||
// hydroponic only uses timer based controll
|
||||
return true;
|
||||
@ -122,13 +128,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
float getCurrentTemperature(){
|
||||
if(mTemperature_degree.getCount() == 0){
|
||||
return PLANT_WITHOUT_TEMPSENSOR;
|
||||
}
|
||||
return mTemperature_degree.getMedian();
|
||||
}
|
||||
|
||||
float getCurrentMoisturePCT()
|
||||
{
|
||||
switch (getSensorMode())
|
||||
@ -138,9 +137,7 @@ public:
|
||||
case CAPACITIVE_FREQUENCY:
|
||||
return mapf(mMoisture_raw.getMedian(), MOIST_SENSOR_MAX_FRQ, MOIST_SENSOR_MIN_FRQ, 0, 100);
|
||||
case ANALOG_RESISTANCE_PROBE:
|
||||
return mapf(mMoisture_raw.getMedian(), ANALOG_SENSOR_MIN_MV, ANALOG_SENSOR_MAX_MV, 0, 100);
|
||||
case SHT20:
|
||||
return mMoisture_raw.getMedian();
|
||||
return mapf(mMoisture_raw.getMedian(), ANALOG_SENSOR_MAX_MV, ANALOG_SENSOR_MIN_MV, 0, 100);
|
||||
}
|
||||
return MISSING_SENSOR;
|
||||
}
|
||||
@ -210,6 +207,12 @@ public:
|
||||
{
|
||||
return this->mSetting->pPumpDuration->get();
|
||||
}
|
||||
long getPumpMl()
|
||||
{
|
||||
return this->mSetting->pPumpMl->get();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
188
esp32/include/ulp-pwm.h
Normal file
188
esp32/include/ulp-pwm.h
Normal file
@ -0,0 +1,188 @@
|
||||
#ifndef ULP_PWM_h
|
||||
#define ILP_PWM_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "driver/rtc_io.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "esp32/ulp.h"
|
||||
#include "ControllerConfiguration.h"
|
||||
|
||||
#define LBL_START 1
|
||||
#define LBL_DELAY_ON 2
|
||||
#define LBL_DELAY_OFF 3
|
||||
#define LBL_SKIP_ON 4
|
||||
#define LBL_SKIP_OFF 5
|
||||
#define REGISTER_DELAY_LOOP_COUNTER R0
|
||||
#define REGISTER_TICKS_ON R1
|
||||
#define REGISTER_TICKS_OFF R2
|
||||
#define TOTAL_TICKS_DELAY 255
|
||||
#define PIN TIMED_LIGHT_PIN
|
||||
|
||||
//support 20 vars
|
||||
const size_t ulp_var_offset = CONFIG_ULP_COPROC_RESERVE_MEM - 20;
|
||||
//use the first for dimming
|
||||
const size_t ulp_dimm_offset = ulp_var_offset + 1;
|
||||
const size_t ulp_alive_offset = ulp_var_offset + 2;
|
||||
|
||||
//see https://github.com/perseus086/ESP32-notes
|
||||
const uint32_t rtc_bit[40] = {
|
||||
25, //gpio0
|
||||
0, //gpio1
|
||||
26, //gpio2
|
||||
0, //gpio3
|
||||
24, //gpio4
|
||||
0, //gpio5
|
||||
0, //gpio6
|
||||
0, //gpio7
|
||||
0, //gpio8
|
||||
0, //gpio9
|
||||
0, //gpio10
|
||||
0, //gpio11
|
||||
29, //gpio12
|
||||
28, //gpio13
|
||||
30, //gpio14
|
||||
27, //gpio15
|
||||
0, //gpio16
|
||||
31, //gpio17
|
||||
0, //gpio18
|
||||
0, //gpio19
|
||||
0, //gpio20
|
||||
0, //gpio21
|
||||
0, //gpio22
|
||||
0, //gpio23
|
||||
0, //gpio24
|
||||
20, //gpio25
|
||||
21, //gpio26
|
||||
0, //gpio27
|
||||
0, //gpio28
|
||||
0, //gpio29
|
||||
0, //gpio30
|
||||
0, //gpio31
|
||||
23, //gpio32
|
||||
22, //gpio33
|
||||
18, //gpio34
|
||||
19, //gpio35
|
||||
14, //gpio36
|
||||
15, //gpio37
|
||||
16, //gpio38
|
||||
17 //gpio39
|
||||
};
|
||||
|
||||
static inline void ulp_internal_data_write(size_t offset, uint16_t value)
|
||||
{
|
||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
||||
{
|
||||
Serial.print("Invalid ULP offset detected, refusing write!");
|
||||
Serial.print(offset);
|
||||
Serial.print("-");
|
||||
Serial.print(ulp_var_offset);
|
||||
Serial.print("-");
|
||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
RTC_SLOW_MEM[offset] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t ulp_internal_data_read(size_t offset)
|
||||
{
|
||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
||||
{
|
||||
Serial.print("Invalid ULP offset detected");
|
||||
Serial.print(offset);
|
||||
Serial.print("-");
|
||||
Serial.print(ulp_var_offset);
|
||||
Serial.print("-");
|
||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||
}
|
||||
return RTC_SLOW_MEM[offset] & 0xffff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_io_number_get(gpio_num_t gpio_num)
|
||||
{
|
||||
assert(rtc_gpio_is_valid_gpio(gpio_num) && "Invalid GPIO for RTC");
|
||||
uint32_t bit = rtc_bit[gpio_num];
|
||||
Serial.print("Resolved GPIO ");
|
||||
Serial.print(gpio_num);
|
||||
Serial.print(" to rtc bit ");
|
||||
Serial.println(bit);
|
||||
return bit;
|
||||
}
|
||||
|
||||
void ulp_internal_start(void)
|
||||
{
|
||||
rtc_gpio_init(PIN);
|
||||
rtc_gpio_set_direction(PIN, RTC_GPIO_MODE_OUTPUT_ONLY);
|
||||
rtc_gpio_set_level(PIN, 0);
|
||||
const uint32_t rtc_gpio = rtc_io_number_get(PIN);
|
||||
|
||||
// Define ULP program
|
||||
const ulp_insn_t ulp_prog[] = {
|
||||
M_LABEL(LBL_START),
|
||||
|
||||
I_MOVI(REGISTER_DELAY_LOOP_COUNTER, 1),
|
||||
I_MOVI(REGISTER_TICKS_ON, 0),
|
||||
I_ST(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON, ulp_alive_offset), //store 1 with 0 offset into alive
|
||||
|
||||
I_LD(REGISTER_TICKS_ON, REGISTER_TICKS_ON, ulp_dimm_offset), //REGISTER_TICKS_ON = RTC_DATA[0+ulp_dimm_offset]
|
||||
//in total there is always 255 delay loop iterations, but in different duty cycle
|
||||
I_MOVI(REGISTER_TICKS_OFF, TOTAL_TICKS_DELAY),
|
||||
I_SUBR(REGISTER_TICKS_OFF, REGISTER_TICKS_OFF, REGISTER_TICKS_ON),
|
||||
|
||||
//on phase
|
||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON),
|
||||
M_BL(LBL_SKIP_ON, 1), //if never on, skip on phase
|
||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, HIGH), // on
|
||||
M_LABEL(LBL_DELAY_ON),
|
||||
I_DELAY(1), //wait 1 clock
|
||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
||||
M_BGE(LBL_DELAY_ON, 1), //if time left, goto start of on loop
|
||||
M_LABEL(LBL_SKIP_ON),
|
||||
|
||||
//off phase
|
||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_OFF),
|
||||
|
||||
M_BL(LBL_SKIP_OFF, 1), //if never off, skip on phase
|
||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, LOW), // on
|
||||
M_LABEL(3),
|
||||
I_DELAY(1), //wait 1 clock
|
||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
||||
M_BGE(3, 1), //if time left, goto start of on loop
|
||||
M_LABEL(LBL_SKIP_OFF),
|
||||
|
||||
M_BX(LBL_START),
|
||||
};
|
||||
// Run ULP program
|
||||
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
|
||||
assert(size < ulp_var_offset && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
|
||||
esp_err_t error = ulp_process_macros_and_load(0, ulp_prog, &size);
|
||||
Serial.print("ULP bootstrap status ");
|
||||
Serial.println(error);
|
||||
|
||||
//allow glitchless start
|
||||
ulp_internal_data_write(ulp_alive_offset, 0);
|
||||
|
||||
error = ulp_run(0);
|
||||
Serial.print("ULP start status ");
|
||||
Serial.println(error);
|
||||
}
|
||||
|
||||
static inline void ulp_pwm_set_level(uint8_t level)
|
||||
{
|
||||
ulp_internal_data_write(ulp_dimm_offset, level);
|
||||
}
|
||||
|
||||
static inline void ulp_pwm_init()
|
||||
{
|
||||
ulp_internal_data_write(ulp_alive_offset, 0);
|
||||
delay(10);
|
||||
if (ulp_internal_data_read(ulp_alive_offset) == 0)
|
||||
{
|
||||
ulp_internal_start();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -13,13 +13,13 @@ platform = espressif32
|
||||
board = esp32doit-devkit-v1
|
||||
framework = arduino
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
||||
-DPLANT0_SENSORTYPE=SHT20
|
||||
-DPLANT0_SENSORTYPE=ANALOG_RESISTANCE_PROBE
|
||||
-DPLANT1_SENSORTYPE=ANALOG_RESISTANCE_PROBE
|
||||
-DPLANT2_SENSORTYPE=CAPACITIVE_FREQUENCY
|
||||
-DPLANT3_SENSORTYPE=CAPACITIVE_FREQUENCY
|
||||
-DPLANT4_SENSORTYPE=CAPACITIVE_FREQUENCY
|
||||
-DPLANT5_SENSORTYPE=CAPACITIVE_FREQUENCY
|
||||
-DPLANT6_SENSORTYPE=CAPACITIVE_FREQUENCY
|
||||
-DPLANT2_SENSORTYPE=ANALOG_RESISTANCE_PROBE
|
||||
-DPLANT3_SENSORTYPE=ANALOG_RESISTANCE_PROBE
|
||||
-DPLANT4_SENSORTYPE=ANALOG_RESISTANCE_PROBE
|
||||
-DPLANT5_SENSORTYPE=ANALOG_RESISTANCE_PROBE
|
||||
-DPLANT6_SENSORTYPE=ANALOG_RESISTANCE_PROBE
|
||||
board_build.partitions = defaultWithSmallerSpiffs.csv
|
||||
|
||||
|
||||
@ -33,4 +33,4 @@ lib_deps = ArduinoJson@6.16.1
|
||||
|
||||
[platformio]
|
||||
|
||||
extra_configs = custom_platformio.ini
|
||||
extra_configs = custom_platformio.ini
|
||||
|
@ -31,7 +31,7 @@ void Plant::init(void)
|
||||
/* Initialize Home Settings validator */
|
||||
this->mSetting->pSensorDry->setDefaultValue(DEACTIVATED_PLANT);
|
||||
this->mSetting->pSensorDry->setValidator([](long candidate)
|
||||
{ return (((candidate >= 0.0) && (candidate <= 100.0)) || equalish(candidate, DEACTIVATED_PLANT) || equalish(candidate, HYDROPONIC_MODE)); });
|
||||
{ return (((candidate >= 0.0) && (candidate <= 100.0)) || equalish(candidate, DEACTIVATED_PLANT) || equalish(candidate, HYDROPONIC_MODE) || equalish(candidate, TIMER_ONLY)); });
|
||||
|
||||
this->mSetting->pPumpAllowedHourRangeStart->setDefaultValue(8); // start at 8:00
|
||||
this->mSetting->pPumpAllowedHourRangeStart->setValidator([](long candidate)
|
||||
@ -39,7 +39,7 @@ void Plant::init(void)
|
||||
this->mSetting->pPumpAllowedHourRangeEnd->setDefaultValue(20); // stop pumps at 20:00
|
||||
this->mSetting->pPumpAllowedHourRangeEnd->setValidator([](long candidate)
|
||||
{ return ((candidate >= 0) && (candidate <= 23)); });
|
||||
this->mSetting->pPumpOnlyWhenLowLight->setDefaultValue(true);
|
||||
this->mSetting->pPumpOnlyWhenLowLight->setDefaultValue(false);
|
||||
this->mSetting->pPumpCooldownInSeconds->setDefaultValue(60 * 60); // 1 hour
|
||||
this->mSetting->pPumpCooldownInSeconds->setValidator([](long candidate)
|
||||
{ return (candidate >= 0); });
|
||||
@ -47,7 +47,7 @@ void Plant::init(void)
|
||||
this->mSetting->pPumpDuration->setDefaultValue(30);
|
||||
this->mSetting->pPumpDuration->setValidator([](long candidate)
|
||||
{ return ((candidate >= 0) && (candidate <= 1000)); });
|
||||
this->mSetting->pPumpMl->setDefaultValue(0);
|
||||
this->mSetting->pPumpMl->setDefaultValue(1000);
|
||||
this->mSetting->pPumpMl->setValidator([](long candidate)
|
||||
{ return ((candidate >= 0) && (candidate <= 5000)); });
|
||||
this->mSetting->pPumpPowerLevel->setDefaultValue(100);
|
||||
@ -91,7 +91,6 @@ void Plant::initSensors(void)
|
||||
adcAttachPin(this->mPinSensor);
|
||||
break;
|
||||
}
|
||||
case SHT20:
|
||||
case NONE:
|
||||
{
|
||||
// do nothing
|
||||
@ -113,34 +112,6 @@ void Plant::blockingMoistureMeasurement(void)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SHT20:
|
||||
{
|
||||
|
||||
//do not assume valid i2c state, reinit
|
||||
TwoWire sensorWire = TwoWire(0);
|
||||
sensorWire.setPins(SENSOR_TANK_SDA, SHARED_SCL);
|
||||
sht20.begin(&sensorWire);
|
||||
sht20.reset();
|
||||
delay(100);
|
||||
if(!sht20.isConnected()){
|
||||
Serial.println("SHT20 connection error");
|
||||
}
|
||||
bool success = sht20.read();
|
||||
int error = sht20.getError();
|
||||
if (error)
|
||||
{
|
||||
log(LOG_LEVEL_ERROR, "Failure reading SHT20 " + String(error), LOG_SENSOR_MISSING);
|
||||
}
|
||||
if (!success || error)
|
||||
{
|
||||
this->mMoisture_raw.clear();
|
||||
this->mMoisture_raw.add(MISSING_SENSOR);
|
||||
return;
|
||||
}
|
||||
mMoisture_raw.add(sht20.getHumidity());
|
||||
mTemperature_degree.add(sht20.getTemperature());
|
||||
break;
|
||||
}
|
||||
case CAPACITIVE_FREQUENCY:
|
||||
case NONE:
|
||||
{
|
||||
@ -160,7 +131,6 @@ void Plant::startMoistureMeasurement(void)
|
||||
pcnt_counter_resume(unit);
|
||||
break;
|
||||
}
|
||||
case SHT20:
|
||||
case ANALOG_RESISTANCE_PROBE:
|
||||
case NONE:
|
||||
{
|
||||
@ -192,7 +162,6 @@ void Plant::stopMoistureMeasurement(void)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SHT20:
|
||||
case ANALOG_RESISTANCE_PROBE:
|
||||
case NONE:
|
||||
{
|
||||
|
@ -1,18 +1,18 @@
|
||||
/** \addtogroup Controller
|
||||
* @{
|
||||
*
|
||||
*
|
||||
* @file main.cpp
|
||||
* @author Ollo
|
||||
* @brief PlantControl
|
||||
* @version 0.1
|
||||
* @date 2020-05-01
|
||||
*
|
||||
*
|
||||
* @copyright Copyright (c) 2020
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* INCLUDES
|
||||
******************************************************************************/
|
||||
******************************************************************************/
|
||||
#include "LogDefines.h"
|
||||
#include "FileUtils.h"
|
||||
#include "TimeUtils.h"
|
||||
@ -35,28 +35,32 @@
|
||||
#include <VL53L0X.h>
|
||||
#include "driver/pcnt.h"
|
||||
#include "MQTTUtils.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#if defined(TIMED_LIGHT_PIN)
|
||||
#include "ulp-pwm.h"
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* DEFINES
|
||||
******************************************************************************/
|
||||
******************************************************************************/
|
||||
#define AMOUNT_SENOR_QUERYS 8
|
||||
#define MAX_TANK_DEPTH 2000
|
||||
#define REBOOT_LOOP_DETECTION_ERROR 5
|
||||
|
||||
/******************************************************************************
|
||||
* FUNCTION PROTOTYPES
|
||||
******************************************************************************/
|
||||
******************************************************************************/
|
||||
|
||||
int determineNextPump(bool lowLight);
|
||||
void plantcontrol();
|
||||
void readPowerSwitchedSensors();
|
||||
bool determineTimedLightState(bool lowLight);
|
||||
bool otaRunning = false;
|
||||
|
||||
/******************************************************************************
|
||||
* NON VOLATILE VARIABLES in DEEP SLEEP
|
||||
******************************************************************************/
|
||||
|
||||
******************************************************************************/
|
||||
#if defined(TIMED_LIGHT_PIN)
|
||||
RTC_DATA_ATTR bool timedLightOn = false; /**< allow fast recovery after poweron */
|
||||
RTC_DATA_ATTR bool timedLightLowVoltageTriggered = false; /**remember if it was shut down due to voltage level */
|
||||
#endif // TIMED_LIGHT_PIN
|
||||
|
||||
@ -65,7 +69,7 @@ RTC_DATA_ATTR long consecutiveWateringPlant[MAX_PLANTS] = {0};
|
||||
|
||||
/******************************************************************************
|
||||
* LOCAL VARIABLES
|
||||
******************************************************************************/
|
||||
******************************************************************************/
|
||||
bool volatile mDownloadMode = false; /**< Controller must not sleep */
|
||||
bool volatile mSensorsRead = false; /**< Sensors are read without Wifi or MQTT */
|
||||
int volatile pumpToRun = -1; /** pump to run at the end of the cycle */
|
||||
@ -80,6 +84,7 @@ unsigned long setupFinishedTimestamp;
|
||||
|
||||
bool pumpStarted = false;
|
||||
long pumpTarget = -1;
|
||||
long pumpStartTime = 0;
|
||||
long lastSendPumpUpdate = 0;
|
||||
#ifdef FLOWMETER_PIN
|
||||
long pumpTargetMl = -1;
|
||||
@ -125,13 +130,30 @@ Plant mPlants[MAX_PLANTS] = {
|
||||
|
||||
/******************************************************************************
|
||||
* LOCAL FUNCTIONS
|
||||
******************************************************************************/
|
||||
******************************************************************************/
|
||||
|
||||
void finsihedCycleSucessfully()
|
||||
{
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_ota_img_states_t ota_state;
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
|
||||
{
|
||||
log(LOG_LEVEL_INFO, "Get State Partition was Successfull", LOG_BOOT_ERROR_DETECTION);
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
|
||||
{
|
||||
log(LOG_LEVEL_INFO, "Diagnostics completed successfully! Marking as valid", LOG_BOOT_ERROR_DETECTION);
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void espDeepSleep(bool afterPump = false)
|
||||
{
|
||||
if (mDownloadMode)
|
||||
{
|
||||
log(LOG_LEVEL_DEBUG, "abort deepsleep, DownloadMode active", LOG_DEBUG_CODE);
|
||||
// if we manage to get to the download mode, the device can be restored
|
||||
finsihedCycleSucessfully();
|
||||
return;
|
||||
}
|
||||
if (aliveWasRead())
|
||||
@ -154,17 +176,7 @@ void espDeepSleep(bool afterPump = false)
|
||||
}
|
||||
}
|
||||
|
||||
//allo hold for all digital pins
|
||||
gpio_deep_sleep_hold_en();
|
||||
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON);
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON);
|
||||
|
||||
#if defined(TIMED_LIGHT_PIN)
|
||||
gpio_hold_en(TIMED_LIGHT_PIN);
|
||||
#endif // TIMED_LIGHT_PIN
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
long secondsToSleep = -1;
|
||||
|
||||
@ -187,6 +199,7 @@ void espDeepSleep(bool afterPump = false)
|
||||
}
|
||||
}
|
||||
|
||||
finsihedCycleSucessfully();
|
||||
esp_sleep_enable_timer_wakeup((secondsToSleep * 1000U * 1000U));
|
||||
if (aliveWasRead())
|
||||
{
|
||||
@ -199,7 +212,7 @@ void espDeepSleep(bool afterPump = false)
|
||||
}
|
||||
}
|
||||
|
||||
//requires homie being started
|
||||
// requires homie being started
|
||||
void readOneWireSensors()
|
||||
{
|
||||
for (uint8_t i = 0; i < sensors.getDeviceCount(); i++)
|
||||
@ -227,7 +240,7 @@ void readOneWireSensors()
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
//wrong family or crc errors on each retry
|
||||
// wrong family or crc errors on each retry
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -302,30 +315,22 @@ void readPowerSwitchedSensors()
|
||||
Plant plant = mPlants[i];
|
||||
switch (plant.getSensorMode())
|
||||
{
|
||||
case CAPACITIVE_FREQUENCY: {
|
||||
Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " hz " << mPlants[i].getCurrentMoisturePCT() << "%" << endl;
|
||||
break;
|
||||
}
|
||||
case ANALOG_RESISTANCE_PROBE : {
|
||||
Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " mV " << mPlants[i].getCurrentMoisturePCT() << "%" << endl;
|
||||
break;
|
||||
}
|
||||
case SHT20:{
|
||||
Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentTemperature() << "°C " << mPlants[i].getCurrentMoisturePCT() << "rH%" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
case NONE : {
|
||||
|
||||
}
|
||||
case CAPACITIVE_FREQUENCY:
|
||||
{
|
||||
Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " hz " << mPlants[i].getCurrentMoisturePCT() << "%" << endl;
|
||||
break;
|
||||
}
|
||||
case ANALOG_RESISTANCE_PROBE:
|
||||
{
|
||||
Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " mV " << mPlants[i].getCurrentMoisturePCT() << "%" << endl;
|
||||
break;
|
||||
}
|
||||
case NONE:
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//do not assume valid i2c state, force release of hardware
|
||||
Wire = TwoWire(0);
|
||||
Wire.setPins(SENSOR_TANK_SDA, SHARED_SCL);
|
||||
Wire.begin();
|
||||
|
||||
waterRawSensor.clear();
|
||||
tankSensor.setTimeout(500);
|
||||
long start = millis();
|
||||
@ -398,10 +403,7 @@ void onHomieEvent(const HomieEvent &event)
|
||||
{
|
||||
mPlants[i].deactivatePump();
|
||||
}
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
|
||||
digitalWrite(OUTPUT_ENABLE_PUMP, HIGH);
|
||||
delay(100);
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1);
|
||||
otaRunning = true;
|
||||
mDownloadMode = true;
|
||||
break;
|
||||
case HomieEventType::OTA_SUCCESSFUL:
|
||||
@ -458,7 +460,7 @@ int determineNextPump(bool isLowLight)
|
||||
log(LOG_LEVEL_DEBUG, String(String(i) + " No pump required: due to light"), LOG_DEBUG_CODE);
|
||||
continue;
|
||||
}
|
||||
if (!plant.isHydroponic())
|
||||
if (! (plant.isHydroponic() || plant.isTimerOnly()))
|
||||
{
|
||||
if (equalish(plant.getCurrentMoistureRaw(), MISSING_SENSOR))
|
||||
{
|
||||
@ -485,10 +487,17 @@ int determineNextPump(bool isLowLight)
|
||||
}
|
||||
else
|
||||
{
|
||||
plant.publishState("active");
|
||||
if (mDownloadMode)
|
||||
{
|
||||
plant.publishState("active+supressed");
|
||||
}
|
||||
else
|
||||
{
|
||||
plant.publishState("active");
|
||||
}
|
||||
}
|
||||
|
||||
if (!plant.isHydroponic())
|
||||
if (! (plant.isHydroponic() || plant.isTimerOnly()))
|
||||
{
|
||||
consecutiveWateringPlant[i]++;
|
||||
}
|
||||
@ -514,7 +523,7 @@ int determineNextPump(bool isLowLight)
|
||||
else
|
||||
{
|
||||
plant.publishState("wet");
|
||||
//plant was detected as wet, remove consecutive count
|
||||
// plant was detected as wet, remove consecutive count
|
||||
consecutiveWateringPlant[i] = 0;
|
||||
}
|
||||
}
|
||||
@ -523,7 +532,7 @@ int determineNextPump(bool isLowLight)
|
||||
|
||||
/**
|
||||
* @brief Handle Mqtt commands to keep controller alive
|
||||
*
|
||||
*
|
||||
* @param range multiple transmitted values (not used for this function)
|
||||
* @param value single value
|
||||
* @return true when the command was parsed and executed succuessfully
|
||||
@ -600,12 +609,12 @@ bool switch7(const HomieRange &range, const String &value)
|
||||
|
||||
void initPumpLogic()
|
||||
{
|
||||
//set targets
|
||||
// set targets
|
||||
|
||||
#ifdef FLOWMETER_PIN
|
||||
pumpTargetMl = mPlants[pumpToRun].getPumpDuration();
|
||||
pumpTargetMl = mPlants[pumpToRun].getPumpMl();
|
||||
|
||||
//0-6 are used for moisture measurment
|
||||
// 0-6 are used for moisture measurment
|
||||
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_7);
|
||||
pcnt_config_t pcnt_config = {}; // Instancia PCNT config
|
||||
pcnt_config.pulse_gpio_num = FLOWMETER_PIN; // Configura GPIO para entrada dos pulsos
|
||||
@ -613,8 +622,8 @@ void initPumpLogic()
|
||||
pcnt_config.unit = unit; // Unidade de contagem PCNT - 0
|
||||
pcnt_config.channel = PCNT_CHANNEL_0; // Canal de contagem PCNT - 0
|
||||
pcnt_config.counter_h_lim = INT16_MAX; // Limite maximo de contagem - 20000
|
||||
pcnt_config.pos_mode = PCNT_COUNT_DIS; // Incrementa contagem na subida do pulso
|
||||
pcnt_config.neg_mode = PCNT_COUNT_INC; // Incrementa contagem na descida do pulso
|
||||
pcnt_config.pos_mode = PCNT_COUNT_INC; // Incrementa contagem na subida do pulso
|
||||
pcnt_config.neg_mode = PCNT_COUNT_DIS; // Incrementa contagem na descida do pulso
|
||||
pcnt_config.lctrl_mode = PCNT_MODE_KEEP; // PCNT - modo lctrl desabilitado
|
||||
pcnt_config.hctrl_mode = PCNT_MODE_KEEP; // PCNT - modo hctrl - se HIGH conta incrementando
|
||||
pcnt_unit_config(&pcnt_config); // Configura o contador PCNT
|
||||
@ -622,10 +631,15 @@ void initPumpLogic()
|
||||
pcnt_counter_clear(unit); // Zera o contador PCNT
|
||||
pcnt_counter_resume(unit);
|
||||
#endif
|
||||
pumpStartTime = millis();
|
||||
pumpTarget = millis() + (mPlants[pumpToRun].getPumpDuration() * 1000);
|
||||
#ifdef FLOWMETER_PIN
|
||||
log(LOG_LEVEL_INFO, "Starting pump " + String(pumpToRun) + " for " + String(mPlants[pumpToRun].getPumpDuration()) + "s or " + String(pumpTargetMl) + "ml", LOG_PUMP_STARTED_CODE);
|
||||
#else
|
||||
log(LOG_LEVEL_INFO, "Starting pump " + String(pumpToRun) + " for " + String(mPlants[pumpToRun].getPumpDuration()) + "s", LOG_PUMP_STARTED_CODE);
|
||||
#endif
|
||||
|
||||
//enable power
|
||||
// enable power
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
|
||||
digitalWrite(OUTPUT_ENABLE_PUMP, HIGH);
|
||||
delay(100);
|
||||
@ -652,8 +666,8 @@ void pumpActiveLoop()
|
||||
mqttUpdateTick = true;
|
||||
}
|
||||
|
||||
long duration = millis() - pumpStartTime;
|
||||
#ifdef FLOWMETER_PIN
|
||||
|
||||
int16_t pulses;
|
||||
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_7);
|
||||
esp_err_t result = pcnt_get_counter_value(unit, &pulses);
|
||||
@ -661,29 +675,27 @@ void pumpActiveLoop()
|
||||
{
|
||||
log(LOG_LEVEL_ERROR, LOG_HARDWARECOUNTER_ERROR_MESSAGE, LOG_HARDWARECOUNTER_ERROR_CODE);
|
||||
targetReached = true;
|
||||
log(LOG_LEVEL_INFO, "Reached pump target ml " + String(pumpToRun), LOG_PUMP_STARTED_CODE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/**FLOWMETER_FLOWFACTOR * (L/Min) = F;
|
||||
given 1L/min -> FLOWMETER_FLOWFACTOR*60 pulses per liter
|
||||
-> 1000/result -> ml pro pulse;
|
||||
-> result * pulses ->*/
|
||||
long pumped = (FLOWMETER_FLOWFACTOR * 60) * pulses / 1000;
|
||||
if (pumped >= pumpTargetMl)
|
||||
float mLPumped = pulses / FLOWMETER_PULSES_PER_ML; // mLperMs*duration;
|
||||
if (mLPumped >= pumpTargetMl)
|
||||
{
|
||||
targetReached = true;
|
||||
pcnt_counter_pause(unit);
|
||||
mPlants[pumpToRun].setProperty("waterusage").send(String(pumped));
|
||||
mPlants[pumpToRun].setProperty("pulses").send(String(pulses));
|
||||
mPlants[pumpToRun].setProperty("waterusage").send(String(mLPumped));
|
||||
}
|
||||
|
||||
else if (mqttUpdateTick)
|
||||
{
|
||||
mPlants[pumpToRun].setProperty("waterusage").send(String(pumped));
|
||||
mPlants[pumpToRun].setProperty("pulses").send(String(pulses));
|
||||
mPlants[pumpToRun].setProperty("waterusage").send(String(mLPumped));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
long pumpStarted = pumpTarget - (mPlants[pumpToRun].getPumpDuration() * 1000);
|
||||
long duration = millis() - pumpStarted;
|
||||
if (millis() > pumpTarget)
|
||||
{
|
||||
mPlants[pumpToRun].setProperty("watertime").send(String(duration));
|
||||
@ -696,15 +708,13 @@ void pumpActiveLoop()
|
||||
|
||||
if (targetReached)
|
||||
{
|
||||
//disable all
|
||||
|
||||
// disable all
|
||||
digitalWrite(OUTPUT_ENABLE_PUMP, LOW);
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
mPlants[i].deactivatePump();
|
||||
}
|
||||
//disable loop, to prevent multi processing
|
||||
mPlants[pumpToRun].deactivatePump();
|
||||
// disable loop, to prevent multi processing
|
||||
pumpStarted = false;
|
||||
//if runtime is larger than cooldown, else it would run continously
|
||||
// if runtime is larger than cooldown, else it would run continously
|
||||
rtcLastWateringPlant[pumpToRun] = getCurrentTime();
|
||||
espDeepSleep(true);
|
||||
}
|
||||
@ -721,11 +731,9 @@ void safeSetup()
|
||||
WiFi.mode(WIFI_OFF);
|
||||
Serial.flush();
|
||||
|
||||
//restore state before releasing pin, to prevent flickering
|
||||
// restore state before releasing pin, to prevent flickering
|
||||
#if defined(TIMED_LIGHT_PIN)
|
||||
pinMode(TIMED_LIGHT_PIN, OUTPUT);
|
||||
digitalWrite(TIMED_LIGHT_PIN, timedLightOn);
|
||||
gpio_hold_dis(TIMED_LIGHT_PIN);
|
||||
ulp_pwm_init();
|
||||
#endif // TIMED_LIGHT_PIN
|
||||
|
||||
/* Intialize Plant */
|
||||
@ -743,26 +751,28 @@ void safeSetup()
|
||||
|
||||
pinMode(OUTPUT_ENABLE_SENSOR, OUTPUT);
|
||||
|
||||
static_assert(HomieInternals::MAX_CONFIG_SETTING_SIZE >= MAX_CONFIG_SETTING_ITEMS, "Limits.hpp not adjusted MAX_CONFIG_SETTING_ITEMS");
|
||||
if (HomieInternals::MAX_CONFIG_SETTING_SIZE < MAX_CONFIG_SETTING_ITEMS)
|
||||
{
|
||||
//increase the config settings
|
||||
// increase the config settings
|
||||
Serial << "Limits.hpp is not adjusted, please search for this string and increase" << endl;
|
||||
return;
|
||||
}
|
||||
static_assert(HomieInternals::MAX_JSON_CONFIG_FILE_SIZE >= MAX_JSON_CONFIG_FILE_SIZE_CUSTOM, "Limits.hpp not adjusted MAX_JSON_CONFIG_FILE_SIZE");
|
||||
if (HomieInternals::MAX_JSON_CONFIG_FILE_SIZE < MAX_JSON_CONFIG_FILE_SIZE_CUSTOM)
|
||||
{
|
||||
//increase the config settings
|
||||
// increase the config settings
|
||||
Serial << "Limits.hpp is not adjusted, please search for this string and increase" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/************************* Start Homie Framework ***************/
|
||||
/************************* Start Homie Framework ***************/
|
||||
Homie_setFirmware("PlantControl", FIRMWARE_VERSION);
|
||||
Homie.disableLedFeedback();
|
||||
Homie_setBrand("PlantControl");
|
||||
// Set default values
|
||||
|
||||
//in seconds
|
||||
// in seconds
|
||||
deepSleepTime.setDefaultValue(600).setValidator([](long candidate)
|
||||
{ return (candidate > 0) && (candidate < (60 * 60 * 2) /** 2h max sleep */); });
|
||||
deepSleepNightTime.setDefaultValue(600);
|
||||
@ -778,13 +788,15 @@ void safeSetup()
|
||||
{ return (candidate > 0) && (candidate < (20)); });
|
||||
|
||||
#if defined(TIMED_LIGHT_PIN)
|
||||
timedLightPowerLevel.setDefaultValue(25).setValidator([](long candidate)
|
||||
{ return (candidate > 0) && (candidate <= (255)); });
|
||||
timedLightStart.setDefaultValue(18).setValidator([](long candidate)
|
||||
{ return (candidate > 0) && (candidate < (25)); });
|
||||
timedLightEnd.setDefaultValue(23).setValidator([](long candidate)
|
||||
{ return (candidate > 0) && (candidate < (24)); });
|
||||
timedLightOnlyWhenDark.setDefaultValue(true);
|
||||
timedLightVoltageCutoff.setDefaultValue(3.8).setValidator([](double candidate)
|
||||
{ return (candidate > 3.3) && (candidate < (4.2)); });
|
||||
{ return ((candidate > 3.3 || candidate == -1) && (candidate < (50))); });
|
||||
#endif // TIMED_LIGHT_PIN
|
||||
|
||||
Homie.setLoopFunction(homieLoop);
|
||||
@ -797,12 +809,13 @@ void safeSetup()
|
||||
}
|
||||
readPowerSwitchedSensors();
|
||||
|
||||
|
||||
Homie.setup();
|
||||
|
||||
Wire = TwoWire(0);
|
||||
Wire.setPins(SENSOR_TANK_SDA, SENSOR_TANK_SCL);
|
||||
Wire.begin();
|
||||
|
||||
|
||||
/************************* Start One-Wire bus ***************/
|
||||
/************************* Start One-Wire bus ***************/
|
||||
int tempInitStartTime = millis();
|
||||
uint8_t sensorCount = 0U;
|
||||
|
||||
@ -820,7 +833,7 @@ void safeSetup()
|
||||
/* Measure temperature TODO idea: move this into setup */
|
||||
if (sensorCount > 0)
|
||||
{
|
||||
//sensors.setResolution(DS18B20_RESOLUTION);
|
||||
// sensors.setResolution(DS18B20_RESOLUTION);
|
||||
sensors.requestTemperatures();
|
||||
}
|
||||
|
||||
@ -830,10 +843,6 @@ void safeSetup()
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
mPlants[i].advertise();
|
||||
//write to temperature node instead
|
||||
if(!equalish(mPlants[i].getCurrentTemperature(),PLANT_WITHOUT_TEMPSENSOR)){
|
||||
mqttWrite(&sensorTemp, "Plant" + String(i), String(mPlants[i].getCurrentTemperature()));
|
||||
}
|
||||
}
|
||||
mPlants[0].setSwitchHandler(switch1);
|
||||
mPlants[1].setSwitchHandler(switch2);
|
||||
@ -894,7 +903,7 @@ void safeSetup()
|
||||
}
|
||||
|
||||
readOneWireSensors();
|
||||
//prevent BOD to be paranoid
|
||||
// prevent BOD to be paranoid
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
|
||||
digitalWrite(OUTPUT_ENABLE_PUMP, HIGH);
|
||||
delay(100);
|
||||
@ -970,7 +979,14 @@ void loop()
|
||||
digitalWrite(OUTPUT_ENABLE_SENSOR, !digitalRead(OUTPUT_ENABLE_SENSOR));
|
||||
if (mConfigured)
|
||||
{
|
||||
nextBlink = millis() + 500;
|
||||
if (otaRunning)
|
||||
{
|
||||
nextBlink = millis() + 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextBlink = millis() + 501;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1056,11 +1072,24 @@ void plantcontrol()
|
||||
Serial.flush();
|
||||
}
|
||||
|
||||
bool isLowLight = (mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE);
|
||||
bool hasWater = true; //FIXME remaining > waterLevelMin.get();
|
||||
//FIXME no water warning message
|
||||
#if defined(TIMED_LIGHT_PIN)
|
||||
bool isLowLight = mSolarVoltage <= 9;
|
||||
bool shouldLight = determineTimedLightState(isLowLight);
|
||||
if (shouldLight)
|
||||
{
|
||||
ulp_pwm_set_level(timedLightPowerLevel.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
ulp_pwm_set_level(0);
|
||||
}
|
||||
|
||||
#endif // TIMED_LIGHT_PIN
|
||||
|
||||
bool hasWater = true; // FIXME remaining > waterLevelMin.get();
|
||||
// FIXME no water warning message
|
||||
pumpToRun = determineNextPump(isLowLight);
|
||||
//early aborts
|
||||
// early aborts
|
||||
if (pumpToRun != -1)
|
||||
{
|
||||
if (hasWater)
|
||||
@ -1083,12 +1112,6 @@ void plantcontrol()
|
||||
{
|
||||
espDeepSleep();
|
||||
}
|
||||
|
||||
#if defined(TIMED_LIGHT_PIN)
|
||||
bool shouldLight = determineTimedLightState(isLowLight);
|
||||
timedLightOn = shouldLight;
|
||||
digitalWrite(TIMED_LIGHT_PIN, shouldLight);
|
||||
#endif // TIMED_LIGHT_PIN
|
||||
}
|
||||
|
||||
/** @}*/
|
||||
@ -1100,7 +1123,7 @@ bool determineTimedLightState(bool lowLight)
|
||||
long hoursStart = timedLightStart.get();
|
||||
long hoursEnd = timedLightEnd.get();
|
||||
|
||||
//ntp missing
|
||||
// ntp missing
|
||||
if (getCurrentTime() < 10000)
|
||||
{
|
||||
timedLightNode.setProperty("state").send(String("Off, missing ntp"));
|
||||
@ -1113,13 +1136,19 @@ bool determineTimedLightState(bool lowLight)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((hoursStart > hoursEnd) &&
|
||||
(getCurrentHour() >= hoursStart || getCurrentHour() <= hoursEnd)) ||
|
||||
/* Handle e.g. start = 8, end = 21 */
|
||||
int curHour = getCurrentHour();
|
||||
|
||||
bool condition1 = ((hoursStart > hoursEnd) &&
|
||||
(curHour >= hoursStart || curHour <= hoursEnd));
|
||||
bool condition2 = /* Handle e.g. start = 8, end = 21 */
|
||||
|
||||
((hoursStart < hoursEnd) &&
|
||||
(getCurrentHour() >= hoursStart && getCurrentHour() <= hoursEnd)))
|
||||
(curHour >= hoursStart && curHour <= hoursEnd));
|
||||
timedLightNode.setProperty("debug").send(String(curHour) + " " + String(hoursStart) + " " + String(hoursEnd) + " " + String(condition1) + " " + String(condition2));
|
||||
if (condition1 || condition2)
|
||||
{
|
||||
if (!timedLightLowVoltageTriggered && battery.getVoltage(BATTSENSOR_INDEX_BATTERY) >= timedLightVoltageCutoff.get())
|
||||
bool voltageOk = !timedLightLowVoltageTriggered && battery.getVoltage(BATTSENSOR_INDEX_BATTERY) >= timedLightVoltageCutoff.get();
|
||||
if (voltageOk || equalish(timedLightVoltageCutoff.get(), -1))
|
||||
{
|
||||
timedLightNode.setProperty("state").send(String("On"));
|
||||
return true;
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* DS2438.h
|
||||
*
|
||||
* by Joe Bechter
|
||||
*
|
||||
* (C) 2012, bechter.com
|
||||
*
|
||||
* All files, software, schematics and designs are provided as-is with no warranty.
|
||||
* All files, software, schematics and designs are for experimental/hobby use.
|
||||
* Under no circumstances should any part be used for critical systems where safety,
|
||||
* life or property depends upon it. You are responsible for all use.
|
||||
* You are free to use, modify, derive or otherwise extend for your own non-commercial purposes provided
|
||||
* 1. No part of this software or design may be used to cause injury or death to humans or animals.
|
||||
* 2. Use is non-commercial.
|
||||
* 3. Credit is given to the author (i.e. portions © bechter.com), and provide a link to the original source.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DS2438_h
|
||||
#define DS2438_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <OneWire.h>
|
||||
|
||||
#define DS2438_TEMPERATURE_CONVERSION_COMMAND 0x44
|
||||
#define DS2438_VOLTAGE_CONVERSION_COMMAND 0xb4
|
||||
#define DS2438_WRITE_SCRATCHPAD_COMMAND 0x4e
|
||||
#define DS2438_COPY_SCRATCHPAD_COMMAND 0x48
|
||||
#define DS2438_READ_SCRATCHPAD_COMMAND 0xbe
|
||||
#define DS2438_RECALL_MEMORY_COMMAND 0xb8
|
||||
|
||||
#define PAGE_MIN 0
|
||||
#define PAGE_MAX 7
|
||||
|
||||
#define DS2438_CHA 0
|
||||
#define DS2438_CHB 1
|
||||
|
||||
#define DS2438_MODE_CHA 0x01
|
||||
#define DS2438_MODE_CHB 0x02
|
||||
#define DS2438_MODE_TEMPERATURE 0x04
|
||||
|
||||
#define DS2438_TEMPERATURE_DELAY 10
|
||||
#define DS2438_VOLTAGE_CONVERSION_DELAY 8
|
||||
|
||||
#define DEFAULT_PAGE0(var) uint8_t var[8] { \
|
||||
0b00001011 /* X, ADB=0, NVB=0, TB=0, AD=1, EE=0, CA=1, IAD=1 */, \
|
||||
0, /* Temperatur */ \
|
||||
0, /* Temperatur */ \
|
||||
0, /* Voltage */ \
|
||||
0, /* Voltage */ \
|
||||
0, /* Current */ \
|
||||
0, /* Current */ \
|
||||
0 /* Threashold */ \
|
||||
}
|
||||
|
||||
typedef struct PageOne {
|
||||
uint8_t eleapsedTimerByte0; /**< LSB of timestamp */
|
||||
uint8_t eleapsedTimerByte1;
|
||||
uint8_t eleapsedTimerByte2;
|
||||
uint8_t eleapsedTimerByte3; /**< MSB of timestamp */
|
||||
uint8_t ICA; /**< Integrated Current Accumulator (current flowing into and out of the battery) */
|
||||
uint8_t offsetRegisterByte0; /**< Offset for ADC calibdation */
|
||||
uint8_t offsetRegisterByte1; /**< Offset for ADC calibdation */
|
||||
uint8_t reserved;
|
||||
} PageOne_t;
|
||||
|
||||
typedef struct PageSeven {
|
||||
uint8_t userByte0;
|
||||
uint8_t userByte1;
|
||||
uint8_t userByte2;
|
||||
uint8_t userByte3;
|
||||
uint8_t CCA0; /**< Charging Current Accumulator (CCA) */
|
||||
uint8_t CCA1; /**< Charging Current Accumulator (CCA) */
|
||||
uint8_t DCA0; /**< Discharge Current Accumulator (DCA) */
|
||||
uint8_t DCA1; /**< Discharge Current Accumulator (DCA) */
|
||||
} PageSeven_t;
|
||||
|
||||
typedef uint8_t DeviceAddress[8];
|
||||
|
||||
class DS2438 {
|
||||
public:
|
||||
DS2438(OneWire *ow, float currentShunt);
|
||||
DS2438(OneWire *ow, uint8_t *address);
|
||||
|
||||
void begin();
|
||||
void update();
|
||||
double getTemperature();
|
||||
float getVoltage(int channel=DS2438_CHA);
|
||||
float getCurrent();
|
||||
boolean isError();
|
||||
boolean isFound();
|
||||
private:
|
||||
bool validAddress(const uint8_t*);
|
||||
bool validFamily(const uint8_t* deviceAddress);
|
||||
|
||||
bool deviceFound = false;
|
||||
OneWire *_ow;
|
||||
DeviceAddress _address;
|
||||
uint8_t _mode;
|
||||
double _temperature;
|
||||
float _voltageA;
|
||||
float _voltageB;
|
||||
float _current;
|
||||
float _currentShunt;
|
||||
boolean _error;
|
||||
boolean startConversion(int channel, boolean doTemperature);
|
||||
boolean selectChannel(int channel);
|
||||
void writePage(int page, uint8_t *data);
|
||||
boolean readPage(int page, uint8_t *data);
|
||||
};
|
||||
|
||||
#endif
|
187
esp32test/Esp32DeepSleepTest/include/ulp-pwm.h
Normal file
187
esp32test/Esp32DeepSleepTest/include/ulp-pwm.h
Normal file
@ -0,0 +1,187 @@
|
||||
#ifndef ULP_PWM_h
|
||||
#define ILP_PWM_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "driver/rtc_io.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "esp32/ulp.h"
|
||||
|
||||
#define LBL_START 1
|
||||
#define LBL_DELAY_ON 2
|
||||
#define LBL_DELAY_OFF 3
|
||||
#define LBL_SKIP_ON 4
|
||||
#define LBL_SKIP_OFF 5
|
||||
#define REGISTER_DELAY_LOOP_COUNTER R0
|
||||
#define REGISTER_TICKS_ON R1
|
||||
#define REGISTER_TICKS_OFF R2
|
||||
#define TOTAL_TICKS_DELAY 255
|
||||
#define PIN GPIO_NUM_12
|
||||
|
||||
//support 20 vars
|
||||
const size_t ulp_var_offset = CONFIG_ULP_COPROC_RESERVE_MEM - 20;
|
||||
//use the first for dimming
|
||||
const size_t ulp_dimm_offset = ulp_var_offset + 1;
|
||||
const size_t ulp_alive_offset = ulp_var_offset + 2;
|
||||
|
||||
//see https://github.com/perseus086/ESP32-notes
|
||||
const uint32_t rtc_bit[40] = {
|
||||
25, //gpio0
|
||||
0, //gpio1
|
||||
26, //gpio2
|
||||
0, //gpio3
|
||||
24, //gpio4
|
||||
0, //gpio5
|
||||
0, //gpio6
|
||||
0, //gpio7
|
||||
0, //gpio8
|
||||
0, //gpio9
|
||||
0, //gpio10
|
||||
0, //gpio11
|
||||
29, //gpio12
|
||||
28, //gpio13
|
||||
30, //gpio14
|
||||
27, //gpio15
|
||||
0, //gpio16
|
||||
31, //gpio17
|
||||
0, //gpio18
|
||||
0, //gpio19
|
||||
0, //gpio20
|
||||
0, //gpio21
|
||||
0, //gpio22
|
||||
0, //gpio23
|
||||
0, //gpio24
|
||||
20, //gpio25
|
||||
21, //gpio26
|
||||
0, //gpio27
|
||||
0, //gpio28
|
||||
0, //gpio29
|
||||
0, //gpio30
|
||||
0, //gpio31
|
||||
23, //gpio32
|
||||
22, //gpio33
|
||||
18, //gpio34
|
||||
19, //gpio35
|
||||
14, //gpio36
|
||||
15, //gpio37
|
||||
16, //gpio38
|
||||
17 //gpio39
|
||||
};
|
||||
|
||||
static inline void ulp_data_write(size_t offset, uint16_t value)
|
||||
{
|
||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
||||
{
|
||||
Serial.print("Invalid ULP offset detected, refusing write!");
|
||||
Serial.print(offset);
|
||||
Serial.print("-");
|
||||
Serial.print(ulp_var_offset);
|
||||
Serial.print("-");
|
||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
RTC_SLOW_MEM[offset] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t ulp_data_read(size_t offset)
|
||||
{
|
||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
||||
{
|
||||
Serial.print("Invalid ULP offset detected");
|
||||
Serial.print(offset);
|
||||
Serial.print("-");
|
||||
Serial.print(ulp_var_offset);
|
||||
Serial.print("-");
|
||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||
}
|
||||
return RTC_SLOW_MEM[offset] & 0xffff;
|
||||
}
|
||||
|
||||
static inline uint32_t rtc_io_number_get(gpio_num_t gpio_num)
|
||||
{
|
||||
assert(rtc_gpio_is_valid_gpio(gpio_num) && "Invalid GPIO for RTC");
|
||||
uint32_t bit = rtc_bit[gpio_num];
|
||||
Serial.print("Resolved GPIO ");
|
||||
Serial.print(gpio_num);
|
||||
Serial.print(" to rtc bit ");
|
||||
Serial.println(bit);
|
||||
return bit;
|
||||
}
|
||||
|
||||
void ulp_pwm_start(void)
|
||||
{
|
||||
rtc_gpio_init(PIN);
|
||||
rtc_gpio_set_direction(PIN, RTC_GPIO_MODE_OUTPUT_ONLY);
|
||||
rtc_gpio_set_level(PIN, 0);
|
||||
const uint32_t rtc_gpio = rtc_io_number_get(PIN);
|
||||
|
||||
// Define ULP program
|
||||
const ulp_insn_t ulp_prog[] = {
|
||||
M_LABEL(LBL_START),
|
||||
|
||||
I_MOVI(REGISTER_DELAY_LOOP_COUNTER, 1),
|
||||
I_MOVI(REGISTER_TICKS_ON, 0),
|
||||
I_ST(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON, ulp_alive_offset), //store 1 with 0 offset into alive
|
||||
|
||||
I_LD(REGISTER_TICKS_ON, REGISTER_TICKS_ON, ulp_dimm_offset), //REGISTER_TICKS_ON = RTC_DATA[0+ulp_dimm_offset]
|
||||
//in total there is always 255 delay loop iterations, but in different duty cycle
|
||||
I_MOVI(REGISTER_TICKS_OFF, TOTAL_TICKS_DELAY),
|
||||
I_SUBR(REGISTER_TICKS_OFF, REGISTER_TICKS_OFF, REGISTER_TICKS_ON),
|
||||
|
||||
//on phase
|
||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON),
|
||||
M_BL(LBL_SKIP_ON, 1), //if never on, skip on phase
|
||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, HIGH), // on
|
||||
M_LABEL(LBL_DELAY_ON),
|
||||
I_DELAY(1), //wait 1 clock
|
||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
||||
M_BGE(LBL_DELAY_ON, 1), //if time left, goto start of on loop
|
||||
M_LABEL(LBL_SKIP_ON),
|
||||
|
||||
//off phase
|
||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_OFF),
|
||||
|
||||
M_BL(LBL_SKIP_OFF, 1), //if never off, skip on phase
|
||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, LOW), // on
|
||||
M_LABEL(3),
|
||||
I_DELAY(1), //wait 1 clock
|
||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
||||
M_BGE(3, 1), //if time left, goto start of on loop
|
||||
M_LABEL(LBL_SKIP_OFF),
|
||||
|
||||
M_BX(LBL_START),
|
||||
};
|
||||
// Run ULP program
|
||||
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
|
||||
assert(size < ulp_var_offset && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
|
||||
esp_err_t error = ulp_process_macros_and_load(0, ulp_prog, &size);
|
||||
Serial.print("ULP bootstrap status ");
|
||||
Serial.println(error);
|
||||
|
||||
//allow glitchless start
|
||||
ulp_data_write(ulp_alive_offset, 0);
|
||||
|
||||
error = ulp_run(0);
|
||||
Serial.print("ULP start status ");
|
||||
Serial.println(error);
|
||||
}
|
||||
|
||||
static inline void ulp_pwm_set_level(uint8_t level)
|
||||
{
|
||||
ulp_data_write(ulp_dimm_offset, level);
|
||||
}
|
||||
|
||||
static inline void ulp_pwm_init()
|
||||
{
|
||||
ulp_data_write(ulp_alive_offset, 0);
|
||||
delay(10);
|
||||
if (ulp_data_read(ulp_alive_offset) == 0)
|
||||
{
|
||||
ulp_pwm_start();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,286 +0,0 @@
|
||||
/*
|
||||
* DS2438.cpp
|
||||
*
|
||||
* by Joe Bechter
|
||||
*
|
||||
* (C) 2012, bechter.com
|
||||
*
|
||||
* All files, software, schematics and designs are provided as-is with no warranty.
|
||||
* All files, software, schematics and designs are for experimental/hobby use.
|
||||
* Under no circumstances should any part be used for critical systems where safety,
|
||||
* life or property depends upon it. You are responsible for all use.
|
||||
* You are free to use, modify, derive or otherwise extend for your own non-commercial purposes provided
|
||||
* 1. No part of this software or design may be used to cause injury or death to humans or animals.
|
||||
* 2. Use is non-commercial.
|
||||
* 3. Credit is given to the author (i.e. portions © bechter.com), and provide a link to the original source.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "DS2438.h"
|
||||
|
||||
// DSROM FIELDS
|
||||
#define DSROM_FAMILY 0
|
||||
#define DSROM_CRC 7
|
||||
|
||||
#define DS2438MODEL 0x26
|
||||
|
||||
DS2438::DS2438(OneWire *ow, float currentShunt = 1.0f) {
|
||||
_ow = ow;
|
||||
_currentShunt = currentShunt;
|
||||
};
|
||||
|
||||
void DS2438::begin(){
|
||||
DeviceAddress searchDeviceAddress;
|
||||
|
||||
_ow->reset_search();
|
||||
memset(searchDeviceAddress,0, 8);
|
||||
_temperature = 0;
|
||||
_voltageA = 0.0;
|
||||
_voltageB = 0.0;
|
||||
_error = true;
|
||||
_mode = (DS2438_MODE_CHA | DS2438_MODE_CHB | DS2438_MODE_TEMPERATURE);
|
||||
|
||||
deviceFound = false; // Reset the number of devices when we enumerate wire devices
|
||||
|
||||
while (_ow->search(searchDeviceAddress)) {
|
||||
if (validAddress(searchDeviceAddress)) {
|
||||
if (validFamily(searchDeviceAddress)) {
|
||||
memcpy(_address,searchDeviceAddress,8);
|
||||
DEFAULT_PAGE0(defaultConfig);
|
||||
writePage(0, defaultConfig);
|
||||
deviceFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DS2438::isFound(){
|
||||
return deviceFound;
|
||||
}
|
||||
|
||||
bool DS2438::validAddress(const uint8_t* deviceAddress) {
|
||||
return (_ow->crc8(deviceAddress, 7) == deviceAddress[DSROM_CRC]);
|
||||
}
|
||||
|
||||
bool DS2438::validFamily(const uint8_t* deviceAddress) {
|
||||
switch (deviceAddress[DSROM_FAMILY]) {
|
||||
case DS2438MODEL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DS2438::update() {
|
||||
uint8_t data[9];
|
||||
|
||||
_error = true;
|
||||
if(!isFound()){
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mode & DS2438_MODE_CHA || _mode == DS2438_MODE_TEMPERATURE) {
|
||||
boolean doTemperature = _mode & DS2438_MODE_TEMPERATURE;
|
||||
if (!startConversion(DS2438_CHA, doTemperature)) {
|
||||
Serial.println("Error starting temp conversion ds2438 channel a");
|
||||
return;
|
||||
}
|
||||
if (!readPage(0, data)){
|
||||
|
||||
Serial.println("Error reading zero page ds2438 channel a");
|
||||
return;
|
||||
}
|
||||
Serial.print(data[0],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[1],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[2],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[3],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[4],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[5],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[6],16);
|
||||
Serial.print(" ");
|
||||
Serial.println(data[7],16);
|
||||
|
||||
|
||||
if (doTemperature) {
|
||||
_temperature = (double)(((((int16_t)data[2]) << 8) | (data[1] & 0x0ff)) >> 3) * 0.03125;
|
||||
}
|
||||
if (_mode & DS2438_MODE_CHA) {
|
||||
_voltageA = (((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0;
|
||||
}
|
||||
}
|
||||
if (_mode & DS2438_MODE_CHB) {
|
||||
boolean doTemperature = _mode & DS2438_MODE_TEMPERATURE && !(_mode & DS2438_MODE_CHA);
|
||||
if (!startConversion(DS2438_CHB, doTemperature)) {
|
||||
Serial.println("Error starting temp conversion channel b ds2438");
|
||||
return;
|
||||
}
|
||||
if (!readPage(0, data)){
|
||||
Serial.println("Error reading zero page ds2438 channel b");
|
||||
return;
|
||||
}
|
||||
if (doTemperature) {
|
||||
int16_t upperByte = ((int16_t)data[2]) << 8;
|
||||
int16_t lowerByte = data[1] >> 3;
|
||||
int16_t fullByte = (upperByte | lowerByte);
|
||||
_temperature = ((double)fullByte) * 0.03125;
|
||||
}
|
||||
_voltageB = (((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0;
|
||||
}
|
||||
|
||||
int16_t upperByte = ((int16_t)data[6]) << 8;
|
||||
int16_t lowerByte = data[5];
|
||||
int16_t fullByte = (int16_t)(upperByte | lowerByte);
|
||||
float fullByteb = fullByte;
|
||||
_current = (fullByteb) / ((4096.0f * _currentShunt));
|
||||
_error = false;
|
||||
Serial.print(data[0],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[1],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[2],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[3],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[4],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[5],16);
|
||||
Serial.print(" ");
|
||||
Serial.print(data[6],16);
|
||||
Serial.print(" ");
|
||||
Serial.println(data[7],16);
|
||||
Serial.println("-");
|
||||
|
||||
|
||||
|
||||
uint16_t ICA = 0;
|
||||
if (readPage(1, data)){
|
||||
PageOne_t *pOne = (PageOne_t *) data;
|
||||
Serial.println(pOne->ICA);
|
||||
float Ah = pOne->ICA / (2048.0f * _currentShunt);
|
||||
Serial.print("Ah=");
|
||||
Serial.println(Ah);
|
||||
ICA = pOne->ICA;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (readPage(7, data)){
|
||||
PageSeven_t *pSeven = (PageSeven_t *) data;
|
||||
int16_t CCA = pSeven->CCA0 | ((int16_t) pSeven->CCA1) << 8;
|
||||
int16_t DCA = pSeven->DCA0 | ((int16_t) pSeven->DCA1) << 8;
|
||||
Serial.println("ICA, DCA, CCA");
|
||||
Serial.print(ICA);
|
||||
Serial.print(", ");
|
||||
Serial.print(DCA);
|
||||
Serial.print(", ");
|
||||
Serial.println(CCA);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
double DS2438::getTemperature() {
|
||||
return _temperature;
|
||||
}
|
||||
|
||||
float DS2438::getVoltage(int channel) {
|
||||
if (channel == DS2438_CHA) {
|
||||
return _voltageA;
|
||||
} else if (channel == DS2438_CHB) {
|
||||
return _voltageB;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
float DS2438::getCurrent() {
|
||||
return _current;
|
||||
}
|
||||
|
||||
boolean DS2438::isError() {
|
||||
return _error;
|
||||
}
|
||||
|
||||
boolean DS2438::startConversion(int channel, boolean doTemperature) {
|
||||
if(!isFound()){
|
||||
return false;
|
||||
}
|
||||
if (!selectChannel(channel)){
|
||||
return false;
|
||||
}
|
||||
_ow->reset();
|
||||
_ow->select(_address);
|
||||
if (doTemperature) {
|
||||
_ow->write(DS2438_TEMPERATURE_CONVERSION_COMMAND, 0);
|
||||
delay(DS2438_TEMPERATURE_DELAY);
|
||||
_ow->reset();
|
||||
_ow->select(_address);
|
||||
}
|
||||
_ow->write(DS2438_VOLTAGE_CONVERSION_COMMAND, 0);
|
||||
delay(DS2438_VOLTAGE_CONVERSION_DELAY);
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean DS2438::selectChannel(int channel) {
|
||||
if(!isFound()){
|
||||
return false;
|
||||
}
|
||||
uint8_t data[9];
|
||||
if (readPage(0, data)) {
|
||||
if (channel == DS2438_CHB){
|
||||
data[0] = data[0] | 0x08;
|
||||
}
|
||||
else {
|
||||
data[0] = data[0] & 0xf7;
|
||||
}
|
||||
writePage(0, data);
|
||||
return true;
|
||||
}
|
||||
Serial.println("Could not read page zero data");
|
||||
return false;
|
||||
}
|
||||
|
||||
void DS2438::writePage(int page, uint8_t *data) {
|
||||
_ow->reset();
|
||||
_ow->select(_address);
|
||||
_ow->write(DS2438_WRITE_SCRATCHPAD_COMMAND, 0);
|
||||
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
|
||||
_ow->write(page, 0);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 8; i++){
|
||||
_ow->write(data[i], 0);
|
||||
}
|
||||
_ow->reset();
|
||||
_ow->select(_address);
|
||||
_ow->write(DS2438_COPY_SCRATCHPAD_COMMAND, 0);
|
||||
_ow->write(page, 0);
|
||||
}
|
||||
|
||||
boolean DS2438::readPage(int page, uint8_t *data) {
|
||||
//TODO if all data is 0 0 is a valid crc, but most likly not as intended
|
||||
_ow->reset();
|
||||
_ow->select(_address);
|
||||
_ow->write(DS2438_RECALL_MEMORY_COMMAND, 0);
|
||||
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
|
||||
_ow->write(page, 0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_ow->reset();
|
||||
_ow->select(_address);
|
||||
_ow->write(DS2438_READ_SCRATCHPAD_COMMAND, 0);
|
||||
_ow->write(page, 0);
|
||||
for (int i = 0; i < 9; i++){
|
||||
data[i] = _ow->read();
|
||||
}
|
||||
return _ow->crc8(data, 8) == data[8];
|
||||
}
|
||||
|
@ -13,7 +13,10 @@ int16_t pulses2 = 0;
|
||||
int plantId = 0;
|
||||
|
||||
void setup() {
|
||||
|
||||
RTC_SLOW_ATTR uint8_t tick = 0;
|
||||
RTC_SLOW_ATTR bool dir = true;
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
pinMode(OUTPUT_SENSOR, OUTPUT);
|
||||
|
||||
@ -55,4 +58,4 @@ void loop() {
|
||||
pcnt_counter_clear(PCNT_UNIT_0);
|
||||
|
||||
Serial.println(pulses*2);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user