Peristaltic Doser - Doser set amount over 24hr period

I just want to verify that I called the function properly, this is what I typed in the CLI

particle call Doser setupChannel 0,22,1.26,120

I got the response “1”, is that the verification that the programming went through?

I believe so. You should get a 1 returned if the function was called. I call my functions from an iOS app, so I’m not familiar with the syntax for calling a function from the CLI. You could add some print statements to the bottom of the function to see if the values were properly parsed out.

whats the IOS app?

Also, do I need to do anything to incorporate time into the build now, or is Time built into the core now?

I only ask because I had run the command syntax as noted before and the relay did not trigger.

I write my own iOS apps. Yes, Time is built in; you can look it up in the docs. I don’t know why it wouldn’t have triggered without some troubleshooting on your part. I haven’t tested the code, so there could be mistakes in it. I’ll see if I can do some testing here. What command did you send? The one you showed in your previous post?

Yeah, I had quite a few errors in that code. I was creating new struct instead of getting a reference to the one I had in the array, and my time check was backwards; should have been <= not >=. Anyway, here is the fixed up code that I tested. I added some print statements so you could see what the current time is as compared to the time you’re passing in. Notice that I changed the delay in loop() to 5000 for testing purposes (so it doesn’t print the value of “now” every half second). Also, I forgot to set the pinMode to OUTPUT for all the connected pins.

typedef struct {
    int onTime;
    int offTime;
    float totalDose;
    float doseRate; // mL/min
    int connectedPin;
} Channel;

 void updateChannel(Channel c);
 float now;

Channel channels[4];


void setup() {
    channels[0].connectedPin = D0;
    channels[1].connectedPin = D1;
    channels[2].connectedPin = D2;
    channels[3].connectedPin = D3;
    pinMode(D0,OUTPUT);
    pinMode(D1,OUTPUT);
    pinMode(D2,OUTPUT);
    pinMode(D3,OUTPUT);
    Serial.begin(9600);
    Time.zone(-5);
    delay(3000);
    Particle.function("setupChannel", setupChannel);
}



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;
    }
}


int setupChannel(String cmd) { // cmd syntax: "channel#,startTime,doseRate, dose" (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->doseRate = atof(strtok_r(NULL, ",", &rmdr));
    c->totalDose = atof(rmdr);
    float dosePerActuation = c->totalDose/12;
    float durationPerActuation = (dosePerActuation/c->doseRate) * 60; // assuming doseRate is in volume/minute
    c->offTime = c->onTime + durationPerActuation;
    
    Serial.printlnf("channel#: %d, onTime: %d, doseRate: %f, totalDose: %f", index, c->onTime, c->doseRate, c->totalDose);
    return 1;
} 


Just as a side note:
Due to your parentheses your (100/12) will be calculated first as an integer calculation giving you 8.0 instead of 8.333, introducing some 4% error to start with.
If you wrote (100/12.0) you’d be fine tho’

@james211, iOS is the operating system on Apple mobile devices like iPhone/iPad.
So an iOS app is an application running on these devices.

I understand what iOS is, I was curious what the exact iOS app was he was using. Thank you for chiming in though @ScruffR I appreciate having an extra set of eyes.

@Ric, thank you as well for taking a second look at the code. I guess when it all verified fine I presumed it was fine, I’m going to give it another shot this morning.

Couple of questions…

  1. Is it fine to change Serial.println(now); to Serial.println(Time.timeStr()); Its easier to read than the unix time stamp.

  2. So here’s something interesting, when I send the following call function
    particle call doser setupChannel 0,9:28,1.26,120
    The serial monitor prints the following, but the relay never completely fires, I only see a faint flash on the relay shield, but never a full on switch with a click.

The time is in a format of seconds into the day. As I wrote in the comments next to the Particle function, the time needs to be sent in the format hour3600 + minute60 + seconds. So if you want it to fire at 9 Am, you would send 9*3600 = 32400. This could easily be changed to send hour, minute, and second. I would still convert it to the seconds format within the Photon code so that you can do a single comparison with “now”, instead of having to check for equality with hour, min, and sec.

Ah,so my call function, particle call doser setupChannel 0,9,1.26,120 doesn’t work, it would have to be

particle call doser setupChannel 0,3600,1.26,120

am I understanding that correctly?

That 100 number was an example value for totalDose. Since totalDose is a float, dividing it by an integer should use floating point math, unless my memory fails me. Although, I did notice that I used atoi, where I should have used atof when parsing the string for totalDose. I’ve edited my code to fix that.

1 Like

That would fire it at 1 AM (1 * 3600). The hour needs to be in 24 hour format, so if you want it to fire at 1 PM, the value would be 13 * 3600 = 46800.

Gotcha! Am I burdening you yet? I feel like such a much…:frowning:
But I appreciate it.

So I’ll update the code and try sending it as particle call doser setupChannel 0,46800,1.26,120

No problem, we’re all learners here. There is one minor change to the code that should be made that I mentioned in my response to ScruffR. In the Particle function code, change c.totalDose = atoi(rmdir); to c.totalDose = atof(rmdr); The function atoi converts a string to an integer, but totalDose is a float, so we should use atof.

So it seems to be working, thank you.

I was playing with the idea of adding a variable so I could retrieve the values via a webpage and know what each pump is set at. Right now I notice there is no way to do that. In the end I want to have a webpage for easier programming.

I added Particle.variable("Channel", Channel); But I get an error on verification. I was referencing my previous code which had Particle.variable("getRelay4", relayStr[3], STRING); There was one line for each pump.

A Particle variable can only be an int, double, or string, so you can’t use Channel, which is a struct. What information do you want to get about the pumps?

Right now I’d just like to be able to get the information on what the settings are for each pump. The time isn’t really isn’t necessary since that will be spread out over a 24hr period, but being able to retrieve the pump number and corresponding dose rate would be good.

You can construct a string with whatever information you need, just like I did in the printlnf statement in the Particle function code. Instead of printlnf, you use sprintf to put the string into a variable instead of printing to Serial.

So I did a bit of research, added the following, but it didn’t verify.

Added this prior to setup:

char channelStr[26]="";

And then constructed this string:

sprintf(channelStr, "channel#: %d, onTime: %d, doseRate: %f, totalDose: %f", index, onTime, doseRate, totalDose);

Also, on a side note, is there a way to manually fire the relays? This would be for calibration. I tried to send a digitalWrite command, but nothing is programmed to do that. Is there another way?

Did you create a Particle variable that reads channelStr? Also, channelStr is too short. Just the fixed part of the string (the names inside the quotation marks) is 45 characters, so you need to be that long plus the length of each value plus 1 for the termination character. You probably want to restrict the number of decimal places on your floats too (%.1f will give you one decimal place). It doesn’t hurt to make it bigger than you need, so I would probably make it about 75 chars long.