Smart Irrigation System With ESP32-S3 Software
Please read Liability Disclaimer and License Agreement CAREFULLY
#include <Wire.h>
#include <EEPROM.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include "SparkFun_BMP581_Arduino_Library.h"
// Use it for printing messages
//#define DEBUG
// If we use the water pressure sensor to monitor pressure and leaks
//#define HAS_PRESSURE
// Control valve if a leak is detected
//#define HAS_LEAK_CONTROL
#if defined DEBUG
#define debug_begin(x) Serial.begin(x)
#define debug(x) Serial.print(x)
#define debugln(x) Serial.println(x)
#else
#define debug_begin(x)
#define debug(x)
#define debugln(x)
#endif
// We have 3 or 4 sensors?
#ifdef HAS_PRESSURE
constexpr uint8_t SENSORS_NO = 4;
#else
constexpr uint8_t SENSORS_NO = 3;
#endif
// 388 bytes for calibration, 1 byte for C(alibration), 1 byte checksum, 2 bytes start/end message marker
// 388 bytes for calibration, 1 byte for C(alibration), 1 byte checksum, 2 bytes start/end message marker
constexpr uint16_t CALIB_DATA_LEN = 388;
// Buffer length Tx and Rx
constexpr uint16_t BUFFER_LEN = CALIB_DATA_LEN + 4;
// Max. no. of devices
constexpr uint8_t DEV_MAX = 10;
// Where in EEPROM we start to store calibration data
constexpr uint8_t CAL_START = DEV_MAX + 1; // Pos 11
// Where in EEPROM we stop to store calibration data
constexpr uint16_t CAL_END = CAL_START + CALIB_DATA_LEN; // Pos 38
// Used in serial communication with VL sensor
constexpr uint8_t START_MARKER = 60; // < Start marker for serial
constexpr uint8_t END_MARKER = 62; // > End marker for serial
constexpr uint8_t CMD_CALIBRATE = 67; // Send C to start calibration
constexpr uint8_t CMD_STORE_CAL = 83; // Send S to store calibration data we send
constexpr uint8_t RESPONSE_LENGTH = 76; // Get L to inform about length data
constexpr uint8_t RESPONSE_CAL_DONE = 67; // Get C to inform about calibration data
constexpr uint8_t RESPONSE_FAIL = 70; // Get F to indicate something went wrong
constexpr uint8_t RESPONSE_OK = 83; // CMD_STORE_CAL // Get S to indicate something went OK
#ifdef HAS_PRESSURE
// 4.5v 4,7k 1.47k 1.072V
//constexpr float ADC_FACTOR 0.000805664062f
constexpr float VOLT_TO_BAR_F1 = 0.008308813477f; // This is 10.313f * ADC_FACTOR
constexpr float VOLT_TO_BAR_F2 = 1.25f;
constexpr uint8_t PRESS_PIN = 2; // Analog read Pressure
// R1 = 9.95, R2=3.264 => 1.112V from 4.5V
// P = 0.0024 * ADC
#endif
// Pins used
constexpr uint8_t BTN_PIN = 1; // Analog read buttons
constexpr uint8_t LED_PIN = 4;
constexpr uint8_t LEVEL_PIN = 7;
constexpr uint8_t CTRL_PIN2 = 15;
// WiFi Connect period in ms
constexpr uint16_t UART_SPEED = 9600;
// WiFi Connect period in ms
constexpr uint16_t WiFi_Period = 1000;
// Send data to MQTT brocker every 30 seconds
constexpr uint16_t mqttPeriod = 10;
// Min. water level in the well in m
constexpr float minWellLevel = 0.5;
// Delay for MQTT functions
constexpr uint8_t delayMQTT = 50;
// WiFi Connect period
constexpr uint16_t VL53L4CX_WAIT = 6000;
// Debounce time in milliseconds
constexpr uint8_t DEBOUNCE_DELAY = 100;
// ADC Values for buttons 1 to 10
constexpr uint16_t BTN_ADC_MIN[] = {430, 870, 1280, 1650, 1990, 2315, 2615, 2895, 3155, 3400};
constexpr uint16_t BTN_ADC_MAX[] = {470, 830, 1320, 1690, 2030, 2355, 2655, 2935, 3195, 3440};
enum Button { None, Btn1, Btn2, Btn3, Btn4, Btn5, Btn6, Btn7, Btn8, Btn9, Btn10 };
enum DeviceStatus { Off, On };
enum SensorNumber { Level, Temp, Press, WaterPress};
typedef struct ButtonObject {
const char* topicState;
const char* topicTime;
uint8_t pin;
uint8_t pinState;
uint8_t timeTarget;
volatile uint16_t timeSeconds;
};
typedef struct SensorObject {
const char* name;
float val;
};
#ifdef HAS_LEAK_CONTROL
ButtonObject myBackUp[2];
#endif
ButtonObject myDevices[DEV_MAX];
SensorObject mySensors[SENSORS_NO];
// MQTT Server IP
const char* mqtt_server = "XXX.XXX.XXX.XXX";
// MQTT Server username
const char* mqtt_username = "MQTT_USER";
// MQTT Server password
const char* mqtt_password = "MQTT_PASSWORD";
// Client name
const char* clientID = "Valves";
// Number of devices to be used
const char* maxDevNo = "water/MaxDevNo";
// Calibration topic
constexpr char *topic_CalDist = "Level/Calibrate";
#ifdef HAS_LEAK_CONTROL
// Leak alarm topic
constexpr char *topic_LeakAlarm = "water/LeakAlarm";
// Pressure drop value for alarm in Bar
constexpr char *topic_LeakValue = "water/LeakValue";
#endif
// MQTT Server port
constexpr uint16_t MQTT_Port = 1883;
volatile uint8_t interruptBMP = 0;
// Array stored in RTC memory to hold:
// 0-9 Time for each device in Min, 10 Maximum number of devices used
uint8_t DEV_IN_USE = 1;
// Stores the time when the sensors must be read
volatile uint16_t mqttTime;
// Current button
uint8_t crtButton = None;
// Timer
hw_timer_t * timer = NULL;
// Calibration Distance
uint8_t calibrateDistance = 0;
// Used for timeout
uint32_t timeCheck;
#ifdef HAS_LEAK_CONTROL
// Store the alarm status
uint8_t alarmLeak = 0;
// Store the last pressure reading
float prevPressureVal = 0.0f;
// If pressure drops with 1 Bar the alarm is raised
float pressureThreshold = 1.0f;
#endif
BMP581 pressureSensor;
WiFiClient wifi_Client;
PubSubClient mqtt_Client(wifi_Client);
// Interrupt callback function for timer
void IRAM_ATTR onTimer() {
mqttTime++;
for (int cnt = 0; cnt < DEV_IN_USE; cnt++) {
// If the device is On, increment the timer
if(myDevices[cnt].pinState) {
myDevices[cnt].timeSeconds++;
}
}
}
// Interrupt callback function for BMP581
void bmp581InterruptHandler() {
interruptBMP = 1;
}
void sendToSTM(const uint8_t msgType) {
uint16_t txLen = 4;
// Determine the message length and fill the buffer based on message type
if(msgType == CMD_STORE_CAL) {
txLen = BUFFER_LEN;
}
uint8_t txBuffer[txLen];
// Initialize the common header for all messages
txBuffer[0] = START_MARKER;
txBuffer[1] = msgType;
uint8_t checkSum = msgType;
if(msgType == CMD_STORE_CAL) {
for (uint16_t cnt = CAL_START; cnt < CAL_END; cnt++) {
uint16_t tmp = cnt - 9;
txBuffer[tmp] = EEPROM.read(cnt);
checkSum += txBuffer[tmp];
}
}
// Add checksum and end marker
txBuffer[txLen - 2] = checkSum;
txBuffer[txLen - 1] = END_MARKER;
// Send the message
Serial1.write(txBuffer, txLen);
}
uint8_t ProcessReceivedMessage() {
digitalWrite(LED_PIN, LOW);
uint8_t retunValue = 0;
// If we have 4 bytes in the buffer and the first one is <
if((Serial1.available() > 3) && (Serial1.read() == START_MARKER)) {
// Get the message type
uint8_t msgType = Serial1.read();
// Keep in mind that we already read 2 bytes
// Depending on message type we need to wait for some more bytes
uint16_t expectedMsgLen = 0;
// expectedMsgLen in below switch is ignoring the last 2 bytes we receive
switch(msgType) {
case RESPONSE_LENGTH: // We get 6 bytes in total
expectedMsgLen = 2;
break;
case RESPONSE_CAL_DONE: // We get 392 bytes in total
expectedMsgLen = CALIB_DATA_LEN;
break;
default: // RESPONSE_OK, RESPONSE_FAIL // We get 4 bytes in total
expectedMsgLen = 0;
break;
}
uint8_t receivedCheckSum = 0;
// If we get a 4 byte message < M M >
// The third byte we read is the checkSum
if(!expectedMsgLen) {
receivedCheckSum = Serial1.read();
if(receivedCheckSum == msgType) {
retunValue = msgType;
}
} else {
// Wait to receive all bytes or return timeout
timeCheck = millis();
while(Serial1.available() < expectedMsgLen) {
// If we have a timeout, exit the function
if((millis() - timeCheck) > VL53L4CX_WAIT) {
// Reuse timeCheck
timeCheck = 0;
break;
}
}
// If we do not have a timeout
if(timeCheck) {
// Buffer to hold received values, START_MARKER not included
uint8_t rxBuffer[expectedMsgLen];
// Add message type as it is needed in check sum calculation
uint8_t checksum = msgType;
// Add all bytes in the buffer and calculate check sum in the same loop
for (uint16_t i = 0; i < expectedMsgLen; i++) {
rxBuffer[i] = Serial1.read();
// Calculate the check sum
checksum += rxBuffer[i];
// Serial.print(i); Serial.print(">>>"); Serial.println(rxBuffer[i]);
}
// By now we have received the last 2 bytes
receivedCheckSum = Serial1.read();
// Do not check for > end marker as we test the check sum
// Check message validity
if (checksum == receivedCheckSum) {
// What type of message we have?
switch (msgType) {
case RESPONSE_OK:
retunValue = RESPONSE_OK;
break;
case RESPONSE_FAIL:
// Handle failure response
retunValue = RESPONSE_FAIL;
break;
case RESPONSE_LENGTH:
// Process length data
{
mySensors[Level].val = (float)*(uint16_t*)&rxBuffer[0];
retunValue = RESPONSE_LENGTH;
}
break;
case RESPONSE_CAL_DONE:
{
// Process calibration data. From 0 to DEV_MAX we have the device data
for(uint16_t cnt = CAL_START; cnt < CAL_END; cnt++) {
EEPROM.write(cnt, rxBuffer[cnt - CAL_START]);
// Serial.print(cnt);Serial.print("---");Serial.print(cnt - CAL_START);
// Serial.print("---");Serial.println(rxBuffer[cnt - CAL_START]);
}
// No need to wait after commit
EEPROM.commit();
retunValue = RESPONSE_CAL_DONE;
}
break;
} // End switch
} // End CheckSum check
} // End timeout check
} // End longer message check
}
digitalWrite(LED_PIN, HIGH);
Serial1.flush();
return retunValue;
}
// Signal errors error
void ledSignal(uint8_t err) {
// Delay for MQTT functions
constexpr uint8_t stateDelay = 250;
uint8_t blinkCnt = 0;
switch(err) {
case 1:
blinkCnt = 3;
break;
case 2:
blinkCnt = 4;
break;
default:
blinkCnt = 2;
}
for(uint8_t c = 0; c < blinkCnt; c++){
digitalWrite(LED_PIN, HIGH);
delay(stateDelay);
digitalWrite(LED_PIN, LOW);
delay(stateDelay);
}
}
void writeDefaultEEPROM() {
for(uint8_t cnt = 0; cnt < DEV_MAX; cnt++) {
EEPROM.write(cnt, 0);
}
EEPROM.write(DEV_MAX, 1);
EEPROM.commit();
delay(delayMQTT);
}
void setup() {
constexpr uint8_t TX1_PIN = 17;
constexpr uint8_t RX1_PIN = 18;
analogSetAttenuation(ADC_2_5db);
analogReadResolution(12);
Serial1.begin(UART_SPEED, SERIAL_8N1, RX1_PIN, TX1_PIN);
#ifdef DEBUG
debug_begin(UART_SPEED);
#endif
// Serial.begin(9600);
EEPROM.begin(CAL_END);
// writeDefaultEEPROM();
pinMode(LED_PIN, OUTPUT);
pinMode(LEVEL_PIN, OUTPUT);
pinMode(CTRL_PIN2, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Level pin not used. The VL sensor is powerd all the time
digitalWrite(LEVEL_PIN, LOW);
digitalWrite(CTRL_PIN2, LOW);
// Initialize valves and sensors
initObjects();
// Load number of devices from EEPROM
DEV_IN_USE = EEPROM.read(DEV_MAX);
Serial1.flush();
// Wait for STM board to wakeUp. Getting here takes 115ms
while((millis() - timeCheck) < VL53L4CX_WAIT) {
if(ProcessReceivedMessage()) break;
delay(5);
}
// Now send the calibration data
sendToSTM(CMD_STORE_CAL);
// // Setup BMP581 temperature and pressure sensor
bmpInit();
// Setup and connect to WiFi
startWiFi();
// Set MQTT server IP and port
mqtt_Client.setServer(mqtt_server, MQTT_Port);
// Attach the MQTT callback function
mqtt_Client.setCallback(mqttCallback);
// Connect to MQTT broker
ConnectToMQTT();
if (!mqtt_Client.connected()) {
mqtt_reconnect();
}
// Timer initialization
// Set timer frequency to 1Mhz (default timer frequency is 80MHz)
timer = timerBegin(0, 80, true);
// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer, true);
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
timerAlarmWrite(timer, 1000000, true);
timerAlarmEnable(timer);
ledSignal(0);
crtButton = None;
}
void loop() {
// // Used to check ADC values
// if (mqttTime >= mqttPeriod) {
// uint16_t adc_val = analogRead(BTN_PIN);
// debugln(adc_val);
// mqttTime = 0;
// }
yield();
ProcessReceivedMessage();
mqtt_Client.loop();
// Is any button pressed?
getButtons();
if (crtButton) {
uint8_t tmp = crtButton - 1;
crtButton = None;
// Accept button press events only for devices that are used
if(tmp < DEV_IN_USE) {
// Is the device already turned ON?
if(myDevices[tmp].pinState) {
// If a button is pressed and device is ON then turn off the device, set the time and publish MQTT
deviceOnOff(tmp, Off, 1);
} else {
// If a button is pressed turn on the device, set the time and publish MQTT
deviceOnOff(tmp, On, 1);
}
}
}
// Check if any device needs to be turned Off
for (uint8_t cnt = 0; cnt < DEV_IN_USE; cnt++) {
// Target time is in Minutes, START_MARKER has a value of 60, so...
// If timeSeconds is not 0 and timeSeconds is equal or larger than target time in seconds
if ((myDevices[cnt].timeSeconds) && (myDevices[cnt].timeSeconds >= (myDevices[cnt].timeTarget * START_MARKER))) {
deviceOnOff(cnt, Off, 1);
}
}
// Read temperature, pressure and water level
if (mqttTime >= mqttPeriod) {
#ifdef HAS_PRESSURE
// Get data from Analog pressure sensor
getWaterPressure();
#endif
// Get data from onboard BMP581
getTempPress();
// Check if we have new data from VL53L4CX
ProcessReceivedMessage();
// Publish the topics
publishSensors();
// If water level is too low turn Off all devices, set the time to 0 and publish MQTT
if (mySensors[Level].val <= minWellLevel) {
for (int cnt = 0; cnt < DEV_IN_USE; cnt++) {
deviceOnOff(cnt, Off, 1);
}
}
mqttTime = 0;
}
// Check if calibration was requested
if(calibrateDistance) {
// Send request to STM board
sendToSTM(CMD_CALIBRATE);
// Wait for reply
delay(VL53L4CX_WAIT);
// If calibration is OK
if(ProcessReceivedMessage() == RESPONSE_CAL_DONE){
publishToMqtt(topic_CalDist, "1");
} else {
publishToMqtt(topic_CalDist, "0");
}
calibrateDistance = 0;
}
}
#ifdef HAS_PRESSURE
// Voltage divider 10k/3.2k
// V_In max = 4.5V, V_Div = 1.09V
// P = 0 Bar V = 0.121V
// P = 5 Bar V = 0.606V
// P = 10 Bar V = 1.09
// P = 10.313*V - 1.25
void getWaterPressure() {
// Transform ADC reading in volts
//float voltage = (float)adc1_get_raw(ADC1_CHANNEL_1) * ADC_FACTOR;
// Transform voltage in Bar
mySensors[WaterPress].val = (float)analogRead(PRESS_PIN) * VOLT_TO_BAR_F1 + VOLT_TO_BAR_F2;
#ifdef HAS_LEAK_CONTROL
if((mySensors[WaterPress].val - prevPressureVal) > pressureThreshold) {
digitalWrite(LEVEL_PIN, HIGH);
alarmLeak = 1;
myBackUp[0] = myDevices[0];
myBackUp[1] = myDevices[1];
// Cut off water supply. Valve 0 NC and 1 NO
myDevices[0].pinState = 0;
publishToMqtt(myDevices[0].topicState, String(myDevices[0].pinState));
digitalWrite(myDevices[0].pin, myDevices[0].pinState);
myDevices[1].pinState = 1;
publishToMqtt(myDevices[1].topicState, String(myDevices[1].pinState));
digitalWrite(myDevices[1].pin, myDevices[1].pinState);
publishToMqtt(topic_LeakAlarm, "1");
} else {
// If the alarm is no longer valid
if(alarmLeak) {
digitalWrite(LEVEL_PIN, LOW);
alarmLeak = 0;
myDevices[0] = myBackUp[0];
publishToMqtt(myDevices[0].topicState, String(myDevices[0].pinState));
digitalWrite(myDevices[0].pin, myDevices[0].pinState);
myDevices[1] = myBackUp[1];
publishToMqtt(myDevices[1].topicState, String(myDevices[1].pinState));
digitalWrite(myDevices[1].pin, myDevices[1].pinState);
publishToMqtt(topic_LeakAlarm, "0");
}
}
prevPressureVal = mySensors[WaterPress].val;
#endif
}
#endif
// Function to debounce the button
void getButtons() {
uint16_t adc_val = analogRead(BTN_PIN);
crtButton = None;
// Reset current button if adc_val is greater than a threshold
if (adc_val > BTN_ADC_MAX[9]) {
return;
} else {
delay(DEBOUNCE_DELAY);
adc_val = analogRead(BTN_PIN);
for (uint8_t cnt = 0; cnt < DEV_MAX; cnt++) {
if((adc_val > BTN_ADC_MIN[cnt]) && (adc_val < BTN_ADC_MAX[cnt])) {
crtButton = (cnt + 1);
return;
}
}
}
}
//Turns On or Off a device and updates MQTT topic
void deviceOnOff(uint8_t idx, uint8_t State, uint8_t sendMessage) {
myDevices[idx].pinState = State;
// If the state is On
if (State) {
digitalWrite(myDevices[idx].pin, HIGH);
} else {
digitalWrite(myDevices[idx].pin, LOW);
}
myDevices[idx].timeSeconds = 0; // resetTimer
if (sendMessage) {
publishToMqtt(myDevices[idx].topicState, String(myDevices[idx].pinState));
}
}
//Publish value to MQTT topic
void publishToMqtt(const char* mqtt_topic, String mqtt_value) {
if (!mqtt_Client.publish(mqtt_topic, mqtt_value.c_str())) {
mqtt_Client.connect(clientID, mqtt_username, mqtt_password);
delay(delayMQTT);
mqtt_Client.publish(mqtt_topic, mqtt_value.c_str());
}
delay(delayMQTT);
}
void publishTopics() {
// Publis and subscribe for each valve
for (uint8_t cnt = 0; cnt < DEV_IN_USE; cnt++) {
publishToMqtt(myDevices[cnt].topicState, String(myDevices[cnt].pinState));
delay(delayMQTT);
mqtt_Client.subscribe(myDevices[cnt].topicState);
delay(delayMQTT);
publishToMqtt(myDevices[cnt].topicTime, String(myDevices[cnt].timeTarget));
delay(delayMQTT);
mqtt_Client.subscribe(myDevices[cnt].topicTime);
delay(delayMQTT);
}
#ifdef HAS_LEAK_CONTROL
publishToMqtt(topic_LeakAlarm, "0");
delay(delayMQTT);
mqtt_Client.subscribe(topic_LeakAlarm);
delay(delayMQTT);
publishToMqtt(topic_LeakValue, String(pressureThreshold));
delay(delayMQTT);
mqtt_Client.subscribe(topic_LeakValue);
delay(delayMQTT);
#endif
// Publish/subscribe the calibration Distance, should be 0
publishToMqtt(topic_CalDist, String(calibrateDistance));
delay(delayMQTT);
mqtt_Client.subscribe(topic_CalDist);
delay(delayMQTT);
}
void publishSensors() {
for (uint8_t cnt = 0; cnt < SENSORS_NO; cnt++) {
publishToMqtt(mySensors[cnt].name, String(mySensors[cnt].val));
}
}
// Connect to MQTT brocker
void ConnectToMQTT() {
if (mqtt_Client.connect(clientID, mqtt_username, mqtt_password)) {
publishSensors();
publishTopics();
publishToMqtt(maxDevNo, String(DEV_IN_USE));
delay(delayMQTT);
mqtt_Client.subscribe(maxDevNo);
} else {
ledSignal(2);
}
}
//Reconnect to MQTT brocker
void mqtt_reconnect() {
uint8_t retry = 10;
while (!mqtt_Client.connected() && retry) {
ConnectToMQTT();
retry--;
}
}
void setNewDevNo() {
EEPROM.write(DEV_MAX, DEV_IN_USE);
EEPROM.commit();
for(uint8_t cnt = DEV_IN_USE; cnt < DEV_MAX; cnt++) {
deviceOnOff(cnt, Off, 0);
mqtt_Client.unsubscribe(myDevices[cnt].topicState);
mqtt_Client.unsubscribe(myDevices[cnt].topicTime);
delay(delayMQTT);
}
// To update the devices
publishTopics();
}
// MQTT callback function
void mqttCallback(char* topic, byte* payload, unsigned int length) {
debug("Message arrived [");
debug(topic);
debugln("]");
String messageTemp;
for (int i = 0; i < length; i++) {
messageTemp += (char)payload[i];
}
// If we get calibration value of 1
if (strcmp(topic, topic_CalDist) == 0) {
calibrateDistance = messageTemp.toInt();
}
#ifdef HAS_LEAK_CONTROL
if (strcmp(topic, topic_LeakValue) == 0) {
pressureThreshold = messageTemp.toFloat();
}
#endif
// If we change the number of devices
if (strcmp(topic, maxDevNo) == 0) {
DEV_IN_USE = messageTemp.toInt();
if(DEV_IN_USE > DEV_MAX) DEV_IN_USE = DEV_MAX;
if(DEV_IN_USE < 1) DEV_IN_USE = 1;
setNewDevNo();
} else {
for (uint8_t cnt = 0; cnt < DEV_IN_USE; cnt++) {
// If the device state is changed
if (strcmp(topic, myDevices[cnt].topicState) == 0) {
myDevices[cnt].pinState = messageTemp.toInt();
deviceOnOff(cnt, myDevices[cnt].pinState, 0);
}
// If the device time is changed
if (strcmp(topic, myDevices[cnt].topicTime) == 0) {
myDevices[cnt].timeTarget = messageTemp.toInt();
EEPROM.write(cnt, myDevices[cnt].timeTarget);
EEPROM.commit();
delay(delayMQTT);
}
}
}
}
// Wi-Fi connection SetUp - Wasted a day with connection issues because of buggy library
void startWiFi() {
// WiFi network name
constexpr char *ssid = "WIFI_NETWORK_NAME";
// WiFi network password
constexpr char *password = ""WIFI_NETWORK_PASSWORD";
WiFi.mode(WIFI_STA);
WiFi.hostname(clientID);
// debugln(WiFi.macAddress());
WiFi.useStaticBuffers(true);
// WiFi.setMinSecurity(WIFI_AUTH_WPA_PSK); //WIFI_AUTH_WEP
WiFi.disconnect();
delay(WiFi_Period);
WiFi.begin(ssid, password);
// Wait to connect to WiFi
// WiFi.waitForConnectResult(1000);
while (WiFi.waitForConnectResult(WiFi_Period) != WL_CONNECTED) {
ledSignal(1);
}
}
// Initialize devices, sensors
void initObjects() {
constexpr char* deviceTopics[] = {
"water/State1", "water/State2", "water/State3", "water/State4", "water/State5",
"water/State6", "water/State7", "water/State8", "water/State9", "water/State10",
"water/Time1", "water/Time2", "water/Time3", "water/Time4", "water/Time5",
"water/Time6", "water/Time7", "water/Time8", "water/Time9", "water/Time10"
};
constexpr uint8_t pins[] = {10, 11, 12, 13, 14, 21, 47, 48, 35, 36};
for (uint8_t cnt = 0; cnt < DEV_MAX; cnt++) {
myDevices[cnt].topicState = deviceTopics[cnt];
myDevices[cnt].topicTime = deviceTopics[cnt + 10];
myDevices[cnt].timeTarget = EEPROM.read(cnt);
myDevices[cnt].timeSeconds = 0;
myDevices[cnt].pin = pins[cnt];
pinMode(myDevices[cnt].pin, OUTPUT);
digitalWrite(myDevices[cnt].pin, LOW);
}
constexpr char* sensorNames[] = {
"water/Level", "water/AirTemperature", "water/AirPressure", "water/WaterPressure"
};
for (uint8_t cnt = 0; cnt < SENSORS_NO; cnt++) {
mySensors[cnt].name = sensorNames[cnt];
mySensors[cnt].val = 0.0f;
}
}
// Initialize BMP581 sensor
void bmpInit() {
constexpr uint32_t oorCenter = 101325;
constexpr uint8_t oorWindow = 255;
constexpr uint8_t SDA_Pin = 8;
constexpr uint8_t SCL_Pin = 9;
constexpr uint8_t INT_PIN = 16;
Wire.begin(SDA_Pin, SCL_Pin);
while (pressureSensor.beginI2C(BMP581_I2C_ADDRESS_SECONDARY) != BMP5_OK) {
debugln("Error: BMP581 not connected, check wiring and I2C address!");
delay(WiFi_Period);
}
int8_t err = BMP5_OK;
err = pressureSensor.setODRFrequency(BMP5_ODR_01_HZ);
if (err != BMP5_OK) {
debugln("ODR setting failed! Error code: ");
debugln(err);
}
bmp5_oor_press_configuration oorConfig {
.oor_thr_p = oorCenter,
.oor_range_p = oorWindow,
.cnt_lim = BMP5_OOR_COUNT_LIMIT_3,
.oor_sel_iir_p = BMP5_DISABLE
};
pressureSensor.setOORConfig(&oorConfig);
BMP581_InterruptConfig interruptConfig = {
.enable = BMP5_INTR_ENABLE,
.drive = BMP5_INTR_PUSH_PULL,
.polarity = BMP5_ACTIVE_HIGH,
.mode = BMP5_PULSED,
.sources = {
.drdy_en = BMP5_ENABLE,
.fifo_full_en = BMP5_DISABLE,
.fifo_thres_en = BMP5_DISABLE,
.oor_press_en = BMP5_DISABLE
}
};
err = pressureSensor.setInterruptConfig(&interruptConfig);
if (err != BMP5_OK) {
debugln("Interrupt settings failed! Error code: ");
debugln(err);
}
attachInterrupt(INT_PIN, bmp581InterruptHandler, RISING);
}
// Get temperature and pressure from BMP581
void getTempPress() {
if (interruptBMP) {
interruptBMP = 0;
int8_t err = BMP5_OK;
uint8_t interruptStatus = 0;
err = pressureSensor.getInterruptStatus(&interruptStatus);
if (err != BMP5_OK) {
debugln("Get interrupt status failed! Error code: ");
debugln(err);
return;
}
if (interruptStatus & BMP5_INT_ASSERTED_DRDY) {
bmp5_sensor_data data = {0, 0};
err = pressureSensor.getSensorData(&data);
if (err == BMP5_OK) {
mySensors[Temp].val = data.temperature;
mySensors[Press].val = data.pressure;
} else {
debug("Error getting data from sensor! Error code: ");
debugln(err);
}
}
if (interruptStatus & BMP5_INT_ASSERTED_PRESSURE_OOR) {
debugln("Out of range condition triggered!");
}
if (!(interruptStatus & (BMP5_INT_ASSERTED_PRESSURE_OOR | BMP5_INT_ASSERTED_DRDY))) {
debugln("Wrong interrupt condition!");
}
}
}
Comments powered by CComment