#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();
}