Via the particle relay board. And this is my code thanks to @Moors7 @Ric @ScruffR @bko
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
typedef struct {
int onTime;
int offTime;
float totalDose;
float doseRate; // mL/min
int connectedPin;
int calibrationEnd = 90000; // a vaule greater than "now" can ever be
int numberOfDoses; //number of doses in 24hr period
} Channel;
void updateChannel(Channel c);
float now;
retained char getRelay1[75];
retained char getRelay2[75];
retained char getRelay3[75];
retained char getRelay4[75];
retained Channel channels[4];
void setup() {
channels[0].connectedPin = D3;
channels[1].connectedPin = D4;
channels[2].connectedPin = D5;
channels[3].connectedPin = D6;
pinMode(D3,OUTPUT);
pinMode(D4,OUTPUT);
pinMode(D5,OUTPUT);
pinMode(D6,OUTPUT);
Serial.begin(9600);
Time.zone(-4);
delay(3000);
Particle.function("setDoseRate", setDoseRate);
Particle.function("setupChannel", setupChannel);
Particle.function("calibrate",calibrate);
Particle.function("manual",manual);
Particle.variable("getRelay1", getRelay1);
Particle.variable("getRelay2", getRelay2);
Particle.variable("getRelay3", getRelay3);
Particle.variable("getRelay4", getRelay4);
}
void loop() {
now = Time.hour() * 3600 + Time.minute() * 60 + Time.second();
Serial.println(now);
for (int i=0;i<4;i++) {
updateChannel(i);
}
delay(5000);
}
void updateChannel(int index) {
Channel *c = &channels[index];
if (c->onTime <= now && c->totalDose > 0) {
digitalWrite(c->connectedPin, HIGH);
Serial.printlnf("Fired Relay %d", index);
c->onTime += 7200;
}
if (c->offTime <= now && c->totalDose > 0) {
digitalWrite(c->connectedPin, LOW);
Serial.printlnf("Turned off Relay %d", index);
c->offTime += 7200;
}
if (c->calibrationEnd <= now) {
digitalWrite(c->connectedPin, LOW);
c->calibrationEnd = 90000;
}
}
int setDoseRate(String cmd) {
char *rmdr;
char *stringArgs = (char*)cmd.c_str();
int index = atoi(strtok_r(stringArgs, ",", &rmdr));
Channel* c = &channels[index];
c->doseRate = atof(rmdr);
return 1;
}
int setupChannel(String cmd) { // cmd syntax: "channel#,startTime,doseRate, numberOfDoses, totalDose" (startTime is sent as hour*3600 + minute*60 + second)
Serial.println(cmd);
char *rmdr;
char *stringArgs = (char*)cmd.c_str();
int index = atoi(strtok_r(stringArgs, ",", &rmdr));
Channel* c = &channels[index];
c->onTime = atof(strtok_r(NULL, ",", &rmdr));
c->numberOfDoses = atoi(strtok_r(NULL, ",", &rmdr));
c->totalDose = atof(rmdr);
float dosePerActuation = c->totalDose/c->numberOfDoses;
float durationPerActuation = (dosePerActuation/c->doseRate) * 60; // assuming doseRate is in volume/minute
c->offTime = c->onTime + durationPerActuation;
Serial.printlnf("channel#: %d, onTime: %d, totalDose: %f, numberOfDoses: %d", index, c->onTime, c->totalDose, c->numberOfDoses);
char *str;
switch (index) {
case 0:
str = getRelay1;
break;
case 1:
str = getRelay2;
break;
case 2:
str = getRelay3;
break;
case 3:
str = getRelay4;
break;
}
sprintf(str, "onTime: %d, totalDose: %.1f, numberOfDoses: %.1d", c->onTime, c->totalDose, c->numberOfDoses);
return 1;
}
int calibrate(String cmd) {
char *rmdr;
char *stringArgs = (char*)cmd.c_str();
int index = atoi(strtok_r(stringArgs, ",", &rmdr));
Channel* c = &channels[index];
if (strcmp(rmdr, "on") == 0) {
digitalWrite(c->connectedPin,HIGH);
c->calibrationEnd = Time.hour() * 3600 + Time.minute() * 60 + Time.second() + 60;
return 1;
}
else if (strcmp(rmdr, "off") == 0) {
digitalWrite(c->connectedPin,LOW);
return 0;
}
else {
return -1;
}
}
int manual(String cmd) {
char *rmdr;
char *stringArgs = (char*)cmd.c_str();
int index = atoi(strtok_r(stringArgs, ",", &rmdr));
Channel* c = &channels[index];
if (strcmp(rmdr, "on") == 0) {
digitalWrite(c->connectedPin,HIGH);
return 1;
}
else if (strcmp(rmdr, "off") == 0) {
digitalWrite(c->connectedPin,LOW);
return 0;
}
else {
return -1;
}
}