Photointerruptor for fermenter not tallying and publishing


#1

Hello and Thanks in advance!

I am constructing a bubble counter to monitor fermentation rate in home brew. It will eventually use a photointerruptor in a configuration like this:


It will be filled with an opaque liquid and any time a bubble passes, the sensor will go from LOW to HIGH. The goal is to have this tallied, averaged, and Particle.published every ~5 minutes. That will then go to a Google Sheet via IFTTT for tracking.

Currently my fiddling setup looks like this (data to D3):

I am currently just trying to get this to keep count of how many times the sensor is “open” (I always start the program with it blocked) and am having a devil of a time. At this point I just want it to 1) turn the D7 led on when the gate is unobstructed, 2) use “bubbletally” to count the number of times that happens, and 3) publish that number to the dashboard. I will worry about the math of averaging it later.

Here is the code:
//Title: Turns LED on and Publishes when interupter is clear.
//Photogate reads “HIGH” when obstructed.

int LED = D7;
int PI = D3;
bool currentState = FALSE;
bool lastState = FALSE;
int bubbletally = 0;

void setup() {
pinMode(LED, OUTPUT);
pinMode(PI, INPUT);
 }

void loop() {

currentState = digitalRead(PI);

if (currentState == TRUE && lastState == FALSE)
{
    digitalWrite(LED, HIGH);
    bubbletally = (bubbletally + 1);                     //Add 1 to bubble tally 
    Particle.publish("Bubble Count", bubbletally);   //Formatting for Particle.publish is confusing to me.
    delay(500);
    digitalWrite(LED, LOW);
    delay(500);
    
}
else {
    delay(500);
}
lastState = currentState;
}

Currently it runs and the LED turns on sporadically when the gate opens. This is what publishes to Dashboard:


I’m very new to C++ code so…sorry. I have reviewed these threads and was not able to glean anything that I understood or worked for my project. I have also reviewed the Docs section on Build and did not get there.

Any help is greatly appreciated. Will deliver home brew to 21+ individuals in contiguous 48!


#2

Hi @andyr6184

Are you sure your circuit is correct? It is hard to see what is on the little board for the sensor but there needs to a resistor for the LED side of photo interrupter and typically three wires for the output side (+V, data, and GND). If the sensor is made to do this for you, that is great, but we can’t tell from the picture.

Can you connect a simple LED and resistor or a voltmeter to the sensor output and see if the LED lights up or voltage changes when you block the beam with your finger?

You might need a pull-up resistor on the output.


#3

bko,

Thanks for the snappy reply. There is an LED on the sensor module that is lit up when it is unobstructed. The LED turns off when I put my finer in the way. I think this is the same module (http://goo.gl/g1FU5y).

Do I need another resistor somewhere?

Thanks again!


#4

bko,

I was doing some further poking around and saw someone put “String(XXXXX)” in their Particle.publish command and I tried that:

if (currentState == TRUE && lastState == FALSE)
{
    digitalWrite(LED, HIGH);
    bubbletally = (bubbletally + 1);                     //Add 1 to bubble tally 
    Particle.publish("Bubble Count", String(bubbletally));   //Formatting for Particle.publish is confusing     to me.
    delay(500);
    digitalWrite(LED, LOW);
    delay(500);

…and that has worked. I just need to set it to run for ~5 minutes and then divide the bubble tally for a rate.

If you are still interested I would love to know why this worked. I have read a bit about strings, but was totally confused.

Thanks!


#5

Hi @andyr6184

Sorry! I should have read your code first. Yes, Particle.publish takes two Strings or char arrays and sends a “message” out on to the cloud. Publish is a “push” technology. A String is an object (check the Arduino doc for details) that has various nice methods that can help you handle character type data. A char array is the older C-style string that is just an array of bytes in memory that get interpreted as text strings. Both work.

But your original code was just trying to use an integer where publish expected a String or char array, so the code did not know how to interpret that integer into human readable text. When you call String(bubbletally) you are creating a new String object whose character contents are the value of the integer. So if bubbletally has the integer value 1234, String() of that becomes an object that contains “1234” stored as ASCII characters in memory.

You may want to use sprint() or even better snprintf() to print your variables into a char array. Here is a tutorial on a web-friendly way to do that:

You could also use the “pull” which would be Particle.variable. Here you would declare bubbletally as a cloud variable in setup and then something on the internet could “pull” the value whenever it needed it.


#6
void loop() {

currentState = digitalRead(PI);

if (currentState == TRUE && lastState == FALSE)
{
    digitalWrite(LED, HIGH);
    bubbletally = (bubbletally + 1);                     //Add 1 to bubble tally 
    Particle.publish("Bubble Count", bubbletally);   //Formatting for Particle.publish is confusing to me.
    delay(500);
    digitalWrite(LED, LOW);
    delay(500);

}
else {
    delay(500);
}
lastState = currentState;
}

It might be no issue for your use case, but the way you’ve written the loop() code might miss some bubbles if they’re coming quicker than one per second (or you’ve got cloud connection troubles).

How about this

SYSTEM_THREAD(ENABLED)
...
void loop() {
  static uint32_t ms;     // to keep track of the publishing rate limit (only every 1010ms due to a current glitch)
  static int lastPubVal;  // to know when we need to publish
  char buff[16];          // for string building

  currentState = digitalRead(PI);
  if (currentState && !lastState)
  {
    bubbletally++;                                   // increment value  
    digitalWrite(LED, !digitalRead(LED));            // toggle LED
  }

  if (bubbletally != lastPubVal && (millis() - ms > 1010))
  {
    snprintf(buff, sizeof(buff), "%d", bubbletally); // build a string from a number 
    Particle.publish("Bubble Count", buff); 
    lastPubVal = bubbletally;                        // remember what we last published
    ms = millis();                                   // and when
  }

  lastState = currentState;
}

This way the LED will toggle each time a bubble passes.
And if you feel adventerous, you could use an interrupt for the counting :wink:

BTW, try to move the Photon to the edge of the breadboard to avoid strain on the USB connector. Your USB cable looks painfully wedged in there.


#7

Gentlemen,

Thank you both for your time and wisdom.

@bko , that is concise way to describing String objects and makes things a bit clearer. As for sprint() or nspintf(), that is a bit above my current pay grade but I will keep them on my reading list as my skills progress. I am familiar with Particle.variables (barely), but for my current purpose I think Particle.publish will fit the bill.

@ScruffR, I knew my timing would need some tweaking in order to count optimally…thank you for doing the work! I ran yours and it worked considerably better than my own. I even moved the blocking object out and back repeatedly as quickly as I could and it still kept count! The peculiar shape of the USB is due to a different device (I would never drop a Photon) that got dropped while charging. That is just the shape it stays in now. Normally I hang the USB port off the edge of the breadboard.

Anyone, Since the device is now counting flawlessly (Thanks @ScruffR) I now need the counting loop to run for 1 minute (to start with), then publish the results, and repeat. I have tried a " While(millis() < 60000) {" before the counting loop but it ran a single time (incorrectly) and quit. I want the simplest method possible so that my noob skills can tweak it later. It seems like the While() statement could work. Or I could use an Interrupt() as @ScruffR recommended, but I have no clue how those work. Or, I think, a timer() could work. I also don’t know those at all.

Which do you gents think is the simplest direction to head?

Thanks again for all of your help!


#8

That is an easy alteration to my code above.
If I understand you correctly, you want to reset the counter each minute aswell, yes?

Just replace 1010 with 60000 and if you also want zero-counts published remove bubbletally != lastPubVal && from the if statement too.
And instead of lastPubVal = bubbletally; put bubbletally = 0;

That should be it.

void loop() {
  static uint32_t ms = millis();                     // to keep track of time
  char buff[16];                                     // for string building

  currentState = digitalRead(PI);
  if (currentState && !lastState)
  {
    bubbletally++;                                   // increment value  
    digitalWrite(LED, !digitalRead(LED));            // toggle LED
  }

  if (millis() - ms >= 60000)                        // when difference between now (=millis()) and the previous time (=ms) of action is equal or greater your desired timespan
  {
    ms = millis();                                   // for more precise timing put this here
    snprintf(buff, sizeof(buff), "%d", bubbletally); // build a string from a number 
    Particle.publish("Bubble Count", buff); 
    bubbletally = 0;                                 // reset counter for next periode
  }
  lastState = currentState;
}

#9

@ScruffR, that worked flawlessly! You have saved me hours. My next batch of homebrew will be in your honor. I will post when I get the sensor mounted and working so you guys can see the progress. I’ll probably put the whole setup in the Project Share too.

Thanks again!


#10

Ha! That implies that older may not be better!

@bko do you feel that String class on Particle is stable compared to the pre-Cambrian C string?

I find that Photon handles the ancients better than their more contemporary counterparts (I.e. fewer inexplicable restarts).

I wonder if my experiences are similar to others here in Particleland.


#11

Hi @BulldogLowell

I always use C-style char arrays in my Particle programs. It is possible to use String() in memory friendly way, but you have to be extremely diligent. This is also true on Arduino.

// Bad
String A;
A = "foo";  
A = "bar";  // new storage for "bar" is created and the storage holding "foo" is free'ed meaning that a little four byte space in memory is created

// memory friendly way
String A;
A = "foo";
A = ""; // the storage for "foo" is just reset but not free'ed
A.concat("bar");  // no new storage is allocated unless "bar" is longer than what String A ever held before