#include <LowPower.h> #include <SPI.h> #include <LoRa.h> #include <EEPROM.h> int motor_in_1 = 5; int motor_in_2 = 6; int motor_sleep_pin = 4; #define SAMPLES 5 int sample_idx = 0; int voltage_reads[SAMPLES]; int moisture_reads[SAMPLES]; bool radio_initialized = false; #define NODEBUG #ifdef DEBUG #define DPRINTLN(str) Serial.println(str) #define DPRINT(str) Serial.print(str) #define DPRINTHEX(b) Serial.print(b, HEX) #define DFLUSH Serial.flush() #else #define DPRINTLN(str) #define DPRINT(str) #define DPRINTHEX(b) #define DFLUSH #endif /* * voltage: 161 fully charged, 1.37V each cell * voltage: 162 fully charged + bit of sun. * voltage: 155 after running quite a bit * voltage: 130 minimum below which radio no longer works * moisture: 880 dry / 600 wet */ #define WAIT_FOR_DELAY_OR_DRY 0 #define WATER_TILL_WET_MAX_TIME 1 #define WAIT_FOR_MAX_TIME 2 #define DRY 690 #define WET 660 #define DELAY 8 // hours #define MAX_TIME 20 // mins #define PUMP_SPEED 128 #define STATION_ID 2 #define HOME_ID 1 #define INTERVAL 10 // seconds struct Header { uint8_t to; uint8_t from; uint8_t seq; uint8_t flags; }; struct State { unsigned int voltage, moisture; byte pumpSpeed; bool pumpRunning; byte errors; }; #define PROC_PUMP_OFF 0 #define PROC_PUMP_ON 1 #define PROC_SLEEP 2 struct Cmd { byte proc, arg; }; Header my_header, their_header; State state; Cmd cmd; bool setupLoRa() { LoRa.setPins(10, 9, 2); LoRa.enableCrc(); LoRa.onReceive(onReceive); return LoRa.begin(868E6); } void setup() { #ifdef DEBUG Serial.begin(9600); #endif my_header = { HOME_ID, STATION_ID, 0, 0 }; their_header = { 0, 0, 0, 0 }; state = { 0, 0, PUMP_SPEED, false, 0 }; cmd = { 0, 0 }; // DPRINTLN("Setting one time state to EEPROM"); // EEPROM.put(0, state); analogReference(DEFAULT); pumpOff(); int voltage = 0; for (int i=0; i<SAMPLES; i++) { voltage_reads[i] = analogRead(A0); voltage = voltage + voltage_reads[i]; moisture_reads[i] = analogRead(A1); delay(50); } voltage = voltage / SAMPLES; if (voltage > 130) { radio_initialized = setupLoRa(); if (radio_initialized) { LoRa.receive(); } } } void pumpOn(int speed) { DPRINT("Switching pump on at "); DPRINTLN(speed); pinMode(motor_in_1, OUTPUT); pinMode(motor_in_2, OUTPUT); pinMode(motor_sleep_pin, OUTPUT); digitalWrite(motor_sleep_pin, HIGH); digitalWrite(motor_in_1, LOW); analogWrite(motor_in_2, speed); state.pumpSpeed = speed; state.pumpRunning = true; } void pumpOff() { DPRINTLN("Switching pump off"); pinMode(motor_in_1, OUTPUT); pinMode(motor_in_2, OUTPUT); pinMode(motor_sleep_pin, OUTPUT); digitalWrite(motor_in_1, LOW); digitalWrite(motor_in_2, LOW); digitalWrite(motor_sleep_pin, LOW); state.pumpRunning = false; } void loop() { voltage_reads[sample_idx] = analogRead(A0); moisture_reads[sample_idx] = analogRead(A1); sample_idx = (sample_idx + 1) % SAMPLES; int voltage = voltage_reads[0]; int moisture = moisture_reads[0]; for (int i = 1; i < SAMPLES; i++) { voltage = voltage + voltage_reads[i]; moisture = moisture + moisture_reads[i]; } state.voltage = voltage / SAMPLES; state.moisture = moisture / SAMPLES; if (state.voltage <= 135) { LoRa.sleep(); LoRa.end(); radio_initialized = false; pumpOff(); } else { sendState(); delay(2000); LoRa.sleep(); } DPRINT("voltage = "); DPRINTLN(state.voltage); DPRINT("moisture = "); DPRINTLN(state.moisture); DPRINT("radio = "); DPRINTLN(radio_initialized); DFLUSH; if (state.pumpRunning) { delay(8000); // LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_ON, TIMER1_ON, TIMER0_ON, // SPI_OFF, USART0_OFF, TWI_OFF); } else { LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); } } void sendState() { if (radio_initialized) { my_header.seq++; LoRa.beginPacket(); LoRa.write((byte *)&(my_header), sizeof(Header)); LoRa.write((byte *)&(state), sizeof(State)); LoRa.endPacket(); LoRa.receive(); } else { radio_initialized = setupLoRa(); } } void onReceive(int packetSize) { DPRINT("Received packet, size="); DPRINTLN(packetSize); if (packetSize == (sizeof(Header) + sizeof(Cmd))) { DPRINT("Reading: "); for (int i=0; i<sizeof(Header); i++) { byte b = LoRa.read(); DPRINTHEX(b); ((byte *)&their_header)[i] = b; } bool for_me = ((their_header.to == STATION_ID) && (their_header.from == HOME_ID)); DPRINTLN(""); DPRINT("For me: "); DPRINTLN(for_me); for (int i=0; i<sizeof(Cmd); i++) { byte b = LoRa.read(); if (for_me) { ((byte *)&cmd)[i] = b; } DPRINTHEX(b); } if (for_me) { if (cmd.proc == PROC_PUMP_OFF) { pumpOff(); } else if (cmd.proc == PROC_PUMP_ON) { pumpOn(cmd.arg); } } } DPRINTLN(""); DPRINTLN("Finished onReceive()"); // LoRa.sleep(); }