Components and supplies
BTF-LIGHTING 5 Pin Electrical Connector 24AWG IP65 Male Female Connector 7.87in/20cm Extension Cable for Car
1/4" DC 12V Solenoid Valve N/C Normally Closed Water Inlet Flow Switch
12V Power Supply - RXZ12V2A-A
URATOT 60 Pieces 360 Degree Adjustable Irrigation Drippers with Barbed Connector
Kalolary irrigation fittings
Raindrip 016010T 1/4-Inch by 100-Feet Black Tubing
Favordrory 6 Pieces 304 Stainless Steel 3mm x 300mm Model Straight Metal Round Shaft Rods
Arduino Nano R3
3D Printer Heatsink Kit + Thermal Conductive Adhesive Tape, Cooler Heat Sink Cooling TMC2130 TMC2100 A4988 DRV8825 TMC2208 Stepper Motor Driver Module (22pcs)
22AWG 2 Conductor Wire, 20M/65.6ft Flexible Black PVC Jacketed Hookup Wire
22 Gauge 2 Conductor Electrical Wire, 20M/65.6ft Flexible Black PVC Jacketed Hookup Wire, 22 AWG Tinned Copper Extension Cord
Irwin Hanson 6-32NC Self-Aligning Die
Ogrmar ABS Plastic Dustproof Waterproof IP65 Junction Box Universal Durable Electrical Project Enclosure With Lock (8.6"x6.7"x4.3")
DZS Elec 2-Pack 5V 4-Channel Relay Module with Optocoupler Active Low Level Amplifier Trigger JD-VCC Relay Power VCC Power
Raindrip R325C 3/4-Inch Hose Thread Swivel to 1/4-Inch Tubing Adaptor
10pcs NRF24L01+ 2.4GHz Wireless RF Transceiver Module
Arduino Mega 2560
HiLetgo ILI9341 2.8" SPI TFT LCD Display Touch Panel 240X320 with PCB 5V/3.3V STM32
Lantee PG 9 Cable Gland
MAKERELE waterproof outdoor enclosure
Tools and machines
Soldering iron (generic)
3D Printer (generic)
Project description
Code
Nano Irrigation_System2.96.ino
c_cpp
1#define VERSION "2.96" 2/* REVISION HISTORY 3 * Written by Paul 4 M. Zavracky 5 * Borrows heavily from the work of zillions of others 6 * 7 8 * The irrigation system comprises a set of 1 to 8 1/4 12v water valves, a moisture 9 sensor, an arduino, 10 * an NRF24 Radio, and 8 relays. The Arduino takes both 11 the digital and analog signals from the 12 * moisture sensor and uses this information 13 to determine if watering is needed. If so, the Arduino 14 * sends a signal to 15 the relay board, actualing the appropriate relay. The relay connects a 12V supply 16 17 * to the water valve. The Arduino continues monitoring the moisture sensor. Once 18 a predetermined level 19 * is reached, the Arduino turns off the water flow. 20 21 * Uses Larry Bank's Bit Bang I2C library. You can find it here: 22 * https://github.com/bitbank2/BitBang_I2C 23 24 */ 25 26// setup for MCP23008 port expander 27// definitions for mcp23008 28#define 29 MCP23008_ADDR 0x23 //Address of MCP23008 30#define IODIR 0x00 // I/O DIRECTION 31 REGISTER 32#define IPOL 0x01 // INPUT POLARITY PORT REGISTER 33#define GPINTEN 34 0x02 // INTERRUPT-ON-CHANGE PINS 35#define DEFVAL 0x03 // DEFAULT VALUE REGISTER 36#define 37 INTCON 0x04 // INTERRUPT-ON-CHANGE CONTROL REGISTER 38#define OCON 0x05 // I/O 39 EXPANDER CONFIGURATION REGISTER 40#define GPPU 0x06 // GPIO PULL-UP RESISTOR REGISTER 41#define 42 INTF 0x07 // INTERRUPT FLAG REGISTER 43#define INTCAP 0x08 // INTERRUPT CAPTURED 44 VALUE FOR PORT REGISTER 45#define GPIO 0x09 // GENERAL PURPOSE I/O PORT REGISTER 46#define 47 OLAT 0x0A // OUTPUT LATCH REGISTER 0 48 49// setup for MySensors 50#define MY_NODE_ID 51 30 52//#define MY_DEBUG // comment out this line to remove MySensors use of serial 53 port!!! 54#define SERIAL_TEST true // d0 and d1 can not be used if the serial bus 55 is active!!! 56 57#define MY_PARENT_NODE_ID 0 58#define MY_PARENT_NODE_IS_STATIC 59 60// 61 Enable and select radio type attached 62#define MY_RADIO_RF24 63#define MY_RF24_PA_LEVEL 64 RF24_PA_LOW 65 66// Set this to the pin you connected the Water Leak Detector's 67 analog pins to 68// The Analog pins are used to sense the moisture level 69#define 70 AI0_PIN A0 71#define AI1_PIN A2 72#define AI2_PIN A1 73#define AI3_PIN A3 74#define 75 AI4_PIN A4 76#define AI5_PIN A5 77#define AI6_PIN A6 78#define AI7_PIN A7 79 80// 81 Arduino output pin setup 82#define INTERRUPT_PIN 8 // For MCP23008, pins 8 - 5 83#define 84 RESET_PIN 7 85#define SDA_PIN 6 86#define SCL_PIN 5 87#define POWER_PIN 4 // 88 Sensor Power Pin 89#define FLOW_PIN 3 // Flow sensor input pin 90#define MCP_PIN5 91 2 // IRQ for NF24 Radio 92#define MCP_PIN6 1 // Currently unused. This pin and 93 the one below will not work in SERIAL_MODE OR MY_DEBUG are true!!! 94#define MCP_PIN7 95 0 96 97// analog inputs 98#define CHILD_ID_START_OF_AI 0 99#define CHILD_ID_AI0 100 0 101#define CHILD_ID_AI1 1 102#define CHILD_ID_AI2 2 103#define CHILD_ID_AI3 3 104#define 105 CHILD_ID_AI4 4 106#define CHILD_ID_AI5 5 107#define CHILD_ID_AI6 6 108#define CHILD_ID_AI7 109 7 110 111// digital outputs (switches in HA) 112#define CHILD_ID_START_OF_RELAYS 113 10 114#define CHILD_ID_DO0 10 115#define CHILD_ID_DO1 11 116#define CHILD_ID_DO2 117 12 118#define CHILD_ID_DO3 13 119#define CHILD_ID_DO4 14 120#define CHILD_ID_DO5 121 15 122#define CHILD_ID_DO6 16 123#define CHILD_ID_DO7 17 124 125//Auto/Manual Mode 126 Switches 127#define CHILD_ID_START_OF_AUTOS 20 128#define CHILD_ID_AutoMan0 20 129 130#define CHILD_ID_AutoMan1 21 131#define CHILD_ID_AutoMan2 22 132#define CHILD_ID_AutoMan3 133 23 134#define CHILD_ID_AutoMan4 24 135#define CHILD_ID_AutoMan5 25 136#define CHILD_ID_AutoMan6 137 26 138#define CHILD_ID_AutoMan7 27 139 140#define CHILD_ID_FLOW 30 141#define CHILD_ID_DAY_NIGHT 142 31 // day night sensor gets data from Home Assistant 143#define CHILD_ID_LOW_FLOW 144 32 // low flow alarm indicator for HA - sends alarm, shuts down system 145#define 146 CHILD_ID_OPEN_SENSOR 33 // open sensor alarm indicator for HA - sends alarm, shuts 147 down system 148#define CHILD_ID_SAFEFLOW_SWITCH 34 // if there is an automatic shutdown 149 (safeFlow = false), this switch let's the system restart (safeFlow = true) 150 151// 152 remeber that fully dry give 1023 reading 153#define MAX_VALVE_TIME 5 // maximum 154 time a valve can stay on in minutes. If moisture level hasn't dropped on next cycle, 155 valve will 156 // turn on againg 157#define VALVES_OFF_MOISTURE_MEASUREMENT_TIME 158 15 // Time in minutes between moisture sensor readings when no valves are open. 159#define 160 VALVES_ON_MOISTURE_MEASUREMENT_TIME 0.1 // Time in minutes between moisture sensor 161 readings when a valve is open. 162 163// remember that fully dry gives 1023 reading 164// 165 you calculate the values for the following targets from the desired moisture level 166 as follows: 167// TARGET = 1024/(Rseries/(151.4*M%^-3.7) + 1), where M% is the fractional 168 moisture level -- 60% = 0.6 169// So, for a range of 40% to 60%: (1000 is the value 170 of the series resistor!) 171// 1024/(1000/(151.4(.4)^-3.7) + 1) = 837 172// 1024/(1000/(151.4(.6)^-3.7) 173 +1) = 512 174// If you are not using Aref by connecting the sensor drive signal 175 to Aref, then check the sensor open circuit value for AI 176// In my case, that 177 value is 973, so 837*973/1024 = 765 and 512*972/1024 = 486.5 178#define TARGET_FULL_MOISTURE 179 487 // maximum expected reading for fully irrigated soil (soil is wet enough) 180#define 181 TARGET_MIN_MOISTURE 837 // minimum expected reading for fully irrigated soil (soil 182 is too dry) 183#define NUM_VALVES 6 // the total numbeer of valves (max 8) 184 185#define 186 RELAY_ON 1 187#define RELAY_OFF 0 188#define K 2.28 // 1380 pulses/liter found online. 189 Max Flow through 30' should be about 5 GPH or 20 liters/hour or 0.33 liters/min 190 or .0055 liters per sec. 191 // That implies about 8 pulses per 192 second. Some report closer to 30 Hz? Our unit should be milliliters per second, 193 so K should be 1.38. 194 // For the sensor used in this project, 195 the reported value K = 1.38 was too small. Flow (pulsed based) yeilded 73, for 196 44 ml/sec. 197 // In this code, flowRate = count/2K, where counts 198 are made on both rising and falling edges. Flow rate should equal flowRate/1.65, 199 so 200 // Knew = 1.28 * 1.65 or 2.28. 201 202//#define Rseries 1000.00 203 // series resistance used in moisture measurement 204// load libraries 205 206#include 207 <SPI.h> 208#include <MySensors.h> 209#include <BitBang_I2C.h> 210 211// Start I2C 212BBI2C 213 bbi2c; 214 215// Variables Definitions 216uint16_t moisture_Level[NUM_VALVES]; 217 // moisture level is just the binary voltage reading at the analog input. It's 218 converted to % by Home Assistant. 219 // Convertion 220 equation: M% = (((((1000.0 / (924 / AnalogReading - 1)))/151.35)^(-0.27))*100) 221 222 // where 924 is the maximum open circuit sensor 223 reading. (on next itteration will tie drive voltage to Vref! 224uint16_t oldmoisture_Level[NUM_VALVES] 225 = {0}; // initialize all old data to zero 226uint16_t startMoistureLevel = 1024; 227 // used for sensor testing 228 229boolean newLoop = true; // to update HA after 230 power outage 231//unsigned long oldNoDataTime = 0; // used to check for no data 232 to force communication with HA 233unsigned long oldValveOnTime = 0; // used to control 234 the amount of time a valve is open 235unsigned long oldNoMoistureTime = 0; // minutes 236 between moisture measurements when no valve is on 237 238byte AI_pin[] = {AI0_PIN, 239 AI1_PIN, AI2_PIN, AI3_PIN, AI4_PIN, AI5_PIN, AI6_PIN, AI7_PIN}; 240byte CHILD_ID_AI[] 241 = {CHILD_ID_AI0, CHILD_ID_AI1, CHILD_ID_AI2, CHILD_ID_AI3, CHILD_ID_AI4, CHILD_ID_AI5, 242 CHILD_ID_AI6, CHILD_ID_AI7}; 243byte CHILD_ID_DO[] = {CHILD_ID_DO0, CHILD_ID_DO1, 244 CHILD_ID_DO2, CHILD_ID_DO3, CHILD_ID_DO4, CHILD_ID_DO5, CHILD_ID_DO6, CHILD_ID_DO7}; 245byte 246 CHILD_ID_AutoMan[] = {CHILD_ID_AutoMan0, CHILD_ID_AutoMan1, CHILD_ID_AutoMan2, CHILD_ID_AutoMan3, 247 248 CHILD_ID_AutoMan4, CHILD_ID_AutoMan5, CHILD_ID_AutoMan6, CHILD_ID_AutoMan7}; 249 250bool 251 autoMan_State[NUM_VALVES] = {false}; // man if false, auto if true 252bool ack = 253 1; 254float flowRate = 0.0; 255//float Rsoil = 0.0; 256bool safeFlow = true; // 257 if flow measures to long, the system will set this flag false and prevent valves 258 from being actuated (prevent over heating) 259bool lowFlow = false; // if the flow 260 is too low, the system sets this flag and shuts down. HA can see this flag. 261bool 262 openSensor = false; // if a sensor is open and doesn't responde to irrigation, the 263 system sets this flag and shuts down. HA can see this flag. 264byte flowTestCount 265 = 0; // keeps track of the number of zero flow measurements made - system shuts 266 down after NUM_FLOW_TESTS 267bool dayNight = false; // if true, it is daytime and 268 the system should not water plants 269uint8_t bitValue[] = {1,2,4,8,16,32,64,128}; 270 271// 272 MySensors messages 273MyMessage msgAI[NUM_VALVES]; // these messages tell HA the 274 moisture level 275MyMessage msgGPIO[NUM_VALVES]; // these messages let HA know what 276 relays 277MyMessage msgAutoMan[NUM_VALVES]; // these messages let HA know what relays 278MyMessage 279 msgFlow(CHILD_ID_FLOW, V_FLOW); 280MyMessage msgDayNight(CHILD_ID_DAY_NIGHT, V_TRIPPED); 281 // used to prevent watering during the day 282MyMessage msgLowFlow(CHILD_ID_LOW_FLOW, 283 V_TRIPPED); // used to let HA know that we have no water flow 284MyMessage msgOpenSensor(CHILD_ID_OPEN_SENSOR, 285 V_TRIPPED); // used to let HA know that we may have an open sensor 286MyMessage 287 msgRestart(CHILD_ID_SAFEFLOW_SWITCH, V_STATUS); // safeMode switch, shut down due 288 to system failure, restart through HA 289 290void setup(){ 291 // setup MCP23008 292 for output 293 pinMode(RESET_PIN, OUTPUT); 294 digitalWrite(RESET_PIN, 1); // 295 reset pin on mcp23008 296 pinMode(POWER_PIN, OUTPUT); 297 digitalWrite(POWER_PIN, 298 false); // turn off sensor power, power direct from pin here!!!! 299 pinMode(INTERRUPT_PIN, 300 INPUT); 301 302 //Setup inputs and outputs 303 for (int i = 0; i < NUM_VALVES; 304 i++) { 305 pinMode(AI_pin[i], INPUT); 306 } 307 pinMode(FLOW_PIN, INPUT_PULLUP); 308 // flow sensor requires the input have a pullup resistor 309 310 // initialize 311 messages 312 for (int i = 0; i < NUM_VALVES; i++) { 313 msgAI[i].sensor = i; 314 // Analog sensors start at child ID 0 315 msgAI[i].type = V_LEVEL; // these are 316 the moisture sensors 317 msgGPIO[i].sensor = i + 10; // Relays start at child 318 ID 10 319 msgGPIO[i].type = V_STATUS; // these are the sprinkler valves 320 msgAutoMan[i].sensor 321 = i + 20; // AutoMan switches start at child ID 20 322 msgAutoMan[i].type = V_STATUS; 323 // these are the switch to set mode 324 } 325 326 // I2C initialization 327 328 memset(&bbi2c, 0, sizeof(bbi2c)); 329 bbi2c.bWire = 0; // use bit bang, not wire 330 library 331 bbi2c.iSDA = SDA_PIN; 332 bbi2c.iSCL = SCL_PIN; 333 I2CInit(&bbi2c, 334 100000L); 335 delay(100); // allow devices to power up 336 337 // I2C ready to 338 go, set up for output 339 writeRegister(IODIR, 0); // make all GPIO pins outputs 340 341 writeRegister(IPOL, 0); // make all GPIO pins not inverted 342 writeRegister(GPIO, 343 255); // turn all bits on, all valves off (valves are active low!) 344} 345 346void 347 presentation() 348{ 349 // Send the sketch version information to the gateway 350 351 sendSketchInfo("IRRIGATION SYSTEM ", VERSION); 352 Serial.print("Irrigation 353 System, 2021, Version ");Serial.println(VERSION); // this line will identify version 354 even when MySensor Debug is turned off. 355 356 // Register all sensors to gw (they 357 will be created as child devices) 358 for (int i = 0; i < NUM_VALVES; i++) { 359 360 present(CHILD_ID_AI[i], S_MOISTURE); 361 present(CHILD_ID_DO[i], S_SPRINKLER); 362 363 present(CHILD_ID_AutoMan[i], S_LIGHT); 364 } 365 present(CHILD_ID_FLOW, S_WATER, 366 "Water Flow Rate"); 367 present(CHILD_ID_DAY_NIGHT, S_DOOR); // used to prevent 368 watering during the day - binary sensor 369 present(CHILD_ID_LOW_FLOW, S_DOOR); 370 // used to let HA know that we have no water flow - binary sensor 371 present(CHILD_ID_OPEN_SENSOR, 372 S_DOOR); // used to let HA know that we have an open sensor - binary sensor 373 374 present(CHILD_ID_SAFEFLOW_SWITCH, S_LIGHT); // if off, system is idle, if one, 375 system is active 376 377 send(msgRestart.set(false)); 378} 379 380void loop() 381 382{ 383 if (newLoop) { updateAll(); newLoop = false; } // make sure HA 384 is aware of current state after powerdown 385 // 386 and collect moisture levels 387 if (!dayNight) { // if !dayNight = true, it's night 388 time and good to go 389 if (safeFlow) { 390 // check time to see if we need 391 a moisture reading - different delay during watering 392 if (millis() < oldNoMoistureTime) 393 { oldNoMoistureTime = 0; } // millis() can roll over making it smaller than oldNoDataTime 394 395 if ((millis()- oldNoMoistureTime) > ((isAnyValveOn()?1:0)*VALVES_ON_MOISTURE_MEASUREMENT_TIME 396 + 397 (isAnyValveOn()?0:1)*VALVES_OFF_MOISTURE_MEASUREMENT_TIME) 398 * 60000) { // send data to HA to let it know where still alive 399 oldNoMoistureTime 400 = millis(); // update time 401 newLoop = true; // forces an update to HA 402 403 readMoisture(); // reads all sensors 404 flowRate = flowMeasurement(); 405 // this is not the only place where a flowrate measurement is made; check getLastFlowRate() 406 - used when closing valves 407 } 408 // open and close valves automatically 409 when set by autoMan_State and send moisture readings to HA when appropriate 410 411 // valves are turned on and off manually through the receive() function!! 412 413 for (int i = 0; i < NUM_VALVES; i++) { 414 if (abs(moisture_Level[i] 415 - oldmoisture_Level[i]) > 50 ) { // only send data upon change of state 416 send(msgAI[i].set(moisture_Level[i],1)); 417 // will automatically send all moisture_levels on first newLoop 418 oldmoisture_Level[i] 419 = moisture_Level[i]; 420 } 421 if (autoMan_State[i] == true) { // 422 must be in auto mode to change valve state 423 if ((moisture_Level[i] < 424 TARGET_FULL_MOISTURE) && valveState(i) == true) { 425 writeRegister(GPIO, 426 255); // turn all bits on, all valves off (valves are active low!) 427 send(msgGPIO[i].set(valveState(i))); 428 429 getLastFlowRate(); // valves are closed, make sure flow has stopped 430 431 } 432 if ((moisture_Level[i] > TARGET_MIN_MOISTURE) && !isAnyValveOn()) 433 { // only turn on if all other valves are off 434 writeNotGPIO(i, true); 435 // turn on water (valves are active low, but writeNotGPIO does inversion) 436 send(msgGPIO[i].set(valveState(i))); 437 438 startMoistureLevel = moisture_Level[i]; // save for sensor check below 439 440 oldValveOnTime = millis(); // set start time for valve[i] on duration. 441 only one valve can be on at a time, so we need only one oldValveOnTime! 442 443 } 444 } 445 } 446 // Check flow and moisture response 447 if valve is on 448 if (isAnyValveOn() == true) { // one of the valves is on, 449 let's check to see if moisture sensor and the flow meter are responding 450 if 451 (millis() < oldValveOnTime) { oldValveOnTime = 0; } // millis() can roll over making 452 it smaller than oldNoDataTime 453 if ((millis()- oldValveOnTime) 454 > MAX_VALVE_TIME * 60000) { // time to turn off the valve to prevent over watering 455 456 if (flowRate < 5.0) { // oops low flow 457 lowFlow = true; 458 459 send(msgOpenSensor.set(true)); // let HA know the system is shutting 460 down due to an open sensor 461 safeFlowShutdown(); 462 } 463 464 else if(startMoistureLevel >= oldmoisture_Level[whichValveIsOn()] ) { 465 // if the measured moisture level hasn't dropped 466 openSensor = true; 467 468 send(msgLowFlow.set(true)); // send alarm to HA to indicate system shut 469 down. 470 safeFlowShutdown(); // shutdown system and let 471 HA know valves are all off 472 } 473 } 474 } 475 else 476 { oldValveOnTime = millis(); } // resets every loop cycle if no valve is on 477 478 } 479 } 480} 481 482void receive(const MyMessage &message) { 483 byte relayPinIndex; 484 485 486 if (message.isAck()) { 487 // if (SERIAL_TEST) {Serial.println("This 488 is an ack from gateway");} 489 } 490 if (message.type == V_STATUS) { // possible 491 sensors are: 492 // Digital IO -> 10 to 17 493 494 // AutoMan Switches -> 20 - 27 495 // 496 SafeFlow Switch -> 34 497 if (message.sensor < 18 && message.sensor > 9) { 498 // must be a relay 499 // Change relay state 500 // Calculate relay from 501 sensor number 502 relayPinIndex = message.sensor - CHILD_ID_START_OF_RELAYS; 503 // relayPinIndex[0] thru relayPinIndex[7] 504 if (!autoMan_State[relayPinIndex] 505 && safeFlow) { // only change relay state if in manual mode and safeFlow 506 writeNotGPIO(relayPinIndex, 507 message.getBool()); // turns off all valves and sets state of valve(relayPinIndex) 508 true or false 509 if(!message.getBool()) getLastFlowRate(); // make sure 510 we get a flow measurement after valves are turned off 511 for (int i = 0; 512 i < NUM_VALVES; i++) { // set all valveStates except valveState(relayPinIndx) to 513 false. 514 send(msgGPIO[i].set(valveState(i))); 515 } 516 } 517 518 else if(!safeFlow) { 519 for (int i = 0; i < NUM_VALVES; i++) { // 520 turn off all valves 521 writeNotGPIO(i, false); // turn off all valves 522 523 send(msgGPIO[i].set(false)); // don't know why this is necessary 524 } 525 526 getLastFlowRate(); // make sure we get a flow measurement after valves are 527 turned off 528 } 529 } 530 else if (message.sensor < 28 && message.sensor 531 > 19){ // must be an AutoMan Switch 532 autoMan_State[message.sensor - 20] 533 = message.getBool(); // this means state was changed by HASS 534 } 535 else 536 if (message.sensor == 34) { // HA requesting SafeFlow reset 537 if(message.getBool() 538 == true) { 539 safeFlow = true; // user can only turn switch on. here it's 540 turned off again after one second. 541 lowFlow = false; 542 openSensor 543 = false; 544 delay(1000); 545 send(msgRestart.set(false)); // turn 546 off the HA switch 547 oldValveOnTime = millis(); 548 } 549 } 550 551 } 552 if (message.type == V_TRIPPED) { // recieved dayNight state from HA 553 554 if (message.sensor == 31) { // this is the day night switch that HA sends to 555 prevent daytime watering 556 dayNight = message.getBool(); // if true, it's 557 daytime, do not water 558 } 559 } 560} 561 562// Update All 563void updateAll() 564{ 565 566 for (int i = 0; i < NUM_VALVES; i++) { 567 send(msgGPIO[i].set(valveState(i))); 568 569 send(msgAI[i].set(moisture_Level[i],1)); 570 oldmoisture_Level[i] = moisture_Level[i]; 571 572 send(msgAutoMan[i].set(autoMan_State[i])); 573 } 574 send(msgFlow.set(flowRate,2)); 575 576 send(msgLowFlow.set(lowFlow)); // used to let HA know that we have no water flow 577 578 send(msgOpenSensor.set(openSensor)); // used to let HA know that we may have an 579 open sensor 580} 581 582bool isAnyValveOn() { 583 bool result = true; 584 585 586 if(readRegister(GPIO) == 255 ) {result = false;} 587 // Serial.print("isAny 588 = ");Serial.println(result);Serial.print("GPIO = ");Serial.println(readRegister(GPIO)); 589 590 return(result); 591} 592 593bool valveState(uint8_t whichBit) { 594 return(!bitRead(readRegister(GPIO), 595 whichBit)); 596} 597 598byte whichValveIsOn() { 599 for (byte i = 0; i < NUM_VALVES; 600 i++) { 601 if(valveState(i)) {return(i);} 602 } 603} 604 605void readMoisture() 606 { 607 // read all the moisture levels, but don't send to HA 608 digitalWrite(POWER_PIN, 609 true); // Turn on sensor power; powers all sensors; active low 610 delay(10); //delay 611 10 milliseconds to allow reading to settle - determined by testing 612 for (int 613 i = 0; i < NUM_VALVES; i++) { // read all sensors 614 moisture_Level[i] = analogRead(AI_pin[i]); 615 // update moisture levels 616 } 617 digitalWrite(POWER_PIN, false); // turn 618 power off to sensors again 619} 620 621float flowMeasurement() { // take a second 622 to check flow rate 623 // this is a crude method used here 624 because there are no interrupt pins left 625 // in this 626 implementation. At the max reported flow rate through 1/4" drip 627 // 628 tubing 0f 20 GPH, we can anticipate at pulse frequency of 30 Hz. That's 629 // 630 slow enough to make this routine work well. 631 632 bool state = digitalRead(FLOW_PIN); 633 634 bool oldState = state; 635 bool notDone = true; 636 unsigned long int count 637 = 0; 638 unsigned long int oldMillis = millis(); 639 640 while (notDone) { 641 642 state = digitalRead(FLOW_PIN); 643 if (state != oldState) { 644 oldState 645 = state; 646 count++; 647 } 648 if( oldMillis > millis()) {oldMillis 649 = 0;} // rollover event 650 notDone = (millis() - oldMillis < 1000); // 1000 651 = 1 sec: when this is false, we are done 652 } 653 flowRate = count/(2*K); // 654 gets two counts per square wave; but rising and falling! 655 return(flowRate); 656 // K is the pulses per liter (1380), so we are returning the flow rate in liters 657 per second 658 // Should be 0.022 liters per second or there abouts. 659} 660 661void 662 getLastFlowRate() { 663 delay(2000); // delay to let valve close and flow rate 664 to settle 665 flowRate = flowMeasurement(); // this takes one second to execute 666 667 send(msgFlow.set(flowRate, 2)); // Update HA after valves have closed. Should 668 be zero, but it no, there's a problem. 669} 670 671uint8_t readRegister(uint8_t 672 regAddr) { 673 uint8_t Data; 674 uint8_t *pu8Data = &Data; 675 676 I2CReadRegister(&bbi2c, 677 MCP23008_ADDR, regAddr, pu8Data, 1); 678 return(Data); 679} 680 681void writeRegister(uint8_t 682 regAddr, uint8_t data) { 683 uint8_t Data[2] = {regAddr, data}; 684 uint8_t *pu8Data; 685 686 687 pu8Data = &Data[0]; 688 I2CWrite(&bbi2c, MCP23008_ADDR, pu8Data, 2); // 689 write both the register addr and the data 690} 691 692void writeNotGPIO(uint8_t 693 pin, bool state) { // set particular pin true or false (only one bit true at a time!!! 694 695 uint8_t newState; 696 697 writeRegister(GPIO, 255); // turn all bits on, all 698 valves off 699 if(state) { 700 newState = ~bitValue[pin]; // inverts the binary 701 number 702 writeRegister(GPIO, newState); 703 } 704} 705 706void safeFlowShutdown() 707 { 708 safeFlow = false; // system shuts down until attended to. When repaired, 709 turn system off and on again. 710 writeRegister(GPIO, 255); // turn all bits on, 711 all valves off (valves are active low!) 712 for (int i = 0; i < NUM_VALVES; i++) 713 { 714 send(msgGPIO[i].set(valveState(i))); 715 } 716 getLastFlowRate(); // 717 make sure flow has shut down. 718 send(msgRestart.set(false)); // let HA know that 719 we have a problem - this is a switch in HA 720} 721
HA Lovelace File (Nano System)
yaml
1UI-Lovelace.yaml 2 - title: Irrigation System 3 cards: 4 - type: vertical-stack 5 title: Controls 6 cards: 7 - type: entities 8 title: Water Valves 9 show_header_toggle: false 10 entities: 11 - entity: switch.irrigation_system_30_10 12 name: Railing 1 Valve 13 - entity: switch.irrigation_system_30_11 14 name: Hanging 1 Valve 15 - entity: switch.irrigation_system_30_12 16 name: Railing 2 Valve 17 - entity: switch.irrigation_system_30_13 18 name: Hanging 2 Valve 19 - entity: switch.irrigation_system_30_14 20 name: Tomato 1 Valve 21 - entity: switch.irrigation_system_30_15 22 name: Tomato 2 Valve 23 - type: entities 24 title: Manual (off), Auto(on)) 25 show_header_toggle: false 26 entities: 27 - entity: switch.irrigation_system_30_20 28 name: Railing 1 Valve 29 - entity: switch.irrigation_system_30_21 30 name: Hanging 1 Valve 31 - entity: switch.irrigation_system_30_22 32 name: Railing 2 Valve 33 - entity: switch.irrigation_system_30_23 34 name: Hanging 2 Valve 35 - entity: switch.irrigation_system_30_24 36 name: Tomato 1 Valve 37 - entity: switch.irrigation_system_30_25 38 name: Tomato 2 Valve 39 - type: entities 40 title: Manual (off), Auto(on)) 41 show_header_toggle: false 42 entities: 43 - entity: binary_sensor.irrigation_system_30_31 44 name: Day/Night 45 - entity: binary_sensor.irrigation_system_30_32 46 name: Low Flow Error 47 device_class: safety 48 - entity: binary_sensor.irrigation_system_30_33 49 name: Open Sensor Error 50 - entity: switch.irrigation_system_30_34 51 name: System Restart 52 - type: vertical-stack 53 title: Moisture Sensors Gauges 54 cards: 55 - type: gauge 56 entity: sensor.moisture_0 57 name: Railing 1 Moisture 58 unit: wt% 59 - type: gauge 60 entity: sensor.moisture_1 61 name: Hanging 1 Moisture 62 unit: wt% 63 - type: gauge 64 entity: sensor.moisture_2 65 name: Railing 2 Moisture 66 unit: wt% 67 - type: gauge 68 entity: sensor.moisture_3 69 name: Hanging 2 Moisture 70 unit: wt% 71 - type: gauge 72 entity: sensor.moisture_4 73 name: Floor 1 Moisture 74 unit: wt% 75 - type: gauge 76 entity: sensor.moisture_5 77 name: Floor 2 Moisture 78 unit: wt% 79 - type: gauge 80 entity: sensor.irrigation_system_30_30 81 name: Water Flow Rate 82 unit: ml/sec 83 - type: history-graph 84 title: Moisture Sensors Graphs 85 hours_to_show: 12 86 entities: 87 - entity: sensor.moisture_0 88 name: Railing 1 Moisture 89 - entity: sensor.moisture_1 90 name: Hanging 1 Moisture 91 - entity: sensor.moisture_2 92 name: Railing 2 Moisture 93 - entity: sensor.moisture_3 94 name: Hanging 2 Moisture 95 - entity: sensor.moisture_4 96 name: Floor 1 Moisture 97 - entity: sensor.moisture_5 98 name: Floor 2 Moisture 99 - type: history-graph 100 title: Raw Moisture Sensors Graphs 101 hours_to_show: 12 102 entities: 103 - entity: sensor.irrigation_system_30_0 104 name: Railing 1 Moisture 105 unit_of_measure: C 106 - entity: sensor.irrigation_system_30_1 107 name: Hanging 1 Moisture 108 - entity: sensor.irrigation_system_30_2 109 name: Railing 2 Moisture 110 - entity: sensor.irrigation_system_30_3 111 name: Hanging 2 Moisture 112 - entity: sensor.irrigation_system_30_4 113 name: Floor 1 Moisture 114 - entity: sensor.irrigation_system_30_5 115 name: Floor 2 Moisture 116
Template Sensors (Nano System)
yaml
1- platform: template 2 sensors: 3 moisture_0: 4 friendly_name: Moisture 0 5 unit_of_measurement: "%" 6 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_0') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 7 moisture_1: 8 friendly_name: Moisture 1 9 unit_of_measurement: "%" 10 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_1') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 11 moisture_2: 12 friendly_name: Moisture 2 13 unit_of_measurement: "%" 14 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_2') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 15 moisture_3: 16 friendly_name: Moisture 3 17 unit_of_measurement: "%" 18 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_3') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 19 moisture_4: 20 friendly_name: Moisture 4 21 unit_of_measurement: "%" 22 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_4') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 23 moisture_5: 24 friendly_name: Moisture 5 25 unit_of_measurement: "%" 26 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_5') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}"
HA Automation File (Nano)
yaml
1 - alias: "Irrigation Day" 2 - alias: "Irrigation Day" 3 trigger: 4 platform: sun 5 event: sunrise 6 offset: "3:00:00" 7 action: 8 service: python_script.set_state 9 data_template: 10 entity_id: binary_sensor.irrigation_system_30_31 11 state: 'on' 12 13 - alias: "Irrigation Night" 14 trigger: 15 platform: sun 16 event: sunset 17 offset: "-3:00:00" 18 action: 19 service: python_script.set_state 20 data_template: 21 entity_id: binary_sensor.irrigation_system_30_31 22 state: 'off' 23 24 25 - alias: "Irrigation Flow Failure Alert" 26 trigger: 27 - platform: state 28 entity_id: binary_sensor.irrigation_system_20_32 29 to: "on" 30 action: 31 - service: notify.gmail 32 data: 33 message: "Sharon Irrigation Flow Failure" 34 title: "Irrigation Alert" 35 36 - alias: "Irrigation Sensor Failure Alert" 37 trigger: 38 - platform: state 39 entity_id: binary_sensor.irrigation_system_20_33 40 to: "on" 41 action: 42 - service: notify.gmail 43 data: 44 message: "Sharon Irrigation Sensor Failure" 45 title: "Irrigation Alert" 46
Template Sensors (Nano System)
yaml
1- platform: template 2 sensors: 3 moisture_0: 4 friendly_name: Moisture 0 5 unit_of_measurement: "%" 6 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_0') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 7 moisture_1: 8 friendly_name: Moisture 1 9 unit_of_measurement: "%" 10 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_1') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 11 moisture_2: 12 friendly_name: Moisture 2 13 unit_of_measurement: "%" 14 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_2') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 15 moisture_3: 16 friendly_name: Moisture 3 17 unit_of_measurement: "%" 18 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_3') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 19 moisture_4: 20 friendly_name: Moisture 4 21 unit_of_measurement: "%" 22 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_4') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 23 moisture_5: 24 friendly_name: Moisture 5 25 unit_of_measurement: "%" 26 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_5') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}"
(obsolete)
c_cpp
HA Template Sensors (Nano System)
yaml
These template sensors were created to convert raw analog input data into moisture percent. For this system, there is no temperature compensation. Even so, the system will still do a good job of keeping plants watered.
1- platform: template 2 sensors: 3 moisture_0: 4 friendly_name: Moisture 0 5 unit_of_measurement: "%" 6 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_0') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 7 moisture_1: 8 friendly_name: Moisture 1 9 unit_of_measurement: "%" 10 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_1') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 11 moisture_2: 12 friendly_name: Moisture 2 13 unit_of_measurement: "%" 14 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_2') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 15 moisture_3: 16 friendly_name: Moisture 3 17 unit_of_measurement: "%" 18 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_3') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 19 moisture_4: 20 friendly_name: Moisture 4 21 unit_of_measurement: "%" 22 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_4') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 23 moisture_5: 24 friendly_name: Moisture 5 25 unit_of_measurement: "%" 26 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_5') | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}"
(obsolete)
c_cpp
HA Lovelace File (Nano System)
yaml
1UI-Lovelace.yaml 2 - title: Irrigation System 3 cards: 4 - type: vertical-stack 5 title: Controls 6 cards: 7 - type: entities 8 title: Water Valves 9 show_header_toggle: false 10 entities: 11 - entity: switch.irrigation_system_30_10 12 name: Railing 1 Valve 13 - entity: switch.irrigation_system_30_11 14 name: Hanging 1 Valve 15 - entity: switch.irrigation_system_30_12 16 name: Railing 2 Valve 17 - entity: switch.irrigation_system_30_13 18 name: Hanging 2 Valve 19 - entity: switch.irrigation_system_30_14 20 name: Tomato 1 Valve 21 - entity: switch.irrigation_system_30_15 22 name: Tomato 2 Valve 23 - type: entities 24 title: Manual (off), Auto(on)) 25 show_header_toggle: false 26 entities: 27 - entity: switch.irrigation_system_30_20 28 name: Railing 1 Valve 29 - entity: switch.irrigation_system_30_21 30 name: Hanging 1 Valve 31 - entity: switch.irrigation_system_30_22 32 name: Railing 2 Valve 33 - entity: switch.irrigation_system_30_23 34 name: Hanging 2 Valve 35 - entity: switch.irrigation_system_30_24 36 name: Tomato 1 Valve 37 - entity: switch.irrigation_system_30_25 38 name: Tomato 2 Valve 39 - type: entities 40 title: Manual (off), Auto(on)) 41 show_header_toggle: false 42 entities: 43 - entity: binary_sensor.irrigation_system_30_31 44 name: Day/Night 45 - entity: binary_sensor.irrigation_system_30_32 46 name: Low Flow Error 47 device_class: safety 48 - entity: binary_sensor.irrigation_system_30_33 49 name: Open Sensor Error 50 - entity: switch.irrigation_system_30_34 51 name: System Restart 52 - type: vertical-stack 53 title: Moisture Sensors Gauges 54 cards: 55 - type: gauge 56 entity: sensor.moisture_0 57 name: Railing 1 Moisture 58 unit: wt% 59 - type: gauge 60 entity: sensor.moisture_1 61 name: Hanging 1 Moisture 62 unit: wt% 63 - type: gauge 64 entity: sensor.moisture_2 65 name: Railing 2 Moisture 66 unit: wt% 67 - type: gauge 68 entity: sensor.moisture_3 69 name: Hanging 2 Moisture 70 unit: wt% 71 - type: gauge 72 entity: sensor.moisture_4 73 name: Floor 1 Moisture 74 unit: wt% 75 - type: gauge 76 entity: sensor.moisture_5 77 name: Floor 2 Moisture 78 unit: wt% 79 - type: gauge 80 entity: sensor.irrigation_system_30_30 81 name: Water Flow Rate 82 unit: ml/sec 83 - type: history-graph 84 title: Moisture Sensors Graphs 85 hours_to_show: 12 86 entities: 87 - entity: sensor.moisture_0 88 name: Railing 1 Moisture 89 - entity: sensor.moisture_1 90 name: Hanging 1 Moisture 91 - entity: sensor.moisture_2 92 name: Railing 2 Moisture 93 - entity: sensor.moisture_3 94 name: Hanging 2 Moisture 95 - entity: sensor.moisture_4 96 name: Floor 1 Moisture 97 - entity: sensor.moisture_5 98 name: Floor 2 Moisture 99 - type: history-graph 100 title: Raw Moisture Sensors Graphs 101 hours_to_show: 12 102 entities: 103 - entity: sensor.irrigation_system_30_0 104 name: Railing 1 Moisture 105 unit_of_measure: C 106 - entity: sensor.irrigation_system_30_1 107 name: Hanging 1 Moisture 108 - entity: sensor.irrigation_system_30_2 109 name: Railing 2 Moisture 110 - entity: sensor.irrigation_system_30_3 111 name: Hanging 2 Moisture 112 - entity: sensor.irrigation_system_30_4 113 name: Floor 1 Moisture 114 - entity: sensor.irrigation_system_30_5 115 name: Floor 2 Moisture 116
MEGA Irrigation_System_w_Display_1.3_w_Mysensors_w_Drive_Board2.30.ino
c_cpp
I've added a precomiler #define called MY_MYSENSORS. If this line of code is defined, then MySensors will be compiled with the code. If not, MySensors will be left out and the system will compile with the display functionality, but without the MySensors interface to your home automation system.
1 2#define PROGRAM "Irrigation System w/Display 1.3 w/Drive_Board" 3#define VERSION "version 2.30" 4/* MEGA Irrigation System w Display w Mysensors 5 * P. Zavracky, (c) 2021 6/* REVISION HISTORY 7 * Written by Paul M. Zavracky (C) 2021 8 * Borrows heavily from the work of zillions of others 9 * This is an upgrade of the original Nano version (2.95) 10 * 11 * The irrigation system comprises a set of 1 to 8 1/4 12v water valves, a moisture sensor, an arduino, 12 * an NRF24 Radio, and 8 drivers. The Arduino takes both the digital and analog signals from the 13 * moisture sensor and uses this information to determine if watering is needed. If so, the Arduino 14 * sends a signal to the driver board, actualing the appropriate valve. The driver connects a 12V supply 15 * to the water valve. The Arduino continues monitoring the moisture sensor. Once a predetermined level 16 * is reached, the Arduino turns off the water flow. 17 * 18 * This version adds the drive board. That means that instead of active low drivers, we now have active high 19 * transistor drivers. We also have a shutoff driver for the shutoff valve. Therefore, system leak detection 20 * is added and the shutoff valve must be actuated whenever any valve is on and turned off (except when 21 * conducting a leak test) when no valves are on. 22 * 23 * In this scheme, the routine 'writePORTA' which originally inverted the port data byte, now does not! So, 24 * the name carried over no longer accurately portrays the routines function. Therefore the routine is renamed to 25 * writePORTA. 26 * 27 * Added in this iteration is a leak check of the plumbing between the shutoff valve and the zone valves. This region 28 * has a significant number of fittings that could develop leaks. It is also a region, that without a shutoff valve 29 * would continue to leak as long as the system is running. So, the shutoff valve removes pressure from this area, 30 * however, without some kind of leak check, a catastrophic failure of the plumbing would pour water out each time a 31 * zone is activated. To prevent this from happening, periodic leak checks are conducted. If a leak is discovered, 32 * the system will shut down and Home Assistant will send an appropriate message. 33 * 34 * This code now uses the precompiler name MY_MYSENSORS which is #defined at the beginning of the code. If you 35 * comment this line out, MySensors will be eliminated from the compilation and you can use the system with the display 36 * only. 37*/ 38 39 40// setup for MySensors 41#define MY_NODE_ID 41 42#define MY_DEBUG // comment out this line to remove MySensors use of serial port!!! 43#define MY_MYSENSORS // comment out this line to remove MySensors altogether 44 45#define MY_PARENT_NODE_ID 0 46#define MY_PARENT_NODE_IS_STATIC 47 48// Enable and select radio type attached 49#define MY_RADIO_RF24 50#define MY_RF24_PA_LEVEL RF24_PA_MAX 51// RF24_PA_MIN = -18dBm 52// RF24_PA_LOW = -12dBm 53// RF24_PA_HIGH = -6dBm 54// RF24_PA_MAX = 0dBm 55 56// Specifiaclly for MEGA 57#define MY_RF24_CE_PIN 11 58#define MY_RF24_CS_PIN 12 59 60// analog inputs 61#define CHILD_ID_START_OF_AI 0 // the below AI IDs allow rearrangement of sensors so that during installation, they may be properly coupled to valves 62#define CHILD_ID_AI0 0 63#define CHILD_ID_AI1 1 64#define CHILD_ID_AI2 2 65#define CHILD_ID_AI3 3 66#define CHILD_ID_AI4 4 67#define CHILD_ID_AI5 5 68#define CHILD_ID_AI6 6 69#define CHILD_ID_AI7 7 70 71// digital outputs (switches in HA) 72#define CHILD_ID_START_OF_VALVES 10 73 74//oneWire Temperature Sensors 75#define CHILD_ID_START_OF_TEMPS 20 76 77//Auto/Manual Mode Switches 78#define CHILD_ID_START_OF_AUTOS 30 79 80#define CHILD_ID_FLOW 40 81#define CHILD_ID_DAY_NIGHT 41 // day night sensor gets data from Home Assistant 82#define CHILD_ID_LOW_FLOW 42 // low flow alarm indicator for HA - sends alarm, shuts down system 83#define CHILD_ID_UNRESPONSIVE_SENSOR 43 // open sensor alarm indicator for HA - sends alarm, shuts down system 84#define CHILD_ID_SAFEFLOW_SWITCH 44 // if there is an automatic shutdown (safeFlow = false), this switch let's the system restart (safeFlow = true) 85#define CHILD_ID_LEAK_DETECTED 45 // used to alert HA of a leak. This has saved me from disaster. 86 87#define CHILD_ID_START_OF_TEMP_STATES 80 // keeps track of the condition of the temperature sensors (there might be 8 of these CHILD_IDs) 88#define CHILD_ID_START_OF_MOISTURE_OPEN_STATES 90 // keeps track of the condition of the moisture sensors (there might be 8 of these CHILD_IDs) 89#define CHILD_ID_START_OF_MOISTURE_SHORT_STATES 100 // open circuit or shorted 90 91// Display and Touchscreen Parameters 92#define TFT_CS 48 93#define TFT_RST 44 94#define TFT_CD 46 95#define T_CS 42 96 97// The Analog pins are used to sense the moisture level 98#define AI0_PIN 54 99#define AI1_PIN 55 100#define AI2_PIN 56 101#define AI3_PIN 57 102#define AI4_PIN 58 103#define AI5_PIN 59 104#define AI6_PIN 60 105#define AI7_PIN 61 106 107#define MENUHEIGHT 20 108#define MENUWIDTH 80 109#define LABLE0 "OPERATE" 110#define LABLE1 "ALARMS" 111#define LABLE2 "SET PTS" 112#define LABLE3 VERSION 113 114// Arduino output pin setup 115#define TMPS0 2 // This is the first of three bits for selecting the eight analog switches connecting single Dallas temp sensors to the one wire temp input (TEMP0_PIN) 116#define TMPS1 3 // see below definition of variable tempSensorConfig[] 117#define TMPS2 4 118#define TEMP0_PIN 5 // this is one of two pins for temperature input. This one goes to the analog switch between 8 different sensors 119#define FLOW_PIN 6 // Flow sensor input pin 120#define LED_PIN 8 // LED backlight control pin 121#define POWER_PIN 10 // Sensor Power Pin 122#define TEMP1_PIN 13 // Temperature oneWire pin (also defined for oneWire below) 123#define GATE 47 // must be turned on for outputs to fire! 124#define SHUTOFF_VALVE 49 // Safety shutoff valve 125 126#define NUM_VALVES 8 // number of valves in use - up to eight 127#define TOUCH_TIME 300 // time in milliseconds between touch readings 128#define BASE_EEPROM_ADDR 100 // starting address of data in eeprom. must leave room for other lib uses 129 130#define ONE_WIRE_BUS TEMP0_PIN // Pin where dallas sensors are connected 131 132#define K 2.28 // 1380 pulses/liter found online. Max Flow through 30' should be about 5 GPH or 20 liters/hour or 0.33 liters/min or .0055 liters per sec. 133// That implies about 8 pulses per second. Some report closer to 30 Hz? Our unit should be milliliters per second, so K should be 1.38. 134// For the sensor used in this project, the reported value K = 1.38 was too small. Flow (pulsed based) yeilded 73, for 44 ml/sec. 135// In this code, flowRate = count/2K, where counts are made on both rising and falling edges. Flow rate should equal flowRate/1.65, so 136// Knew = 1.28 * 1.65 or 2.28. 137 138#define FLOW_MOIST_TEST_TIME 5 // the time in minutes a valve can stay on before conducting a flow and moisture variation measurement 139 // This is only done once, because the system will shutdown anyway at MAX_VALVE_TIME 140#define MAX_VALVE_TIME 20 // maximum time in minutes a valve can stay on in minutes. If moisture level hasn't dropped on next cycle, valve will 141// turn on againg 142#define MAX_LED_TIME 5 // maximum time in minutes the LED backlight stays on without a touch 143#define VALVES_OFF_MOISTURE_MEASUREMENT_TIME 15 // Time in minutes between moisture sensor readings when no valves are open. 144#define VALVES_ON_MOISTURE_MEASUREMENT_TIME 0.1 // Time in minutes between moisture sensor readings when a valve is open. 145#define LEAK_CHECK_TIME 60 // time between leakchecks 146#define LEAK_DURATION_TIME 30 // basically the delay time in seconds between turning on the shutoff valve and making a flow measurement. This allows the initial 147 // pressure pulse to settle down before making the leak test. 148#define DOT_TIME 1 // time in seconds between dots printed as system cycles through main loop 149 150#define Rseries 1000.00 // series resistance used in moisture measurement 151 152#include <Arduino.h> 153#include <SPI.h> 154#ifdef MY_MYSENSORS 155 #include <MySensors.h> 156#endif 157#include "SimpleILI9341.h" 158#include <avr/pgmspace.h> 159#include <EEPROM.h> 160#include <DallasTemperature.h> 161#include <OneWire.h> 162 163// start Dallas Temp Sensors 164OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) 165DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature. 166// Addresses of DS18B20 sensors connected to the 1-Wire bus are not needed. This system uses an analog multiplexer to select a particular device 167// The multiplexer (74HC4051) outputs are routed in a convenient layout format. But this format lead to the following configuration of connectors: 168// [ 2 ] [ 4 ] 169// [ 1 ] [ 6 ] 170// [ 0 ] [ 7 ] 171// [ 3 ] [ 5 ] 172// The preferred order might be 173// [ 0 ] [ 1 ] 174// [ 2 ] [ 3 ] 175// [ 4 ] [ 5 ] 176// [ 6 ] [ 7 ] 177// The following variable is used to make this translation. Therefore, when connecting to the board, your sensors labled S0...S7 are connected to the 178// positions using the preferred order. 179byte tempSensorConfig[] = {2,4,1,6,0,7,3,5}; // so tempSensorConfig[0] will translate to an index of 2 180 181// variables for Mysensors 182 183byte AI_pin[] = {AI0_PIN, AI1_PIN, AI2_PIN, AI3_PIN, AI4_PIN, AI5_PIN, AI6_PIN, AI7_PIN}; 184byte CHILD_ID_AI[] = {CHILD_ID_AI0, CHILD_ID_AI1, CHILD_ID_AI2, CHILD_ID_AI3, CHILD_ID_AI4, CHILD_ID_AI5, CHILD_ID_AI6, CHILD_ID_AI7}; 185 186bool ack = 1; 187 188 189// Variables Definitions 190boolean newLoop = true; // to update HA after power outage 191unsigned long oldValveOnTime = 0; // used to control the amount of time a valve is open 192unsigned long oldNoMoistureTime = 0; // minutes between moisture measurements when no valve is on 193unsigned long oldLEDTime = 0; // start time for LED backlight on time 194unsigned long oldLeakCheckTime = 0; // used to control amount of time between leak checks 195unsigned long oldLeakDurationTime = 0; // used to avoid the pressure pulse delay that ensues when opening the shutoff valve 196unsigned long oldDotTime = 0; // system will print a dot each 197 198byte currentscreen; 199 200bool autoManState[NUM_VALVES]; // man if false, auto if true 201bool tempState[NUM_VALVES]; // used to keep track of functional temperature sensors 202bool moistureOpenState[NUM_VALVES]; // tracks open sensors 203bool moistureShortState[NUM_VALVES]; // tracks shorted sensors 204bool lowFlow = false; // if the flow is too low, the system sets this flag and shuts down. HA can see this flag. 205bool unresponsiveSensor = false; // if a sensor is open and doesn't responde to irrigation, the system sets this flag and shuts down. HA can see this flag. 206bool leakDetected = false; // check flow rate when no valves are open and check for leaks. 207bool systemSafe = true; 208bool leakCheckFlag = false; // this flag is used to initiate a system leak check 209float flowRate = 5.0; // just set arbitrary initial flow rate. This will clear on first cycle through loop 210 211// Update variables 212bool oldValveState[NUM_VALVES]; 213float oldTemp[NUM_VALVES]; 214float oldMoistureLevel[NUM_VALVES] = {0.0}; // initialize all old data to zero 215bool oldAutoManState[NUM_VALVES]; // used in update all to prevent sending repetitive information 216bool oldTempState[NUM_VALVES]; // change of state forces update!! 217bool oldMoistureOpenState[NUM_VALVES]; 218bool oldMoistureShortState[NUM_VALVES]; 219bool oldLowFlow = true; 220bool oldUnresponsiveSensor = true; 221bool oldLeakDetected = true; 222bool oldSystemSafe = true; 223float oldFlowRate = 1.0; 224 225uint8_t bitValue[] = {1, 2, 4, 8, 16, 32, 64, 128}; 226 227bool safeFlow = true; // if flow measures to long, the system will set this flag false and prevent valves from being actuated (prevent over heating) 228byte flowTestCount = 0; // keeps track of the number of zero flow measurements made - system shuts down after NUM_FLOW_TESTS 229bool dayNight = false; // if true, it is daytime and the system should not water plants. This variable can only be changed by MySensors. 230float temp[NUM_VALVES]; 231float moistureLevel[NUM_VALVES]; 232float startMoistureLevel = 0.0; // used for sensor testing 233byte currentScreen = 0; 234byte oldCurrentScreen = 0; 235bool firstTime = true; 236bool startup = true; // used to send HA message once on startup 237byte sensor = 0; // this is just for test purposes 238byte tempSensorCount = 0; // number of functioning temperature sensors 239byte upperMoistureLevel[NUM_VALVES]; 240byte lowerMoistureLevel[NUM_VALVES]; 241unsigned long int lastTouchTime = millis(); 242uint16_t color; 243byte dotCounter = 0; // counts the dots printed for each line and occasionally prints a line feed. 244 245// Dallas Temp Sensor Variables 246bool metric = false; 247 248#ifdef MY_MYSENSORS 249 // MySensors messages 250 MyMessage msgAI[NUM_VALVES]; // these messages tell HA the moisture level 251 MyMessage msgPORTA[NUM_VALVES]; // these messages let HA know what drivers 252 MyMessage msgTemp[NUM_VALVES]; // these messages let HA know the temperature 253 MyMessage msgAutoMan[NUM_VALVES]; // these messages let HA know what drivers 254 MyMessage msgTempState[NUM_VALVES]; // these messages let HA know about the condition of the temperature sensors (working/not-working) 255 MyMessage msgMoistureOpenState[NUM_VALVES]; // these messages let HA know about the condition of the moisture sensors (working/not-working) 256 MyMessage msgMoistureShortState[NUM_VALVES]; 257 MyMessage msgFlow(CHILD_ID_FLOW, V_FLOW); 258 MyMessage msgDayNight(CHILD_ID_DAY_NIGHT, V_TRIPPED); // used to prevent watering during the day 259 MyMessage msgLowFlow(CHILD_ID_LOW_FLOW, V_TRIPPED); // used to let HA know that we have no water flow 260 MyMessage msgOpenSensor(CHILD_ID_UNRESPONSIVE_SENSOR, V_TRIPPED); // used to let HA know that we have an unresponsive sensor 261 MyMessage msgLeakDetected(CHILD_ID_LEAK_DETECTED, V_TRIPPED); // system monitors flow sensor even when no valve is on. If flow is indicated, must be a leak! 262 MyMessage msgRestart(CHILD_ID_SAFEFLOW_SWITCH, V_STATUS); // safeMode switch, shut down due to system failure, restart through HA 263#endif 264 265void setup(void) { 266 Serial.begin(115200); 267 Serial.print(PROGRAM);Serial.println(VERSION); 268 269 ILI9341Begin(48, 46, 44, 320, 240, ILI9341_Rotation3); 270 BeginTouch(42, 3); 271 272 //analogReference(EXTERNAL); // use this command if you're going to use the switched supply as the reference. Not necessary. 273 // set initial array values 274 for (int i = 0; i < NUM_VALVES; i++) { 275 autoManState[i] = false; // man if false, auto if true 276 tempState[i] = false; // used to keep track of functional temperature sensors 277 moistureOpenState[i] = false; // tracks open sensors 278 moistureShortState[i] = false; // tracks shorted sensors 279 oldValveState[i] = true; // forces HA update 280 oldTemp[i] = 0.0; 281 oldMoistureLevel[i] = 0.0; // initialize all old data to zero 282 oldAutoManState[i] = true; // used in update all to prevent sending repetitive information 283 oldTempState[i] = false; // change of state forces update!! 284 oldMoistureOpenState[i] = false; 285 oldMoistureShortState[i] = false; 286 } 287 288 //Setup inputs and outputs 289 pinMode(TMPS0, OUTPUT); // these are for the temperature sensors 290 pinMode(TMPS1, OUTPUT); 291 pinMode(TMPS2, OUTPUT); 292 digitalWrite(TMPS0, false); // set for channel zero. due to board layout considerations, this is actually sensor 4 293 digitalWrite(TMPS1, false); 294 digitalWrite(TMPS2, false); 295 for (int i = 0; i < NUM_VALVES; i++) { 296 pinMode(AI_pin[i], INPUT); 297 } 298 pinMode(FLOW_PIN, INPUT_PULLUP); // flow sensor requires the input have a pullup resistor 299 pinMode(SHUTOFF_VALVE, OUTPUT); 300 pinMode(GATE, OUTPUT); 301 digitalWrite(GATE, true); // make sure gate is off to prevent switch inputs to drivers (drivers all off) 302 digitalWrite(SHUTOFF_VALVE, false); // make sure SHUTOFF VALVE is off 303 pinMode(POWER_PIN, OUTPUT); 304 pinMode(LED_PIN, OUTPUT); 305 pinMode(30, OUTPUT); // set up power relay pin for output 306 // test power pin 307 for (int i = 0; i < 3; i++) { 308 digitalWrite(POWER_PIN, false); // turn off power to moisture sensors; power pin is true low. 309 //digitalWrite(30, false); 310 delay(500); 311 digitalWrite(POWER_PIN, true); 312 //digitalWrite(30, true); 313 delay(500); 314 } 315 digitalWrite(LED_PIN, false); // turn on LED backlight - active low 316 oldLEDTime = millis(); // establish on time 317 DDRA = B11111111; // set up Mega PortA for output - these control the valves 318 PORTA = 0; // all valves off 319 digitalWrite(30, true); // turn on power relay 320 digitalWrite(GATE, false); // make sure gate is on to pass switch inputs to drivers 321 322#ifdef MY_MYSENSORS 323 // initialize messages for MySensors 324 for (int i = 0; i < NUM_VALVES; i++) { 325 msgAI[i].sensor = i; // Analog sensors start at child ID 0 326 msgAI[i].type = V_LEVEL; // these are the moisture sensors 327 msgPORTA[i].sensor = i + CHILD_ID_START_OF_VALVES; 328 msgPORTA[i].type = V_STATUS; // these are the sprinkler valves 329 msgTemp[i].sensor = i + CHILD_ID_START_OF_TEMPS; 330 msgTemp[i].type = V_TEMP; // these are the temperature sensors 331 msgAutoMan[i].sensor = i + CHILD_ID_START_OF_AUTOS; 332 msgAutoMan[i].type = V_STATUS; // these are the switch to set mode 333 msgTempState[i].sensor = i + CHILD_ID_START_OF_TEMP_STATES; 334 msgTempState[i].type = V_TRIPPED; // these are binary sensors (S_BINARY) 335 msgMoistureOpenState[i].sensor = i + CHILD_ID_START_OF_MOISTURE_OPEN_STATES; 336 msgMoistureOpenState[i].type = V_TRIPPED; 337 msgMoistureShortState[i].sensor = i + CHILD_ID_START_OF_MOISTURE_SHORT_STATES; 338 msgMoistureShortState[i].type = V_TRIPPED; // these are binary sensors (S_BINARY) 339 } 340#endif 341 342 // get set points from eeprom 343 for (int i = 0; i < NUM_VALVES; i++) { 344 upperMoistureLevel[i] = EEPROM.read(BASE_EEPROM_ADDR + i); 345 lowerMoistureLevel[i] = EEPROM.read(BASE_EEPROM_ADDR + NUM_VALVES + i); // always move the to position of the max number of valves 346 if (upperMoistureLevel[i] > 100) { 347 upperMoistureLevel[i] = 80; // EEPROM hasn't been writen yet, so use 80 to start. Will write after updating. 348 } 349 if (lowerMoistureLevel[i] > 100) { 350 lowerMoistureLevel[i] = 40; // EEPROM hasn't been writen yet, so use 40 to start. 351 } 352 } 353 // set up dallas temp sensors 354 sensors.begin(); 355 356 // display setup 357 ClearDisplay(TFT_BLUE); 358 DrawBox(0, 0, MENUWIDTH, MENUHEIGHT, TFT_NAVY); // upper lefthand corner location and size of box 359 DrawFrame(0, 0, MENUWIDTH, MENUHEIGHT, TFT_WHITE); 360 ILI9341SetCursor(15, 15); 361 DrawString(LABLE0, SmallFont, TFT_WHITE); 362 DrawBox(MENUWIDTH, 0, MENUWIDTH, MENUHEIGHT, TFT_NAVY); 363 DrawFrame(MENUWIDTH, 0, MENUWIDTH, MENUHEIGHT, TFT_BLACK); 364 ILI9341SetCursor(MENUWIDTH + 15, 15); 365 DrawString(LABLE1, SmallFont, TFT_WHITE); 366 DrawBox(MENUWIDTH * 2, 0, MENUWIDTH, MENUHEIGHT, TFT_NAVY); 367 DrawFrame(MENUWIDTH * 2, 0, MENUWIDTH, MENUHEIGHT, TFT_BLACK); 368 ILI9341SetCursor(MENUWIDTH * 2 + 15, 15); 369 DrawString(LABLE2, SmallFont, TFT_WHITE); 370 DrawBox(MENUWIDTH * 3, 0, MENUWIDTH, MENUHEIGHT, TFT_NAVY); 371 DrawFrame(MENUWIDTH * 3, 0, MENUWIDTH, MENUHEIGHT, TFT_BLACK); 372 ILI9341SetCursor(MENUWIDTH * 3 + 15, 15); 373 DrawString(LABLE3, SmallFont, TFT_WHITE); 374 375 //DrawFrame(0+1, 0+1, MENUWIDTH-2, MENUHEIGHT-2, TFT_WHITE); 376} 377 378void presentation() 379{ 380 // Send the sketch version information to the gateway 381 382 Serial.print("Irrigation System, 2021, Version ");Serial.println(VERSION); // this line will identify version even when MySensor Debug is turned off. 383 384#ifdef MY_MYSENSORS 385 sendSketchInfo("IRRIGATION SYSTEM ", VERSION, true); 386 // Register all sensors to gw (they will be created as child devices) 387 for (int i = 0; i < NUM_VALVES; i++) { 388 present(CHILD_ID_AI[i], S_MOISTURE); 389 present(CHILD_ID_START_OF_VALVES + i, S_SPRINKLER); 390 present(CHILD_ID_START_OF_TEMPS + i, S_TEMP); 391 present(CHILD_ID_START_OF_AUTOS + i, S_LIGHT); 392 present(CHILD_ID_START_OF_TEMP_STATES + i, S_DOOR); // indicates when there is no response from sensor 393 present(CHILD_ID_START_OF_MOISTURE_OPEN_STATES + i, S_DOOR); // uses AI[] to determine open or short 394 present(CHILD_ID_START_OF_MOISTURE_SHORT_STATES + i, S_DOOR); 395 } 396 present(CHILD_ID_FLOW, S_WATER); 397 present(CHILD_ID_DAY_NIGHT, S_DOOR); // used to prevent watering during the day - binary sensor 398 present(CHILD_ID_LOW_FLOW, S_DOOR); // used to let HA know that we have no water flow - binary sensor 399 present(CHILD_ID_UNRESPONSIVE_SENSOR, S_DOOR); // used to let HA know that we have an open sensor - binary sensor 400 present(CHILD_ID_LEAK_DETECTED, S_DOOR); // alert HA of a leak 401 present(CHILD_ID_SAFEFLOW_SWITCH, S_LIGHT); // if off, system is idle, if one, system is active 402#endif 403} 404 405void loop(void) { 406 int x, y; 407 408#ifdef MY_MYSENSORS 409 if (startup) { 410 send(msgRestart.set(false)); 411 send(msgDayNight.set(true)); // this gets home assistant going 412 startup = false; 413 } 414#endif 415 if (newLoop) { 416 readMoistureTemp(); // reads all sensors 417 flowRate = flowMeasurement(); // this is not the only place where a flowrate measurement is made; check getLastFlowRate() - used when closing valves 418 updateAllOld(); 419 newLoop = false; } // make sure HA is aware of current state after powerdown 420 // and collect moisture levels 421 if (!dayNight) { // if !dayNight = true, it's night time and good to go 422 if (safeFlow) { 423 // check time to see if we need a moisture reading - different delay during watering 424 if (millis() < oldNoMoistureTime) { 425 oldNoMoistureTime = 0; // millis() can roll over making it smaller than oldNoDataTime 426 } 427 if ((millis() - oldNoMoistureTime) > ((isAnyValveOn() ? 1 : 0)*VALVES_ON_MOISTURE_MEASUREMENT_TIME + 428 (isAnyValveOn() ? 0 : 1)*VALVES_OFF_MOISTURE_MEASUREMENT_TIME) * 60000) { // send data to HA to let it know where still alive 429 oldNoMoistureTime = millis(); // update time 430 readMoistureTemp(); // reads all sensors 431 flowRate = flowMeasurement(); // this is not the only place where a flowrate measurement is made; check getLastFlowRate() - used when closing valves 432 if (currentScreen == 0) { updateScreen0(); updateAllOld();} else if (currentScreen == 1) {updateScreen1(); updateAllOld();} // update screens on change of sensor data 433 } 434 // open and close valves automatically when set by autoManState and send moisture readings to HA when appropriate 435 // valves are turned on and off manually through the receive() function or touch screen!! 436 for (int i = 0; i < NUM_VALVES; i++) { 437 if (abs(moistureLevel[i] - oldMoistureLevel[i]) > 1.0 ) { // only send data upon change of state by more that 1% 438#ifdef MY_MYSENSORS 439 send(msgAI[i].set(moistureLevel[i],1)); // will automatically send all moistureLevels on first newLoop 440 send(msgTemp[i].set(temp[i],2)); // same here with temps 441#endif 442 oldMoistureLevel[i] = moistureLevel[i]; 443 oldTemp[i] = temp[i]; 444 } 445 if (autoManState[i] == true) { // must be in auto mode to change valve state 446 if ((moistureLevel[i] > upperMoistureLevel[i]) && valveState(i)) { 447 PORTA = 0; // turn all valves off (valves are active low!) 448#ifdef MY_MYSENSORS 449 send(msgPORTA[i].set(valveState(i))); 450#endif 451 getLastFlowRate(); // valves are closed, make sure flow has stopped 452 } 453 if ((moistureLevel[i] < lowerMoistureLevel[i]) && !isAnyValveOn()) { // only turn on if all other valves are off 454 writePORTA(i, true); // turn on water (valves are active low, but writePORTA does inversion) 455#ifdef MY_MYSENSORS 456 send(msgPORTA[i].set(valveState(i))); 457#endif 458 startMoistureLevel = moistureLevel[i]; // save for sensor check below, is sensor responding? 459 oldValveOnTime = millis(); // set start time for valve[i] on duration. only one valve can be on at a time, so we need only one oldValveOnTime! 460 } 461 } 462 } 463 // Check flow and moisture response if valve is on 464 if (isAnyValveOn() == true) { // one of the valves is on, let's check to see if moisture sensor and the flow meter are responding 465 if (millis() < oldValveOnTime) {oldValveOnTime = 0; } // millis() can roll over making it smaller than oldNoDataTime 466 if ((millis() - oldValveOnTime) > FLOW_MOIST_TEST_TIME * 60000) { // time to turn off the valve to prevent over watering 467 if (flowRate < 5.0) { // oops low flow 468 lowFlow = true; 469#ifdef MY_MYSENSORS 470 send(msgLowFlow.set(true)); // send alarm to HA to indicate system shut down. 471#endif 472 safeFlowShutdown(); 473 } 474 if (startMoistureLevel >= oldMoistureLevel[whichValveIsOn()] + 2 ) { // if the measured moisture level hasn't increased 475 // 2% is added to current reading to overcome noise 476 unresponsiveSensor = true; 477#ifdef MY_MYSENSORS 478 send(msgOpenSensor.set(true)); // let HA know the system is shutting down due to an open sensor 479#endif 480 safeFlowShutdown(); // shutdown system and let HA know valves are all off 481 } 482 } 483 if ((millis() - oldValveOnTime) > MAX_VALVE_TIME * 60000) {// Even though moisture response and lowflow pass, this valve 484 // has been on too long. Let's turn if off. 485 lowFlow = true; // turn lowFlow and unresponsiveSensor flags on to indicate this failure mode 486 unresponsiveSensor = true; 487 safeFlowShutdown(); 488 } 489 if (!digitalRead(SHUTOFF_VALVE)) { 490 digitalWrite(SHUTOFF_VALVE, true); // turn on water when any other valve is on 491 Serial.println();Serial.println("Shut off valve turned on because another valve was turned on"); 492 } 493 } 494 else { // no valves are on, must reset timer and turn off shutoff valve 495 oldValveOnTime = millis(); // resets every loop cycle if no valve is on 496 if (digitalRead(SHUTOFF_VALVE)) { 497 digitalWrite(SHUTOFF_VALVE, false); // shutoff water when no other valves are on 498 Serial.println();Serial.println("Shut off valve turned off because no other valve it on"); 499 } 500 } 501 } 502 } 503 // test to see if it's time for a leak check 504 if (millis() < oldLeakCheckTime) {oldLeakCheckTime = 0; } // millis() can roll over making it smaller than oldLeakCheckTime 505 if ((millis() - oldLeakCheckTime) > LEAK_CHECK_TIME * 60000) { // let's do a leak check 506 oldLeakCheckTime = millis(); // reset time for next cycle 507 if (isAnyValveOn() == false) { // ok, no valve is on, we can at least start to conduct a leak test 508 if (!digitalRead(SHUTOFF_VALVE)) { 509 digitalWrite(SHUTOFF_VALVE, true); // turn on shutoff valve to test for leaks between the supply and the zone valves 510 Serial.println();Serial.println("Shut off valve turned on for leak test"); 511 } 512 leakCheckFlag = true; // enables the leak check routine below 513 oldLeakDurationTime = millis(); // start the clock for the next leak check 514 } 515 } 516 // the below code block conducts a leak check after waiting for LEAK_DURATION_TIME. 517 if (leakCheckFlag == true) { 518 if (isAnyValveOn() == true) { leakCheckFlag = false;} // oops, a valve has been turned on during the leak check. Abort!!! Wait for next cycle. 519 else { // ok, the shutoff valve is open, we need to delay long enough for the pressure pulse to pass, then make a flow measurement 520 if (millis() < oldLeakDurationTime) {oldLeakDurationTime = 0; } // millis() can roll over making it smaller than oldLeakCheckTime 521 if ((millis() - oldLeakDurationTime) > LEAK_DURATION_TIME * 1000) { // the pressure pulse delay time has passed, time to check the flow rate 522 // now let's check to make sure that if no valves are on, then no flow is measured. If it is, we have a leak! 523 flowRate = flowMeasurement(); // get the flowrate 524 if (flowRate != 0) { // oops, we have a leak 525 leakDetected = true; 526#ifdef MY_MYSENSORS 527 send(msgLeakDetected.set(false)); delay(500); send(msgLeakDetected.set(true)); // a transition from false to true triggers an automated message from HA to email. 528#endif 529 oldLeakDetected = leakDetected; 530 safeFlowShutdown(); // shutdown system and let HA know valves are all off 531 } 532 else { 533 leakDetected = false; 534 leakCheckFlag = false; // we're done here, reset the flag 535#ifdef MY_MYSENSORS 536 if (oldLeakDetected != leakDetected) {send(msgLeakDetected.set(leakDetected));} // send message only upon change of state; this triggers on first cycle 537#endif 538 oldLeakDetected = leakDetected; 539 } 540 if (digitalRead(SHUTOFF_VALVE)) { 541 digitalWrite(SHUTOFF_VALVE, false); // leak test complete, time to turn off the shutoff valve again 542 Serial.println();Serial.println("Shut off valve turned off after leak test"); 543 } 544 } 545 } 546 } 547 548 if (isAnyValveOn() == false && leakCheckFlag == false) { // if all valves off and we are not conducting a leak test, 549 // must turn off SHUTOFF_VALVE - necessary for manual op during daylight! 550 if (digitalRead(SHUTOFF_VALVE)) { 551 digitalWrite(SHUTOFF_VALVE, false); // shutoff water when no other valves are on 552 Serial.println();Serial.println("shutoff closed at end of main loop before display update"); 553 } 554 } 555 else if (isAnyValveOn() == true) { 556 if (!digitalRead(SHUTOFF_VALVE)) { 557 digitalWrite(SHUTOFF_VALVE, true); 558 Serial.println();Serial.println("shutoff open at end of main loop"); 559 } 560 } // turn on shutoff valve when another valve is on 561 562 // check input from touch screen 563 if (GetTouch(&x, &y)) { 564 digitalWrite(LED_PIN, false); // display was touched, time to turn on the backlight 565 oldLEDTime = millis(); // reset LED backlight on time 566 if (y < MENUHEIGHT) { // this is a screen select command 567 if (x < 3 * MENUWIDTH) { // touched one of three actionable screen selections 568 if (oldCurrentScreen == 2) { // just managed setpoints, must update EEPROM 569 for (int i = 0; i < NUM_VALVES; i++) { // error check 570 if (lowerMoistureLevel[i] >= upperMoistureLevel[i]) { 571 return; // don't change from Screen 2 unless errors are repaired 572 } 573 } 574 for (int i = 0; i < NUM_VALVES; i++) { 575 EEPROM.update(BASE_EEPROM_ADDR + i, upperMoistureLevel[i]); 576 EEPROM.update(BASE_EEPROM_ADDR + NUM_VALVES + i, lowerMoistureLevel[i]); 577 } 578 } 579 currentScreen = int(x / MENUWIDTH); 580 DrawFrame(MENUWIDTH * currentScreen, 0, MENUWIDTH, MENUHEIGHT, TFT_WHITE); 581 DrawFrame(MENUWIDTH * oldCurrentScreen, 0, MENUWIDTH, MENUHEIGHT, TFT_BLACK); 582 oldCurrentScreen = currentScreen; 583 firstTime = true; 584 } 585 else return; // don't respond to menu 3 586 } 587 } 588 if (currentScreen == 0) { 589 if (firstTime) { 590 Screen0SetUp(); 591 firstTime = false; 592 } 593 if (lastTouchTime > millis()) { lastTouchTime = 0; } // clock rolled over 594 if ((millis() - lastTouchTime) > TOUCH_TIME) { 595 lastTouchTime = millis(); 596 if (GetTouch(&x, &y)) { 597 byte button = whichButton(x, y); 598 if (button == 0) { 599 return; // touched screen in non active area 600 } 601 if (button > 19) { // auto/man buttons 602 button = button - 20; // reduce to single digit 603 autoManState[button] = !autoManState[button]; // change state 604 writePORTA(button, false); // turn off associated valve because we changed auto state on that valve 605 } 606 else { // must be a valve 607 button = button - 10; // reduce to single digit 608 if (!autoManState[button]) { // do not turn on valve if in auto state 609 if (!valveState(button)) { // if the valve is off, we're going to turn it on 610 if (!isAnyAutoValveOn()) { // is a valve in auto state on. If not, we can turn all valves off and then this valve on. 611 PORTA = 0; // turn off all valves 612 writePORTA(button, true); // turn on appropriate valve 613 } 614 } 615 else { // the valve is on, and must be the only one on. Must turn off 616 PORTA = 0; 617 } 618 } 619 } 620 } 621 updateScreen0(); // this is where the screen display is aligned to any state changes above 622 } 623 } 624 if (currentScreen == 1) { 625 if (firstTime) { 626 Screen1SetUp(); 627 firstTime = false; 628 //Serial.println("finished setup"); 629 } 630 if (lastTouchTime > millis()) { lastTouchTime = 0; } // clock rolled over 631 if ((millis() - lastTouchTime) > TOUCH_TIME) { 632 lastTouchTime = millis(); 633 updateScreen1(); 634 } 635 } 636 if (currentScreen == 2) { 637 if (firstTime) { 638 Screen2SetUp(); 639 firstTime = false; 640 //Serial.println("finished setup"); 641 } 642 if (GetTouch(&x, &y)) { 643 if (lastTouchTime > millis()) { lastTouchTime = 0; } // clock rolled over 644 if ((millis() - lastTouchTime) > TOUCH_TIME) { 645 lastTouchTime = millis(); 646 byte arrow = whichArrow(x, y); 647 if (arrow == 0) { 648 return; // touch not in sweet spot 649 } 650 //Serial.print("which arrow = "); Serial.println(arrow); 651 if (arrow > 39) { // auto/man arrows 652 arrow = arrow - 40; // reduce to single digit, 0 to 7 653 lowerMoistureLevel[arrow] -= 5; // decrement upper limit by 5 654 if (lowerMoistureLevel[arrow] < 5) { lowerMoistureLevel[arrow] = 5;} // can't lower below five 655 setArrowColor(arrow); // checks for level inversion and paints errors red 656 //Serial.print("lower Moisture Level"); Serial.print(arrow); Serial.print("\ "); Serial.println(lowerMoistureLevel[arrow]); 657 writeMoistureLevel(3, arrow); // update arrow 658 } 659 else if (arrow > 29) { // must be a valve 660 arrow = arrow - 30; // reduce to single digit 661 lowerMoistureLevel[arrow] += 5; // increment upper limit by 5 662 if (lowerMoistureLevel[arrow] > 95) { lowerMoistureLevel[arrow] = 95;} // can't raise above 95 663 setArrowColor(arrow); // checks for level inversion and paints errors red 664 //Serial.print("lower Moisture Level"); Serial.print(arrow); Serial.print("\ "); Serial.println(lowerMoistureLevel[arrow]); 665 writeMoistureLevel(2, arrow); // update arrow 666 } 667 else if (arrow > 19) { // must be a valve 668 arrow = arrow - 20; // reduce to single digit 669 upperMoistureLevel[arrow] -= 5; // decrement lower limit by 5 670 if (upperMoistureLevel[arrow] < 10) { upperMoistureLevel[arrow] = 10;} // can't lower below ten 671 setArrowColor(arrow); // checks for level inversion and paints errors red 672 //Serial.print("upper Moisture Level"); Serial.print(arrow); Serial.print("\ "); Serial.println(upperMoistureLevel[arrow]); 673 writeMoistureLevel(1, arrow); // update arrow 674 } 675 else { // must be a valve 676 arrow = arrow - 10; // reduce to single digit 677 upperMoistureLevel[arrow] += 5; // increment lower limit by 5 678 if (upperMoistureLevel[arrow] > 100) { upperMoistureLevel[arrow] = 100;} // can't raise above 100 679 setArrowColor(arrow); // checks for level inversion and paints errors red 680 //Serial.print("upper Moisture Level"); Serial.print(arrow); Serial.print("\ "); Serial.println(upperMoistureLevel[arrow]); 681 writeMoistureLevel(0, arrow); // update arrow 682 } 683 } 684 } 685 } 686 if (oldLEDTime > millis()) {oldLEDTime = 0;} // clock rolled over 687 if (millis() - oldLEDTime > MAX_LED_TIME * 60000) { // time to turn off backlight 688 digitalWrite(LED_PIN, true); // turn off LED backlight 689 } 690 if (oldDotTime > millis()) {oldDotTime = 0;} // clock rolled over 691 if (millis() - oldDotTime > DOT_TIME * 1000) { // time to print a dot 692 dotCounter++; 693 if(dotCounter == 80) { Serial.println(); dotCounter = 0;} 694 Serial.print("."); 695 oldDotTime = millis(); 696 } 697} 698 699void Screen0SetUp() { 700 char result[8]; 701 uint16_t color; 702 703 flowRate = flowMeasurement(); // this provides the initial data for subsequent comparisons 704 readMoistureTemp(); // reads all sensors 705 DrawBox(0, 20, 320, 240, TFT_BLUE); // clear working area 706 ILI9341SetCursor(170, 32); 707 DrawString("Flow Rate (ml/sec) ->", SmallFont, TFT_WHITE); 708 DrawFrame(270, 22, 26, 16, TFT_WHITE); // box to contain flow rate 709 ILI9341SetCursor(268, 32); 710 DrawString(dtostrf(flowRate, 6, 1, result), SmallFont, TFT_WHITE); // initial value when entering screen 711 ILI9341SetCursor(5, 160 + 15); 712 DrawString("man", SmallFont, TFT_WHITE); 713 ILI9341SetCursor(5, 200 + 15); 714 DrawString("auto", SmallFont, TFT_WHITE); 715 ILI9341SetCursor(5, 40); 716 DrawString("100%", SmallFont, TFT_GREEN); 717 ILI9341SetCursor(25, 40); 718 DrawString("/", SmallFont, TFT_WHITE); 719 ILI9341SetCursor(30, 40); 720 DrawString("40C", SmallFont, TFT_RED); 721 ILI9341SetCursor(10, 140); 722 DrawString("0%", SmallFont, TFT_GREEN); 723 ILI9341SetCursor(25, 140); 724 DrawString("/", SmallFont, TFT_WHITE); 725 ILI9341SetCursor(30, 140); 726 DrawString("0C", SmallFont, TFT_RED); 727 ILI9341SetCursor(15, 90); 728 DrawString("M", SmallFont, TFT_GREEN); 729 ILI9341SetCursor(25, 90); 730 DrawString("/", SmallFont, TFT_WHITE); 731 ILI9341SetCursor(30, 90); 732 DrawString("T", SmallFont, TFT_RED); 733 for (int i = 0; i < NUM_VALVES; i++) { 734 // deal with temps first (red) then moisture 735 for (int i = 0; i < NUM_VALVES; i++) { 736 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2, 4, 100, TFT_WHITE); // set the background 737 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2 + int((40 - temp[i]) * 100 / 40), 4, 100 - int((40 - temp[i]) * 100 / 40), TFT_RED); // create a red bar to indicate temperature 738 DrawBox(36 * i + 32 + 15, MENUHEIGHT * 2 + int(100 - moistureLevel[i]), 8, 2, TFT_GREEN); // create a green horizontal bar to indicate moisture level - bar height 100 pixels! 739 } 740 color = valveState(i) ? TFT_GREEN : TFT_RED; 741 DrawButton(36 * i + 19 + 17, MENUHEIGHT * 2 + 120, 30, 30, color); // upper left and box size 742 color = autoManState[i] ? TFT_GREEN : TFT_RED; 743 DrawButton(36 * i + 19 + 17, MENUHEIGHT * 2 + 160, 30, 30, color); 744 } 745} 746 747void updateScreen0() { 748 char result[8]; 749 uint16_t color; 750 bool updateFlag = false; // Flag used to check if any item was updated. If so, runs updateAllOld() 751 752 if (oldFlowRate != flowRate) { // update flow if necessary 753 DrawBox(271, 25, 24, 10, TFT_BLUE); 754 ILI9341SetCursor(268, 32); 755 DrawString(dtostrf(flowRate, 6, 1, result), SmallFont, TFT_WHITE); 756 } 757 758 for (int i = 0; i < NUM_VALVES; i++) { // update temps and moisture 759 // deal with temps first (red) then moisture 760 for (int i = 0; i < NUM_VALVES; i++) { // update guages 761 if ((temp[i] != oldTemp[i]) || (moistureLevel[i] != oldMoistureLevel[i])) { // if either temp or moisture changes, rewrite entire bar 762 DrawBox(36 * i + 32 + 15, MENUHEIGHT * 2, 8, 104, TFT_BLUE); // create a blue horizontal bar to clear gauge 763 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2, 4, 100, TFT_WHITE); // set the background 764 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2 + int((40 - temp[i]) * 100 / 40), 4, 100 - int((40 - temp[i]) * 100 / 40), TFT_RED); // create a red bar to indicate temperature 765 DrawBox(36 * i + 32 + 15, MENUHEIGHT * 2 + int(100 - moistureLevel[i]), 8, 2, TFT_GREEN); // create a green horizontal bar to indicate moisture level 766 updateFlag = true; 767 } 768 } 769 // update manual and auto buttons 770 if (valveState(i) != oldValveState[i]) { 771 writeValveButton(i, valveState(i)); // update buttons 772 updateFlag = true; 773 } 774 if (autoManState[i] != oldAutoManState[i]) { 775 writeAutoManButton(i, autoManState[i]); 776 updateFlag = true; 777 } 778 } 779 if (updateFlag) {updateAllOld();} 780} 781 782void Screen1SetUp() { 783 uint16_t x0, x1, x2, y0, y1, y2; 784 uint16_t color; 785 786 DrawBox(0, 20, 320, 240, TFT_BLUE); // clear working area 787 788 ILI9341SetCursor(50, 50); 789 DrawString("System Status", MediumFont, TFT_WHITE); 790 791 systemSafe = !lowFlow && !unresponsiveSensor && !leakDetected; 792 oldSystemSafe = systemSafe; 793 if (systemSafe) { 794 ILI9341SetCursor(80, 70); 795 DrawString("System OK", SmallFont, TFT_GREEN); 796 } 797 else { // check each of the below and indicate alarms appropriately 798 if (lowFlow) { 799 ILI9341SetCursor(80, 60); 800 DrawString("Low Flow", SmallFont, TFT_RED); 801 } 802 if (unresponsiveSensor) { 803 ILI9341SetCursor(80, 70); 804 DrawString("Open Sensor", SmallFont, TFT_RED); 805 } 806 if (leakDetected) { 807 ILI9341SetCursor(80, 80); 808 DrawString("Leak Detected", SmallFont, TFT_RED); 809 } 810 } 811 DrawHLine(0, 120, 319, TFT_WHITE); 812 ILI9341SetCursor(50, 110); 813 DrawString("Sensor Status", MediumFont, TFT_WHITE); 814 815 ILI9341SetCursor(5, 140); 816 DrawString("temp", SmallFont, TFT_WHITE); 817 ILI9341SetCursor(5, 150); 818 DrawString("status", SmallFont, TFT_WHITE); 819 ILI9341SetCursor(5, 140 + 36); 820 DrawString("Msense", SmallFont, TFT_WHITE); 821 ILI9341SetCursor(5, 150 + 36); 822 DrawString("open", SmallFont, TFT_WHITE); 823 ILI9341SetCursor(5, 140 + 72); 824 DrawString("Msense", SmallFont, TFT_WHITE); 825 ILI9341SetCursor(5, 150 + 72); 826 DrawString("short", SmallFont, TFT_WHITE); 827 828 for (uint16_t j = 0; j < 3; j++) { // draw triangles 829 for (uint16_t i = 0; i < NUM_VALVES; i++) { 830 x0 = 36 + 36 * i; 831 x1 = x0 + 26; 832 x2 = 36 + 13 + 36 * i; 833 y0 = 150 + 36 * j; 834 y1 = y0; 835 y2 = y0 - 20; 836 837 if (j == 0) { // these are temp sensors 838 color = tempState[i] ? TFT_GREEN : TFT_RED; 839 } 840 else if ( j == 1) { // open moisture sensors 841 color = moistureOpenState[i] ? TFT_RED : TFT_GREEN; 842 } 843 else { // shorted moisture sensors 844 color = moistureShortState[i] ? TFT_RED : TFT_GREEN; 845 } 846 DrawAlarmSymbol( x2, y2, x0, y0, x1, y1, color); // x2, y2 is the peak point of the triangle 847 } 848 } 849} 850 851void updateScreen1() { 852 uint16_t x0, x1, x2, y0, y1, y2; 853 uint16_t color; 854 bool updateFlag = false; 855 856 systemSafe = !lowFlow && !unresponsiveSensor && !leakDetected; 857 if ((systemSafe != oldSystemSafe) && systemSafe) { 858 DrawBox(80, 52, 100, 40, TFT_BLUE); // clear top area 859 ILI9341SetCursor(80, 70); 860 DrawString("System OK", SmallFont, TFT_GREEN); 861 oldSystemSafe = systemSafe; 862 } 863 else if ((systemSafe != oldSystemSafe) && !systemSafe){ 864 DrawBox(80, 52, 100, 40, TFT_BLUE); // clear top area for new alarm 865 oldSystemSafe = systemSafe; 866 } 867 868 if ((lowFlow != oldLowFlow) && lowFlow) { 869 ILI9341SetCursor(80, 60); 870 DrawString("Low Flow", SmallFont, TFT_RED); 871 } 872 else if ((lowFlow != oldLowFlow) && !lowFlow) { 873 DrawBox(80, 52, 100, 10, TFT_BLUE); // clear area 874 } 875 if ((unresponsiveSensor != oldUnresponsiveSensor) && unresponsiveSensor) { 876 ILI9341SetCursor(80, 70); 877 DrawString("Open Sensor", SmallFont, TFT_RED); 878 } 879 else if ((unresponsiveSensor != oldUnresponsiveSensor) && unresponsiveSensor) { 880 DrawBox(80, 62, 100, 10, TFT_BLUE); // clear area 881 } 882 if ((leakDetected != oldLeakDetected) && leakDetected) { 883 ILI9341SetCursor(80, 80); 884 DrawString("Leak Detected", SmallFont, TFT_RED); 885 } 886 else if ((leakDetected != oldLeakDetected) && !leakDetected) { 887 DrawBox(80, 72, 100, 10, TFT_BLUE); // clear area 888 } 889 890 for (int j = 0; j < 3; j++) { // draw triangles 891 for (int i = 0; i < NUM_VALVES; i++) { 892 x0 = 36 + 36 * i; 893 x1 = x0 + 26; 894 x2 = 36 + 13 + 36 * i; 895 y0 = 150 + 36 * j; 896 y1 = y0; 897 y2 = y0 - 20; 898 899 if (j == 0) { // these are the failed temp sensors 900 if (tempState[i] != oldTempState[i]) { 901 color = tempState[i] ? TFT_GREEN : TFT_RED; 902 DrawAlarmSymbol( x2, y2, x0, y0, x1, y1, color); 903 updateFlag = true; 904 } 905 } 906 else if ( j == 1) { // open moisture sensors 907 if (moistureOpenState[i] != oldMoistureOpenState[i]) { 908 color = moistureOpenState[i] ? TFT_RED : TFT_GREEN; 909 DrawAlarmSymbol( x2, y2, x0, y0, x1, y1, color); 910 updateFlag = true; 911 } 912 } 913 else { // shorted moisture sensors 914 if (moistureShortState[i] != oldMoistureShortState[i]) { 915 color = moistureShortState[i] ? TFT_RED : TFT_GREEN; 916 DrawAlarmSymbol( x2, y2, x0, y0, x1, y1, color); 917 updateFlag = true; 918 } 919 } 920 } 921 } 922 if (updateFlag) {updateAllOld();} // makes sure that oldMoistureState[] etc. are reset 923} 924 925void Screen2SetUp() { 926 uint16_t x0, x1, x2, y0, y1, y2; 927 char result[16]; 928 929 DrawBox(0, 20, 320, 240, TFT_BLUE); // clear working area 930 ILI9341SetCursor(2, MENUHEIGHT + 60); 931 DrawString("UPPER", SmallFont, TFT_WHITE); 932 ILI9341SetCursor(2, MENUHEIGHT + 60 + 100); 933 DrawString("LOWER", SmallFont, TFT_WHITE); 934 935 for (int j = 0; j < 2; j++) { 936 for (int i = 0; i < NUM_VALVES; i++) { 937 DrawFrame(36 * i + 36, MENUHEIGHT + 50 + j * 100, 26, 16, TFT_WHITE); // set the background 938 ILI9341SetCursor(36 * i + 36 + 8, MENUHEIGHT + 60 + j * 100); 939 if (j == 0) { 940 DrawString(itoa(upperMoistureLevel[i], result, 10), SmallFont, TFT_WHITE); 941 } 942 else { 943 DrawString(itoa(lowerMoistureLevel[i], result, 10), SmallFont, TFT_WHITE); 944 } 945 } 946 } 947 for (uint16_t i = 0; i < NUM_VALVES; i++) { // draw triangles 948 x0 = 36 + 36 * i; 949 x1 = x0 + 26; 950 x2 = 36 + 13 + 36 * i; 951 y0 = 65; 952 y1 = y0; 953 y2 = y0 - 20; 954 DrawTriangle( x2, y2, x0, y0, x1, y1, TFT_GREEN); 955 } 956 for (uint16_t i = 0; i < NUM_VALVES; i++) { // draw triangles 957 x0 = 36 + 36 * i; 958 x1 = x0 + 26; 959 x2 = 36 + 13 + 36 * i; 960 y0 = 90; 961 y1 = y0; 962 y2 = y0 + 20; 963 DrawTriangle(x1, y1, x0, y0, x2, y2, TFT_GREEN); 964 } 965 for (uint16_t i = 0; i < NUM_VALVES; i++) { // draw triangles 966 x0 = 36 + 36 * i; 967 x1 = x0 + 26; 968 x2 = 36 + 13 + 36 * i; 969 y0 = 165; 970 y1 = y0; 971 y2 = y0 - 20; 972 DrawTriangle(x2, y2, x0, y0, x1, y1, TFT_GREEN); 973 } 974 for (uint16_t i = 0; i < NUM_VALVES; i++) { // draw triangles 975 x0 = 36 + 36 * i; 976 x1 = x0 + 26; 977 x2 = 36 + 13 + 36 * i; 978 y0 = 190; 979 y1 = y0; 980 y2 = y0 + 20; 981 DrawTriangle(x1, y1, x0, y0, x2, y2, TFT_GREEN); 982 } 983} 984 985#ifdef MY_MYSENSORS 986// MySensors Specific Functions 987void receive(const MyMessage &message) { 988 byte driverPinIndex; 989 990 if (message.isAck()) { 991 // if (SERIAL_TEST) {Serial.println("This is an ack from gateway");} 992 } 993 if (message.type == V_STATUS) { // possible sensors are: 994 // Digital IO -> 10 to 17 995 // AutoMan Switches -> 20 - 27 996 // SafeFlow Switch -> 44 997 if (message.sensor < CHILD_ID_START_OF_VALVES + 8 && message.sensor >= CHILD_ID_START_OF_VALVES) { // must be a driver 998 // Change driver state 999 // Calculate driver from sensor number 1000 driverPinIndex = message.sensor - CHILD_ID_START_OF_VALVES; // driverPinIndex[0] thru driverPinIndex[7] 1001 if (!autoManState[driverPinIndex] && safeFlow) { // only change driver state if in manual mode and safeFlow 1002 writePORTA(driverPinIndex, message.getBool()); // turns off all valves and sets state of valve(driverPinIndex) true or false 1003 if(!message.getBool()) getLastFlowRate(); // make sure we get a flow measurement after valves are turned off 1004 for (int i = 0; i < NUM_VALVES; i++) { // set all valveStates except valveState(driverPinIndx) to false. 1005 if(i != driverPinIndex) {send(msgPORTA[i].set(false));} 1006 } 1007 } 1008 else if(!safeFlow) { 1009 for (int i = 0; i < NUM_VALVES; i++) { // turn off all valves 1010 writePORTA(i, false); // turn off all valves 1011 send(msgPORTA[i].set(false)); // don't know why this is necessary 1012 } 1013 getLastFlowRate(); // make sure we get a flow measurement after valves are turned off 1014 updateScreen0(); 1015 } 1016 } 1017 else if (message.sensor < CHILD_ID_START_OF_AUTOS + 8 && message.sensor >= CHILD_ID_START_OF_AUTOS){ // must be an AutoMan Switch 1018 autoManState[message.sensor - CHILD_ID_START_OF_AUTOS] = message.getBool(); // this means state was changed by HASS 1019 if(!message.getBool() && valveState(message.sensor - CHILD_ID_START_OF_AUTOS)) {PORTA = 0;} // if the autoState is turning off, must also turn off associated valve if on 1020 updateScreen0(); 1021 } 1022 else if (message.sensor == CHILD_ID_SAFEFLOW_SWITCH) { // HA requesting SafeFlow reset 1023 if(message.getBool() == true) { 1024 safeFlow = true; // user can only turn switch on. here it's turned off again after one second. 1025 lowFlow = false; 1026 send(msgOpenSensor.set(lowFlow)); // let HA know the system is shutting down due to an open sensor 1027 unresponsiveSensor = false; 1028 send(msgLowFlow.set(unresponsiveSensor)); // send alarm to HA to indicate system shut down. 1029 send(msgRestart.set(false)); // turn off the HA switch 1030 delay(1000); 1031 oldValveOnTime = millis(); 1032 } 1033 } 1034 } 1035 if (message.type == V_TRIPPED) { // recieved dayNight state from HA 1036 if (message.sensor == CHILD_ID_DAY_NIGHT) { // this is the day night switch that HA sends to prevent daytime watering 1037 dayNight = message.getBool(); // if true, it's daytime, do not water 1038 } 1039 } 1040} 1041#endif 1042 1043// Update All 1044void updateAllOld(){ // update changed states 1045 1046#ifdef MY_MYSENSORS 1047 Serial.println("made it to MySensors UpdateAllOld"); 1048 for (int i = 0; i < NUM_VALVES; i++) { 1049 if (oldValveState[i] == !valveState(i)) {send(msgPORTA[i].set(valveState(i))); oldValveState[i] = valveState(i);} 1050 if (oldMoistureLevel[i] != moistureLevel[i]) {send(msgAI[i].set(moistureLevel[i],1)); oldMoistureLevel[i] = moistureLevel[i];} 1051 if (oldTemp[i] != temp[i]) {send(msgTemp[i].set(temp[i],1)); oldTemp[i] = temp[i];} 1052 if (oldAutoManState[i] != autoManState[i]) {send(msgAutoMan[i].set(autoManState[i])); oldAutoManState[i] = autoManState[i];} 1053 Serial.print("oldTempState[");Serial.print(i);Serial.print("] = ");Serial.print(oldTempState[i]);Serial.print(" TempState[");Serial.print(i);Serial.print("] = ");Serial.println(tempState[i]); 1054 if (oldTempState[i] != tempState[i]) {send(msgTempState[i].set(tempState[i])); oldTempState[i] = tempState[i];} 1055 Serial.print("oldMoistureOpenState[");Serial.print(i);Serial.print("] = ");Serial.print(oldMoistureOpenState[i]);Serial.print(" moistureOpenState[");Serial.print(i);Serial.print("] = ");Serial.println(moistureOpenState[i]); 1056 if (oldMoistureOpenState[i] != moistureOpenState[i]) { 1057 send(msgMoistureOpenState[i].set(moistureOpenState[i])); oldMoistureOpenState[i] = moistureOpenState[i]; 1058 } 1059 Serial.print("oldMoistureShortState[");Serial.print(i);Serial.print("] = ");Serial.print(oldMoistureShortState[i]);Serial.print(" moistureShortState[");Serial.print(i);Serial.print("] = ");Serial.println(moistureShortState[i]); 1060 if (oldMoistureShortState[i] != moistureShortState[i]) { 1061 send(msgMoistureShortState[i].set(moistureShortState[i])); oldMoistureShortState[i] = moistureShortState[i]; 1062 } 1063 } 1064 if (oldFlowRate != flowRate) {send(msgFlow.set(flowRate,2)); oldFlowRate = flowRate;} 1065 if (oldLowFlow != lowFlow) {send(msgLowFlow.set(lowFlow)); oldLowFlow = lowFlow;} // used to let HA know that we have no water flow 1066 if (oldUnresponsiveSensor != unresponsiveSensor) {send(msgOpenSensor.set(unresponsiveSensor)); oldUnresponsiveSensor = unresponsiveSensor;}// used to let HA know that we may have an open sensor 1067#else 1068 for (int i = 0; i < NUM_VALVES; i++) { 1069 if (oldValveState[i] == !valveState(i)) { oldValveState[i] = valveState(i);} 1070 if (oldMoistureLevel[i] != moistureLevel[i]) {oldMoistureLevel[i] = moistureLevel[i];} 1071 if (oldTemp[i] != temp[i]) {oldTemp[i] = temp[i];} 1072 if (oldAutoManState[i] != autoManState[i]) { oldAutoManState[i] = autoManState[i];} 1073 if (oldTempState[i] != tempState[i]) { oldTempState[i] = tempState[i];} 1074 if (oldMoistureOpenState[i] != moistureOpenState[i]) { 1075 oldMoistureOpenState[i] = moistureOpenState[i]; 1076 } 1077 if (oldMoistureShortState[i] != moistureShortState[i]) { 1078 oldMoistureShortState[i] = moistureShortState[i]; 1079 } 1080 } 1081 if (oldFlowRate != flowRate) { oldFlowRate = flowRate;} 1082 if (oldLowFlow != lowFlow) { oldLowFlow = lowFlow;} // used to let HA know that we have no water flow 1083 if (oldUnresponsiveSensor != unresponsiveSensor) { oldUnresponsiveSensor = unresponsiveSensor;}// used to let HA know that we may have an open sensor 1084#endif 1085 Serial.print("*"); 1086 for (int i = 0; i < NUM_VALVES; i++) { 1087 Serial.println();Serial.print("moisture level(");Serial.print(i);Serial.print(") = ");Serial.print(moistureLevel[i]); 1088 Serial.println();Serial.print("temperature(");Serial.print(i);Serial.print(") = ");Serial.print(temp[i]); 1089 } 1090 Serial.println(); 1091} 1092 1093// General Functions 1094void UpdateValvesGauges() { 1095 uint16_t color; 1096 1097 for (int i = 0; i < NUM_VALVES; i++) { 1098 // deal with temps first (red) then moisture 1099 for (int i = 0; i < NUM_VALVES; i++) { 1100 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2, 4, 100, TFT_WHITE); // set the background 1101 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2 + (40 - temp[i]) * 100 / 40, 4, 100 - (40 - temp[i]) * 100 / 40, TFT_RED); // create a red bar to indicate temperature 1102 DrawBox(36 * i + 32 + 15, MENUHEIGHT * 2 + (1 - moistureLevel[i]) * 100, 8, 2, TFT_GREEN); // create a green horizontal bar to indicate moisture level 1103 } 1104 color = valveState(i) ? TFT_GREEN : TFT_RED; 1105 DrawButton(36 * i + 19 + 17, MENUHEIGHT * 2 + 120, 30, 30, color); // upper left and box size 1106 color = autoManState[i] ? TFT_GREEN : TFT_RED; 1107 DrawButton(36 * i + 19 + 17, MENUHEIGHT * 2 + 160, 30, 30, color); 1108 } 1109} 1110 1111byte whichButton(int x, int y) { 1112 if (x < 36 || x > 318 || y < 160 || (int((x - 36) / 36) > NUM_VALVES)) { // x out of button range 1113 return (0); 1114 } 1115 else if (y < 195) { // first row of buttons - valves 1116 return (10 + int((x - 36) / 36)); // returns a number between 11 and 18 to indicate row 1, collumn 0-7 1117 } 1118 else { // second row of buttons 1119 return (20 + int((x - 36) / 36)); // returns a number between 21 and 28 to indicate row 1, collumn 0-7 1120 } 1121} 1122 1123byte whichArrow(int x, int y) { 1124 if (x < 36 || x > 318 || y < 40 || (int((x - 36) / 36) > NUM_VALVES)) { // x out of button range 1125 return (0); 1126 } 1127 else if (y < 80) { // first row of arrows 1128 return (10 + int((x - 36) / 36)); // returns a number between 11 and 18 to indicate row 1, collumn 0-7 1129 } 1130 else if (y < 130) { // second row of arrows 1131 return (20 + int((x - 36) / 36)); // returns a number between 21 and 28 to indicate row 1, collumn 0-7 1132 } 1133 else if (y < 180) { // third row of arrows 1134 return (30 + int((x - 36) / 36)); // returns a number between 31 and 38 to indicate row 1, collumn 0-7 1135 } 1136 else { // fourth row of arrows 1137 return (40 + int((x - 36) / 36)); // returns a number between 41 and 48 to indicate row 1, collumn 0-7 1138 } 1139} 1140 1141void setArrowColor(byte arrow) { 1142 uint16_t x0, x1, x2, y0, y1, y2; 1143 byte row; 1144 byte column; 1145 1146 row = int(arrow / 10); 1147 column = (arrow - row * 10); // need to know what column we are changing 1148 1149 if (lowerMoistureLevel[column] >= upperMoistureLevel[column]) { 1150 color = TFT_RED; 1151 } 1152 else { 1153 color = TFT_GREEN; 1154 } 1155 x0 = 36 + 36 * column; 1156 x1 = x0 + 26; 1157 x2 = 36 + 13 + 36 * column; 1158 y0 = 65; 1159 y1 = y0; 1160 y2 = y0 - 20; 1161 DrawTriangle( x2, y2, x0, y0, x1, y1, color); 1162 1163 x0 = 36 + 36 * column; 1164 x1 = x0 + 26; 1165 x2 = 36 + 13 + 36 * column; 1166 y0 = 190; 1167 y1 = y0; 1168 y2 = y0 + 20; 1169 DrawTriangle(x1, y1, x0, y0, x2, y2, color); 1170} 1171 1172void writePORTA(uint8_t pin, bool state) { // set particular pin true or false (only one bit true at a time!!! 1173 uint8_t newState; 1174 1175 if (state) { // if the valve should be turned on 1176 newState = bitValue[pin]; // turns all valves off except the desired one 1177 PORTA = newState; 1178 } 1179 else { // if the valve should be turned off 1180 if (valveState(pin)) { 1181 PORTA = 0; // if the request valve's state is true, it must be the only on valve, therefore we can turn all valves off 1182 } 1183 // otherwise, we don't want to turn unassociated valves off 1184 } 1185} 1186 1187void writeAutoManButton(uint8_t button, bool state) { 1188 uint16_t color; 1189 1190 color = state ? TFT_GREEN : TFT_RED; 1191 DrawButton(36 * button + 19 + 17, MENUHEIGHT * 2 + 160, 30, 30, color); 1192} 1193 1194void writeValveButton(uint8_t button, bool state) { 1195 uint16_t color; 1196 1197 color = state ? TFT_GREEN : TFT_RED; 1198 DrawButton(36 * button + 19 + 17, MENUHEIGHT * 2 + 120, 30, 30, color); // upper left and box size 1199} 1200 1201void writeTempState(uint8_t sensor) { 1202 uint16_t x0, x1, x2, y0, y1, y2; 1203 uint16_t color; 1204 1205 x0 = 36 + 36 * sensor; 1206 x1 = x0 + 26; 1207 x2 = 36 + 13 + 36 * sensor; 1208 y0 = 150; 1209 y1 = y0; 1210 y2 = y0 - 20; 1211 1212 color = tempState[sensor] ? TFT_GREEN : TFT_RED; 1213 DrawTriangle( x2, y2, x0, y0, x1, y1, color); 1214} 1215 1216void writeOpenSensor(uint8_t sensor) { 1217 uint16_t x0, x1, x2, y0, y1, y2; 1218 uint16_t color; 1219 1220 x0 = 36 + 36 * sensor; //Serial.println(x0); 1221 x1 = x0 + 26; //Serial.println(x1); 1222 x2 = 36 + 13 + 36 * sensor; //Serial.println(x2); 1223 y0 = 150 + 36; //Serial.println(y0); 1224 y1 = y0; //Serial.println(y1); 1225 y2 = y0 - 20; //Serial.println(y2); Serial.println(); 1226 1227 color = moistureOpenState[sensor] ? TFT_GREEN : TFT_RED; 1228 DrawTriangle( x2, y2, x0, y0, x1, y1, color); 1229} 1230 1231void writeShortedSensor(uint8_t sensor) { 1232 uint16_t x0, x1, x2, y0, y1, y2; 1233 uint16_t color; 1234 1235 x0 = 36 + 36 * sensor; //Serial.println(x0); 1236 x1 = x0 + 26; //Serial.println(x1); 1237 x2 = 36 + 13 + 36 * sensor; //Serial.println(x2); 1238 y0 = 150 + 72; //Serial.println(y0); 1239 y1 = y0; //Serial.println(y1); 1240 y2 = y0 - 20; //Serial.println(y2); Serial.println(); 1241 1242 color = moistureShortState[sensor] * TFT_GREEN + !moistureShortState[sensor] * TFT_RED; 1243 DrawTriangle( x2, y2, x0, y0, x1, y1, color); 1244} 1245 1246void writeMoistureLevel(byte row, byte column) { // row is 0 or 1, column is 0 thru 7 1247 char result[16]; 1248 1249 if (row < 2) { 1250 row = 0; 1251 } else { 1252 row = 1; 1253 } 1254 DrawBox(36 * column + 36, MENUHEIGHT + 50 + row * 100, 26, 16, TFT_BLUE); // delete contents 1255 DrawFrame(36 * column + 36, MENUHEIGHT + 50 + row * 100, 26, 16, TFT_WHITE); // set the background 1256 ILI9341SetCursor(36 * column + 36 + 8, MENUHEIGHT + 60 + row * 100); 1257 if (row == 0) { 1258 DrawString(itoa(upperMoistureLevel[column], result, 10), SmallFont, TFT_WHITE); 1259 } 1260 else { 1261 DrawString(itoa(lowerMoistureLevel[column], result, 10), SmallFont, TFT_WHITE); 1262 } 1263} 1264 1265// non-display related functions 1266 1267bool isAnyValveOn() { 1268 bool result = true; 1269 1270 if (PORTA == 0 ) {result = false;} 1271 return(result); 1272} 1273 1274bool isAnyAutoValveOn() { 1275 1276 if (PORTA == 0 ) { 1277 return (false); // no valves are on, so we're ok 1278 } 1279 else { // some valve is on, is it also in auto mode? 1280 for (int i = 0; i < NUM_VALVES; i++) { 1281 if (valveState(i) && autoManState[i]) { 1282 return (true); 1283 } 1284 } 1285 return (false); // if we made it here, no valve that might be in auto state is on now 1286 } 1287} 1288 1289bool valveState(uint8_t whichBit) { 1290 return (bitRead(PORTA, whichBit)); 1291} 1292 1293byte whichValveIsOn() { 1294 for (byte i = 0; i < NUM_VALVES; i++) { 1295 if (valveState(i)) { 1296 return (i); 1297 } 1298 } 1299} 1300 1301void readMoistureTemp() { 1302 // get temperature data then read soil resistance and calculate moisture level 1303 Serial.println("made it to readMoistureTemp()"); 1304 // read all sensors by indexing through the eight analog switches in the multiplexer (74HC4051) 1305 for (int index = 0; index < NUM_VALVES; index++) { 1306 digitalWrite(TMPS0, bitRead(tempSensorConfig[index], 0)); // write the address to the multiplexer 1307 digitalWrite(TMPS1, bitRead(tempSensorConfig[index], 1)); // tempSensorConfig modifies the index for the mapping 1308 digitalWrite(TMPS2, bitRead(tempSensorConfig[index], 2)); // of an ordered sensor configuration - see map defined above 1309 // call sensors.requestTemperatures() to issue a global temperature 1310 // as configured, there will only be one device on the bus at a time!!! 1311 sensors.requestTemperatures(); 1312 // query conversion time and sleep until conversion completed 1313 int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution()); 1314 // sleep()/wait()call can be replaced by wait() call if node need to process incoming messages (or if node is repeater) 1315 delay(conversionTime); 1316 // get temps individually 1317 // We use the function ByIndex to get the temperature from the first and only sensor. 1318 temp[index] = sensors.getTempCByIndex(0); // read the currently selected temperature sensor 1319 Serial.print("temp(");Serial.print(index);Serial.print(") = ");Serial.println(temp[index]); 1320 updateTemp(index); 1321 Serial.println(); 1322 } 1323 // read all the moisture levels 1324 digitalWrite(POWER_PIN, false); // Turn on sensor power; powers all sensors; active low 1325 delay(10); //delay 10 milliseconds to allow reading to settle - determined by testing 1326 for (int i = 0; i < NUM_VALVES; i++) { // read all moisture sensors 1327 uint16_t reading = analogRead(AI_pin[i]); 1328 Serial.print("moistureAnalogReading[");Serial.print(i);Serial.print("] = ");Serial.println(reading); 1329 if (reading > 900) { 1330 moistureOpenState[i] = true; // are sensors open? 1331 } else { 1332 moistureOpenState[i] = false; 1333 } 1334 if (reading < 23) { 1335 moistureShortState[i] = true; // are sensors shorted 1336 } else { 1337 moistureShortState[i] = false; 1338 } 1339 float x = Rseries / (1023.0001 / reading - 1.0); // 1023 is largest AI value. 0.0001 prevents overflow. 1340 // A = -0.0112*T +2.52 1341 // B = -3.6x10^-3 * T - 3.6 1342 float a = -0.0112 * temp[i] + 2.52; 1343 a = pow(10, a); 1344 float b = 1 / (-.0036 * temp[i] - 3.6); 1345 //Serial.print("Soil Resistance = "); Serial.println(x); 1346 moistureLevel[i] = pow(x / a, b) * 100; // was pow(x/151.35,-.27)*100 1347 if (moistureLevel[i] > 100) {moistureLevel[i] = 100;} // the curve fit equations do not work beyond about 80% moisture 1348 //Serial.print("moisture level("); Serial.print(i); Serial.print(") = "); Serial.println(moistureLevel[i]); 1349 //Serial.print("Analog reading = "); Serial.println(analogRead(AI_pin[i])); 1350 //Serial.print("Moisture Open = "); Serial.print(moistureOpenState[i]); Serial.print(" Old Moisture Open = "); Serial.println(oldMoistureOpenState[i]); 1351 //Serial.print("Moisture Short = "); Serial.print(moistureShortState[i]); Serial.print(" Old Moisture Short = "); Serial.println(oldMoistureShortState[i]); 1352 } 1353 digitalWrite(POWER_PIN, true); // turn off power to moisture sensors 1354 // Fetch temperatures from Dallas sensors 1355} 1356 1357void updateTemp(int i) { // this simplifies the code for reading the sensors. 1358 if (temp[i] == DEVICE_DISCONNECTED_C || temp[i] == -127) {// either condition indicates no sensor connected 1359 temp[i] = 22; // set temperature to std temp so measurements can continue 1360 tempState[i] = false; // keep track of failing sensors 1361 } 1362 else { 1363 tempState[i] = true; // keep track of working sensors 1364 } 1365 Serial.print("temp");Serial.print(i);Serial.print(" = "); Serial.println(temp[i]); 1366 Serial.print("tempState");Serial.print(i);Serial.print(" = "); Serial.println(tempState[i]); 1367} 1368 1369float flowMeasurement() { // take a second to check flow rate 1370 // this is a crude method used here because there are no interrupt pins left 1371 // in this implementation. At the max reported flow rate through 1/4" drip 1372 // tubing 0f 20 GPH, we can anticipate at pulse frequency of 30 Hz. That's 1373 // slow enough to make this routine work well. 1374 1375 bool state = digitalRead(FLOW_PIN); 1376 bool oldState = state; 1377 bool notDone = true; 1378 unsigned long int count = 0; 1379 unsigned long int oldMillis = millis(); 1380 1381 while (notDone) { 1382 state = digitalRead(FLOW_PIN); 1383 if (state != oldState) { 1384 oldState = state; 1385 count++; 1386 } 1387 if ( oldMillis > millis()) { 1388 oldMillis = 0; // rollover event 1389 } 1390 notDone = (millis() - oldMillis < 1000); // 1000 = 1 sec: when this is false, we are done 1391 } 1392 flowRate = count / (2 * K); // gets two counts per square wave; but rising and falling! 1393 return (flowRate); // K is the pulses per liter (1380), so we are returning the flow rate in liters per second 1394 // Should be 0.022 liters per second or there abouts. 1395} 1396 1397void getLastFlowRate() { 1398 delay(2000); // delay to let valve close and flow rate to settle 1399 flowRate = flowMeasurement(); // this takes one second to execute 1400} 1401 1402void safeFlowShutdown() { 1403 safeFlow = false; // system shuts down until attended to. When repaired, turn system off and on again. 1404 leakCheckFlag == true; // get ready for next leak test - this could be done on restart 1405 PORTA = 0; // turn all valves off (valves are active high!) 1406 getLastFlowRate(); // make sure flow has shut down. 1407 digitalWrite(SHUTOFF_VALVE, false); // turn off shutoff valve 1408 Serial.println();Serial.println("Shut off valve turned off in safeFlowShutdown"); 1409} 1410 1411void DrawButton(uint16_t x1, uint16_t y1, uint16_t s1, uint16_t s2, uint16_t color) { 1412 1413 DrawBox(x1, y1, s1, s2, TFT_BLACK); 1414 DrawBox(x1 + 4, y1 + 4, s1 - 8, s2 - 8, color); 1415 DrawFrame(x1 + 8, y1 + 8, s1 - 16, s2 - 16, TFT_BLACK); 1416} 1417 1418void DrawAlarmSymbol(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) { 1419 1420 DrawTriangle( x1, y1, x2, y2, x3, y3, TFT_BLACK); // x1, y1 is the peak point of the triangle 1421 DrawTriangle( x1, y1 + 4, x2 + 4, y2 - 2, x3 - 4, y3 - 2, color); 1422} 1423
Nano Irrigation_System2.96.ino
c_cpp
1#define VERSION "2.96" 2/* REVISION HISTORY 3 * Written by Paul M. Zavracky 4 * Borrows heavily from the work of zillions of others 5 * 6 * The irrigation system comprises a set of 1 to 8 1/4 12v water valves, a moisture sensor, an arduino, 7 * an NRF24 Radio, and 8 relays. The Arduino takes both the digital and analog signals from the 8 * moisture sensor and uses this information to determine if watering is needed. If so, the Arduino 9 * sends a signal to the relay board, actualing the appropriate relay. The relay connects a 12V supply 10 * to the water valve. The Arduino continues monitoring the moisture sensor. Once a predetermined level 11 * is reached, the Arduino turns off the water flow. 12 * Uses Larry Bank's Bit Bang I2C library. You can find it here: 13 * https://github.com/bitbank2/BitBang_I2C 14 */ 15 16// setup for MCP23008 port expander 17// definitions for mcp23008 18#define MCP23008_ADDR 0x23 //Address of MCP23008 19#define IODIR 0x00 // I/O DIRECTION REGISTER 20#define IPOL 0x01 // INPUT POLARITY PORT REGISTER 21#define GPINTEN 0x02 // INTERRUPT-ON-CHANGE PINS 22#define DEFVAL 0x03 // DEFAULT VALUE REGISTER 23#define INTCON 0x04 // INTERRUPT-ON-CHANGE CONTROL REGISTER 24#define OCON 0x05 // I/O EXPANDER CONFIGURATION REGISTER 25#define GPPU 0x06 // GPIO PULL-UP RESISTOR REGISTER 26#define INTF 0x07 // INTERRUPT FLAG REGISTER 27#define INTCAP 0x08 // INTERRUPT CAPTURED VALUE FOR PORT REGISTER 28#define GPIO 0x09 // GENERAL PURPOSE I/O PORT REGISTER 29#define OLAT 0x0A // OUTPUT LATCH REGISTER 0 30 31// setup for MySensors 32#define MY_NODE_ID 30 33//#define MY_DEBUG // comment out this line to remove MySensors use of serial port!!! 34#define SERIAL_TEST true // d0 and d1 can not be used if the serial bus is active!!! 35 36#define MY_PARENT_NODE_ID 0 37#define MY_PARENT_NODE_IS_STATIC 38 39// Enable and select radio type attached 40#define MY_RADIO_RF24 41#define MY_RF24_PA_LEVEL RF24_PA_LOW 42 43// Set this to the pin you connected the Water Leak Detector's analog pins to 44// The Analog pins are used to sense the moisture level 45#define AI0_PIN A0 46#define AI1_PIN A2 47#define AI2_PIN A1 48#define AI3_PIN A3 49#define AI4_PIN A4 50#define AI5_PIN A5 51#define AI6_PIN A6 52#define AI7_PIN A7 53 54// Arduino output pin setup 55#define INTERRUPT_PIN 8 // For MCP23008, pins 8 - 5 56#define RESET_PIN 7 57#define SDA_PIN 6 58#define SCL_PIN 5 59#define POWER_PIN 4 // Sensor Power Pin 60#define FLOW_PIN 3 // Flow sensor input pin 61#define MCP_PIN5 2 // IRQ for NF24 Radio 62#define MCP_PIN6 1 // Currently unused. This pin and the one below will not work in SERIAL_MODE OR MY_DEBUG are true!!! 63#define MCP_PIN7 0 64 65// analog inputs 66#define CHILD_ID_START_OF_AI 0 67#define CHILD_ID_AI0 0 68#define CHILD_ID_AI1 1 69#define CHILD_ID_AI2 2 70#define CHILD_ID_AI3 3 71#define CHILD_ID_AI4 4 72#define CHILD_ID_AI5 5 73#define CHILD_ID_AI6 6 74#define CHILD_ID_AI7 7 75 76// digital outputs (switches in HA) 77#define CHILD_ID_START_OF_RELAYS 10 78#define CHILD_ID_DO0 10 79#define CHILD_ID_DO1 11 80#define CHILD_ID_DO2 12 81#define CHILD_ID_DO3 13 82#define CHILD_ID_DO4 14 83#define CHILD_ID_DO5 15 84#define CHILD_ID_DO6 16 85#define CHILD_ID_DO7 17 86 87//Auto/Manual Mode Switches 88#define CHILD_ID_START_OF_AUTOS 20 89#define CHILD_ID_AutoMan0 20 90#define CHILD_ID_AutoMan1 21 91#define CHILD_ID_AutoMan2 22 92#define CHILD_ID_AutoMan3 23 93#define CHILD_ID_AutoMan4 24 94#define CHILD_ID_AutoMan5 25 95#define CHILD_ID_AutoMan6 26 96#define CHILD_ID_AutoMan7 27 97 98#define CHILD_ID_FLOW 30 99#define CHILD_ID_DAY_NIGHT 31 // day night sensor gets data from Home Assistant 100#define CHILD_ID_LOW_FLOW 32 // low flow alarm indicator for HA - sends alarm, shuts down system 101#define CHILD_ID_OPEN_SENSOR 33 // open sensor alarm indicator for HA - sends alarm, shuts down system 102#define CHILD_ID_SAFEFLOW_SWITCH 34 // if there is an automatic shutdown (safeFlow = false), this switch let's the system restart (safeFlow = true) 103 104// remeber that fully dry give 1023 reading 105#define MAX_VALVE_TIME 5 // maximum time a valve can stay on in minutes. If moisture level hasn't dropped on next cycle, valve will 106 // turn on againg 107#define VALVES_OFF_MOISTURE_MEASUREMENT_TIME 15 // Time in minutes between moisture sensor readings when no valves are open. 108#define VALVES_ON_MOISTURE_MEASUREMENT_TIME 0.1 // Time in minutes between moisture sensor readings when a valve is open. 109 110// remember that fully dry gives 1023 reading 111// you calculate the values for the following targets from the desired moisture level as follows: 112// TARGET = 1024/(Rseries/(151.4*M%^-3.7) + 1), where M% is the fractional moisture level -- 60% = 0.6 113// So, for a range of 40% to 60%: (1000 is the value of the series resistor!) 114// 1024/(1000/(151.4(.4)^-3.7) + 1) = 837 115// 1024/(1000/(151.4(.6)^-3.7) +1) = 512 116// If you are not using Aref by connecting the sensor drive signal to Aref, then check the sensor open circuit value for AI 117// In my case, that value is 973, so 837*973/1024 = 765 and 512*972/1024 = 486.5 118#define TARGET_FULL_MOISTURE 487 // maximum expected reading for fully irrigated soil (soil is wet enough) 119#define TARGET_MIN_MOISTURE 837 // minimum expected reading for fully irrigated soil (soil is too dry) 120#define NUM_VALVES 6 // the total numbeer of valves (max 8) 121 122#define RELAY_ON 1 123#define RELAY_OFF 0 124#define K 2.28 // 1380 pulses/liter found online. Max Flow through 30' should be about 5 GPH or 20 liters/hour or 0.33 liters/min or .0055 liters per sec. 125 // That implies about 8 pulses per second. Some report closer to 30 Hz? Our unit should be milliliters per second, so K should be 1.38. 126 // For the sensor used in this project, the reported value K = 1.38 was too small. Flow (pulsed based) yeilded 73, for 44 ml/sec. 127 // In this code, flowRate = count/2K, where counts are made on both rising and falling edges. Flow rate should equal flowRate/1.65, so 128 // Knew = 1.28 * 1.65 or 2.28. 129 130//#define Rseries 1000.00 // series resistance used in moisture measurement 131// load libraries 132 133#include <SPI.h> 134#include <MySensors.h> 135#include <BitBang_I2C.h> 136 137// Start I2C 138BBI2C bbi2c; 139 140// Variables Definitions 141uint16_t moisture_Level[NUM_VALVES]; // moisture level is just the binary voltage reading at the analog input. It's converted to % by Home Assistant. 142 // Convertion equation: M% = (((((1000.0 / (924 / AnalogReading - 1)))/151.35)^(-0.27))*100) 143 // where 924 is the maximum open circuit sensor reading. (on next itteration will tie drive voltage to Vref! 144uint16_t oldmoisture_Level[NUM_VALVES] = {0}; // initialize all old data to zero 145uint16_t startMoistureLevel = 1024; // used for sensor testing 146 147boolean newLoop = true; // to update HA after power outage 148//unsigned long oldNoDataTime = 0; // used to check for no data to force communication with HA 149unsigned long oldValveOnTime = 0; // used to control the amount of time a valve is open 150unsigned long oldNoMoistureTime = 0; // minutes between moisture measurements when no valve is on 151 152byte AI_pin[] = {AI0_PIN, AI1_PIN, AI2_PIN, AI3_PIN, AI4_PIN, AI5_PIN, AI6_PIN, AI7_PIN}; 153byte CHILD_ID_AI[] = {CHILD_ID_AI0, CHILD_ID_AI1, CHILD_ID_AI2, CHILD_ID_AI3, CHILD_ID_AI4, CHILD_ID_AI5, CHILD_ID_AI6, CHILD_ID_AI7}; 154byte CHILD_ID_DO[] = {CHILD_ID_DO0, CHILD_ID_DO1, CHILD_ID_DO2, CHILD_ID_DO3, CHILD_ID_DO4, CHILD_ID_DO5, CHILD_ID_DO6, CHILD_ID_DO7}; 155byte CHILD_ID_AutoMan[] = {CHILD_ID_AutoMan0, CHILD_ID_AutoMan1, CHILD_ID_AutoMan2, CHILD_ID_AutoMan3, 156 CHILD_ID_AutoMan4, CHILD_ID_AutoMan5, CHILD_ID_AutoMan6, CHILD_ID_AutoMan7}; 157 158bool autoMan_State[NUM_VALVES] = {false}; // man if false, auto if true 159bool ack = 1; 160float flowRate = 0.0; 161//float Rsoil = 0.0; 162bool safeFlow = true; // if flow measures to long, the system will set this flag false and prevent valves from being actuated (prevent over heating) 163bool lowFlow = false; // if the flow is too low, the system sets this flag and shuts down. HA can see this flag. 164bool openSensor = false; // if a sensor is open and doesn't responde to irrigation, the system sets this flag and shuts down. HA can see this flag. 165byte flowTestCount = 0; // keeps track of the number of zero flow measurements made - system shuts down after NUM_FLOW_TESTS 166bool dayNight = false; // if true, it is daytime and the system should not water plants 167uint8_t bitValue[] = {1,2,4,8,16,32,64,128}; 168 169// MySensors messages 170MyMessage msgAI[NUM_VALVES]; // these messages tell HA the moisture level 171MyMessage msgGPIO[NUM_VALVES]; // these messages let HA know what relays 172MyMessage msgAutoMan[NUM_VALVES]; // these messages let HA know what relays 173MyMessage msgFlow(CHILD_ID_FLOW, V_FLOW); 174MyMessage msgDayNight(CHILD_ID_DAY_NIGHT, V_TRIPPED); // used to prevent watering during the day 175MyMessage msgLowFlow(CHILD_ID_LOW_FLOW, V_TRIPPED); // used to let HA know that we have no water flow 176MyMessage msgOpenSensor(CHILD_ID_OPEN_SENSOR, V_TRIPPED); // used to let HA know that we may have an open sensor 177MyMessage msgRestart(CHILD_ID_SAFEFLOW_SWITCH, V_STATUS); // safeMode switch, shut down due to system failure, restart through HA 178 179void setup(){ 180 // setup MCP23008 for output 181 pinMode(RESET_PIN, OUTPUT); 182 digitalWrite(RESET_PIN, 1); // reset pin on mcp23008 183 pinMode(POWER_PIN, OUTPUT); 184 digitalWrite(POWER_PIN, false); // turn off sensor power, power direct from pin here!!!! 185 pinMode(INTERRUPT_PIN, INPUT); 186 187 //Setup inputs and outputs 188 for (int i = 0; i < NUM_VALVES; i++) { 189 pinMode(AI_pin[i], INPUT); 190 } 191 pinMode(FLOW_PIN, INPUT_PULLUP); // flow sensor requires the input have a pullup resistor 192 193 // initialize messages 194 for (int i = 0; i < NUM_VALVES; i++) { 195 msgAI[i].sensor = i; // Analog sensors start at child ID 0 196 msgAI[i].type = V_LEVEL; // these are the moisture sensors 197 msgGPIO[i].sensor = i + 10; // Relays start at child ID 10 198 msgGPIO[i].type = V_STATUS; // these are the sprinkler valves 199 msgAutoMan[i].sensor = i + 20; // AutoMan switches start at child ID 20 200 msgAutoMan[i].type = V_STATUS; // these are the switch to set mode 201 } 202 203 // I2C initialization 204 memset(&bbi2c, 0, sizeof(bbi2c)); 205 bbi2c.bWire = 0; // use bit bang, not wire library 206 bbi2c.iSDA = SDA_PIN; 207 bbi2c.iSCL = SCL_PIN; 208 I2CInit(&bbi2c, 100000L); 209 delay(100); // allow devices to power up 210 211 // I2C ready to go, set up for output 212 writeRegister(IODIR, 0); // make all GPIO pins outputs 213 writeRegister(IPOL, 0); // make all GPIO pins not inverted 214 writeRegister(GPIO, 255); // turn all bits on, all valves off (valves are active low!) 215} 216 217void presentation() 218{ 219 // Send the sketch version information to the gateway 220 sendSketchInfo("IRRIGATION SYSTEM ", VERSION); 221 Serial.print("Irrigation System, 2021, Version ");Serial.println(VERSION); // this line will identify version even when MySensor Debug is turned off. 222 223 // Register all sensors to gw (they will be created as child devices) 224 for (int i = 0; i < NUM_VALVES; i++) { 225 present(CHILD_ID_AI[i], S_MOISTURE); 226 present(CHILD_ID_DO[i], S_SPRINKLER); 227 present(CHILD_ID_AutoMan[i], S_LIGHT); 228 } 229 present(CHILD_ID_FLOW, S_WATER, "Water Flow Rate"); 230 present(CHILD_ID_DAY_NIGHT, S_DOOR); // used to prevent watering during the day - binary sensor 231 present(CHILD_ID_LOW_FLOW, S_DOOR); // used to let HA know that we have no water flow - binary sensor 232 present(CHILD_ID_OPEN_SENSOR, S_DOOR); // used to let HA know that we have an open sensor - binary sensor 233 present(CHILD_ID_SAFEFLOW_SWITCH, S_LIGHT); // if off, system is idle, if one, system is active 234 235 send(msgRestart.set(false)); 236} 237 238void loop() 239{ 240 if (newLoop) { updateAll(); newLoop = false; } // make sure HA is aware of current state after powerdown 241 // and collect moisture levels 242 if (!dayNight) { // if !dayNight = true, it's night time and good to go 243 if (safeFlow) { 244 // check time to see if we need a moisture reading - different delay during watering 245 if (millis() < oldNoMoistureTime) { oldNoMoistureTime = 0; } // millis() can roll over making it smaller than oldNoDataTime 246 if ((millis()- oldNoMoistureTime) > ((isAnyValveOn()?1:0)*VALVES_ON_MOISTURE_MEASUREMENT_TIME + 247 (isAnyValveOn()?0:1)*VALVES_OFF_MOISTURE_MEASUREMENT_TIME) * 60000) { // send data to HA to let it know where still alive 248 oldNoMoistureTime = millis(); // update time 249 newLoop = true; // forces an update to HA 250 readMoisture(); // reads all sensors 251 flowRate = flowMeasurement(); // this is not the only place where a flowrate measurement is made; check getLastFlowRate() - used when closing valves 252 } 253 // open and close valves automatically when set by autoMan_State and send moisture readings to HA when appropriate 254 // valves are turned on and off manually through the receive() function!! 255 for (int i = 0; i < NUM_VALVES; i++) { 256 if (abs(moisture_Level[i] - oldmoisture_Level[i]) > 50 ) { // only send data upon change of state 257 send(msgAI[i].set(moisture_Level[i],1)); // will automatically send all moisture_levels on first newLoop 258 oldmoisture_Level[i] = moisture_Level[i]; 259 } 260 if (autoMan_State[i] == true) { // must be in auto mode to change valve state 261 if ((moisture_Level[i] < TARGET_FULL_MOISTURE) && valveState(i) == true) { 262 writeRegister(GPIO, 255); // turn all bits on, all valves off (valves are active low!) 263 send(msgGPIO[i].set(valveState(i))); 264 getLastFlowRate(); // valves are closed, make sure flow has stopped 265 } 266 if ((moisture_Level[i] > TARGET_MIN_MOISTURE) && !isAnyValveOn()) { // only turn on if all other valves are off 267 writeNotGPIO(i, true); // turn on water (valves are active low, but writeNotGPIO does inversion) 268 send(msgGPIO[i].set(valveState(i))); 269 startMoistureLevel = moisture_Level[i]; // save for sensor check below 270 oldValveOnTime = millis(); // set start time for valve[i] on duration. only one valve can be on at a time, so we need only one oldValveOnTime! 271 } 272 } 273 } 274 // Check flow and moisture response if valve is on 275 if (isAnyValveOn() == true) { // one of the valves is on, let's check to see if moisture sensor and the flow meter are responding 276 if (millis() < oldValveOnTime) { oldValveOnTime = 0; } // millis() can roll over making it smaller than oldNoDataTime 277 if ((millis()- oldValveOnTime) > MAX_VALVE_TIME * 60000) { // time to turn off the valve to prevent over watering 278 if (flowRate < 5.0) { // oops low flow 279 lowFlow = true; 280 send(msgOpenSensor.set(true)); // let HA know the system is shutting down due to an open sensor 281 safeFlowShutdown(); 282 } 283 else if(startMoistureLevel >= oldmoisture_Level[whichValveIsOn()] ) { // if the measured moisture level hasn't dropped 284 openSensor = true; 285 send(msgLowFlow.set(true)); // send alarm to HA to indicate system shut down. 286 safeFlowShutdown(); // shutdown system and let HA know valves are all off 287 } 288 } 289 } 290 else { oldValveOnTime = millis(); } // resets every loop cycle if no valve is on 291 } 292 } 293} 294 295void receive(const MyMessage &message) { 296 byte relayPinIndex; 297 298 if (message.isAck()) { 299 // if (SERIAL_TEST) {Serial.println("This is an ack from gateway");} 300 } 301 if (message.type == V_STATUS) { // possible sensors are: 302 // Digital IO -> 10 to 17 303 // AutoMan Switches -> 20 - 27 304 // SafeFlow Switch -> 34 305 if (message.sensor < 18 && message.sensor > 9) { // must be a relay 306 // Change relay state 307 // Calculate relay from sensor number 308 relayPinIndex = message.sensor - CHILD_ID_START_OF_RELAYS; // relayPinIndex[0] thru relayPinIndex[7] 309 if (!autoMan_State[relayPinIndex] && safeFlow) { // only change relay state if in manual mode and safeFlow 310 writeNotGPIO(relayPinIndex, message.getBool()); // turns off all valves and sets state of valve(relayPinIndex) true or false 311 if(!message.getBool()) getLastFlowRate(); // make sure we get a flow measurement after valves are turned off 312 for (int i = 0; i < NUM_VALVES; i++) { // set all valveStates except valveState(relayPinIndx) to false. 313 send(msgGPIO[i].set(valveState(i))); 314 } 315 } 316 else if(!safeFlow) { 317 for (int i = 0; i < NUM_VALVES; i++) { // turn off all valves 318 writeNotGPIO(i, false); // turn off all valves 319 send(msgGPIO[i].set(false)); // don't know why this is necessary 320 } 321 getLastFlowRate(); // make sure we get a flow measurement after valves are turned off 322 } 323 } 324 else if (message.sensor < 28 && message.sensor > 19){ // must be an AutoMan Switch 325 autoMan_State[message.sensor - 20] = message.getBool(); // this means state was changed by HASS 326 } 327 else if (message.sensor == 34) { // HA requesting SafeFlow reset 328 if(message.getBool() == true) { 329 safeFlow = true; // user can only turn switch on. here it's turned off again after one second. 330 lowFlow = false; 331 openSensor = false; 332 delay(1000); 333 send(msgRestart.set(false)); // turn off the HA switch 334 oldValveOnTime = millis(); 335 } 336 } 337 } 338 if (message.type == V_TRIPPED) { // recieved dayNight state from HA 339 if (message.sensor == 31) { // this is the day night switch that HA sends to prevent daytime watering 340 dayNight = message.getBool(); // if true, it's daytime, do not water 341 } 342 } 343} 344 345// Update All 346void updateAll() 347{ 348 for (int i = 0; i < NUM_VALVES; i++) { 349 send(msgGPIO[i].set(valveState(i))); 350 send(msgAI[i].set(moisture_Level[i],1)); 351 oldmoisture_Level[i] = moisture_Level[i]; 352 send(msgAutoMan[i].set(autoMan_State[i])); 353 } 354 send(msgFlow.set(flowRate,2)); 355 send(msgLowFlow.set(lowFlow)); // used to let HA know that we have no water flow 356 send(msgOpenSensor.set(openSensor)); // used to let HA know that we may have an open sensor 357} 358 359bool isAnyValveOn() { 360 bool result = true; 361 362 if(readRegister(GPIO) == 255 ) {result = false;} 363 // Serial.print("isAny = ");Serial.println(result);Serial.print("GPIO = ");Serial.println(readRegister(GPIO)); 364 return(result); 365} 366 367bool valveState(uint8_t whichBit) { 368 return(!bitRead(readRegister(GPIO), whichBit)); 369} 370 371byte whichValveIsOn() { 372 for (byte i = 0; i < NUM_VALVES; i++) { 373 if(valveState(i)) {return(i);} 374 } 375} 376 377void readMoisture() { 378 // read all the moisture levels, but don't send to HA 379 digitalWrite(POWER_PIN, true); // Turn on sensor power; powers all sensors; active low 380 delay(10); //delay 10 milliseconds to allow reading to settle - determined by testing 381 for (int i = 0; i < NUM_VALVES; i++) { // read all sensors 382 moisture_Level[i] = analogRead(AI_pin[i]); // update moisture levels 383 } 384 digitalWrite(POWER_PIN, false); // turn power off to sensors again 385} 386 387float flowMeasurement() { // take a second to check flow rate 388 // this is a crude method used here because there are no interrupt pins left 389 // in this implementation. At the max reported flow rate through 1/4" drip 390 // tubing 0f 20 GPH, we can anticipate at pulse frequency of 30 Hz. That's 391 // slow enough to make this routine work well. 392 393 bool state = digitalRead(FLOW_PIN); 394 bool oldState = state; 395 bool notDone = true; 396 unsigned long int count = 0; 397 unsigned long int oldMillis = millis(); 398 399 while (notDone) { 400 state = digitalRead(FLOW_PIN); 401 if (state != oldState) { 402 oldState = state; 403 count++; 404 } 405 if( oldMillis > millis()) {oldMillis = 0;} // rollover event 406 notDone = (millis() - oldMillis < 1000); // 1000 = 1 sec: when this is false, we are done 407 } 408 flowRate = count/(2*K); // gets two counts per square wave; but rising and falling! 409 return(flowRate); // K is the pulses per liter (1380), so we are returning the flow rate in liters per second 410 // Should be 0.022 liters per second or there abouts. 411} 412 413void getLastFlowRate() { 414 delay(2000); // delay to let valve close and flow rate to settle 415 flowRate = flowMeasurement(); // this takes one second to execute 416 send(msgFlow.set(flowRate, 2)); // Update HA after valves have closed. Should be zero, but it no, there's a problem. 417} 418 419uint8_t readRegister(uint8_t regAddr) { 420 uint8_t Data; 421 uint8_t *pu8Data = &Data; 422 423 I2CReadRegister(&bbi2c, MCP23008_ADDR, regAddr, pu8Data, 1); 424 return(Data); 425} 426 427void writeRegister(uint8_t regAddr, uint8_t data) { 428 uint8_t Data[2] = {regAddr, data}; 429 uint8_t *pu8Data; 430 431 pu8Data = &Data[0]; 432 I2CWrite(&bbi2c, MCP23008_ADDR, pu8Data, 2); // write both the register addr and the data 433} 434 435void writeNotGPIO(uint8_t pin, bool state) { // set particular pin true or false (only one bit true at a time!!! 436 uint8_t newState; 437 438 writeRegister(GPIO, 255); // turn all bits on, all valves off 439 if(state) { 440 newState = ~bitValue[pin]; // inverts the binary number 441 writeRegister(GPIO, newState); 442 } 443} 444 445void safeFlowShutdown() { 446 safeFlow = false; // system shuts down until attended to. When repaired, turn system off and on again. 447 writeRegister(GPIO, 255); // turn all bits on, all valves off (valves are active low!) 448 for (int i = 0; i < NUM_VALVES; i++) { 449 send(msgGPIO[i].set(valveState(i))); 450 } 451 getLastFlowRate(); // make sure flow has shut down. 452 send(msgRestart.set(false)); // let HA know that we have a problem - this is a switch in HA 453} 454
MEGA Irrigaton System with Display 1.3 (obsolete)
c_cpp
HA Customize File (Nano System)
yaml
1binary_sensor.irrigation_system_30_32: 2 friendly_name: Low Flow Error 3 device_class: safety 4binary_sensor.irrigation_system_30_31: 5 friendly_name: Day/Night 6 device_class: light 7 entity_picture: /local/day-and-night-icon.png 8binary_sensor.irrigation_system_30_33: 9 friendly_name: Open Sensor 10 device_class: safety 11
(obsolete)
c_cpp
HA Python Scripts (Nano System)
yaml
1/config/python_scripts/set_state.py 2# by Rod Payne 3 4inputEntity = data.get('entity_id') 5if inputEntity is None: 6 logger.warning("===== entity_id is required if you want to set something.") 7elif hass.states.get(inputEntity) is None: 8 logger.warning("===== unknown entity_id: %s", inputEntity) 9else: 10 inputStateObject = hass.states.get(inputEntity) 11 inputState = inputStateObject.state 12 inputAttributesObject = inputStateObject.attributes.copy() 13 14 for item in data: 15 newAttribute = data.get(item) 16 logger.debug("===== item = {0}; value = {1}".format(item,newAttribute)) 17 if item == 'entity_id': 18 continue # already handled 19 elif item == 'state': 20 inputState = newAttribute 21 else: 22 inputAttributesObject[item] = newAttribute 23 24 hass.states.set(inputEntity, inputState, inputAttributesObject) 25
Dallas_Sensor_Address_Display_Routine.ino
c_cpp
This program is used to get the addresses of the Dallas temperature sensors you will use in this project and to test their operation. The code is a modification of one of the examples provided on Github for the DallasTemperature.h library.
1/* This code was obtain on Github from the examples for Miles Burtons DallasTemperature.h library and then 2 * modified slightly by me to simply obtain the device address for one sensor and print its data. 3 * The OneWire code has been derived from http://www.arduino.cc/playground/Learning/OneWire. 4 * 5 * Origin Credits go to: 6 * Miles Burton miles@mnetcs.com originally developed this library. 7 * Tim Newsome nuisance@casualhacker.net added support for multiple sensors on the same bus. 8 * Guil Barros [gfbarros@bappos.com] added getTempByAddress (v3.5) 9 * Note: these are implemented as getTempC(address) and getTempF(address) 10 * Rob Tillaart [rob.tillaart@gmail.com] added async modus (v3.7.0) 11 */ 12 13// Include the libraries we need 14#include <OneWire.h> 15#include <DallasTemperature.h> 16 17// Data wire is plugged into port 13 on the Irrigation System Board 18#define ONE_WIRE_BUS 13 19#define TEMPERATURE_PRECISION 9 20 21// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) 22OneWire oneWire(ONE_WIRE_BUS); 23 24// Pass our oneWire reference to Dallas Temperature. 25DallasTemperature sensors(&oneWire); 26 27// an array is required to hold each device address 28DeviceAddress DallasTempSensor; 29 30void setup(void) 31{ 32 // start serial port 33 Serial.begin(115200); 34 Serial.println("Dallas Temperature Address Display Routine"); 35 36 // Start up the library 37 sensors.begin(); 38 39 // locate devices on the bus 40 Serial.print("Locating devices..."); 41 Serial.print("Found "); 42 Serial.print(sensors.getDeviceCount(), DEC); 43 Serial.println(" devices."); 44 45 // report parasite power requirements 46 Serial.print("Parasite power is: "); 47 if (sensors.isParasitePowerMode()) Serial.println("ON"); 48 else Serial.println("OFF"); 49 50 // Search for devices on the bus and assign based on an index. Ideally, 51 // you would do this to initially discover addresses on the bus and then 52 // use those addresses and manually assign them (see MEGA Irrigation System) once you know 53 // the devices on your bus (and assuming they don't change). 54 // 55 // method 1: by index 56 if (!sensors.getAddress(DallasTempSensor, 0)) Serial.println("Unable to find address for Device 0"); 57 58 // method 2: search() 59 // search() looks for the next device. Returns 1 if a new address has been 60 // returned. A zero might mean that the bus is shorted, there are no devices, 61 // or you have already retrieved all of them. It might be a good idea to 62 // check the CRC to make sure you didn't get garbage. The order is 63 // deterministic. You will always get the same devices in the same order 64 // 65 // Must be called before search() 66 //oneWire.reset_search(); 67 // assigns the first address found to DallasTempSensor 68 //if (!oneWire.search(DallasTempSensor)) Serial.println("Unable to find address for DallasTempSensor"); 69 70 // show the addresses we found on the bus 71 Serial.print("Device 0 Address: "); 72 printAddress(DallasTempSensor); 73 Serial.println(); 74 75 // set the resolution to 9 bit per device 76 sensors.setResolution(DallasTempSensor, TEMPERATURE_PRECISION); 77 78 Serial.print("Device 0 Resolution: "); 79 Serial.print(sensors.getResolution(DallasTempSensor), DEC); 80 Serial.println(); 81} 82 83// function to print a device address 84void printAddress(DeviceAddress deviceAddress) 85{ 86 for (uint8_t i = 0; i < 8; i++) 87 { 88 // zero pad the address if necessary 89 if (deviceAddress[i] < 16) Serial.print("0"); 90 Serial.print(deviceAddress[i], HEX); 91 } 92} 93 94// function to print the temperature for a device 95void printTemperature(DeviceAddress deviceAddress) 96{ 97 float tempC = sensors.getTempC(deviceAddress); 98 if(tempC == DEVICE_DISCONNECTED_C) 99 { 100 Serial.println("Error: Could not read temperature data"); 101 return; 102 } 103 Serial.print("Temp C: "); 104 Serial.print(tempC); 105 Serial.print(" Temp F: "); 106 Serial.print(DallasTemperature::toFahrenheit(tempC)); 107} 108 109// function to print a device's resolution 110void printResolution(DeviceAddress deviceAddress) 111{ 112 Serial.print("Resolution: "); 113 Serial.print(sensors.getResolution(deviceAddress)); 114 Serial.println(); 115} 116 117// main function to print information about a device 118void printData(DeviceAddress deviceAddress) 119{ 120 Serial.print("Device Address: "); 121 printAddress(deviceAddress); 122 Serial.print(" "); 123 printTemperature(deviceAddress); 124 Serial.println(); 125} 126 127/* 128 Main function, calls the temperatures in a loop. 129*/ 130void loop(void) 131{ 132 // call sensors.requestTemperatures() to issue a global temperature 133 // request to all devices on the bus 134 Serial.print("Requesting temperatures..."); 135 sensors.requestTemperatures(); 136 Serial.println("DONE"); 137 138 // print the device information 139 printData(DallasTempSensor); 140 delay(10000); // delay ten seconds 141} 142
(obsolete)
c_cpp
Dallas_Sensor_Address_Display_Routine.ino
c_cpp
This program is used to get the addresses of the Dallas temperature sensors you will use in this project and to test their operation. The code is a modification of one of the examples provided on Github for the DallasTemperature.h library.
1/* This code was obtain on Github from the examples for Miles Burtons 2 DallasTemperature.h library and then 3 * modified slightly by me to simply obtain 4 the device address for one sensor and print its data. 5 * The OneWire code 6 has been derived from http://www.arduino.cc/playground/Learning/OneWire. 7 * 8 9 * Origin Credits go to: 10 * Miles Burton miles@mnetcs.com originally 11 developed this library. 12 * Tim Newsome nuisance@casualhacker.net added support 13 for multiple sensors on the same bus. 14 * Guil Barros [gfbarros@bappos.com] 15 added getTempByAddress (v3.5) 16 * Note: these are implemented as getTempC(address) 17 and getTempF(address) 18 * Rob Tillaart [rob.tillaart@gmail.com] added async 19 modus (v3.7.0) 20 */ 21 22// Include the libraries we need 23#include <OneWire.h> 24#include 25 <DallasTemperature.h> 26 27// Data wire is plugged into port 13 on the Irrigation 28 System Board 29#define ONE_WIRE_BUS 13 30#define TEMPERATURE_PRECISION 9 31 32// 33 Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas 34 temperature ICs) 35OneWire oneWire(ONE_WIRE_BUS); 36 37// Pass our oneWire reference 38 to Dallas Temperature. 39DallasTemperature sensors(&oneWire); 40 41// an array 42 is required to hold each device address 43DeviceAddress DallasTempSensor; 44 45void 46 setup(void) 47{ 48 // start serial port 49 Serial.begin(115200); 50 Serial.println("Dallas 51 Temperature Address Display Routine"); 52 53 // Start up the library 54 sensors.begin(); 55 56 57 // locate devices on the bus 58 Serial.print("Locating devices..."); 59 Serial.print("Found 60 "); 61 Serial.print(sensors.getDeviceCount(), DEC); 62 Serial.println(" devices."); 63 64 65 // report parasite power requirements 66 Serial.print("Parasite power is: "); 67 68 if (sensors.isParasitePowerMode()) Serial.println("ON"); 69 else Serial.println("OFF"); 70 71 72 // Search for devices on the bus and assign based on an index. Ideally, 73 // 74 you would do this to initially discover addresses on the bus and then 75 // use 76 those addresses and manually assign them (see MEGA Irrigation System) once you know 77 78 // the devices on your bus (and assuming they don't change). 79 // 80 // method 81 1: by index 82 if (!sensors.getAddress(DallasTempSensor, 0)) Serial.println("Unable 83 to find address for Device 0"); 84 85 // method 2: search() 86 // search() 87 looks for the next device. Returns 1 if a new address has been 88 // returned. 89 A zero might mean that the bus is shorted, there are no devices, 90 // or you 91 have already retrieved all of them. It might be a good idea to 92 // check the 93 CRC to make sure you didn't get garbage. The order is 94 // deterministic. You 95 will always get the same devices in the same order 96 // 97 // Must be called 98 before search() 99 //oneWire.reset_search(); 100 // assigns the first address 101 found to DallasTempSensor 102 //if (!oneWire.search(DallasTempSensor)) Serial.println("Unable 103 to find address for DallasTempSensor"); 104 105 // show the addresses we found 106 on the bus 107 Serial.print("Device 0 Address: "); 108 printAddress(DallasTempSensor); 109 110 Serial.println(); 111 112 // set the resolution to 9 bit per device 113 sensors.setResolution(DallasTempSensor, 114 TEMPERATURE_PRECISION); 115 116 Serial.print("Device 0 Resolution: "); 117 Serial.print(sensors.getResolution(DallasTempSensor), 118 DEC); 119 Serial.println(); 120} 121 122// function to print a device address 123void 124 printAddress(DeviceAddress deviceAddress) 125{ 126 for (uint8_t i = 0; i < 8; i++) 127 128 { 129 // zero pad the address if necessary 130 if (deviceAddress[i] < 16) 131 Serial.print("0"); 132 Serial.print(deviceAddress[i], HEX); 133 } 134} 135 136// 137 function to print the temperature for a device 138void printTemperature(DeviceAddress 139 deviceAddress) 140{ 141 float tempC = sensors.getTempC(deviceAddress); 142 if(tempC 143 == DEVICE_DISCONNECTED_C) 144 { 145 Serial.println("Error: Could not read 146 temperature data"); 147 return; 148 } 149 Serial.print("Temp C: "); 150 151 Serial.print(tempC); 152 Serial.print(" Temp F: "); 153 Serial.print(DallasTemperature::toFahrenheit(tempC)); 154} 155 156// 157 function to print a device's resolution 158void printResolution(DeviceAddress deviceAddress) 159{ 160 161 Serial.print("Resolution: "); 162 Serial.print(sensors.getResolution(deviceAddress)); 163 164 Serial.println(); 165} 166 167// main function to print information about a device 168void 169 printData(DeviceAddress deviceAddress) 170{ 171 Serial.print("Device Address: 172 "); 173 printAddress(deviceAddress); 174 Serial.print(" "); 175 printTemperature(deviceAddress); 176 177 Serial.println(); 178} 179 180/* 181 Main function, calls the temperatures in 182 a loop. 183*/ 184void loop(void) 185{ 186 // call sensors.requestTemperatures() 187 to issue a global temperature 188 // request to all devices on the bus 189 Serial.print("Requesting 190 temperatures..."); 191 sensors.requestTemperatures(); 192 Serial.println("DONE"); 193 194 195 // print the device information 196 printData(DallasTempSensor); 197 delay(10000); 198 // delay ten seconds 199} 200
HA Python Scripts (Nano System)
yaml
1/config/python_scripts/set_state.py 2# by Rod Payne 3 4inputEntity = data.get('entity_id') 5if inputEntity is None: 6 logger.warning("===== entity_id is required if you want to set something.") 7elif hass.states.get(inputEntity) is None: 8 logger.warning("===== unknown entity_id: %s", inputEntity) 9else: 10 inputStateObject = hass.states.get(inputEntity) 11 inputState = inputStateObject.state 12 inputAttributesObject = inputStateObject.attributes.copy() 13 14 for item in data: 15 newAttribute = data.get(item) 16 logger.debug("===== item = {0}; value = {1}".format(item,newAttribute)) 17 if item == 'entity_id': 18 continue # already handled 19 elif item == 'state': 20 inputState = newAttribute 21 else: 22 inputAttributesObject[item] = newAttribute 23 24 hass.states.set(inputEntity, inputState, inputAttributesObject) 25
MEGA Irrigaton System with Display 1.3 (obsolete)
c_cpp
HA Automation File (Nano)
yaml
1 - alias: "Irrigation Day" 2 - alias: "Irrigation Day" 3 trigger: 4 5 platform: sun 6 event: sunrise 7 offset: "3:00:00" 8 action: 9 10 service: python_script.set_state 11 data_template: 12 entity_id: 13 binary_sensor.irrigation_system_30_31 14 state: 'on' 15 16 - alias: 17 "Irrigation Night" 18 trigger: 19 platform: sun 20 event: sunset 21 22 offset: "-3:00:00" 23 action: 24 service: python_script.set_state 25 26 data_template: 27 entity_id: binary_sensor.irrigation_system_30_31 28 29 state: 'off' 30 31 32 - alias: "Irrigation Flow Failure Alert" 33 34 trigger: 35 - platform: state 36 entity_id: binary_sensor.irrigation_system_20_32 37 38 to: "on" 39 action: 40 - service: notify.gmail 41 data: 42 43 message: "Sharon Irrigation Flow Failure" 44 title: "Irrigation 45 Alert" 46 47 - alias: "Irrigation Sensor Failure Alert" 48 trigger: 49 50 - platform: state 51 entity_id: binary_sensor.irrigation_system_20_33 52 53 to: "on" 54 action: 55 - service: notify.gmail 56 data: 57 58 message: "Sharon Irrigation Sensor Failure" 59 title: "Irrigation 60 Alert" 61
HA Customize File (Nano System)
yaml
1binary_sensor.irrigation_system_30_32: 2 friendly_name: Low Flow Error 3 4 device_class: safety 5binary_sensor.irrigation_system_30_31: 6 friendly_name: 7 Day/Night 8 device_class: light 9 entity_picture: /local/day-and-night-icon.png 10binary_sensor.irrigation_system_30_33: 11 12 friendly_name: Open Sensor 13 device_class: safety 14
HA Template Sensors (Nano System)
yaml
These template sensors were created to convert raw analog input data into moisture percent. For this system, there is no temperature compensation. Even so, the system will still do a good job of keeping plants watered.
1- platform: template 2 sensors: 3 moisture_0: 4 friendly_name: 5 Moisture 0 6 unit_of_measurement: "%" 7 value_template: "{{ (((((1000.0 8 / (973 / (states('sensor.irrigation_system_30_0') | float) - 1)))/151.35)**(-0.27))*100) 9 | round(1) }}" 10 moisture_1: 11 friendly_name: Moisture 1 12 unit_of_measurement: 13 "%" 14 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_1') 15 | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 16 moisture_2: 17 friendly_name: 18 Moisture 2 19 unit_of_measurement: "%" 20 value_template: "{{ (((((1000.0 21 / (973 / (states('sensor.irrigation_system_30_2') | float) - 1)))/151.35)**(-0.27))*100) 22 | round(1) }}" 23 moisture_3: 24 friendly_name: Moisture 3 25 unit_of_measurement: 26 "%" 27 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_3') 28 | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}" 29 moisture_4: 30 friendly_name: 31 Moisture 4 32 unit_of_measurement: "%" 33 value_template: "{{ (((((1000.0 34 / (973 / (states('sensor.irrigation_system_30_4') | float) - 1)))/151.35)**(-0.27))*100) 35 | round(1) }}" 36 moisture_5: 37 friendly_name: Moisture 5 38 unit_of_measurement: 39 "%" 40 value_template: "{{ (((((1000.0 / (973 / (states('sensor.irrigation_system_30_5') 41 | float) - 1)))/151.35)**(-0.27))*100) | round(1) }}"
MEGA Irrigation_System_w_Display_1.3_w_Mysensors_w_Drive_Board2.30.ino
c_cpp
I've added a precomiler #define called MY_MYSENSORS. If this line of code is defined, then MySensors will be compiled with the code. If not, MySensors will be left out and the system will compile with the display functionality, but without the MySensors interface to your home automation system.
1 2#define PROGRAM "Irrigation System w/Display 1.3 w/Drive_Board" 3#define 4 VERSION "version 2.30" 5/* MEGA Irrigation System w Display w Mysensors 6 7 * P. Zavracky, (c) 2021 8/* REVISION HISTORY 9 * Written by Paul M. Zavracky 10 (C) 2021 11 * Borrows heavily from the work of zillions of others 12 * This 13 is an upgrade of the original Nano version (2.95) 14 * 15 * The irrigation 16 system comprises a set of 1 to 8 1/4 12v water valves, a moisture sensor, an arduino, 17 18 * an NRF24 Radio, and 8 drivers. The Arduino takes both the digital and analog 19 signals from the 20 * moisture sensor and uses this information to determine 21 if watering is needed. If so, the Arduino 22 * sends a signal to the driver board, 23 actualing the appropriate valve. The driver connects a 12V supply 24 * to the 25 water valve. The Arduino continues monitoring the moisture sensor. Once a predetermined 26 level 27 * is reached, the Arduino turns off the water flow. 28 * 29 * This 30 version adds the drive board. That means that instead of active low drivers, we 31 now have active high 32 * transistor drivers. We also have a shutoff driver 33 for the shutoff valve. Therefore, system leak detection 34 * is added and the 35 shutoff valve must be actuated whenever any valve is on and turned off (except when 36 37 * conducting a leak test) when no valves are on. 38 * 39 * In this scheme, 40 the routine 'writePORTA' which originally inverted the port data byte, now does 41 not! So, 42 * the name carried over no longer accurately portrays the routines 43 function. Therefore the routine is renamed to 44 * writePORTA. 45 * 46 * 47 Added in this iteration is a leak check of the plumbing between the shutoff valve 48 and the zone valves. This region 49 * has a significant number of fittings that 50 could develop leaks. It is also a region, that without a shutoff valve 51 * would 52 continue to leak as long as the system is running. So, the shutoff valve removes 53 pressure from this area, 54 * however, without some kind of leak check, a catastrophic 55 failure of the plumbing would pour water out each time a 56 * zone is activated. 57 To prevent this from happening, periodic leak checks are conducted. If a leak 58 is discovered, 59 * the system will shut down and Home Assistant will send an 60 appropriate message. 61 * 62 * This code now uses the precompiler name MY_MYSENSORS 63 which is #defined at the beginning of the code. If you 64 * comment this line 65 out, MySensors will be eliminated from the compilation and you can use the system 66 with the display 67 * only. 68*/ 69 70 71// setup for MySensors 72#define 73 MY_NODE_ID 41 74#define MY_DEBUG // comment out this line to remove MySensors use 75 of serial port!!! 76#define MY_MYSENSORS // comment out this line to remove MySensors 77 altogether 78 79#define MY_PARENT_NODE_ID 0 80#define MY_PARENT_NODE_IS_STATIC 81 82// 83 Enable and select radio type attached 84#define MY_RADIO_RF24 85#define MY_RF24_PA_LEVEL 86 RF24_PA_MAX 87// RF24_PA_MIN = -18dBm 88// RF24_PA_LOW = -12dBm 89// RF24_PA_HIGH 90 = -6dBm 91// RF24_PA_MAX = 0dBm 92 93// Specifiaclly for MEGA 94#define MY_RF24_CE_PIN 95 11 96#define MY_RF24_CS_PIN 12 97 98// analog inputs 99#define CHILD_ID_START_OF_AI 100 0 // the below AI IDs allow rearrangement of sensors so that during installation, 101 they may be properly coupled to valves 102#define CHILD_ID_AI0 0 103#define CHILD_ID_AI1 104 1 105#define CHILD_ID_AI2 2 106#define CHILD_ID_AI3 3 107#define CHILD_ID_AI4 4 108 109#define CHILD_ID_AI5 5 110#define CHILD_ID_AI6 6 111#define CHILD_ID_AI7 7 112 113// 114 digital outputs (switches in HA) 115#define CHILD_ID_START_OF_VALVES 10 116 117//oneWire 118 Temperature Sensors 119#define CHILD_ID_START_OF_TEMPS 20 120 121//Auto/Manual 122 Mode Switches 123#define CHILD_ID_START_OF_AUTOS 30 124 125#define CHILD_ID_FLOW 126 40 127#define CHILD_ID_DAY_NIGHT 41 // day night sensor gets data from Home Assistant 128#define 129 CHILD_ID_LOW_FLOW 42 // low flow alarm indicator for HA - sends alarm, shuts down 130 system 131#define CHILD_ID_UNRESPONSIVE_SENSOR 43 // open sensor alarm indicator 132 for HA - sends alarm, shuts down system 133#define CHILD_ID_SAFEFLOW_SWITCH 44 // 134 if there is an automatic shutdown (safeFlow = false), this switch let's the system 135 restart (safeFlow = true) 136#define CHILD_ID_LEAK_DETECTED 45 // used to alert 137 HA of a leak. This has saved me from disaster. 138 139#define CHILD_ID_START_OF_TEMP_STATES 140 80 // keeps track of the condition of the temperature sensors (there might be 8 141 of these CHILD_IDs) 142#define CHILD_ID_START_OF_MOISTURE_OPEN_STATES 90 // keeps 143 track of the condition of the moisture sensors (there might be 8 of these CHILD_IDs) 144#define 145 CHILD_ID_START_OF_MOISTURE_SHORT_STATES 100 // open circuit or shorted 146 147// 148 Display and Touchscreen Parameters 149#define TFT_CS 48 150#define TFT_RST 44 151#define 152 TFT_CD 46 153#define T_CS 42 154 155// The Analog pins are used to sense the moisture 156 level 157#define AI0_PIN 54 158#define AI1_PIN 55 159#define AI2_PIN 56 160#define 161 AI3_PIN 57 162#define AI4_PIN 58 163#define AI5_PIN 59 164#define AI6_PIN 60 165#define 166 AI7_PIN 61 167 168#define MENUHEIGHT 20 169#define MENUWIDTH 80 170#define LABLE0 171 "OPERATE" 172#define LABLE1 "ALARMS" 173#define LABLE2 "SET PTS" 174#define 175 LABLE3 VERSION 176 177// Arduino output pin setup 178#define TMPS0 2 // This is 179 the first of three bits for selecting the eight analog switches connecting single 180 Dallas temp sensors to the one wire temp input (TEMP0_PIN) 181#define TMPS1 3 // 182 see below definition of variable tempSensorConfig[] 183#define TMPS2 4 184#define 185 TEMP0_PIN 5 // this is one of two pins for temperature input. This one goes to 186 the analog switch between 8 different sensors 187#define FLOW_PIN 6 // Flow sensor 188 input pin 189#define LED_PIN 8 // LED backlight control pin 190#define POWER_PIN 191 10 // Sensor Power Pin 192#define TEMP1_PIN 13 // Temperature oneWire pin (also 193 defined for oneWire below) 194#define GATE 47 // must be turned on for outputs to 195 fire! 196#define SHUTOFF_VALVE 49 // Safety shutoff valve 197 198#define NUM_VALVES 199 8 // number of valves in use - up to eight 200#define TOUCH_TIME 300 // time in 201 milliseconds between touch readings 202#define BASE_EEPROM_ADDR 100 // starting 203 address of data in eeprom. must leave room for other lib uses 204 205#define ONE_WIRE_BUS 206 TEMP0_PIN // Pin where dallas sensors are connected 207 208#define K 2.28 // 1380 209 pulses/liter found online. Max Flow through 30' should be about 5 GPH or 20 liters/hour 210 or 0.33 liters/min or .0055 liters per sec. 211// That implies about 8 pulses 212 per second. Some report closer to 30 Hz? Our unit should be milliliters per second, 213 so K should be 1.38. 214// For the sensor used in this project, the reported value 215 K = 1.38 was too small. Flow (pulsed based) yeilded 73, for 44 ml/sec. 216// In 217 this code, flowRate = count/2K, where counts are made on both rising and falling 218 edges. Flow rate should equal flowRate/1.65, so 219// Knew = 1.28 * 1.65 or 2.28. 220 221#define 222 FLOW_MOIST_TEST_TIME 5 // the time in minutes a valve can stay on before conducting 223 a flow and moisture variation measurement 224 // This 225 is only done once, because the system will shutdown anyway at MAX_VALVE_TIME 226#define 227 MAX_VALVE_TIME 20 // maximum time in minutes a valve can stay on in minutes. If 228 moisture level hasn't dropped on next cycle, valve will 229// turn on againg 230#define 231 MAX_LED_TIME 5 // maximum time in minutes the LED backlight stays on without a touch 232#define 233 VALVES_OFF_MOISTURE_MEASUREMENT_TIME 15 // Time in minutes between moisture sensor 234 readings when no valves are open. 235#define VALVES_ON_MOISTURE_MEASUREMENT_TIME 236 0.1 // Time in minutes between moisture sensor readings when a valve is open. 237#define 238 LEAK_CHECK_TIME 60 // time between leakchecks 239#define LEAK_DURATION_TIME 30 // 240 basically the delay time in seconds between turning on the shutoff valve and making 241 a flow measurement. This allows the initial 242 // 243 pressure pulse to settle down before making the leak test. 244#define DOT_TIME 1 245 // time in seconds between dots printed as system cycles through main loop 246 247#define 248 Rseries 1000.00 // series resistance used in moisture measurement 249 250#include 251 <Arduino.h> 252#include <SPI.h> 253#ifdef MY_MYSENSORS 254 #include <MySensors.h> 255 256#endif 257#include "SimpleILI9341.h" 258#include <avr/pgmspace.h> 259#include 260 <EEPROM.h> 261#include <DallasTemperature.h> 262#include <OneWire.h> 263 264// start 265 Dallas Temp Sensors 266OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance 267 to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) 268DallasTemperature 269 sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature. 270// Addresses 271 of DS18B20 sensors connected to the 1-Wire bus are not needed. This system uses 272 an analog multiplexer to select a particular device 273// The multiplexer (74HC4051) 274 outputs are routed in a convenient layout format. But this format lead to the following 275 configuration of connectors: 276// [ 2 ] [ 4 ] 277// [ 1 ] [ 6 ] 278// [ 0 ] [ 7 279 ] 280// [ 3 ] [ 5 ] 281// The preferred order might be 282// [ 0 ] [ 1 ] 283// 284 [ 2 ] [ 3 ] 285// [ 4 ] [ 5 ] 286// [ 6 ] [ 7 ] 287// The following variable is 288 used to make this translation. Therefore, when connecting to the board, your sensors 289 labled S0...S7 are connected to the 290// positions using the preferred order. 291byte 292 tempSensorConfig[] = {2,4,1,6,0,7,3,5}; // so tempSensorConfig[0] will translate 293 to an index of 2 294 295// variables for Mysensors 296 297byte AI_pin[] = {AI0_PIN, 298 AI1_PIN, AI2_PIN, AI3_PIN, AI4_PIN, AI5_PIN, AI6_PIN, AI7_PIN}; 299byte CHILD_ID_AI[] 300 = {CHILD_ID_AI0, CHILD_ID_AI1, CHILD_ID_AI2, CHILD_ID_AI3, CHILD_ID_AI4, CHILD_ID_AI5, 301 CHILD_ID_AI6, CHILD_ID_AI7}; 302 303bool ack = 1; 304 305 306// Variables Definitions 307boolean 308 newLoop = true; // to update HA after power outage 309unsigned long oldValveOnTime 310 = 0; // used to control the amount of time a valve is open 311unsigned long oldNoMoistureTime 312 = 0; // minutes between moisture measurements when no valve is on 313unsigned long 314 oldLEDTime = 0; // start time for LED backlight on time 315unsigned long oldLeakCheckTime 316 = 0; // used to control amount of time between leak checks 317unsigned long oldLeakDurationTime 318 = 0; // used to avoid the pressure pulse delay that ensues when opening the shutoff 319 valve 320unsigned long oldDotTime = 0; // system will print a dot each 321 322byte 323 currentscreen; 324 325bool autoManState[NUM_VALVES]; // man if false, auto if true 326bool 327 tempState[NUM_VALVES]; // used to keep track of functional temperature sensors 328bool 329 moistureOpenState[NUM_VALVES]; // tracks open sensors 330bool moistureShortState[NUM_VALVES]; 331 // tracks shorted sensors 332bool lowFlow = false; // if the flow is too low, the 333 system sets this flag and shuts down. HA can see this flag. 334bool unresponsiveSensor 335 = false; // if a sensor is open and doesn't responde to irrigation, the system sets 336 this flag and shuts down. HA can see this flag. 337bool leakDetected = false; // 338 check flow rate when no valves are open and check for leaks. 339bool systemSafe 340 = true; 341bool leakCheckFlag = false; // this flag is used to initiate a system 342 leak check 343float flowRate = 5.0; // just set arbitrary initial flow rate. This 344 will clear on first cycle through loop 345 346// Update variables 347bool oldValveState[NUM_VALVES]; 348float 349 oldTemp[NUM_VALVES]; 350float oldMoistureLevel[NUM_VALVES] = {0.0}; // initialize 351 all old data to zero 352bool oldAutoManState[NUM_VALVES]; // used in update all 353 to prevent sending repetitive information 354bool oldTempState[NUM_VALVES]; // 355 change of state forces update!! 356bool oldMoistureOpenState[NUM_VALVES]; 357bool 358 oldMoistureShortState[NUM_VALVES]; 359bool oldLowFlow = true; 360bool oldUnresponsiveSensor 361 = true; 362bool oldLeakDetected = true; 363bool oldSystemSafe = true; 364float oldFlowRate 365 = 1.0; 366 367uint8_t bitValue[] = {1, 2, 4, 8, 16, 32, 64, 128}; 368 369bool safeFlow 370 = true; // if flow measures to long, the system will set this flag false and prevent 371 valves from being actuated (prevent over heating) 372byte flowTestCount = 0; // 373 keeps track of the number of zero flow measurements made - system shuts down after 374 NUM_FLOW_TESTS 375bool dayNight = false; // if true, it is daytime and the system 376 should not water plants. This variable can only be changed by MySensors. 377float 378 temp[NUM_VALVES]; 379float moistureLevel[NUM_VALVES]; 380float startMoistureLevel 381 = 0.0; // used for sensor testing 382byte currentScreen = 0; 383byte oldCurrentScreen 384 = 0; 385bool firstTime = true; 386bool startup = true; // used to send HA message 387 once on startup 388byte sensor = 0; // this is just for test purposes 389byte tempSensorCount 390 = 0; // number of functioning temperature sensors 391byte upperMoistureLevel[NUM_VALVES]; 392byte 393 lowerMoistureLevel[NUM_VALVES]; 394unsigned long int lastTouchTime = millis(); 395uint16_t 396 color; 397byte dotCounter = 0; // counts the dots printed for each line and occasionally 398 prints a line feed. 399 400// Dallas Temp Sensor Variables 401bool metric = false; 402 403#ifdef 404 MY_MYSENSORS 405 // MySensors messages 406 MyMessage msgAI[NUM_VALVES]; // these 407 messages tell HA the moisture level 408 MyMessage msgPORTA[NUM_VALVES]; // these 409 messages let HA know what drivers 410 MyMessage msgTemp[NUM_VALVES]; // these messages 411 let HA know the temperature 412 MyMessage msgAutoMan[NUM_VALVES]; // these messages 413 let HA know what drivers 414 MyMessage msgTempState[NUM_VALVES]; // these messages 415 let HA know about the condition of the temperature sensors (working/not-working) 416 417 MyMessage msgMoistureOpenState[NUM_VALVES]; // these messages let HA know about 418 the condition of the moisture sensors (working/not-working) 419 MyMessage msgMoistureShortState[NUM_VALVES]; 420 421 MyMessage msgFlow(CHILD_ID_FLOW, V_FLOW); 422 423 MyMessage msgDayNight(CHILD_ID_DAY_NIGHT, V_TRIPPED); // used to prevent watering 424 during the day 425 MyMessage msgLowFlow(CHILD_ID_LOW_FLOW, V_TRIPPED); // used 426 to let HA know that we have no water flow 427 MyMessage msgOpenSensor(CHILD_ID_UNRESPONSIVE_SENSOR, 428 V_TRIPPED); // used to let HA know that we have an unresponsive sensor 429 MyMessage 430 msgLeakDetected(CHILD_ID_LEAK_DETECTED, V_TRIPPED); // system monitors flow sensor 431 even when no valve is on. If flow is indicated, must be a leak! 432 MyMessage 433 msgRestart(CHILD_ID_SAFEFLOW_SWITCH, V_STATUS); // safeMode switch, shut down due 434 to system failure, restart through HA 435#endif 436 437void setup(void) { 438 Serial.begin(115200); 439 440 Serial.print(PROGRAM);Serial.println(VERSION); 441 442 ILI9341Begin(48, 46, 443 44, 320, 240, ILI9341_Rotation3); 444 BeginTouch(42, 3); 445 446 //analogReference(EXTERNAL); 447 // use this command if you're going to use the switched supply as the reference. 448 Not necessary. 449 // set initial array values 450 for (int i = 0; i < NUM_VALVES; 451 i++) { 452 autoManState[i] = false; // man if false, auto if true 453 tempState[i] 454 = false; // used to keep track of functional temperature sensors 455 moistureOpenState[i] 456 = false; // tracks open sensors 457 moistureShortState[i] = false; // tracks 458 shorted sensors 459 oldValveState[i] = true; // forces HA update 460 oldTemp[i] 461 = 0.0; 462 oldMoistureLevel[i] = 0.0; // initialize all old data to zero 463 464 oldAutoManState[i] = true; // used in update all to prevent sending repetitive 465 information 466 oldTempState[i] = false; // change of state forces update!! 467 468 oldMoistureOpenState[i] = false; 469 oldMoistureShortState[i] = false; 470 471 } 472 473 //Setup inputs and outputs 474 pinMode(TMPS0, OUTPUT); // these are 475 for the temperature sensors 476 pinMode(TMPS1, OUTPUT); 477 pinMode(TMPS2, OUTPUT); 478 479 digitalWrite(TMPS0, false); // set for channel zero. due to board layout considerations, 480 this is actually sensor 4 481 digitalWrite(TMPS1, false); 482 digitalWrite(TMPS2, 483 false); 484 for (int i = 0; i < NUM_VALVES; i++) { 485 pinMode(AI_pin[i], 486 INPUT); 487 } 488 pinMode(FLOW_PIN, INPUT_PULLUP); // flow sensor requires the 489 input have a pullup resistor 490 pinMode(SHUTOFF_VALVE, OUTPUT); 491 pinMode(GATE, 492 OUTPUT); 493 digitalWrite(GATE, true); // make sure gate is off to prevent switch 494 inputs to drivers (drivers all off) 495 digitalWrite(SHUTOFF_VALVE, false); // 496 make sure SHUTOFF VALVE is off 497 pinMode(POWER_PIN, OUTPUT); 498 pinMode(LED_PIN, 499 OUTPUT); 500 pinMode(30, OUTPUT); // set up power relay pin for output 501 // 502 test power pin 503 for (int i = 0; i < 3; i++) { 504 digitalWrite(POWER_PIN, 505 false); // turn off power to moisture sensors; power pin is true low. 506 //digitalWrite(30, 507 false); 508 delay(500); 509 digitalWrite(POWER_PIN, true); 510 //digitalWrite(30, 511 true); 512 delay(500); 513 } 514 digitalWrite(LED_PIN, false); // turn on LED 515 backlight - active low 516 oldLEDTime = millis(); // establish on time 517 DDRA 518 = B11111111; // set up Mega PortA for output - these control the valves 519 PORTA 520 = 0; // all valves off 521 digitalWrite(30, true); // turn on power relay 522 digitalWrite(GATE, 523 false); // make sure gate is on to pass switch inputs to drivers 524 525#ifdef MY_MYSENSORS 526 527 // initialize messages for MySensors 528 for (int i = 0; i < NUM_VALVES; i++) 529 { 530 msgAI[i].sensor = i; // Analog sensors start at child ID 0 531 msgAI[i].type 532 = V_LEVEL; // these are the moisture sensors 533 msgPORTA[i].sensor = i + CHILD_ID_START_OF_VALVES; 534 535 msgPORTA[i].type = V_STATUS; // these are the sprinkler valves 536 msgTemp[i].sensor 537 = i + CHILD_ID_START_OF_TEMPS; 538 msgTemp[i].type = V_TEMP; // these are 539 the temperature sensors 540 msgAutoMan[i].sensor = i + CHILD_ID_START_OF_AUTOS; 541 542 msgAutoMan[i].type = V_STATUS; // these are the switch to set mode 543 544 msgTempState[i].sensor = i + CHILD_ID_START_OF_TEMP_STATES; 545 msgTempState[i].type 546 = V_TRIPPED; // these are binary sensors (S_BINARY) 547 msgMoistureOpenState[i].sensor 548 = i + CHILD_ID_START_OF_MOISTURE_OPEN_STATES; 549 msgMoistureOpenState[i].type 550 = V_TRIPPED; 551 msgMoistureShortState[i].sensor = i + CHILD_ID_START_OF_MOISTURE_SHORT_STATES; 552 553 msgMoistureShortState[i].type = V_TRIPPED; // these are binary sensors (S_BINARY) 554 555 } 556#endif 557 558 // get set points from eeprom 559 for (int i = 0; i < 560 NUM_VALVES; i++) { 561 upperMoistureLevel[i] = EEPROM.read(BASE_EEPROM_ADDR + 562 i); 563 lowerMoistureLevel[i] = EEPROM.read(BASE_EEPROM_ADDR + NUM_VALVES + i); 564 // always move the to position of the max number of valves 565 if (upperMoistureLevel[i] 566 > 100) { 567 upperMoistureLevel[i] = 80; // EEPROM hasn't been writen yet, 568 so use 80 to start. Will write after updating. 569 } 570 if (lowerMoistureLevel[i] 571 > 100) { 572 lowerMoistureLevel[i] = 40; // EEPROM hasn't been writen yet, 573 so use 40 to start. 574 } 575 } 576 // set up dallas temp sensors 577 sensors.begin(); 578 579 580 // display setup 581 ClearDisplay(TFT_BLUE); 582 DrawBox(0, 0, MENUWIDTH, MENUHEIGHT, 583 TFT_NAVY); // upper lefthand corner location and size of box 584 DrawFrame(0, 0, 585 MENUWIDTH, MENUHEIGHT, TFT_WHITE); 586 ILI9341SetCursor(15, 15); 587 DrawString(LABLE0, 588 SmallFont, TFT_WHITE); 589 DrawBox(MENUWIDTH, 0, MENUWIDTH, MENUHEIGHT, TFT_NAVY); 590 591 DrawFrame(MENUWIDTH, 0, MENUWIDTH, MENUHEIGHT, TFT_BLACK); 592 ILI9341SetCursor(MENUWIDTH 593 + 15, 15); 594 DrawString(LABLE1, SmallFont, TFT_WHITE); 595 DrawBox(MENUWIDTH 596 * 2, 0, MENUWIDTH, MENUHEIGHT, TFT_NAVY); 597 DrawFrame(MENUWIDTH * 2, 0, MENUWIDTH, 598 MENUHEIGHT, TFT_BLACK); 599 ILI9341SetCursor(MENUWIDTH * 2 + 15, 15); 600 DrawString(LABLE2, 601 SmallFont, TFT_WHITE); 602 DrawBox(MENUWIDTH * 3, 0, MENUWIDTH, MENUHEIGHT, TFT_NAVY); 603 604 DrawFrame(MENUWIDTH * 3, 0, MENUWIDTH, MENUHEIGHT, TFT_BLACK); 605 ILI9341SetCursor(MENUWIDTH 606 * 3 + 15, 15); 607 DrawString(LABLE3, SmallFont, TFT_WHITE); 608 609 //DrawFrame(0+1, 610 0+1, MENUWIDTH-2, MENUHEIGHT-2, TFT_WHITE); 611} 612 613void presentation() 614{ 615 616 // Send the sketch version information to the gateway 617 618 Serial.print("Irrigation 619 System, 2021, Version ");Serial.println(VERSION); // this line will identify version 620 even when MySensor Debug is turned off. 621 622#ifdef MY_MYSENSORS 623 sendSketchInfo("IRRIGATION 624 SYSTEM ", VERSION, true); 625 // Register all sensors to gw (they will be created 626 as child devices) 627 for (int i = 0; i < NUM_VALVES; i++) { 628 present(CHILD_ID_AI[i], 629 S_MOISTURE); 630 present(CHILD_ID_START_OF_VALVES + i, S_SPRINKLER); 631 present(CHILD_ID_START_OF_TEMPS 632 + i, S_TEMP); 633 present(CHILD_ID_START_OF_AUTOS + i, S_LIGHT); 634 present(CHILD_ID_START_OF_TEMP_STATES 635 + i, S_DOOR); // indicates when there is no response from sensor 636 present(CHILD_ID_START_OF_MOISTURE_OPEN_STATES 637 + i, S_DOOR); // uses AI[] to determine open or short 638 present(CHILD_ID_START_OF_MOISTURE_SHORT_STATES 639 + i, S_DOOR); 640 } 641 present(CHILD_ID_FLOW, S_WATER); 642 present(CHILD_ID_DAY_NIGHT, 643 S_DOOR); // used to prevent watering during the day - binary sensor 644 present(CHILD_ID_LOW_FLOW, 645 S_DOOR); // used to let HA know that we have no water flow - binary sensor 646 present(CHILD_ID_UNRESPONSIVE_SENSOR, 647 S_DOOR); // used to let HA know that we have an open sensor - binary sensor 648 649 present(CHILD_ID_LEAK_DETECTED, S_DOOR); // alert HA of a leak 650 present(CHILD_ID_SAFEFLOW_SWITCH, 651 S_LIGHT); // if off, system is idle, if one, system is active 652#endif 653} 654 655void 656 loop(void) { 657 int x, y; 658 659#ifdef MY_MYSENSORS 660 if (startup) { 661 send(msgRestart.set(false)); 662 663 send(msgDayNight.set(true)); // this gets home assistant going 664 startup 665 = false; 666 } 667#endif 668 if (newLoop) { 669 readMoistureTemp(); // reads 670 all sensors 671 flowRate = flowMeasurement(); // this is not the only place where 672 a flowrate measurement is made; check getLastFlowRate() - used when closing valves 673 674 updateAllOld(); 675 newLoop = false; } // make sure HA is aware of current 676 state after powerdown 677 // and 678 collect moisture levels 679 if (!dayNight) { // if !dayNight = true, it's night 680 time and good to go 681 if (safeFlow) { 682 // check time to see if we need 683 a moisture reading - different delay during watering 684 if (millis() < oldNoMoistureTime) 685 { 686 oldNoMoistureTime = 0; // millis() can roll over making it smaller 687 than oldNoDataTime 688 } 689 if ((millis() - oldNoMoistureTime) > ((isAnyValveOn() 690 ? 1 : 0)*VALVES_ON_MOISTURE_MEASUREMENT_TIME + 691 (isAnyValveOn() 692 ? 0 : 1)*VALVES_OFF_MOISTURE_MEASUREMENT_TIME) * 60000) { // send data to HA to 693 let it know where still alive 694 oldNoMoistureTime = millis(); // update 695 time 696 readMoistureTemp(); // reads all sensors 697 flowRate = 698 flowMeasurement(); // this is not the only place where a flowrate measurement is 699 made; check getLastFlowRate() - used when closing valves 700 if (currentScreen 701 == 0) { updateScreen0(); updateAllOld();} else if (currentScreen == 1) {updateScreen1(); 702 updateAllOld();} // update screens on change of sensor data 703 } 704 // 705 open and close valves automatically when set by autoManState and send moisture readings 706 to HA when appropriate 707 // valves are turned on and off manually through 708 the receive() function or touch screen!! 709 for (int i = 0; i < NUM_VALVES; 710 i++) { 711 if (abs(moistureLevel[i] - oldMoistureLevel[i]) > 1.0 ) { // only 712 send data upon change of state by more that 1% 713#ifdef MY_MYSENSORS 714 715 send(msgAI[i].set(moistureLevel[i],1)); // will automatically send all 716 moistureLevels on first newLoop 717 send(msgTemp[i].set(temp[i],2)); // 718 same here with temps 719#endif 720 oldMoistureLevel[i] = moistureLevel[i]; 721 722 oldTemp[i] = temp[i]; 723 } 724 if (autoManState[i] == true) 725 { // must be in auto mode to change valve state 726 if ((moistureLevel[i] 727 > upperMoistureLevel[i]) && valveState(i)) { 728 PORTA = 0; // turn all 729 valves off (valves are active low!) 730#ifdef MY_MYSENSORS 731 send(msgPORTA[i].set(valveState(i))); 732#endif 733 734 getLastFlowRate(); // valves are closed, make sure flow has stopped 735 736 } 737 if ((moistureLevel[i] < lowerMoistureLevel[i]) && !isAnyValveOn()) 738 { // only turn on if all other valves are off 739 writePORTA(i, true); 740 // turn on water (valves are active low, but writePORTA does inversion) 741#ifdef 742 MY_MYSENSORS 743 send(msgPORTA[i].set(valveState(i))); 744#endif 745 746 startMoistureLevel = moistureLevel[i]; // save for sensor check below, 747 is sensor responding? 748 oldValveOnTime = millis(); // set start time 749 for valve[i] on duration. only one valve can be on at a time, so we need only one 750 oldValveOnTime! 751 } 752 } 753 } 754 // Check flow and 755 moisture response if valve is on 756 if (isAnyValveOn() == true) { // one of 757 the valves is on, let's check to see if moisture sensor and the flow meter are responding 758 759 if (millis() < oldValveOnTime) {oldValveOnTime = 0; } // millis() can roll 760 over making it smaller than oldNoDataTime 761 if ((millis() - oldValveOnTime) 762 > FLOW_MOIST_TEST_TIME * 60000) { // time to turn off the valve to prevent over 763 watering 764 if (flowRate < 5.0) { // oops low flow 765 lowFlow 766 = true; 767#ifdef MY_MYSENSORS 768 send(msgLowFlow.set(true)); // send 769 alarm to HA to indicate system shut down. 770#endif 771 safeFlowShutdown(); 772 773 } 774 if (startMoistureLevel >= oldMoistureLevel[whichValveIsOn()] 775 + 2 ) { // if the measured moisture level hasn't increased 776 // 777 2% is added to current reading to overcome noise 778 unresponsiveSensor 779 = true; 780#ifdef MY_MYSENSORS 781 send(msgOpenSensor.set(true)); // 782 let HA know the system is shutting down due to an open sensor 783#endif 784 785 safeFlowShutdown(); // shutdown system and let HA know valves are all 786 off 787 } 788 } 789 if ((millis() - oldValveOnTime) > MAX_VALVE_TIME 790 * 60000) {// Even though moisture response and lowflow pass, this valve 791 // 792 has been on too long. Let's turn if off. 793 lowFlow = true; // turn lowFlow 794 and unresponsiveSensor flags on to indicate this failure mode 795 unresponsiveSensor 796 = true; 797 safeFlowShutdown(); 798 } 799 if (!digitalRead(SHUTOFF_VALVE)) 800 { 801 digitalWrite(SHUTOFF_VALVE, true); // turn on water when any other 802 valve is on 803 Serial.println();Serial.println("Shut off valve turned 804 on because another valve was turned on"); 805 } 806 } 807 else 808 { // no valves are on, must reset timer and turn off shutoff valve 809 oldValveOnTime 810 = millis(); // resets every loop cycle if no valve is on 811 if (digitalRead(SHUTOFF_VALVE)) 812 { 813 digitalWrite(SHUTOFF_VALVE, false); // shutoff water when no other 814 valves are on 815 Serial.println();Serial.println("Shut off valve turned 816 off because no other valve it on"); 817 } 818 } 819 } 820 } 821 822 // test to see if it's time for a leak check 823 if (millis() < oldLeakCheckTime) 824 {oldLeakCheckTime = 0; } // millis() can roll over making it smaller than oldLeakCheckTime 825 826 if ((millis() - oldLeakCheckTime) > LEAK_CHECK_TIME * 60000) { // let's do a leak 827 check 828 oldLeakCheckTime = millis(); // reset time for next cycle 829 if 830 (isAnyValveOn() == false) { // ok, no valve is on, we can at least start to conduct 831 a leak test 832 if (!digitalRead(SHUTOFF_VALVE)) { 833 digitalWrite(SHUTOFF_VALVE, 834 true); // turn on shutoff valve to test for leaks between the supply and the zone 835 valves 836 Serial.println();Serial.println("Shut off valve turned on for 837 leak test"); 838 } 839 leakCheckFlag = true; // enables the leak check 840 routine below 841 oldLeakDurationTime = millis(); // start the clock for the 842 next leak check 843 } 844 } 845 // the below code block conducts a leak check 846 after waiting for LEAK_DURATION_TIME. 847 if (leakCheckFlag == true) { 848 if 849 (isAnyValveOn() == true) { leakCheckFlag = false;} // oops, a valve has been turned 850 on during the leak check. Abort!!! Wait for next cycle. 851 else { // ok, the 852 shutoff valve is open, we need to delay long enough for the pressure pulse to pass, 853 then make a flow measurement 854 if (millis() < oldLeakDurationTime) {oldLeakDurationTime 855 = 0; } // millis() can roll over making it smaller than oldLeakCheckTime 856 if 857 ((millis() - oldLeakDurationTime) > LEAK_DURATION_TIME * 1000) { // the pressure 858 pulse delay time has passed, time to check the flow rate 859 // now let's 860 check to make sure that if no valves are on, then no flow is measured. If it is, 861 we have a leak! 862 flowRate = flowMeasurement(); // get the flowrate 863 864 if (flowRate != 0) { // oops, we have a leak 865 leakDetected = 866 true; 867#ifdef MY_MYSENSORS 868 send(msgLeakDetected.set(false)); delay(500); 869 send(msgLeakDetected.set(true)); // a transition from false to true triggers an 870 automated message from HA to email. 871#endif 872 oldLeakDetected = leakDetected; 873 874 safeFlowShutdown(); // shutdown system and let HA know valves are all 875 off 876 } 877 else { 878 leakDetected = false; 879 leakCheckFlag 880 = false; // we're done here, reset the flag 881#ifdef MY_MYSENSORS 882 if 883 (oldLeakDetected != leakDetected) {send(msgLeakDetected.set(leakDetected));} // 884 send message only upon change of state; this triggers on first cycle 885#endif 886 887 oldLeakDetected = leakDetected; 888 } 889 if (digitalRead(SHUTOFF_VALVE)) 890 { 891 digitalWrite(SHUTOFF_VALVE, false); // leak test complete, time to 892 turn off the shutoff valve again 893 Serial.println();Serial.println("Shut 894 off valve turned off after leak test"); 895 } 896 } 897 } 898 } 899 900 901 if (isAnyValveOn() == false && leakCheckFlag == false) { // if all valves 902 off and we are not conducting a leak test, 903 // 904 must turn off SHUTOFF_VALVE - necessary for manual op during daylight! 905 if 906 (digitalRead(SHUTOFF_VALVE)) { 907 digitalWrite(SHUTOFF_VALVE, false); // shutoff 908 water when no other valves are on 909 Serial.println();Serial.println("shutoff 910 closed at end of main loop before display update"); 911 } 912 } 913 else if 914 (isAnyValveOn() == true) { 915 if (!digitalRead(SHUTOFF_VALVE)) { 916 digitalWrite(SHUTOFF_VALVE, 917 true); 918 Serial.println();Serial.println("shutoff open at end of main loop"); 919 920 } 921 } // turn on shutoff valve when another valve is on 922 923 // check 924 input from touch screen 925 if (GetTouch(&x, &y)) { 926 digitalWrite(LED_PIN, 927 false); // display was touched, time to turn on the backlight 928 oldLEDTime 929 = millis(); // reset LED backlight on time 930 if (y < MENUHEIGHT) { // this 931 is a screen select command 932 if (x < 3 * MENUWIDTH) { // touched one of three 933 actionable screen selections 934 if (oldCurrentScreen == 2) { // just managed 935 setpoints, must update EEPROM 936 for (int i = 0; i < NUM_VALVES; i++) 937 { // error check 938 if (lowerMoistureLevel[i] >= upperMoistureLevel[i]) 939 { 940 return; // don't change from Screen 2 unless errors are repaired 941 942 } 943 } 944 for (int i = 0; i < NUM_VALVES; i++) { 945 946 EEPROM.update(BASE_EEPROM_ADDR + i, upperMoistureLevel[i]); 947 EEPROM.update(BASE_EEPROM_ADDR 948 + NUM_VALVES + i, lowerMoistureLevel[i]); 949 } 950 } 951 currentScreen 952 = int(x / MENUWIDTH); 953 DrawFrame(MENUWIDTH * currentScreen, 0, MENUWIDTH, 954 MENUHEIGHT, TFT_WHITE); 955 DrawFrame(MENUWIDTH * oldCurrentScreen, 0, MENUWIDTH, 956 MENUHEIGHT, TFT_BLACK); 957 oldCurrentScreen = currentScreen; 958 firstTime 959 = true; 960 } 961 else return; // don't respond to menu 3 962 } 963 964 } 965 if (currentScreen == 0) { 966 if (firstTime) { 967 Screen0SetUp(); 968 969 firstTime = false; 970 } 971 if (lastTouchTime > millis()) { lastTouchTime 972 = 0; } // clock rolled over 973 if ((millis() - lastTouchTime) > TOUCH_TIME) 974 { 975 lastTouchTime = millis(); 976 if (GetTouch(&x, &y)) { 977 byte 978 button = whichButton(x, y); 979 if (button == 0) { 980 return; // 981 touched screen in non active area 982 } 983 if (button > 19) { // 984 auto/man buttons 985 button = button - 20; // reduce to single digit 986 987 autoManState[button] = !autoManState[button]; // change state 988 writePORTA(button, 989 false); // turn off associated valve because we changed auto state on that valve 990 991 } 992 else { // must be a valve 993 button = button - 10; 994 // reduce to single digit 995 if (!autoManState[button]) { // do not turn 996 on valve if in auto state 997 if (!valveState(button)) { // if the valve 998 is off, we're going to turn it on 999 if (!isAnyAutoValveOn()) { // 1000 is a valve in auto state on. If not, we can turn all valves off and then this valve 1001 on. 1002 PORTA = 0; // turn off all valves 1003 writePORTA(button, 1004 true); // turn on appropriate valve 1005 } 1006 } 1007 else 1008 { // the valve is on, and must be the only one on. Must turn off 1009 PORTA 1010 = 0; 1011 } 1012 } 1013 } 1014 } 1015 updateScreen0(); 1016 // this is where the screen display is aligned to any state changes above 1017 } 1018 1019 } 1020 if (currentScreen == 1) { 1021 if (firstTime) { 1022 Screen1SetUp(); 1023 1024 firstTime = false; 1025 //Serial.println("finished setup"); 1026 } 1027 1028 if (lastTouchTime > millis()) { lastTouchTime = 0; } // clock rolled over 1029 1030 if ((millis() - lastTouchTime) > TOUCH_TIME) { 1031 lastTouchTime = millis(); 1032 1033 updateScreen1(); 1034 } 1035 } 1036 if (currentScreen == 2) { 1037 if 1038 (firstTime) { 1039 Screen2SetUp(); 1040 firstTime = false; 1041 //Serial.println("finished 1042 setup"); 1043 } 1044 if (GetTouch(&x, &y)) { 1045 if (lastTouchTime > millis()) 1046 { lastTouchTime = 0; } // clock rolled over 1047 if ((millis() - lastTouchTime) 1048 > TOUCH_TIME) { 1049 lastTouchTime = millis(); 1050 byte arrow = whichArrow(x, 1051 y); 1052 if (arrow == 0) { 1053 return; // touch not in sweet spot 1054 1055 } 1056 //Serial.print("which arrow = "); Serial.println(arrow); 1057 1058 if (arrow > 39) { // auto/man arrows 1059 arrow = arrow - 40; // 1060 reduce to single digit, 0 to 7 1061 lowerMoistureLevel[arrow] -= 5; // decrement 1062 upper limit by 5 1063 if (lowerMoistureLevel[arrow] < 5) { lowerMoistureLevel[arrow] 1064 = 5;} // can't lower below five 1065 setArrowColor(arrow); // checks for 1066 level inversion and paints errors red 1067 //Serial.print("lower Moisture 1068 Level"); Serial.print(arrow); Serial.print("\ "); Serial.println(lowerMoistureLevel[arrow]); 1069 1070 writeMoistureLevel(3, arrow); // update arrow 1071 } 1072 else 1073 if (arrow > 29) { // must be a valve 1074 arrow = arrow - 30; // reduce 1075 to single digit 1076 lowerMoistureLevel[arrow] += 5; // increment upper 1077 limit by 5 1078 if (lowerMoistureLevel[arrow] > 95) { lowerMoistureLevel[arrow] 1079 = 95;} // can't raise above 95 1080 setArrowColor(arrow); // checks for 1081 level inversion and paints errors red 1082 //Serial.print("lower Moisture 1083 Level"); Serial.print(arrow); Serial.print("\ "); Serial.println(lowerMoistureLevel[arrow]); 1084 1085 writeMoistureLevel(2, arrow); // update arrow 1086 } 1087 else 1088 if (arrow > 19) { // must be a valve 1089 arrow = arrow - 20; // reduce 1090 to single digit 1091 upperMoistureLevel[arrow] -= 5; // decrement lower 1092 limit by 5 1093 if (upperMoistureLevel[arrow] < 10) { upperMoistureLevel[arrow] 1094 = 10;} // can't lower below ten 1095 setArrowColor(arrow); // checks for 1096 level inversion and paints errors red 1097 //Serial.print("upper Moisture 1098 Level"); Serial.print(arrow); Serial.print("\ "); Serial.println(upperMoistureLevel[arrow]); 1099 1100 writeMoistureLevel(1, arrow); // update arrow 1101 } 1102 else 1103 { // must be a valve 1104 arrow = arrow - 10; // reduce to single digit 1105 1106 upperMoistureLevel[arrow] += 5; // increment lower limit by 5 1107 if 1108 (upperMoistureLevel[arrow] > 100) { upperMoistureLevel[arrow] = 100;} // can't raise 1109 above 100 1110 setArrowColor(arrow); // checks for level inversion and paints 1111 errors red 1112 //Serial.print("upper Moisture Level"); Serial.print(arrow); 1113 Serial.print("\ "); Serial.println(upperMoistureLevel[arrow]); 1114 writeMoistureLevel(0, 1115 arrow); // update arrow 1116 } 1117 } 1118 } 1119 } 1120 if (oldLEDTime 1121 > millis()) {oldLEDTime = 0;} // clock rolled over 1122 if (millis() - oldLEDTime 1123 > MAX_LED_TIME * 60000) { // time to turn off backlight 1124 digitalWrite(LED_PIN, 1125 true); // turn off LED backlight 1126 } 1127 if (oldDotTime > millis()) {oldDotTime 1128 = 0;} // clock rolled over 1129 if (millis() - oldDotTime > DOT_TIME * 1000) { // 1130 time to print a dot 1131 dotCounter++; 1132 if(dotCounter == 80) { Serial.println(); 1133 dotCounter = 0;} 1134 Serial.print("."); 1135 oldDotTime = millis(); 1136 } 1137} 1138 1139void 1140 Screen0SetUp() { 1141 char result[8]; 1142 uint16_t color; 1143 1144 flowRate = flowMeasurement(); 1145 // this provides the initial data for subsequent comparisons 1146 readMoistureTemp(); 1147 // reads all sensors 1148 DrawBox(0, 20, 320, 240, TFT_BLUE); // clear working 1149 area 1150 ILI9341SetCursor(170, 32); 1151 DrawString("Flow Rate (ml/sec) ->", 1152 SmallFont, TFT_WHITE); 1153 DrawFrame(270, 22, 26, 16, TFT_WHITE); // box to contain 1154 flow rate 1155 ILI9341SetCursor(268, 32); 1156 DrawString(dtostrf(flowRate, 6, 1, 1157 result), SmallFont, TFT_WHITE); // initial value when entering screen 1158 ILI9341SetCursor(5, 1159 160 + 15); 1160 DrawString("man", SmallFont, TFT_WHITE); 1161 ILI9341SetCursor(5, 1162 200 + 15); 1163 DrawString("auto", SmallFont, TFT_WHITE); 1164 ILI9341SetCursor(5, 1165 40); 1166 DrawString("100%", SmallFont, TFT_GREEN); 1167 ILI9341SetCursor(25, 1168 40); 1169 DrawString("/", SmallFont, TFT_WHITE); 1170 ILI9341SetCursor(30, 40); 1171 1172 DrawString("40C", SmallFont, TFT_RED); 1173 ILI9341SetCursor(10, 140); 1174 DrawString("0%", 1175 SmallFont, TFT_GREEN); 1176 ILI9341SetCursor(25, 140); 1177 DrawString("/", SmallFont, 1178 TFT_WHITE); 1179 ILI9341SetCursor(30, 140); 1180 DrawString("0C", SmallFont, TFT_RED); 1181 1182 ILI9341SetCursor(15, 90); 1183 DrawString("M", SmallFont, TFT_GREEN); 1184 ILI9341SetCursor(25, 1185 90); 1186 DrawString("/", SmallFont, TFT_WHITE); 1187 ILI9341SetCursor(30, 90); 1188 1189 DrawString("T", SmallFont, TFT_RED); 1190 for (int i = 0; i < NUM_VALVES; i++) 1191 { 1192 // deal with temps first (red) then moisture 1193 for (int i = 0; i < 1194 NUM_VALVES; i++) { 1195 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2, 4, 100, TFT_WHITE); 1196 // set the background 1197 DrawBox(36 * i + 32 + 17, MENUHEIGHT * 2 + int((40 1198 - temp[i]) * 100 / 40), 4, 100 - int((40 - temp[i]) * 100 / 40), TFT_RED); // create 1199 a red bar to indicate temperature 1200 DrawBox(36 * i + 32 + 15, MENUHEIGHT 1201 * 2 + int(100 - moistureLevel[i]), 8, 2, TFT_GREEN); // create a green horizontal 1202 bar to indicate moisture level - bar height 100 pixels! 1203 } 1204 color = 1205 valveState(i) ? TFT_GREEN : TFT_RED; 1206 DrawButton(36 * i + 19 + 17, MENUHEIGHT 1207 * 2 + 120, 30, 30, color); // upper left and box size 1208 color = autoManState[i] 1209 ? TFT_GREEN : TFT_RED; 1210 DrawButton(36 * i + 19 + 17, MENUHEIGHT * 2 + 160, 1211 30, 30, color); 1212 } 1213} 1214 1215void updateScreen0() { 1216 char result[8]; 1217 1218 uint16_t color; 1219 bool updateFlag = false; // Flag used to check if any item 1220 was updated. If so, runs updateAllOld() 1221 1222 if (oldFlowRate != flowRate) { 1223 // update flow if necessary 1224 DrawBox(271, 25, 24, 10, TFT_BLUE); 1225 ILI9341SetCursor(268, 1226 32); 1227 DrawString(dtostrf(flowRate, 6, 1, result), SmallFont, TFT_WHITE); 1228 1229 } 1230 1231 for (int i = 0; i < NUM_VALVES; i++) { // update temps and moisture 1232 1233 // deal with temps first (red) then moisture 1234 for (int i = 0; i < NUM_VALVES; 1235 i++) { // update guages 1236 if ((temp[i] != oldTemp[i]) || (moistureLevel[i] 1237 != oldMoistureLevel[i])) { // if either temp or moisture changes, rewrite entire 1238 bar 1239 DrawBox(36 * i + 32 + 15, MENUHEIGHT * 2, 8, 104, TFT_BLUE); // create 1240 a blue horizontal bar to clear gauge 1241 DrawBox(36 * i + 32 + 17, MENUHEIGHT 1242 * 2, 4, 100, TFT_WHITE); // set the background 1243 DrawBox(36 * i + 32 + 1244 17, MENUHEIGHT * 2 + int((40 - temp[i]) * 100 / 40), 4, 100 - int((40 - temp[i]) 1245 * 100 / 40), TFT_RED); // create a red bar to indicate temperature 1246 DrawBox(36 1247 * i + 32 + 15, MENUHEIGHT * 2 + int(100 - moistureLevel[i]), 8, 2, TFT_GREEN); // 1248 create a green horizontal bar to indicate moisture level 1249 updateFlag = 1250 true; 1251 } 1252 } 1253 // update manual and auto buttons 1254 if (valveState(i) 1255 != oldValveState[i]) { 1256 writeValveButton(i, valveState(i)); // update buttons 1257 1258 updateFlag = true; 1259 } 1260 if (autoManState[i] != oldAutoManState[i]) 1261 { 1262 writeAutoManButton(i, autoManState[i]); 1263 updateFlag = true; 1264 1265 } 1266 } 1267 if (updateFlag) {updateAllOld();} 1268} 1269 1270void Screen1SetUp() 1271 { 1272 uint16_t x0, x1, x2, y0, y1, y2; 1273 uint16_t color; 1274 1275 DrawBox(0, 1276 20, 320, 240, TFT_BLUE); // clear working area 1277 1278 ILI9341SetCursor(50, 50); 1279 1280 DrawString("System Status", MediumFont, TFT_WHITE); 1281 1282 systemSafe = !lowFlow 1283 && !unresponsiveSensor && !leakDetected; 1284 oldSystemSafe = systemSafe; 1285 if 1286 (systemSafe) { 1287 ILI9341SetCursor(80, 70); 1288 DrawString("System OK", 1289 SmallFont, TFT_GREEN); 1290 } 1291 else { // check each of the below and indicate 1292 alarms appropriately 1293 if (lowFlow) { 1294 ILI9341SetCursor(80, 60); 1295 1296 DrawString("Low Flow", SmallFont, TFT_RED); 1297 } 1298 if (unresponsiveSensor) 1299 { 1300 ILI9341SetCursor(80, 70); 1301 DrawString("Open Sensor", SmallFont, 1302 TFT_RED); 1303 } 1304 if (leakDetected) { 1305 ILI9341SetCursor(80, 80); 1306 1307 DrawString("Leak Detected", SmallFont, TFT_RED); 1308 } 1309 } 1310 DrawHLine(0, 1311 120, 319, TFT_WHITE); 1312 ILI9341SetCursor(50, 110); 1313 DrawString("Sensor Status", 1314 MediumFont, TFT_WHITE); 1315 1316 ILI9341SetCursor(5, 140); 1317 DrawString("temp", 1318 SmallFont, TFT_WHITE); 1319 ILI9341SetCursor(5, 150); 1320 DrawString("status", 1321 SmallFont, TFT_WHITE); 1322 ILI9341SetCursor(5, 140 + 36); 1323 DrawString("Msense", 1324 SmallFont, TFT_WHITE); 1325 ILI9341SetCursor(5, 150 + 36); 1326 DrawString("open", 1327 SmallFont, TFT_WHITE); 1328 ILI9341SetCursor(5, 140 + 72); 1329 DrawString("Msense", 1330 SmallFont, TFT_WHITE); 1331 ILI9341SetCursor(5, 150 + 72); 1332 DrawString("short", 1333 SmallFont, TFT_WHITE); 1334 1335 for (uint16_t j = 0; j < 3; j++) { // draw triangles 1336 1337 for (uint16_t i = 0; i < NUM_VALVES; i++) { 1338 x0 = 36 + 36 * i; 1339 x1 1340 = x0 + 26; 1341 x2 = 36 + 13 + 36 * i; 1342 y0 = 150 + 36 * j; 1343 y1 1344 = y0; 1345 y2 = y0 - 20; 1346 1347 if (j == 0) { // these are temp sensors 1348 1349 color = tempState[i] ? TFT_GREEN : TFT_RED; 1350 } 1351 else if 1352 ( j == 1) { // open moisture sensors 1353 color = moistureOpenState[i] ? TFT_RED 1354 : TFT_GREEN; 1355 } 1356 else { // shorted moisture sensors 1357 color 1358 = moistureShortState[i] ? TFT_RED : TFT_GREEN; 1359 } 1360 DrawAlarmSymbol( 1361 x2, y2, x0, y0, x1, y1, color); // x2, y2 is the peak point of the triangle 1362 1363 } 1364 } 1365} 1366 1367void updateScreen1() { 1368 uint16_t x0, x1, x2, y0, y1, 1369 y2; 1370 uint16_t color; 1371 bool updateFlag = false; 1372 1373 systemSafe = !lowFlow 1374 && !unresponsiveSensor && !leakDetected; 1375 if ((systemSafe != oldSystemSafe) 1376 && systemSafe) { 1377 DrawBox(80, 52, 100, 40, TFT_BLUE); // clear top area 1378 1379 ILI9341SetCursor(80, 70); 1380 DrawString("System OK", SmallFont, TFT_GREEN); 1381 1382 oldSystemSafe = systemSafe; 1383 } 1384 else if ((systemSafe != oldSystemSafe) 1385 && !systemSafe){ 1386 DrawBox(80, 52, 100, 40, TFT_BLUE); // clear top area for 1387 new alarm 1388 oldSystemSafe = systemSafe; 1389 } 1390 1391 if ((lowFlow != oldLowFlow) 1392 && lowFlow) { 1393 ILI9341SetCursor(80, 60); 1394 DrawString("Low Flow", SmallFont, 1395 TFT_RED); 1396 } 1397 else if ((lowFlow != oldLowFlow) && !lowFlow) { 1398 DrawBox(80, 1399 52, 100, 10, TFT_BLUE); // clear area 1400 } 1401 if ((unresponsiveSensor != oldUnresponsiveSensor) 1402 && unresponsiveSensor) { 1403 ILI9341SetCursor(80, 70); 1404 DrawString("Open 1405 Sensor", SmallFont, TFT_RED); 1406 } 1407 else if ((unresponsiveSensor != oldUnresponsiveSensor) 1408 && unresponsiveSensor) { 1409 DrawBox(80, 62, 100, 10, TFT_BLUE); // clear area 1410 1411 } 1412 if ((leakDetected != oldLeakDetected) && leakDetected) { 1413 ILI9341SetCursor(80, 1414 80); 1415 DrawString("Leak Detected", SmallFont, TFT_RED); 1416 } 1417 else 1418 if ((leakDetected != oldLeakDetected) && !leakDetected) { 1419 DrawBox(80, 72, 1420 100, 10, TFT_BLUE); // clear area 1421 } 1422 1423 for (int j = 0; j < 3; j++) { 1424 // draw triangles 1425 for (int i = 0; i < NUM_VALVES; i++) { 1426 x0 = 36 1427 + 36 * i; 1428 x1 = x0 + 26; 1429 x2 = 36 + 13 + 36 * i; 1430 y0 = 150 1431 + 36 * j; 1432 y1 = y0; 1433 y2 = y0 - 20; 1434 1435 if (j == 0) { // 1436 these are the failed temp sensors 1437 if (tempState[i] != oldTempState[i]) 1438 { 1439 color = tempState[i] ? TFT_GREEN : TFT_RED; 1440 DrawAlarmSymbol( 1441 x2, y2, x0, y0, x1, y1, color); 1442 updateFlag = true; 1443 } 1444 1445 } 1446 else if ( j == 1) { // open moisture sensors 1447 if (moistureOpenState[i] 1448 != oldMoistureOpenState[i]) { 1449 color = moistureOpenState[i] ? TFT_RED 1450 : TFT_GREEN; 1451 DrawAlarmSymbol( x2, y2, x0, y0, x1, y1, color); 1452 updateFlag 1453 = true; 1454 } 1455 } 1456 else { // shorted moisture sensors 1457 1458 if (moistureShortState[i] != oldMoistureShortState[i]) { 1459 color 1460 = moistureShortState[i] ? TFT_RED : TFT_GREEN; 1461 DrawAlarmSymbol( x2, 1462 y2, x0, y0, x1, y1, color); 1463 updateFlag = true; 1464 } 1465 } 1466 1467 } 1468 } 1469 if (updateFlag) {updateAllOld();} // makes sure that oldMoistureState[] 1470 etc. are reset 1471} 1472 1473void Screen2SetUp() { 1474 uint16_t x0, x1, x2, y0, y1, 1475 y2; 1476 char result[16]; 1477 1478 DrawBox(0, 20, 320, 240, TFT_BLUE); // clear 1479 working area 1480 ILI9341SetCursor(2, MENUHEIGHT + 60); 1481 DrawString("UPPER", 1482 SmallFont, TFT_WHITE); 1483 ILI9341SetCursor(2, MENUHEIGHT + 60 + 100); 1484 DrawString("LOWER", 1485 SmallFont, TFT_WHITE); 1486 1487 for (int j = 0; j < 2; j++) { 1488 for (int i 1489 = 0; i < NUM_VALVES; i++) { 1490 DrawFrame(36 * i + 36, MENUHEIGHT + 50 + j 1491 * 100, 26, 16, TFT_WHITE); // set the background 1492 ILI9341SetCursor(36 * 1493 i + 36 + 8, MENUHEIGHT + 60 + j * 100); 1494 if (j == 0) { 1495 DrawString(itoa(upperMoistureLevel[i], 1496 result, 10), SmallFont, TFT_WHITE); 1497 } 1498 else { 1499 DrawString(itoa(lowerMoistureLevel[i], 1500 result, 10), SmallFont, TFT_WHITE); 1501 } 1502 } 1503 } 1504 for (uint16_t 1505 i = 0; i < NUM_VALVES; i++) { // draw triangles 1506 x0 = 36 + 36 * i; 1507 x1 1508 = x0 + 26; 1509 x2 = 36 + 13 + 36 * i; 1510 y0 = 65; 1511 y1 = y0; 1512 y2 1513 = y0 - 20; 1514 DrawTriangle( x2, y2, x0, y0, x1, y1, TFT_GREEN); 1515 } 1516 for 1517 (uint16_t i = 0; i < NUM_VALVES; i++) { // draw triangles 1518 x0 = 36 + 36 * 1519 i; 1520 x1 = x0 + 26; 1521 x2 = 36 + 13 + 36 * i; 1522 y0 = 90; 1523 y1 1524 = y0; 1525 y2 = y0 + 20; 1526 DrawTriangle(x1, y1, x0, y0, x2, y2, TFT_GREEN); 1527 1528 } 1529 for (uint16_t i = 0; i < NUM_VALVES; i++) { // draw triangles 1530 x0 1531 = 36 + 36 * i; 1532 x1 = x0 + 26; 1533 x2 = 36 + 13 + 36 * i; 1534 y0 = 165; 1535 1536 y1 = y0; 1537 y2 = y0 - 20; 1538 DrawTriangle(x2, y2, x0, y0, x1, y1, TFT_GREEN); 1539 1540 } 1541 for (uint16_t i = 0; i < NUM_VALVES; i++) { // draw triangles 1542 x0 1543 = 36 + 36 * i; 1544 x1 = x0 + 26; 1545 x2 = 36 + 13 + 36 * i; 1546 y0 = 190; 1547 1548 y1 = y0; 1549 y2 = y0 + 20; 1550 DrawTriangle(x1, y1, x0, y0, x2, y2, TFT_GREEN); 1551 1552 } 1553} 1554 1555#ifdef MY_MYSENSORS 1556// MySensors Specific Functions 1557void 1558 receive(const MyMessage &message) { 1559 byte driverPinIndex; 1560 1561 if (message.isAck()) 1562 { 1563 // if (SERIAL_TEST) {Serial.println("This is an ack from gateway");} 1564 1565 } 1566 if (message.type == V_STATUS) { // possible sensors are: 1567 // 1568 Digital IO -> 10 to 17 1569 // AutoMan Switches 1570 -> 20 - 27 1571 // SafeFlow Switch -> 44 1572 if 1573 (message.sensor < CHILD_ID_START_OF_VALVES + 8 && message.sensor >= CHILD_ID_START_OF_VALVES) 1574 { // must be a driver 1575 // Change driver state 1576 // Calculate driver 1577 from sensor number 1578 driverPinIndex = message.sensor - CHILD_ID_START_OF_VALVES; 1579 // driverPinIndex[0] thru driverPinIndex[7] 1580 if (!autoManState[driverPinIndex] 1581 && safeFlow) { // only change driver state if in manual mode and safeFlow 1582 writePORTA(driverPinIndex, 1583 message.getBool()); // turns off all valves and sets state of valve(driverPinIndex) 1584 true or false 1585 if(!message.getBool()) getLastFlowRate(); // make sure 1586 we get a flow measurement after valves are turned off 1587 for (int i = 0; 1588 i < NUM_VALVES; i++) { // set all valveStates except valveState(driverPinIndx) to 1589 false. 1590 if(i != driverPinIndex) {send(msgPORTA[i].set(false));} 1591 1592 } 1593 } 1594 else if(!safeFlow) { 1595 for (int i = 0; i 1596 < NUM_VALVES; i++) { // turn off all valves 1597 writePORTA(i, false); // 1598 turn off all valves 1599 send(msgPORTA[i].set(false)); // don't know why 1600 this is necessary 1601 } 1602 getLastFlowRate(); // make sure we get 1603 a flow measurement after valves are turned off 1604 updateScreen0(); 1605 } 1606 1607 } 1608 else if (message.sensor < CHILD_ID_START_OF_AUTOS + 8 && message.sensor 1609 >= CHILD_ID_START_OF_AUTOS){ // must be an AutoMan Switch 1610 autoManState[message.sensor 1611 - CHILD_ID_START_OF_AUTOS] = message.getBool(); // this means state was changed 1612 by HASS 1613 if(!message.getBool() && valveState(message.sensor - CHILD_ID_START_OF_AUTOS)) 1614 {PORTA = 0;} // if the autoState is turning off, must also turn off associated valve 1615 if on 1616 updateScreen0(); 1617 } 1618 else if (message.sensor == CHILD_ID_SAFEFLOW_SWITCH) 1619 { // HA requesting SafeFlow reset 1620 if(message.getBool() == true) { 1621 safeFlow 1622 = true; // user can only turn switch on. here it's turned off again after one second. 1623 1624 lowFlow = false; 1625 send(msgOpenSensor.set(lowFlow)); // let HA 1626 know the system is shutting down due to an open sensor 1627 unresponsiveSensor 1628 = false; 1629 send(msgLowFlow.set(unresponsiveSensor)); // send alarm to HA 1630 to indicate system shut down. 1631 send(msgRestart.set(false)); // turn off 1632 the HA switch 1633 delay(1000); 1634 oldValveOnTime = millis(); 1635 1636 } 1637 } 1638 } 1639 if (message.type == V_TRIPPED) { // recieved dayNight 1640 state from HA 1641 if (message.sensor == CHILD_ID_DAY_NIGHT) { // this is the 1642 day night switch that HA sends to prevent daytime watering 1643 dayNight = 1644 message.getBool(); // if true, it's daytime, do not water 1645 } 1646 } 1647} 1648#endif 1649 1650// 1651 Update All 1652void updateAllOld(){ // update changed states 1653 1654#ifdef MY_MYSENSORS 1655 1656 Serial.println("made it to MySensors UpdateAllOld"); 1657 for (int i = 1658 0; i < NUM_VALVES; i++) { 1659 if (oldValveState[i] == !valveState(i)) {send(msgPORTA[i].set(valveState(i))); 1660 oldValveState[i] = valveState(i);} 1661 if (oldMoistureLevel[i] != moistureLevel[i]) 1662 {send(msgAI[i].set(moistureLevel[i],1)); oldMoistureLevel[i] = moistureLevel[i];} 1663 1664 if (oldTemp[i] != temp[i]) {send(msgTemp[i].set(temp[i],1)); oldTemp[i] = temp[i];} 1665 1666 if (oldAutoManState[i] != autoManState[i]) {send(msgAutoMan[i].set(autoManState[i])); 1667 oldAutoManState[i] = autoManState[i];} 1668 Serial.print("oldTempState[");Serial.print(i);Serial.print("] 1669 = ");Serial.print(oldTempState[i]);Serial.print(" TempState[");Serial.print(i);Serial.print("] 1670 = ");Serial.println(tempState[i]); 1671 if (oldTempState[i] != tempState[i]) 1672 {send(msgTempState[i].set(tempState[i])); oldTempState[i] = tempState[i];} 1673 Serial.print("oldMoistureOpenState[");Serial.print(i);Serial.print("] 1674 = ");Serial.print(oldMoistureOpenState[i]);Serial.print(" moistureOpenState[");Serial.print(i);Serial.print("] 1675 = ");Serial.println(moistureOpenState[i]); 1676 if (oldMoistureOpenState[i] != 1677 moistureOpenState[i]) { 1678 send(msgMoistureOpenState[i].set(moistureOpenState[i])); 1679 oldMoistureOpenState[i] = moistureOpenState[i]; 1680 } 1681 Serial.print("oldMoistureShortState[");Serial.print(i);Serial.print("] 1682 = ");Serial.print(oldMoistureShortState[i]);Serial.print(" moistureShortState[");Serial.print(i);Serial.print("] 1683 = ");Serial.println(moistureShortState[i]); 1684 if (oldMoistureShortState[i] 1685 != moistureShortState[i]) { 1686 send(msgMoistureShortState[i].set(moistureShortState[i])); 1687 oldMoistureShortState[i] = moistureShortState[i]; 1688 } 1689 } 1690 if (oldFlowRate 1691 != flowRate) {send(msgFlow.set(flowRate,2)); oldFlowRate = flowRate;} 1692 if (oldLowFlow 1693 != lowFlow) {send(msgLowFlow.set(lowFlow)); oldLowFlow = lowFlow;} // used to let 1694 HA know that we have no water flow 1695 if (oldUnresponsiveSensor != unresponsiveSensor) 1696 {send(msgOpenSensor.set(unresponsiveSensor)); oldUnresponsiveSensor = unresponsiveSensor;}// 1697 used to let HA know that we may have an open sensor 1698#else 1699 for (int i = 0; 1700 i < NUM_VALVES; i++) { 1701 if (oldValveState[i] == !valveState(i)) { oldValveState[i] 1702 = valveState(i);} 1703 if (oldMoistureLevel[i] != moistureLevel[i]) {oldMoistureLevel[i] 1704 = moistureLevel[i];} 1705 if (oldTemp[i] != temp[i]) {oldTemp[i] = temp[i];} 1706 1707 if (oldAutoManState[i] != autoManState[i]) { oldAutoManState[i] = autoManState[i];} 1708 1709 if (oldTempState[i] != tempState[i]) { oldTempState[i] = tempState[i];} 1710 1711 if (oldMoistureOpenState[i] != moistureOpenState[i]) { 1712 oldMoistureOpenState[i] 1713 = moistureOpenState[i]; 1714 } 1715 if (oldMoistureShortState[i] != moistureShortState[i]) 1716 { 1717 oldMoistureShortState[i] = moistureShortState[i]; 1718 } 1719 } 1720 1721 if (oldFlowRate != flowRate) { oldFlowRate = flowRate;} 1722 if (oldLowFlow != 1723 lowFlow) { oldLowFlow = lowFlow;} // used to let HA know that we have no water flow 1724 1725 if (oldUnresponsiveSensor != unresponsiveSensor) { oldUnresponsiveSensor = unresponsiveSensor;}// 1726 used to let HA know that we may have an open sensor 1727#endif 1728 Serial.print("*"); 1729 1730 for (int i = 0; i < NUM_VALVES; i++) { 1731 Serial.println();Serial.print("moisture 1732 level(");Serial.print(i);Serial.print(") = ");Serial.print(moistureLevel[i]); 1733 1734 Serial.println();Serial.print("temperature(");Serial.print(i);Serial.print(") 1735 = ");Serial.print(temp[i]); 1736 } 1737 Serial.println(); 1738} 1739 1740// General 1741 Functions 1742void UpdateValvesGauges() { 1743 uint16_t color; 1744 1745 for (int 1746 i = 0; i < NUM_VALVES; i++) { 1747 // deal with temps first (red) then moisture 1748 1749 for (int i = 0; i < NUM_VALVES; i++) { 1750 DrawBox(36 * i + 32 + 17, MENUHEIGHT 1751 * 2, 4, 100, TFT_WHITE); // set the background 1752 DrawBox(36 * i + 32 + 17, 1753 MENUHEIGHT * 2 + (40 - temp[i]) * 100 / 40, 4, 100 - (40 - temp[i]) * 100 / 40, 1754 TFT_RED); // create a red bar to indicate temperature 1755 DrawBox(36 * i + 1756 32 + 15, MENUHEIGHT * 2 + (1 - moistureLevel[i]) * 100, 8, 2, TFT_GREEN); // create 1757 a green horizontal bar to indicate moisture level 1758 } 1759 color = valveState(i) 1760 ? TFT_GREEN : TFT_RED; 1761 DrawButton(36 * i + 19 + 17, MENUHEIGHT * 2 + 120, 1762 30, 30, color); // upper left and box size 1763 color = autoManState[i] ? TFT_GREEN 1764 : TFT_RED; 1765 DrawButton(36 * i + 19 + 17, MENUHEIGHT * 2 + 160, 30, 30, color); 1766 1767 } 1768} 1769 1770byte whichButton(int x, int y) { 1771 if (x < 36 || x > 318 || y 1772 < 160 || (int((x - 36) / 36) > NUM_VALVES)) { // x out of button range 1773 return 1774 (0); 1775 } 1776 else if (y < 195) { // first row of buttons - valves 1777 return 1778 (10 + int((x - 36) / 36)); // returns a number between 11 and 18 to indicate row 1779 1, collumn 0-7 1780 } 1781 else { // second row of buttons 1782 return (20 + int((x 1783 - 36) / 36)); // returns a number between 21 and 28 to indicate row 1, collumn 0-7 1784 1785 } 1786} 1787 1788byte whichArrow(int x, int y) { 1789 if (x < 36 || x > 318 || y 1790 < 40 || (int((x - 36) / 36) > NUM_VALVES)) { // x out of button range 1791 return 1792 (0); 1793 } 1794 else if (y < 80) { // first row of arrows 1795 return (10 + int((x 1796 - 36) / 36)); // returns a number between 11 and 18 to indicate row 1, collumn 0-7 1797 1798 } 1799 else if (y < 130) { // second row of arrows 1800 return (20 + int((x 1801 - 36) / 36)); // returns a number between 21 and 28 to indicate row 1, collumn 0-7 1802 1803 } 1804 else if (y < 180) { // third row of arrows 1805 return (30 + int((x - 1806 36) / 36)); // returns a number between 31 and 38 to indicate row 1, collumn 0-7 1807 1808 } 1809 else { // fourth row of arrows 1810 return (40 + int((x - 36) / 36)); 1811 // returns a number between 41 and 48 to indicate row 1, collumn 0-7 1812 } 1813} 1814 1815void 1816 setArrowColor(byte arrow) { 1817 uint16_t x0, x1, x2, y0, y1, y2; 1818 byte row; 1819 1820 byte column; 1821 1822 row = int(arrow / 10); 1823 column = (arrow - row * 10); 1824 // need to know what column we are changing 1825 1826 if (lowerMoistureLevel[column] 1827 >= upperMoistureLevel[column]) { 1828 color = TFT_RED; 1829 } 1830 else { 1831 1832 color = TFT_GREEN; 1833 } 1834 x0 = 36 + 36 * column; 1835 x1 = x0 + 26; 1836 1837 x2 = 36 + 13 + 36 * column; 1838 y0 = 65; 1839 y1 = y0; 1840 y2 = y0 - 20; 1841 1842 DrawTriangle( x2, y2, x0, y0, x1, y1, color); 1843 1844 x0 = 36 + 36 * column; 1845 1846 x1 = x0 + 26; 1847 x2 = 36 + 13 + 36 * column; 1848 y0 = 190; 1849 y1 = y0; 1850 1851 y2 = y0 + 20; 1852 DrawTriangle(x1, y1, x0, y0, x2, y2, color); 1853} 1854 1855void 1856 writePORTA(uint8_t pin, bool state) { // set particular pin true or false (only 1857 one bit true at a time!!! 1858 uint8_t newState; 1859 1860 if (state) { // if the 1861 valve should be turned on 1862 newState = bitValue[pin]; // turns all valves off 1863 except the desired one 1864 PORTA = newState; 1865 } 1866 else { // if the valve 1867 should be turned off 1868 if (valveState(pin)) { 1869 PORTA = 0; // if the 1870 request valve's state is true, it must be the only on valve, therefore we can turn 1871 all valves off 1872 } 1873 // otherwise, we don't want to turn unassociated 1874 valves off 1875 } 1876} 1877 1878void writeAutoManButton(uint8_t button, bool state) 1879 { 1880 uint16_t color; 1881 1882 color = state ? TFT_GREEN : TFT_RED; 1883 DrawButton(36 1884 * button + 19 + 17, MENUHEIGHT * 2 + 160, 30, 30, color); 1885} 1886 1887void writeValveButton(uint8_t 1888 button, bool state) { 1889 uint16_t color; 1890 1891 color = state ? TFT_GREEN : 1892 TFT_RED; 1893 DrawButton(36 * button + 19 + 17, MENUHEIGHT * 2 + 120, 30, 30, color); 1894 // upper left and box size 1895} 1896 1897void writeTempState(uint8_t sensor) { 1898 1899 uint16_t x0, x1, x2, y0, y1, y2; 1900 uint16_t color; 1901 1902 x0 = 36 + 36 * 1903 sensor; 1904 x1 = x0 + 26; 1905 x2 = 36 + 13 + 36 * sensor; 1906 y0 = 150; 1907 y1 1908 = y0; 1909 y2 = y0 - 20; 1910 1911 color = tempState[sensor] ? TFT_GREEN : TFT_RED; 1912 1913 DrawTriangle( x2, y2, x0, y0, x1, y1, color); 1914} 1915 1916void writeOpenSensor(uint8_t 1917 sensor) { 1918 uint16_t x0, x1, x2, y0, y1, y2; 1919 uint16_t color; 1920 1921 x0 1922 = 36 + 36 * sensor; //Serial.println(x0); 1923 x1 = x0 + 26; //Serial.println(x1); 1924 1925 x2 = 36 + 13 + 36 * sensor; //Serial.println(x2); 1926 y0 = 150 + 36; //Serial.println(y0); 1927 1928 y1 = y0; //Serial.println(y1); 1929 y2 = y0 - 20; //Serial.println(y2); Serial.println(); 1930 1931 1932 color = moistureOpenState[sensor] ? TFT_GREEN : TFT_RED; 1933 DrawTriangle( x2, 1934 y2, x0, y0, x1, y1, color); 1935} 1936 1937void writeShortedSensor(uint8_t sensor) 1938 { 1939 uint16_t x0, x1, x2, y0, y1, y2; 1940 uint16_t color; 1941 1942 x0 = 36 + 1943 36 * sensor; //Serial.println(x0); 1944 x1 = x0 + 26; //Serial.println(x1); 1945 1946 x2 = 36 + 13 + 36 * sensor; //Serial.println(x2); 1947 y0 = 150 + 72; //Serial.println(y0); 1948 1949 y1 = y0; //Serial.println(y1); 1950 y2 = y0 - 20; //Serial.println(y2); Serial.println(); 1951 1952 1953 color = moistureShortState[sensor] * TFT_GREEN + !moistureShortState[sensor] * 1954 TFT_RED; 1955 DrawTriangle( x2, y2, x0, y0, x1, y1, color); 1956} 1957 1958void writeMoistureLevel(byte 1959 row, byte column) { // row is 0 or 1, column is 0 thru 7 1960 char result[16]; 1961 1962 1963 if (row < 2) { 1964 row = 0; 1965 } else { 1966 row = 1; 1967 } 1968 DrawBox(36 1969 * column + 36, MENUHEIGHT + 50 + row * 100, 26, 16, TFT_BLUE); // delete contents 1970 1971 DrawFrame(36 * column + 36, MENUHEIGHT + 50 + row * 100, 26, 16, TFT_WHITE); // 1972 set the background 1973 ILI9341SetCursor(36 * column + 36 + 8, MENUHEIGHT + 60 + 1974 row * 100); 1975 if (row == 0) { 1976 DrawString(itoa(upperMoistureLevel[column], 1977 result, 10), SmallFont, TFT_WHITE); 1978 } 1979 else { 1980 DrawString(itoa(lowerMoistureLevel[column], 1981 result, 10), SmallFont, TFT_WHITE); 1982 } 1983} 1984 1985// non-display related functions 1986 1987bool 1988 isAnyValveOn() { 1989 bool result = true; 1990 1991 if (PORTA == 0 ) {result = false;} 1992 1993 return(result); 1994} 1995 1996bool isAnyAutoValveOn() { 1997 1998 if (PORTA == 0 1999 ) { 2000 return (false); // no valves are on, so we're ok 2001 } 2002 else { // 2003 some valve is on, is it also in auto mode? 2004 for (int i = 0; i < NUM_VALVES; 2005 i++) { 2006 if (valveState(i) && autoManState[i]) { 2007 return (true); 2008 2009 } 2010 } 2011 return (false); // if we made it here, no valve that might 2012 be in auto state is on now 2013 } 2014} 2015 2016bool valveState(uint8_t whichBit) 2017 { 2018 return (bitRead(PORTA, whichBit)); 2019} 2020 2021byte whichValveIsOn() { 2022 2023 for (byte i = 0; i < NUM_VALVES; i++) { 2024 if (valveState(i)) { 2025 return 2026 (i); 2027 } 2028 } 2029} 2030 2031void readMoistureTemp() { 2032 // get temperature 2033 data then read soil resistance and calculate moisture level 2034 Serial.println("made 2035 it to readMoistureTemp()"); 2036 // read all sensors by indexing through the eight 2037 analog switches in the multiplexer (74HC4051) 2038 for (int index = 0; index < NUM_VALVES; 2039 index++) { 2040 digitalWrite(TMPS0, bitRead(tempSensorConfig[index], 0)); // write 2041 the address to the multiplexer 2042 digitalWrite(TMPS1, bitRead(tempSensorConfig[index], 2043 1)); // tempSensorConfig modifies the index for the mapping 2044 digitalWrite(TMPS2, 2045 bitRead(tempSensorConfig[index], 2)); // of an ordered sensor configuration - see 2046 map defined above 2047 // call sensors.requestTemperatures() to issue a global 2048 temperature 2049 // as configured, there will only be one device on the bus at 2050 a time!!! 2051 sensors.requestTemperatures(); 2052 // query conversion time 2053 and sleep until conversion completed 2054 int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution()); 2055 2056 // sleep()/wait()call can be replaced by wait() call if node need to process 2057 incoming messages (or if node is repeater) 2058 delay(conversionTime); 2059 // 2060 get temps individually 2061 // We use the function ByIndex to get the temperature 2062 from the first and only sensor. 2063 temp[index] = sensors.getTempCByIndex(0); 2064 // read the currently selected temperature sensor 2065 Serial.print("temp(");Serial.print(index);Serial.print(") 2066 = ");Serial.println(temp[index]); 2067 updateTemp(index); 2068 Serial.println(); 2069 2070 } 2071 // read all the moisture levels 2072 digitalWrite(POWER_PIN, false); // 2073 Turn on sensor power; powers all sensors; active low 2074 delay(10); //delay 10 2075 milliseconds to allow reading to settle - determined by testing 2076 for (int i 2077 = 0; i < NUM_VALVES; i++) { // read all moisture sensors 2078 uint16_t reading 2079 = analogRead(AI_pin[i]); 2080 Serial.print("moistureAnalogReading[");Serial.print(i);Serial.print("] 2081 = ");Serial.println(reading); 2082 if (reading > 900) { 2083 moistureOpenState[i] 2084 = true; // are sensors open? 2085 } else { 2086 moistureOpenState[i] = false; 2087 2088 } 2089 if (reading < 23) { 2090 moistureShortState[i] = true; // are 2091 sensors shorted 2092 } else { 2093 moistureShortState[i] = false; 2094 } 2095 2096 float x = Rseries / (1023.0001 / reading - 1.0); // 1023 is largest AI value. 2097 0.0001 prevents overflow. 2098 // A = -0.0112*T +2.52 2099 // B = -3.6x10^-3 2100 * T - 3.6 2101 float a = -0.0112 * temp[i] + 2.52; 2102 a = pow(10, a); 2103 2104 float b = 1 / (-.0036 * temp[i] - 3.6); 2105 //Serial.print("Soil Resistance 2106 = "); Serial.println(x); 2107 moistureLevel[i] = pow(x / a, b) * 100; // was 2108 pow(x/151.35,-.27)*100 2109 if (moistureLevel[i] > 100) {moistureLevel[i] = 100;} 2110 // the curve fit equations do not work beyond about 80% moisture 2111 //Serial.print("moisture 2112 level("); Serial.print(i); Serial.print(") = "); Serial.println(moistureLevel[i]); 2113 2114 //Serial.print("Analog reading = "); Serial.println(analogRead(AI_pin[i])); 2115 2116 //Serial.print("Moisture Open = "); Serial.print(moistureOpenState[i]); Serial.print(" 2117 Old Moisture Open = "); Serial.println(oldMoistureOpenState[i]); 2118 //Serial.print("Moisture 2119 Short = "); Serial.print(moistureShortState[i]); Serial.print(" Old Moisture 2120 Short = "); Serial.println(oldMoistureShortState[i]); 2121 } 2122 digitalWrite(POWER_PIN, 2123 true); // turn off power to moisture sensors 2124 // Fetch temperatures from Dallas 2125 sensors 2126} 2127 2128void updateTemp(int i) { // this simplifies the code for reading 2129 the sensors. 2130 if (temp[i] == DEVICE_DISCONNECTED_C || temp[i] == -127) {// 2131 either condition indicates no sensor connected 2132 temp[i] = 22; // set temperature 2133 to std temp so measurements can continue 2134 tempState[i] = false; // keep track 2135 of failing sensors 2136 } 2137 else { 2138 tempState[i] = true; // keep track 2139 of working sensors 2140 } 2141 Serial.print("temp");Serial.print(i);Serial.print(" 2142 = "); Serial.println(temp[i]); 2143 Serial.print("tempState");Serial.print(i);Serial.print(" 2144 = "); Serial.println(tempState[i]); 2145} 2146 2147float flowMeasurement() { // 2148 take a second to check flow rate 2149 // this is a crude method used here because 2150 there are no interrupt pins left 2151 // in this implementation. At the max reported 2152 flow rate through 1/4" drip 2153 // tubing 0f 20 GPH, we can anticipate at pulse 2154 frequency of 30 Hz. That's 2155 // slow enough to make this routine work well. 2156 2157 2158 bool state = digitalRead(FLOW_PIN); 2159 bool oldState = state; 2160 bool notDone 2161 = true; 2162 unsigned long int count = 0; 2163 unsigned long int oldMillis = millis(); 2164 2165 2166 while (notDone) { 2167 state = digitalRead(FLOW_PIN); 2168 if (state != oldState) 2169 { 2170 oldState = state; 2171 count++; 2172 } 2173 if ( oldMillis > 2174 millis()) { 2175 oldMillis = 0; // rollover event 2176 } 2177 notDone = 2178 (millis() - oldMillis < 1000); // 1000 = 1 sec: when this is false, we are done 2179 2180 } 2181 flowRate = count / (2 * K); // gets two counts per square wave; but rising 2182 and falling! 2183 return (flowRate); // K is the pulses per liter (1380), so we 2184 are returning the flow rate in liters per second 2185 // Should be 0.022 liters 2186 per second or there abouts. 2187} 2188 2189void getLastFlowRate() { 2190 delay(2000); 2191 // delay to let valve close and flow rate to settle 2192 flowRate = flowMeasurement(); 2193 // this takes one second to execute 2194} 2195 2196void safeFlowShutdown() { 2197 safeFlow 2198 = false; // system shuts down until attended to. When repaired, turn system off 2199 and on again. 2200 leakCheckFlag == true; // get ready for next leak test - this 2201 could be done on restart 2202 PORTA = 0; // turn all valves off (valves are active 2203 high!) 2204 getLastFlowRate(); // make sure flow has shut down. 2205 digitalWrite(SHUTOFF_VALVE, 2206 false); // turn off shutoff valve 2207 Serial.println();Serial.println("Shut off 2208 valve turned off in safeFlowShutdown"); 2209} 2210 2211void DrawButton(uint16_t x1, 2212 uint16_t y1, uint16_t s1, uint16_t s2, uint16_t color) { 2213 2214 DrawBox(x1, y1, 2215 s1, s2, TFT_BLACK); 2216 DrawBox(x1 + 4, y1 + 4, s1 - 8, s2 - 8, color); 2217 DrawFrame(x1 2218 + 8, y1 + 8, s1 - 16, s2 - 16, TFT_BLACK); 2219} 2220 2221void DrawAlarmSymbol(uint16_t 2222 x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color) 2223 { 2224 2225 DrawTriangle( x1, y1, x2, y2, x3, y3, TFT_BLACK); // x1, y1 is the peak 2226 point of the triangle 2227 DrawTriangle( x1, y1 + 4, x2 + 4, y2 - 2, x3 - 4, y3 2228 - 2, color); 2229} 2230
(obsolete)
c_cpp
Downloadable files
Arduino Nano Circuit Board
This board does not include temperature sensors and is good for up to 8 valves. (I have been running six valves for a month without issues). The Nano is challenged with very little RAM headroom at 8 valves, but works great at 6 valves.
Arduino Nano Circuit Board
MEGA Driver Board 2.58
This driver board will stack on an Arduino MEGA and drive 9 valves. This updated version includes LEDs on each of the drive transistor outputs.
MEGA Driver Board 2.58
MEGA Irrigaton System with Display 2.63
This spin provides the capability of adding a display and adding an NRF24 radio to interface with MySensors and a home automation system. There is flexibility in it's implementation. You can run the system without the NRFradio using the software for that purpose. The board can be assembled without the components for the radio - a connector, capacitor, LED , resistor and the radio. Of course, you must use the software sans MySensors. If you install the radio, then you can use the software with the MySensors interface. In my case, this allows communication with Home Assistant. This board provides the following interfaces: MySensors NRF24 Radio HiLetgo ILI9341 2.8" SPI TFT LCD Display Touch Panel Backlight control Moisture Sensor Interface Dallas Temp Sensor Interface Valve Control through PORTA
MEGA Irrigaton System with Display 2.63
Arduino Nano Circuit Board
This board does not include temperature sensors and is good for up to 8 valves. (I have been running six valves for a month without issues). The Nano is challenged with very little RAM headroom at 8 valves, but works great at 6 valves.
Arduino Nano Circuit Board
Nano Irrigation Board with Temperature Sensors
This board includes inputs for Dallas Semiconductor Temperature Sensors. My code hasn't been fully tested. I am upgrading to an Arduino MEGA which will incorporate the temperature sensors. I found the program and memory space of the Nano too limiting for over four valves.. You many have better luck.
Nano Irrigation Board with Temperature Sensors
MEGA Irrigaton System with Display 2.63
This spin provides the capability of adding a display and adding an NRF24 radio to interface with MySensors and a home automation system. There is flexibility in it's implementation. You can run the system without the NRFradio using the software for that purpose. The board can be assembled without the components for the radio - a connector, capacitor, LED , resistor and the radio. Of course, you must use the software sans MySensors. If you install the radio, then you can use the software with the MySensors interface. In my case, this allows communication with Home Assistant. This board provides the following interfaces: MySensors NRF24 Radio HiLetgo ILI9341 2.8" SPI TFT LCD Display Touch Panel Backlight control Moisture Sensor Interface Dallas Temp Sensor Interface Valve Control through PORTA
MEGA Irrigaton System with Display 2.63
Arduino Nano Circuit Board
System Controller good for up to 8 valves. The Nano is challenged with very little RAM headroom at 8 valves, but works great at 6 valves.
Arduino Nano Circuit Board
Arduino Nano Circuit Board
This board does not include temperature sensors and is good for up to 8 valves. (I have been running six valves for a month without issues). The Nano is challenged with very little RAM headroom at 8 valves, but works great at 6 valves.
Arduino Nano Circuit Board
Nano Irrigation Board with Temperature Sensors
This board includes inputs for Dallas Semiconductor Temperature Sensors. My code hasn't been fully tested. I am upgrading to an Arduino MEGA which will incorporate the temperature sensors. I found the program and memory space of the Nano too limiting for over four valves.. You many have better luck.
Nano Irrigation Board with Temperature Sensors
MEGA Driver Board 2.58
This driver board will stack on an Arduino MEGA and drive 9 valves. This updated version includes LEDs on each of the drive transistor outputs.
MEGA Driver Board 2.58
Documentation
Sensor Support
This is the part that holds the 3mm rods used as the electrodes for the moisture sensor and the Dallas Temperature Sensor.
Sensor Support
Watering Valve Enclosure
This enclosure will house three valves.
Watering Valve Enclosure
System Shutoff Valve Enclosure
System Shutoff Valve Enclosure
Watering Valve Enclosure Cover
It's a cover to keep the valves as dry as possible. The holes for the tubing in the side of the enclosure are fairly tight, so it should be possible to at least keep insects out.
Watering Valve Enclosure Cover
MEGA Electronics Support Plate
This plate attaches to the bottom of the waterproof enclosure and supports the 12Volt power supply and the MEGA stack.
MEGA Electronics Support Plate
Watering Valve Enclosure
This enclosure will house three valves.
Watering Valve Enclosure
Nano Electronics Support Plate
This plate holds the power supply and the Arduino board. It has holds for mounting in the specified waterproof enclosure.
Nano Electronics Support Plate
Display Support Post
This is used to hold the display above the sensor board.
Display Support Post
Sensor Support
This is the part that holds the 3mm rods used as the electrodes for the moisture sensor and the Dallas Temperature Sensor.
Sensor Support
Shutoff Valve Enclosure Cover
Shutoff Valve Enclosure Cover
Display Support Post
This is used to hold the display above the sensor board.
Display Support Post
Shutoff Valve Enclosure Cover
Shutoff Valve Enclosure Cover
Nano Electronics Support Plate
This plate holds the power supply and the Arduino board. It has holds for mounting in the specified waterproof enclosure.
Nano Electronics Support Plate
MEGA Electronics Support Plate
This plate attaches to the bottom of the waterproof enclosure and supports the 12Volt power supply and the MEGA stack.
MEGA Electronics Support Plate
System Shutoff Valve Enclosure
System Shutoff Valve Enclosure
Watering Valve Enclosure Cover
It's a cover to keep the valves as dry as possible. The holes for the tubing in the side of the enclosure are fairly tight, so it should be possible to at least keep insects out.
Watering Valve Enclosure Cover
Comments
Only logged in users can leave comments
zavracky
2 years ago
Thanks for your comment. Soooo sorry for missing the valves in my parts list. I bought the valves from Amazon. Search for these: DIGITEN DC 12V 1/4" Inlet Feed Water Solenoid Valve Quick Connect N/C normally Closed They're a plastic valve housing with a solenoid on top. They work great. Paul
zavracky
3 years ago
Oops. Just found a wiring error on the mega driver board rev1.1. The Shutoff Valve is connected to pin 48, which is the pin used by the mega_irrigation board for the display CS. This had a surprisingly minor effect on the operation of the system. I cut this line on the driver board and re-routed to pin49. This change has to be reflected in the code by changing SHUTOFF_VALVE from 48 to 49. I will post a modification to the driver board. I've also upgraded the driver board by adding LEDs to the driver outputs. This makes trouble shooting much easier. Also, I added two 74LS157s (smd versions) so that the board is configurable to work with either port A or Port C. This readies the system for upgrading to 16 valves. I will post a new mega irrigation system board to use the second group of 8 analog inputs from the Arduino Mega sometime in the future ( it's a minor change). Software will also need to be upgraded. I've built a second system using the rev 2.6 mega irrigation board and the rev 1.1 driver board so that I can test the system without the MySensors interface. So far it's working great. I think it could use some tweaking to upgrade the touch response time. It's currently usable, but could be better. Since I didn't use interrupts to measure the flow, there's a built in I second delay each time the flow routine is accessed. This is not an issue for the Home Assistant Implementation.
Anonymous user
4 years ago
Can you please suggest me the solenoid valve to be used and where can I get it from. TIA.
zavracky
2 years ago
Thanks for your comment. Soooo sorry for missing the valves in my parts list. I bought the valves from Amazon. Search for these: DIGITEN DC 12V 1/4" Inlet Feed Water Solenoid Valve Quick Connect N/C normally Closed They're a plastic valve housing with a solenoid on top. They work great. Paul
Anonymous user
2 years ago
Can you please suggest me the solenoid valve to be used and where can I get it from. TIA.