and here is the ino file too:
#include <SMS_3G.h> //For sms, it's actually with cosm that it will be sent!
#include <TCP_3G.h>
SMS_3G sms; //As there are no arguments, drop the brackets
TCP_3G info;
char phone_number[]="*********"; //is the number to which the SMS is going to bo sent
int once = 0; //used to send the only once an sms telling the user 22 bottles are filled
// Pin number of the sensor's output:
const int pingPin = 7;
const int iscoPin = 9;
// Time
unsigned int dtLoop = 10000; // [ms]
double dtCstSampling = 60.0; // [s]
double nextSampleTime = 0; // [s]
// Sensor (height in [cm])
const int dhStormThr = 2; // [cm/s] for Chambrone a good threasold would be 5-8 cm/jour
const double dhRecPercThr = 0.1; // [%] recesion detcted when height 10% lower than peak height
float hBase = 0.0f; // The mean distance between the senseor and the water.
float cm = 0.0f, cmPeak = -10000.0f; // The height of the water relative to 'hBase': cm = sensor value - hBase.
// Lowpass filter of the sensor data: alpha with dT_ech and dT_filtre
unsigned int dtFilter = 5000; // [ms]
const float alpha = (dtLoop/1000.0f) / ((dtFilter/1000.0f)/(2.0f*3.14159f)+(dtLoop/1000.0f));
// Model parameters
double c = 1.0/5.0; // in [1/seconds]
// Sampler
unsigned const char nbBottles = 24; // Total number of bottles in the sampler.
unsigned const char stockBottles = 2; // Bottles that should remain after the flood.
unsigned char curBottle = 1; // The current bottle waiting to be filled.
// State of the sensor, because we need to calibrate it (to initialize the hBase variable).
typedef enum {
CALIGBRATING, SENSOR_READY}
SensorState;
SensorState sensorState;
// State of river
typedef enum {
WAIT_STORM, WAIT_RECESSION, RECESSION, SAMPLER_FULL}
StormState;
StormState stormState;
// State of sampling
typedef enum {
NOT_SAMPLING, CST_SAMPLING, PROG_SAMPLING}
SamplingState;
SamplingState samplingState;
void setup()
{
// initialize serial communication.
Serial.begin(115200);
// initialize ISCO command pin
pinMode(iscoPin, OUTPUT);
digitalWrite(iscoPin, LOW);
// initialize the SMS and the TCP connection
// Don't call switchmodule twice!
sms.begin(13, 2);
info.begin(13 ,2);
// initialize states.
sensorState = CALIGBRATING;
stormState = WAIT_STORM;
samplingState = NOT_SAMPLING;
//Serial.println("Calbrating");
}
void loop()
{
long duration = getDistancePulseDuration(); // get the datat from the Ultrasound sensor.
float sensorCM = microsecondsToCentimeters(duration); // convert the time into a distance
if(sensorState == CALIGBRATING) { // Calibrate the sensor -> the base height is the current height.
calibrateSensor(sensorCM);
return;
}
float cmCur, cmDerivative, dhRecThr;
cmCur = hBase - sensorCM;
cmDerivative = cm; // store prev_val here to save the number of variables
cm = alpha*cmCur + (1.0f-alpha)*cm;
cmDerivative = (cm - cmDerivative) / float(dtLoop / 1000.f);
// Storm state management
// (WAIT_STORM -> WAIT_RECESSION (10% of height peak) -> RECESSION -> WAIT_STORM).
switch(stormState) {
case WAIT_STORM: // Waiting for the flood.
if(cmDerivative > dhStormThr) { // The flood is detected when slope of height curve overcome dhStormThr.
stormState = WAIT_RECESSION;
samplingState = CST_SAMPLING;
}
break;
case WAIT_RECESSION: // Waiting that the height of water decrease.
cmPeak = cm > cmPeak ? cm : cmPeak;
dhRecThr = cmPeak * dhRecPercThr;
if(cmPeak - cm > dhRecThr) { // Check if height of water is under dhRecPercThr [%] of the highest water height value.
cmPeak = -10000.0f;
stormState = RECESSION;
samplingState = PROG_SAMPLING;
}
break;
case RECESSION: // In recession, waiting that all the bottles are empty.
if(curBottle > nbBottles-stockBottles) { // We preserve some bottles in case...
if(once == 0){
//-----------------------------------------------------
// send SMS to operator
//-----------------------------------------------------
sms.sendSMS(phone_number, "finished sampling, two bottles left in case");
once++;
}
stormState = WAIT_STORM;
samplingState = NOT_SAMPLING;
}
else if(cmDerivative > dhStormThr) { // A flood is detected during the recetion -> go back to constant sampling and wait for recession.
stormState = WAIT_RECESSION;
samplingState = CST_SAMPLING;
}
break;
}
// Sampling management.
if(samplingState != NOT_SAMPLING) { // We are sampling.
double remainingTime = nextSampleTime - double(millis())/1000.0;
if(remainingTime < 0.0) { // next sample
/*if(curBottle == 1) {
// SMS sent to say the sampling has started
//sms.sendSMS(phone_number, "Sampling started");
//Serial.println("first SMS sent.");
}*/
curBottle++;
if(curBottle >= nbBottles) { // sampler full -> send SMS
//-----------------------------------------------------
// send SMS to operator
//-----------------------------------------------------
samplingState = NOT_SAMPLING;
stormState = SAMPLER_FULL;
//sms.sendSMS(phone_number, "Sampler full, the prediction might have been wrong, or the sampling's totally finished");
//Serial.println("SMS sent.");
}
else {
//-----------------------------------------------------
// send next sample cammand to ISCO Sampler
digitalWrite(iscoPin, HIGH);
delay(10);
digitalWrite(iscoPin, LOW);
//-----------------------------------------------------
if(samplingState == CST_SAMPLING) {
nextSampleTime = computeNextSampleCstTime(dtCstSampling);
}
else {
nextSampleTime = computeNextSampleProgTime(curBottle, nbBottles, c);
}
}
}
}
info.sendInfo(curBottle, cm, cmDerivative); //We send info every time
delay(dtLoop);
}
void calibrateSensor(float height) {
int dt = 100;
const float a = (dt/1000.0f) / ((2000.0f/1000.0f)/(2.0f*3.14159f)+(dt/1000.0f));
hBase = a*height + (1.0f-a)*hBase;
delay(dt);
if(millis() > 5000) // we calibrate sensor during 2s.
sensorState = SENSOR_READY;
}
double computeNextSampleCstTime(double dtCstSampling) {
return dtCstSampling + double(millis())/1000.0;
}
double computeNextSampleProgTime(unsigned char a, unsigned char RS, float c) {
// The time between samples is computed such that each sampling interval correspond
// to an equal volume of flow under the predicated hydrograph recession curve.
double t_sample = (exp( 2.996*double(a) / double(RS)) - 1.0f) / double(c);
t_sample += double(millis())/1000.0;
return t_sample;
}
long getDistancePulseDuration()
{
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH pulse whose duration is the
// time (in microseconds) from the sending of the ping to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
return pulseIn(pingPin, HIGH);
}
float microsecondsToCentimeters(long microseconds)
{
// The speed of sound is 343.2 m/s or 29.1375 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the object we take half of the distance travelled.
return microseconds / 2.0f / 29.1375f;
}