díky
Kód: Vybrat vše
#include <avr/wdt.h> // Watchdog
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include "aREST.h"
#include <EEPROM.h>
#include "xPL.h"
#include "DHT.h" // DTH21 Temperature and Humidity.
#include <LiquidCrystal.h> // LCD
#include "EmonLib.h" // Power current/sensor
EnergyMonitor emon1; // Create an instance for power current
uint8_t pin_ph = A8; // Pin pH Probe
uint8_t pin_redox = A9; // Pin Redox Probe
uint8_t pin_relay_justsalt = 41; // Pin to Relay to JustSalt. If state=high -> salt production in progress (red light on)
uint8_t pin_relay_pool_lights = 40; // Pin to Relay to pool lights.
// uint8_t pin_dth = 2; // Pin sensor DTH21 Temperature and Humidity.
int pin_water_sensor = 39; // Pin water leak sensor. Is High when there is a leak. (I had to set it as int...)
uint8_t pin_power_sensor = A11; // Pin to power sensor.
uint8_t pin_potentiometer_redox = A12; // Pin to potentiometer used to define redox desired range
uint8_t pin_potentiometer_ph = A13; // Pin to the potentiometer used to calibrate pH
#define pinDHT 2
#define typDHT21 DHT21
DHT dht(pinDHT, typDHT21);
float ph_sensor_value = 0.0; // value read in Volt (0 to 5)
float ph_value_float = 0.0; // pH value from 0.0 to 14.0 in float
char ph_value_char[5]; // pH value from 0 to 14 in char
float redox_sensor_value = 0.0; // value read in Volt (0 to 5)
float redox_value_float = 0.0; // redox value from -2000 to 2000 mV in float
char redox_value_char[5]; // redox value from -2000 to 2000 in char
int redox_max; // define the max value of redox, if more then stop clorine production
int redox_min; // define the min value of redox, if less then start clorine production
int redox_range_delta = 20; // used to calculate the range (from-to) of accepted value. Example: 600-> value are from 600-20/2 to 600+20/2 (from 590 to 610)
float temperature_float = 0.0;
char temperature_char[5];
float humidity_float = 0.0;
char humidity_char[5];
float power_value_float = 0.0; // Power consumption in Watt
char power_value_char[5];
bool filtration_bool = 0; // Filtration state. 0 is off, 1 is on
int counter_filtration = 0; // Count minutes of filtration on 24h // To display to numbers of hours of working on 24h. The filtration time should be Temperature of the water divised by two. (Hours=Temp/2)
byte lights_seq_number = 0; // Display the save the lights sequence number (from 1 to 16)
byte lights_eeprom_addr = 0;
unsigned long lastReadingTime = 0;
int count_time_30s = 0; // used to trigger action every 30s (15*2s)
int count_time_30min = 0; // used to trigger action every 30min (60*30s)
int count_time_24h = 0; // used to trigger action every 24h (2880*30)
byte mac[] = { 0x00, 0xAC, 0xAE, 0x3F, 0xF1, 0xAD }; // Production MAC address
IPAddress broadcast(192, 168, 100, 255);
EthernetClient client; // For PushingBox...
EthernetUDP Udp;
xPL xpl;
LiquidCrystal lcd(12, 11, 5, 4, 3, 8);
DHT dht(pinDHT, typDHT21);
// PushingBox part
char devid_water_detection[] = "v0660D5BCF75744C";
char serverName[] = "api.pushingbox.com";
//RestAPI Part (for controlling lights)
EthernetServer server(80);
aREST rest = aREST();
int delay_betweent_relay_lights = 150; // time between relay state change in ms
void setup()
{
//Watchdog part
MCUSR &= ~_BV(WDRF); // Clear the reset bit
WDTCSR |= _BV(WDCE) | _BV(WDE); // Disable the WDT
WDTCSR = 0;
Serial.begin(57600);
pinMode(pin_relay_justsalt, OUTPUT);
digitalWrite(pin_relay_justsalt, 1); // do not start clorine production
pinMode(pin_water_sensor, INPUT_PULLUP);
digitalWrite(pin_water_sensor, HIGH);
// Test Pool Light
pinMode(pin_relay_pool_lights, OUTPUT);
digitalWrite(pin_relay_pool_lights, 1); // lights off by default
dht.begin(); // Start tempature and humidity sensor
emon1.current(11, 66); // Power Current: input pin, calibration.
lcd.begin(20, 4); // Init LCD screen, 4 lignes by 20 chars
lcd.clear();
// Print the default text on the LCD.
lcd.setCursor (0, 0);
lcd.print("Tepl:");
lcd.setCursor (12, 0);
lcd.print("pH:");
lcd.setCursor (0, 1);
lcd.print("Sonda:");
lcd.setCursor (0, 2);
lcd.print("Odber:");
lcd.setCursor (12, 2);
lcd.print("T:");
lcd.setCursor (14, 2);
lcd.print("...");
lcd.setCursor (0, 3);
lcd.print("Cl:");
Serial.println(F(""));
Serial.println(F("Starting (v1.0)"));
printMac(mac);
delay(100); // delay to boot in case of multiple DHCP requests from others Arduinos //TODO: Put 7000
while(Ethernet.begin(mac) == 0)
{
Serial.println(F("Failed to configure Ethernet using DHCP"));
}
printIP(); // Show IP in serial monitor
Udp.begin(xpl.udp_port);
xpl.SendExternal = &SendUdPMessage; // pointer to the send callback
xpl.AfterParseAction = &AfterParseAction; // pointer to a post parsing action callback
xpl.SetSource_P(PSTR("xpl"), PSTR("arduino"), PSTR("piscine")); // parameters for hearbeat message
//RestAPI part
rest.function("lights", lightsControl);
rest.set_id("001");
rest.set_name("arduino_piscine");
wdt_enable(WDTO_8S); //enable 8s watchdog
}
void loop()
{
// RestAPI part
EthernetClient client = server.available(); //TODO :verifier si pas redondant
// rest.handle(client);
xpl.Process(); // heartbeat management
// Parser part. Read input XPL message
if(Udp.parsePacket())
{
char xPLMessageBuff[XPL_MESSAGE_BUFFER_MAX];
Udp.read(xPLMessageBuff, XPL_MESSAGE_BUFFER_MAX); // read the packet into packetBufffer
xpl.ParseInputMessage(xPLMessageBuff); // parse message
}
// Protect if millis return to 0 (every 50 days)
if (millis() - lastReadingTime < 0)
{
lastReadingTime = millis();
}
// Show datas on LCD every 2 seconds
if ((millis() - lastReadingTime) >= 2000)
{
// pH Part
ph_sensor_value = analogRead(pin_ph) * 5000.0 / 1023.0 / 1000.0; // form 0.0 to 5.0 V
ph_value_float = (0.0178 * ph_sensor_value * 200.0) - 1.889; // formula to calcul pH from sensor value
//Serial.println(ph_value_float);
//Serial.println(analogRead(pin_potentiometer_ph));
// add calibration
ph_value_float = ph_value_float + (analogRead(pin_potentiometer_ph) - 512.0) / 500.0; // calibration is from -1 to +1 pH
lcd.setCursor (15, 0);
lcd.print(" "); // Clean lcd old digits
lcd.setCursor (15, 0);
lcd.print(ph_value_float, 2);
// Redox Part
redox_sensor_value = analogRead(pin_redox) * 5000.0 / 1023.0 / 1000.0; // form 0.0 to 5.0 V
redox_value_float = ((2.5 - redox_sensor_value) / 1.037) * 1000.0; // from -2000 to 2000 mV
lcd.setCursor (6, 1);
lcd.print(" "); // Clean lcd old digits
lcd.setCursor (6, 1);
lcd.print(redox_value_float, 0);
// get min-max redox values accepted
int potentiometer_redox = analogRead(pin_potentiometer_redox);
potentiometer_redox = map(potentiometer_redox, 0, 1023, 400, 1100);
redox_min = potentiometer_redox / 10 * 10 - redox_range_delta / 2;
redox_max = potentiometer_redox / 10 * 10 + redox_range_delta / 2;
lcd.setCursor (12, 1);
lcd.print(redox_min);
lcd.print("-");
lcd.print(redox_max);
lcd.print(" ");
// DHT Temp and humidity Part
temperature_float = dht.readTemperature();
humidity_float = dht.readHumidity();
lcd.setCursor (5, 0);
lcd.print(" "); // Clean lcd old digits
lcd.setCursor (5, 0);
lcd.print(temperature_float, 1);
lcd.print("C");
// Relay JustSalt part. Chlorin Production
lcd.setCursor (3, 3);
if (digitalRead(pin_relay_justsalt) == 0)
{
lcd.print("ON ");
}
else
{
lcd.print("OFF");
}
// Power sensor
double Irms = emon1.calcIrms(1480); // Calculate Power current (Irms only)
power_value_float = Irms * 232.0;
lcd.setCursor (6, 2);
lcd.print(" "); // Clean lcd old digits
lcd.setCursor (6, 2);
lcd.print(power_value_float, 0);
// Power state
if (power_value_float > 300) // Power is more than 300W
{
filtration_bool = 1; // on: filtration in progress
}
else
{
filtration_bool = 0;
digitalWrite(pin_relay_justsalt, 1); // salt production is stopped when filtration is stoped...
}
// Water leak detection
if (digitalRead(pin_water_sensor) == LOW)
{
lcd.setCursor (19, 3);
lcd.print("!");
}
else
{
lcd.setCursor (19, 3);
lcd.print(".");
}
//Serial.print(digitalRead(pin_water_sensor));
//lcd.setCursor (19,3);
//lcd.print(digitalRead(pin_water_sensor));
// Lights part
lcd.setCursor (12, 3);
lcd.print("Lum:");
if(digitalRead(pin_relay_pool_lights) == 0)
{
lcd.print(EEPROM.read(lights_eeprom_addr)); // Display the seq number when pool lights are on
lcd.print(" ");
}
else
{
lcd.print("0 "); // Display 0 when Pool lights are off
}
count_time_30s++; // Count 15 cycles for sending XPL every 30s
lastReadingTime = millis();
}
// Send datas as xPL Message every 30 seconds
if (count_time_30s == 15)
{
// pH Part
dtostrf(ph_value_float , 3, 2, ph_value_char); // float to char format: XX.XX
print_sensor_value("pH", analogRead(pin_ph), ph_value_float); // debug print
send_xpl_message("ph", ph_value_char); // send xpl message
// Redox Part
dtostrf(redox_value_float, 5, 0, redox_value_char); // float to char with decimal resolution format: XXXX
print_sensor_value("Redox", analogRead(pin_redox), redox_value_float); // debug print
send_xpl_message("redox", redox_value_char); // send xpl message
// DHT Temp and humidity Part
//Send temperature to XPL
dtostrf(temperature_float , 3, 2, temperature_char);
send_xpl_message("temp", temperature_char); //send xpl message
//Send humidity to XPL
dtostrf(humidity_float , 3, 2, humidity_char);
send_xpl_message("humidity", humidity_char); //send xpl message
// Relay JustSalt part
if (digitalRead(pin_relay_justsalt) == 0)
{
send_xpl_message("justsaltstate", "on"); // on: electrolyse in progress
}
else
{
send_xpl_message("justsaltstate", "off");
}
// Power sensor
dtostrf(power_value_float , 4, 0, power_value_char);
send_xpl_message("current", power_value_char); //send xpl message
// Power state
if (filtration_bool == 1) // Power is more than 300W, filtration in progress
{
send_xpl_message("state", "on"); // on: filtration in progress
}
else
{
send_xpl_message("state", "off");
}
// Water leak sensor
if (digitalRead(pin_water_sensor) == LOW)
{
send_xpl_message("leak", "on"); // water leak detected
sendToPushingBox(devid_water_detection);//Send Push with PushingBox in case of water leak every 30s
}
//if (count_time_30min % 2 == 0) // every 1min, used to count minutes of active filtration on 24h
//{
if (filtration_bool == 1) // if filtration in progress
{
counter_filtration++;
}
//itoa (counter_filtration, char test, 10);
//send_xpl_message("timer", String(counter_filtration));
count_time_24h++;
//}
count_time_30s = 0;
count_time_30min++;
}
if (count_time_30min == 60) // every 30min (60*30s)
{
if ((redox_value_float > redox_max) && (filtration_bool == 1)) // if to much clorine and filtration in progress
{
digitalWrite(pin_relay_justsalt, 1); // Set the relay to stop Clorine production
}
if ((redox_value_float < redox_min) && (filtration_bool == 1))
{
digitalWrite(pin_relay_justsalt, 0); // Set the relay to start Clorine production
}
count_time_30min = 0;
}
if (count_time_24h == 2880) // every 24h (1440*1min)
{
//Serial.println(count_time_24h);
lcd.setCursor (14, 2);
lcd.print(" ");
lcd.setCursor (14, 2);
lcd.print(counter_filtration / 60);
lcd.print("h");
if (counter_filtration % 60 < 10)
{
lcd.print("0");
}
lcd.print(counter_filtration % 60);
counter_filtration = 0;
count_time_24h = 0;
}
wdt_reset(); //Reset the Watchdog timer
}
// Send UDP Message
void SendUdPMessage(char *buffer)
{
Udp.beginPacket(broadcast, xpl.udp_port);
Udp.write(buffer);
Udp.endPacket();
}
// Print MAC Address
void printMac (const byte *buf)
{
Serial.print(F("MAC: "));
for (byte i = 0; i < 6; ++i)
{
if (buf[i] >= 0 && buf[i] <= 16)
Serial.print(F("0"));
Serial.print( buf[i], HEX );
if (i < 5)
Serial.print(F(":"));
}
Serial.println("");
}
// Print IP address
void printIP()
{
// print your local IP address:
Serial.print(F("My IP address: "));
for (byte thisByte = 0; thisByte < 4; thisByte++)
{
Serial.print(Ethernet.localIP()[thisByte], DEC);
Serial.print(F("."));
}
Serial.println();
}
// Print debug info into serial monitor
void print_sensor_value(char* name, int sensor_value, float value)
{
//print the results to the serial monitor for debug:
Serial.print(name);
Serial.print(F(" sensor: "));
Serial.print(sensor_value);
Serial.print(F(" output: "));
Serial.println(value);
}
// Send XPL Message
void send_xpl_message(char* type, char* current)
{
xPL_Message msg;
msg.hop = 1;
msg.type = XPL_TRIG;
msg.SetTarget_P(PSTR("*"));
msg.SetSchema_P(PSTR("sensor"), PSTR("basic"));
msg.AddCommand_P(PSTR("device"), PSTR("piscine"));
msg.AddCommand("type", type);
msg.AddCommand("current", current);
xpl.SendMessage(&msg);
}
// Parse input XPL messages
// usage: xpl-sender -m xpl-cmnd -t xpl-arduino.piscine -c justsalt.on000000
// should have a length of 8 for class because of a bug in the lib :(. That's why I added some "0".
void AfterParseAction(xPL_Message * message)
{
if (xpl.TargetIsMe(message))
{
// If we get an XPL packet, then turn on or off the Chlorine production
if (message->IsSchema_P(PSTR("justsalt"), PSTR("on000000")))
{
digitalWrite(pin_relay_justsalt, 0);
Serial.println(F("Turning ON Justsalt"));
send_xpl_message("justsaltstate", "on");
}
if (message->IsSchema_P(PSTR("justsalt"), PSTR("off00000")))
{
digitalWrite(pin_relay_justsalt, 1);
Serial.println(F("Turning OFF Justsalt"));
send_xpl_message("justsaltstate", "off");
}
}
// show all messages
//Serial.println(message->toString());
}
//Function for sending the request to PushingBox
void sendToPushingBox(char devid[])
{
client.stop();
if (client.connect(serverName, 80))
{
Serial.println("PB connected");
Serial.println("PB sendind request");
client.print("GET /pushingbox?devid=");
client.print(devid);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
}
else
{
Serial.println("PB connection failed");
}
}
// Controle lights of pool from RestAPI
// Usage : curl http://192.168.100.119/lights?params=actionOn
int lightsControl(String command)
{
//Serial.println(command);
if(command == "actionOn")
{
digitalWrite(pin_relay_pool_lights, 0);
return 1;
}
else if(command == "actionOff")
{
digitalWrite(pin_relay_pool_lights, 1);
return 1;
}
else if(command == "actionBlanc")
{
return poolLightRunSequence(1);
}
else if(command == "actionBleu")
{
return poolLightRunSequence(2);
}
else if(command == "actionCyan")
{
return poolLightRunSequence(4);
}
else if(command == "actionVert")
{
return poolLightRunSequence(6);
}
else if(command == "actionJaune")
{
return poolLightRunSequence(8);
}
else if(command == "actionRouge")
{
return poolLightRunSequence(10);
}
else if(command == "actionViolet")
{
return poolLightRunSequence(11);
}
else if(command == "actionVariation1")
{
return poolLightRunSequence(13);
}
else if(command == "actionVariation2")
{
return poolLightRunSequence(14);
}
else if(command == "actionVariation3")
{
return poolLightRunSequence(15);
}
else if(command == "actionVariation4")
{
return poolLightRunSequence(16);
}
else if(command == "boutonC")
{
digitalWrite(pin_relay_pool_lights, 1);
delay(delay_betweent_relay_lights);
digitalWrite(pin_relay_pool_lights, 0);
delay(delay_betweent_relay_lights);
return 1;
}
else
{
return 0;
}
return 0;
}
int poolLightRunSequence(byte desired_seq_number)
{
// Get actual sequence number
int actual_seq_number = EEPROM.read(lights_eeprom_addr);
// Calcul the number of toogle to rich the derised num from the actual seq_num
if(actual_seq_number == desired_seq_number)
{
// Toogle 16 times if same color ?
for (int i = 1; i <= 16; i++)
{
digitalWrite(pin_relay_pool_lights, 1);
delay(delay_betweent_relay_lights);
digitalWrite(pin_relay_pool_lights, 0);
delay(delay_betweent_relay_lights);
}
}
else if(actual_seq_number > desired_seq_number)
{
int num = 16 - actual_seq_number + desired_seq_number;
for (int i = 1; i <= num; i++)
{
digitalWrite(pin_relay_pool_lights, 1);
delay(delay_betweent_relay_lights);
digitalWrite(pin_relay_pool_lights, 0);
delay(delay_betweent_relay_lights);
}
}
else
{
int num = desired_seq_number - actual_seq_number;
for (int i = 1; i <= num; i++)
{
digitalWrite(pin_relay_pool_lights, 1);
delay(delay_betweent_relay_lights);
digitalWrite(pin_relay_pool_lights, 0);
delay(delay_betweent_relay_lights);
}
}
// Write new sequence number into eeprom
EEPROM.write(lights_eeprom_addr, desired_seq_number);
return 1;
}