Multitasking? How?

My code started out polling 1-wire sensors every minute, storing the values and POSTing them to ThingSpeak. That works great; however, I’ve added a more real-time sequence to my main loop and it works until the other functions are called. While those run the loop is “stalled”. It makes sense I guess. FWIW I go out of my way to not use delay(), and any in there are <200.

What my new code does is poll a pin for input (HIGH). That input interval varies between 3 and 10 seconds, but only lasts 10ms. When the 1-wire and ThingSpeak functions are called each minute they take over the loop for about 9 seconds. In that 9 seconds I lose any pin inputs. For now because I’m not circling the moon and I know at least one input was missed, after every 60 seconds I just manually add in another +1. It’s shoddy, but again, not slingshotting around the moon here, but I’d like it to be fixed properly, if it’s possible.

The loop (heavily thinned out for ease of following) looks like this. The powerPoll() runs each loop but not when the IF evaluates TRUE. Maybe I just sprinkle powerPoll() inside the IF clause, too? Can they be made to run in parallel?

void loop() {
  powerPoll(); //collect SDG&E watt/hour IR pulse from meter

  unsigned long currentMillis = millis(); 
  if(currentMillis - previousMillis > interval1) { //interval1 = 60000
    previousMillis = currentMillis;
    
    oneWirePoll();
    
    Serial.println();
    Serial.println();
    
    sendThingSpeakData();
    
    Serial.print("Free bytes of sRAM = ");
    Serial.println(freeRam());
    Serial.println();
  }
} // END OF MAIN LOOP


void powerPoll() {
  powerMeterOld = powerMeterCur;
  powerMeterCur = digitalRead(24);
  if (powerMeterCur > 0) {
    increment the pulse count var so we can POST it
  }
}

void oneWirePoll() {
  for (k=0;k<10;k++) {
    get some temperature sensor data so we can POST it
  }
}

void sendThingSpeakData() {
  EthernetClient thingSpeak;
    if (thingSpeak.connect(thingSpeakServer, 80)) {
      blah blah blah POST some vars to the web
}

It sounds like the pulse from your power meter would better be read by an interrupt. Depending on what your onewire sensors are though, it may be possible to avoid the long delay. With DS18B20 sensors, you can ask them to read the temp and then collect it later. It sounds like you’re waiting for each of them to deliver a reading and that’s causing this huge delay.

Sounds like a job for an interrupt, as wildbill said.

http://www.gammon.com.au/interrupts

Thanks for the suggestion. I’ve never used interrupts, so new territory to explore there. Yes, the sensors are DS18B20’s, six of them now, three more to come, and yes the code scans the bus each minute. That might be some of the delay as well - I don’t know how to target a specific sensor so it’s a full bus query every minute. Perhaps if I knew how to query each DS18B20 without scanning the bus it would decrease the delay some, too? commenceGooglingNow()

That said, since I’m using a digital pin an interrupt should be possible, right? Just a little pin recoding plus the interrupt code and these two sequences can run in parallel?

Thanks for the direction, I’ll give it a try.

<— EDIT →
Like this? I’m using a Mega 2560. The IR NPN emitter leg returns on pin 22. I would need to move that to pin 2 to attach to int.0, correct?

byte wattHourPulses = 0;

powerPoll() {
  wattHourPulses++;
  do some millis() math to see if wattHourPulses should be reset to zero after 60 seconds
}

void setup() {
  attachInterrupt (0, powerPoll, RISING);  // to catch the change from LOW to HIGH
}

void loop() {
  do loopy stuff
}

Yes, pin 2 sounds right for that.

Well it worked, kinda. I had to move the conditionals into the main loop and clean up some rise detect stuff from the original code. It certainly detects the rise with the interrupt but after some indeterminite number of iterations the serial monitor just locks up, as does the Mega.

@Nick: I looked at your link (very detailed) but pushed away from the desk puzzled. Could it be colliding with another interrupt? This is the first one I've coded, but maybe there are internal collisions with this one. Perhaps I need to implement the SREG swap you wrote about?

For now I've reverted back... it's stable that way and while I work this interrupt issue out I'd at least like it to continue processing the other code.

Can’t say without seeing your problem code, however you shouldn’t do serial prints inside an interrupt. That will lock it up.

Ok, been travelling, just getting back to this.
I did some more tweaking on this and figured out my initial issue. Unfortunately it brought up another.
I’m incrementing a var in the ISR and even though I’ve declared the var as volatile unsigned long, when it increment to 256 it just starte outputting that value repeatedly. I moved the

Here is what I have:

volatile unsigned long wattHourPulses = 0;
unsigned long oldWattHourPulses = 0;
byte oldPulsesPer60s = 0;
float kWh;
long interval1 = 60000;
unsigned long previousMillis = 0;

void setup() {
  Serial.begin(115200);
  attachInterrupt (0, powerPoll, RISING);  // to catch the change from LOW to HIGH
}

void powerPoll() {
  wattHourPulses++;
}

void loop() {
  if(millis() - previousMillis > interval1) {
    previousMillis = millis(); 
    Serial.println("Fetching kWh for last 60 seconds");
    getPowerNow();
  }
  
  if (wattHourPulses > oldPulsesPer60s) {
    Serial.print("Meter Pulse Registered >> wattHourPulses = ");
    Serial.println(wattHourPulses);
    oldPulsesPer60s = wattHourPulses;
    for (int i=0;i<512;i++) {
      digitalWrite(26,HIGH);
    }
    digitalWrite(26,LOW);
  }
}


void getPowerNow() {
  kWh = (((wattHourPulses - oldWattHourPulses) * 60) * .001); 
  oldWattHourPulses = wattHourPulses;
  Serial.print("Current kWh = ");
  Serial.println(kWh);
  Serial.println();
}

This outputs

Meter Pulse Registered >> wattHourPulses = 1
Meter Pulse Registered >> wattHourPulses = 2
Meter Pulse Registered >> wattHourPulses = 3
Meter Pulse Registered >> wattHourPulses = 4
Meter Pulse Registered >> wattHourPulses = 5
....
Meter Pulse Registered >> wattHourPulses = 252
Meter Pulse Registered >> wattHourPulses = 253
Meter Pulse Registered >> wattHourPulses = 254
Meter Pulse Registered >> wattHourPulses = 255
Meter Pulse Registered >> wattHourPulses = 256
Meter Pulse Registered >> wattHourPulses = 256
Meter Pulse Registered >> wattHourPulses = 256
Meter Pulse Registered >> wattHourPulses = 256
Meter Pulse Registered >> wattHourPulses = 256
....forever
    for (int i=0;i<512;i++) {
      digitalWrite(26,HIGH);
    }

What do you think this is accomplishing? The compiler is smarter than you and will eliminate the loop.

You should disable interrupts, copy wattHourPulses into another variable, reenable interrupts, and then print the copy. What you see happening looks like the interrupt firing while you are printing wattHourPulses.

PaulS:

    for (int i=0;i<512;i++) {

digitalWrite(26,HIGH);
    }



What do you think this is accomplishing? The compiler is smarter than you and will eliminate the loop.

What loop are you talking about? It flickers an LED every time a pulse is received. It works fine.

PaulS:
You should disable interrupts, copy wattHourPulses into another variable, reenable interrupts, and then print the copy. What you see happening looks like the interrupt firing while you are printing wattHourPulses.

The pulses are at random intervals based on power usage, so I don’t see how that could cause the issue at wattHourPulses > 255 every time. 255 is too curious a number to be by chance, isn’t it?

Do you mean cli(); copy vars and sei();?
Does that affect millis()?
Where in the code would I disable the interrupts?

What loop are you talking about?

The for loop.

It flickers an LED every time a pulse is received.

No. It turns the pin on 512 times. For a pin that is already on, the next 511 calls do nothing. The compiler is smart enough to see that, and simply not generate the loop code.

Do you mean cli(); copy vars and sei();?

Yes.

Does that affect millis()?

Yes. For the length of time it takes to copy 4 bytes - a few hundred nanoseconds.

Where in the code would I disable the interrupts?

Right before you print the value. Enable them again before you print.

PaulS:

Where in the code would I disable the interrupts?

Right before you print the value. Enable them again before you print.

So like this? It takes a while for it to overflow, but it didn’t change things. Same thing when it hits 256.

if (wattHourPulses > oldPulsesPer60s) {
    Serial.print("Meter Pulse Registered >> wattHourPulses = ");
    cli();
    wattHourPulses2 = wattHourPulses;
    sei();
    Serial.println(wattHourPulses2);
    oldPulsesPer60s = wattHourPulses;
    for (int i=0;i<512;i++) {
      digitalWrite(26,HIGH);
    }
    digitalWrite(26,LOW);
  }

PaulS:

It flickers an LED every time a pulse is received.

No. It turns the pin on 512 times. For a pin that is already on, the next 511 calls do nothing. The compiler is smart enough to see that, and simply not generate the loop code.

Actually, the compiler will still generate that loop, for two reasons:

  1. When generating code for the loop, it doesn't have access to the code for digitalWrite, because the code for it is in a separate translation unit, not inlined in a header file.

  2. Even if it did have access to the code, digitalWrite writes to a volatile variable (the I/O port), and such writes in general may have side-effects.

But I agree that writing to the pin multiple times does nothing useful here, unless there is also an ISR that is interrupting the loop and setting the pin LOW again.

If I set it to 512, it lights long enough to be detected and without use of delay(). If I set it to 1 it's barely noticeable, so to my eyes it makes a difference and works. Is there a better way to do this?

Just use delay instead - does exactly the same thing and the code is less baffling.

Regarding the originally posted problem, it is solved with use of cli() and sei(). Instead of letting wattHourPulses to increment forever, every 60 seconds when I calculate the number of pulses I also reset the var to 0. I tried this initially but without the cli/sei calls. That had the nasty effect of no longer reliably registering the pulses, often for several minutes and then one or two would be detected.
Changing this:

void getPowerNow() {
  kWh = (((wattHourPulses - oldWattHourPulses) * 60) * .001); 
  oldWattHourPulses = wattHourPulses;
  Serial.print("Current kWh = ");
  Serial.println(kWh);
  Serial.println();
}

to this fixed the whole thing

void getPowerNow() {  
  kWh = ((wattHourPulses * 60) * .001);
  Serial.print("Current kWh = ");
  Serial.println(kWh);
  Serial.println();
  oldPulsesPer60s = 0;
  cli();
  wattHourPulses = 0;
  sei();
  
}

You should really protect your access to wattHourPulses, after all it might change while you are working on it:

void getPowerNow() {
  cli ();
  kWh = (((wattHourPulses - oldWattHourPulses) * 60) * .001); 
  oldWattHourPulses = wattHourPulses;
  sei ();

Having said that, I can't see in the earlier code why it would get stuck on 256. Nor can I see in the generated assembler why this would happen. Is this a Uno board? What version of the IDE?

The most that would happen, I would have said, was that you might get garbage printed occasionally. But the variable wattHourPulses should have continued to increment. I assume this pulse happens from time to time, not 1000 times a second?

That's correct. The pulse is generated by the power meter on the side of my house at the rate of one pulse per one watt-hour consumed.
The board is a Funduino Mega2560, IDE is Arduino Enhanced 1.0.5 (regular IDE is pitifully slow on this PC).
I tried just resetting the wattHourPulse var to 0 every 60 seconds in the loop but that's when it hung up. So I tried letting the var increment and then just subtracting the previous minutes value from wattHourPulse each minute. That worked until it "overflowed" at 256. It did this without fail, and was painful to wait for it to happen each time I tweaked the code some. The cli/sei wrapping around the wattHourPulse = 0 allowed it to actually reset to zero and continue reliably.

I'd like to know why it "overflowed" just for a better understanding but since I have worked around it it'll have to wait until I get another dev AVR. I suspect it would be easy enough to just watch any pin for a rise to recreate it, but if timing matters the pulses when my home is pretty idle come around 6-8 seconds, so it took about a half-hour to trip on itself.

It would be interesting to compile under the regular IDE (the original one that failed). I can't see any reason for this behaviour in the generated code.

The pulse is generated by the power meter on the side of my house at the rate of one pulse per one watt-hour consumed.

Then, the pulses are happening FAR more often than once an hour. A 100 watt light bulb is going to use 100 watt-hours per hour, or one watt-hour in 36 seconds. Fire up the electric dryer or electric oven and the pulses come hot and heavy.