Need direction on starting project - Photon+Photointrupter

Hello there,

I am about to start my first true photon project and need a little direction. I know very little about coding but this is a side project to help myself learn some things and create a device that I could put to use. I plan to use a photointerupter+photon to count bubble passing through a tube. I need to write (or find/copy) code that will allow me to obtain bubbles/hour or bubble/min (figured if I did bubbles/hour x 21 days, that would be about 504 data points which would work). Iā€™d like to ultimately graph this out as date&time vs bubble/hour and be able to access remotely.

Where should a newbie start on a project like this? Iā€™ve barely done anything but hook up the sensor to the photon and make a pin go HIGH or LOW as things pass through the interupter (yeah, way at the beginning).

I guess step one would be to get code that will actually count how many times the interupter goes from high to low to high again and log that as events/hour?

Anyway, and advice would be appreciated. Especially if you have done something like this before. Iā€™ll keep reading through all the posts and see what I can dig up.

About the graphing out, have a search on the forum for graph, azure, google docs, atomiot and also have a look at dashboard.particle.io.

For your bubble-time-rate calculation you might either use the info available via the Time object or use millis() and increment your counter while millis() - millisAtStart < 1000*60 (per minute) and reset your counter after that or (probably best for bubbles/hour) you measure the time (e.g. millis()) between bubbles and calculate the resulting bubble-time-rate and build a moving average over these rates.

Thanks for all that info @ScruffR! Really appreciate it.

Iā€™m about 1/3 of the way through Simon Monkā€™s Photon book now and Iā€™ll keep hammering away. I have a lot to learn about coding! Anyway, I have noticed another persons bubble-counter project and have the code for it. I know he used the millis() from what I noticed.

Would you recommend just starting out with trying to get the photogate to count the bubbles properly and then worry about the graphing part of it? Basically, I have used a very simple code that just prints a bunch of zeros when the gate is uninterrupted and then 1ā€™s when something moves in its way. I need to figure out how to count just 1 bubble as it passes through or register 1 bubble as the zeros become 1ā€™s and then back to zeros.

@brybrew Also check out www.Ubidots.com since they provide a real easy way to get data out of the Photon over the web and then displayed live and graphed over time.

@RWB, that website looks like it could work perfect! Iā€™ll keep working on trying to get this thing to count correctly! Since the Photon is wifi connected, does it keep an internal clock with date/time? Iā€™d like to be able to log at actual date intervals instead of raw hours. Iā€™m sure its possible.

@brybrew Search for Ubidots examples on this forum to find the code to use to send your data over once your ready. Ubidots has some examples also on their site also.

I do think there is code that allows you to pull time from the next using the Photon. Iā€™d do a forum search for that.

Yes there is a RTC in the device and you can easily access it via the already mentioned Time object.
The time gets synced to UTC on first cloud connect and whenever you call Particle.syncTime().
With Time.zone(x) you can set your local time offset (+/- hours from UTC).

And yes, again! First get your hardware working locally and then go online.

Okay, that was super helpful. I hadnā€™t even been into that ā€œreferencesā€ section of the Particle Docs. Holy cow thatā€™s helpful (see, told you Iā€™m just getting started). Very helpful yet again.

2 Likes

Soliciting another bit of help hereā€¦ baby stepsā€¦

I can get this to work to serial communication.

int Gate = D2;
int gateState = 0;
int led = D7;


void setup() {

  Serial.begin(9600);
  pinMode(Gate, INPUT);
  pinMode(led, OUTPUT);
  Time.zone(-6);
}


void loop() {
    
  gateState = digitalRead(Gate);

  if (gateState == HIGH) {
    digitalWrite(led, HIGH);
    Serial.print("TIME ");
    Serial.print(Time.hourFormat12());
    Serial.print("STATE ");
    Serial.print(gateState);
    
    
  } else {
    digitalWrite(led, LOW);
    Serial.print("TIME ");
    Serial.print(Time.hourFormat12());
    Serial.print("STATE ");
    Serial.print(gateState);
  }
  
  delay(1000);   
}

But I would like to print to particle cloud. Can I use the Particle.publish for that?

I did this:

int Gate = D2;
int gateState = 0;
int led = D7;
time = Time.zone(-6);   //??

void setup() {

  Serial.begin(9600);
  pinMode(Gate, INPUT);
  pinMode(led, OUTPUT);
  Time.zone(-6);       //??
}


void loop() {
    
  gateState = digitalRead(Gate);

  if (gateState == HIGH) {
    digitalWrite(led, HIGH);
    Particle.publish("Time ", time, "State ", gateState);
  } 
  
  else {
    digitalWrite(led, LOW);
    Particle.publish("Time ", time, "State ", gateState);
  }
  
  delay(1000);   
}

And it doesnā€™t work (not surprisingly). Could you lend a little direction into using the Time.zone(x) function and the Particle.publish function for what I am trying to do? Iā€™m really setting the bar low for myself and I keep hitting it anyway. I am just trying to get the thing to print LOW with the corresponding time over and over again until it reads HIGH and then print HIGH and the corresponding time.

I know this is probably super easy but I just canā€™t get it figured out.

Time.zone() inside setup() is fine (drop the other one ;-)).

And for Particle.publish() you should go for the two parameter version (first two parameters always need to be strings!).
e.g.

void loop() 
{
  gateState = digitalRead(Gate);
  digitalWrite(led, gateState);
  Particle.publish("moreUniqueName", 
                   String::format("Time: %s State: %s", 
                                  Time.timeStr().c_str(), 
                                  gateState ? "HIGH" : "LOW"));
  // or millions of other ways to build your eventName and eventData strings
  delay(1000);
}

You need to read the docs for it more carefully. There is no overload with a parameter list (const char*, TimeClass, const char*, int) but this is what you are calling.
When building this you should even have got an error and a corresponding note what overload would be a likely match for your call.

Thanks again @ScruffR. Iā€™m pretty thrown by that. Iā€™ve read the docs and I guess I just donā€™t understand the format. I just downloaded a ā€œBeginning with Arduino C codeā€ book so Iā€™ll have to keep at it.

Iā€™m really confused by this line.

String::format("Time: %s State: %s",

Iā€™m not sure what the Sting::format does and then the %s really throws me. If itā€™s red and in " ", does that mean itā€™s just printed?

And, were you just providing that code as an example because I get an error when I copy it.

bubble3.cpp: In function ā€˜void loop()ā€™:
bubble3.cpp:19:61: error: cannot pass objects of non-trivially-copyable type ā€˜class Stringā€™ through 'ā€¦'
gateState = digitalRead(Gate);
^

make[2]: *** [ā€¦/build/target/user/platform-0-lbubble3.o] Error 1
make[1]: *** [user] Error 2
make: *** [main] Error 2

The docs for String::format() can be found here.
And for the referenced printf-style you can google that :wink:

For your error I missed a bit there Time.timeStr().c_str() - Iā€™ve corrected the code above - sorry 'bout that :blush:

Ahhhh. ā€œProvides printf-style formatting for strings.ā€. That helpsā€¦again :grinning:

Well, I have it working and publishing the individual events to the dashboard with this:

//*Bubble Counter!*//

int Gate = D4;
int Led = D7;
int gateState = 0;

void setup() {

  pinMode(Gate, INPUT);
  pinMode(Led, OUTPUT);
  Time.zone(-6);
}

void loop() {
    
  static byte prevState = 1;
  gateState = digitalRead(Gate);
  
  if(gateState != prevState) 
  {
    if(gateState == LOW)
    {
    digitalWrite(Led, HIGH);
    Particle.publish("Bubbles!", 
                   String::format("Time: %s ", 
                                  Time.timeStr().c_str(), 
                                  "Bubble!"));
    }
    //else ignore
    prevState = gateState; 
  } 
  delay(50);
}

@aguspg, would you (or anyone else) have any suggestions for how to get started on making available with ubidots? I would really like to be able to record bubble/hour(or min) over the course of 14-21 days and possibly get some kind of ā€œreal-timeā€ approximation on bubble/min.

Iā€™ve been reading this tutorial . Iā€™m not sure if I have to change the current loop (ie. get rid of the Particle.publish componet or if uploading to Ubidots is just a matter of adding something to what I currently have.

Any direction for this newbie would be appreciated.

Glad you got it working! Iā€™m curious what your whole apparatus looks like. Are you measuring air bubbles going through a clear liquid-filled tube? Can you accurately catch each bubble?

Do you have a video? As soon as I imagined that i was curious how accurate a photointerrupter would be without fine-tuning the analog output with a comparitor.

@jakeypoo, Iā€™ll keep you posted. Iā€™m not entirely positive Iā€™ll capture all of them but so far it seems to work. Iā€™ll keep checking it out in different situations but Iā€™m just using an airlock with StarSan sanitizer in it. I was having problems with it reading multiple bubbles when just one bubble would come through but I have since worked through that to where it just counts 1 bubble when the leading edge of one comes through.

This is my non-working sorry-as-heck attempt to get it to work and no go.

    #include "HttpClient/HttpClient.h"
    
    #define VARIABLE_ID "XXXXXXXXXXXX"
    #define TOKEN "XXXXXXXX"
    
    int Gate = D4;
    int Led = D7;
    int gateState = 0;
    
    http_header_t headers[] = {
          { "Content-Type", "application/json" },
          { "X-Auth-Token" , TOKEN },
        { NULL, NULL } // NOTE: Always terminate headers will NULL
    };
    
    http_request_t request;
    http_response_t response;
    
    void setup() {
    
      pinMode(Gate, INPUT);
      pinMode(Led, OUTPUT);
      Time.zone(-6);
      request.hostname = "things.ubidots.com";
      request.port = 80;
      Serial.begin(9600);
    }
    
    void loop() {
        
      static byte prevState = 1;
      gateState = digitalRead(Gate);
      digitalWrite(Led, LOW);
      
      if(gateState != prevState) 
      {
        if(gateState == LOW)
        {
        digitalWrite(Led, HIGH);
        request.path = "/api/v1.6/variables/"VARIABLE_ID"/values";
        request.body = "{\"value\":" + String(gateState) + "}";
        http.post(request, response, headers);
        Serial.println(response.body);
        Serial.print("BUBBLE");
        }
        //else ignore
        prevState = gateState; 
      } 
      delay(50);
    }

I know (or believe) the problem lies in here:

        request.path = "/api/v1.6/variables/"VARIABLE_ID"/values";
        request.body = "{\"value\":" + String(gateState) + "}";
        http.post(request, response, headers);
        Serial.println(response.body);
        Serial.print("BUBBLE");

ā€¦but, Iā€™m just guessing at fixes at this point. Education solicited.

This is the error I am seeing.

#include "HttpClient/HttpClient.h"

#define VARIABLE_ID "xxxxxxxxxx"
#define TOKEN "xxxxxxxxxx"

int Gate = D4;
int Led = D7;
int gateState = 1;
HttpClient http;

http_header_t headers[] = {
      { "Content-Type", "application/json" },
      { "X-Auth-Token" , TOKEN },
    { NULL, NULL } // NOTE: Always terminate headers will NULL
};

http_request_t request;
http_response_t response;

void setup() {

  pinMode(Gate, INPUT);
  pinMode(Led, OUTPUT);
  Time.zone(-6);
  request.port = 80;
  request.hostname = "things.ubidots.com";
  request.path = "/api/v1.6/variables/"VARIABLE_ID"/values";
  Serial.begin(9600);
}

void loop() {
        
    static byte prevState = 0;
    gateState = digitalRead(Gate);
    digitalWrite(Led, LOW);
      
    if(gateState != prevState) 
      {
        if(gateState == HIGH)
        {
        digitalWrite(Led, HIGH);
        request.body = "{\"value\":" + String(gateState) + "}";
        http.post(request, response, headers);
        Serial.println(response.status); 
        Serial.println(response.body);
        }
        //else ignore
        prevState = gateState; 
      } 
    delay(50);
}

Forgot the HttpClient Http;. That fixed the error and its uploading to Ubidots just fine now.

Its still a bit sloppy so Iā€™ll keep trying to make it work a bit better. My biggest problem now is that even with the delay(50); it seems a bit slow through the loop. My guess is because its trying to send the data for every bubble and doesnā€™t have time to get to the top of the loop quick enough; thus, missing a bubble every now and then. If anyone has any ideas on how to make it run a bit faster (or more efficiently) that would be great. Otherwise, thanks for all the help thus far.

If you want that speed perhaps your own particle cloud server would help?