Counting cycles and adding variables to variables.

Hi,

I'm currently experimenting with a Digispark.

having come in from the Raspberry pi side - I'm accustomed to being able to push tasks to the background and starting another.

but given the single-task nature of the digispark, I have to be creative.

What I'm currently trying to do is to count the times that Void loop completes, and when it hits a certain number, perform an action.

The way I'm doing this is a trick I have used in VB.net and VB6 for years.

//begin
Variable = 0

//on each cycle
Variable + Variable + 1

if variable = 80000000 then perform task
variable = 0 again.

The problem I'm running into is getting it to reset.

I have posted my code - and I know it's a bit messy, but I have tried to add code notations to make life easier.

I have avoided pin 5 as I'm using a chinese digispark with pin 5 set to reset in factory firmware.

my code runs fine until the "else" loop.

/*
Doorbell beam code
Attached to long range IR perimiter beam relay.
Designed to trigger wireless doorbell immediately on pin 2
Then trigger external lights and sound with delay,
to disguise the physical position and reaction time of the beam.
*/

const int buttonPin = 0;     // the number of the pushbutton pin
const int ledPin =  1;      // the number of the LED pin
double CycleCounter = 0;
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  pinMode(ledPin, OUTPUT); //connected to wireless doorbell transmitter
  pinMode(buttonPin, INPUT); //connected to IR beam sensor output
  pinMode(2, OUTPUT); //connected to "Range active" sign
  pinMode(3, OUTPUT); //connected to warning beeper
  pinMode(4, OUTPUT); //connected to external building light (intended to trigger from cycle count)
  //pinMode(5, OUTPUT);
  digitalWrite(2, HIGH); //turn all relays off (High = off, Low = on)
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(1, HIGH);
}

void loop() {
  // read the state of the IR beam relay
  buttonState = digitalRead(buttonPin);

  // check if the IR beam is broken. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    //Trip the doorbell button for 250 miliseconds
    digitalWrite(2, LOW);
    delay(250);
    digitalWrite(2, HIGH);
    //wait two seconds
    delay(2000);
    //turn on a light relay
    digitalWrite(4, LOW);
    //flash "Range active" sign
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);            
    //end flashing sign.
    //turn off light relay
    digitalWrite(4, HIGH);
    
  } else {
    // make sure doorbell transmitter is not continuously triggered
    digitalWrite(ledPin, HIGH);
    //Check cycle count
    if (CycleCounter = 80000000) {
      digitalWrite(2, LOW);
      delay(2000);
      CycleCounter = 0;
      digitalWrite(2, LOW);
    }
    //add to the cycle counter
    CycleCounter = CycleCounter + 1;
  }
}
double CycleCounter = 0;

Why double? Its a counter, it does not need to hold fractions. I think you should use long. Its also normal in C to start variable names with lower case, eg. "cycleCounter".

count the times that Void loop completes

When referring to a function, we normally put brackets after it, for example loop(). The "void" keyword simply tells you that the function does not return a result of any kind, so its really just a "subroutine".

   digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);           
    //end flashing sign.
    //turn off light relay
    digitalWrite(4, HIGH);

Have you learned about for-loops yet? You can make that section much smaller.

    //add to the cycle counter
    CycleCounter = CycleCounter + 1;
  }
}

I thought you wanted to increase the counter each time in loop()? The fact that you have 2 x } after that line tells you it won't always be increased. Actually its inside the else section, so will only be increased when the sensor is not activated. Is that OK?

if variable = 80000000 then perform task

Why 80 million? That's a strangely large and arbitary number! What are you really trying to achieve?

  if (CycleCounter = 80000000) {

There's your problem. The classic "gotcha" for C newbies. "=" means assignment. "==" means compare. You are setting CycleCounter to 80000000.

Are you trying to make something happen at regular intervals ?

If so, then there are easy and reliable ways to do it that do not involve counting how many times the loop() function executes

Im replying on a mobile device, so bear with the typos.

Re: double. This was originally a long. I was playing with variable types.

Re: void loop()
im aware of this, was simply trying to explain the issue in terms that would make sense.

Re: counting cycles
yes i want it to count each cycle, i dont want to count events.

Re: 80,000,000
16.5mhz / seconds.
loose math for a few seconds.
arbatrary value for testing. It will need to be a couple of hours in future.

Re: ==
thankyou muchly sir - that was a new fact i did not know. I'll try that in the morning

The overall idea is to trigger different events at a certain cycle count, compensating for the lack of timers and to partly get around delays halting all commands.

I plan to eliminate all delay commands.

Re: for loops
im aware of these, but this code is only placeholder, and delays will need to be different lengths. At present i need a set length of time, this was a quick and nasty way to acheive that until the final polish.

There is no lack of timers. It would be better to use millis() to time your periods, instead of using a cycle counter.

So would all code halt while milis is running?

Ok, dude.

Please stop what you are doing and look at the sticky post Using millis() for timing. A beginners guide.. There's also a post "several things at the same time", but I don't have a link to it.

Generally speaking, a single execution of loop() happens very quickly, and there aren't special guarantees about exactly how long it will take (because other hardware stuff happens in between executions of loop()). So counting cycles of loop() makes no sense, the count won't really tell you much.

Rather than counting cycles, you set a pair of variables that say "the doorbell is currently high, and I set it to high at this particular millis() time".

Re: 80,000,000
16.5mhz / seconds.

Where does 16.5 milliHertz come from?

Dranoweb:
So would all code halt while milis is running?

No! You are thinking of delay(). millis() takes almost zero time to run. Its a function that returns the number of milliseconds that have passed since the Arduino was powered up or reset. You use it a bit like you would use a stopwatch (without freezing/stopping the stopwatch), except that it runs in milliseconds rather than seconds. Take a snapshot of the value of millis() when something happens (by storing the value in an unsigned long variable) and then later you can subtract that snapshot value from the current value of millis() to find out how much time has passed. If that exceeds some period, the code can perform the required action.

Take a look at Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

Thanks guys - I'll check out that link - it sounds like that's the function I'm looking for.

Re: milihertz - typo for megahertz.
the digispark i have runs at around 16.5 megahertz, and while i know counting loops is an unreliable means of timing, i was specifically looking for some element of random timing.

The last relay on this project is meant to turn on an external building light to give the impression of a human being present.

If it happens in a slightly irregular fashion, that would give creedence to it being organic behaviour.

I'd also just like to add that I very much appreciate some useful input. It's genuinely helpful.

Thankyou.

Typically I'm met with one of two responses:

"That's stupid/bad practice - just don't do it"

or:

"just avoid that issue altogether by doing it entirely differently"

I'm aware that I'm often doing things in a way that's against design, or my code is written in poor form.
I know more than a few languages, and when A code snippet lands on a forum, it's usually because I'm learning yet another language and it's quirks.

when one is looking at an entire development board that is less than 10mm square, one tends not to expect many features.

I am actually genuinely shocked that there are hardware timers buried in that tiny, tiny little chip.

I entered the computer world around the era of DX33 CPU's, and motherboards full of jumpers - and borland turbo pascal.

I'm literally exhausted by the rate of development these days.

Anyway - I'm off to read about milis now, and how that might compare to languages I know a little better.

I've had the misfortune to encounter some very very basic machine languages that don't even understand "circle". in order to instruct some Yaznac robotic arms to move in a circle you need to give it the start and end coords of each individual line segment - one byte at a time...

when presented with such a tiny package I instantly assume that the size measn some sacrifices have been made.

Keep in mind I had dealt with commodore basic and all it's limitations too.

but jumping forward to vb.net - I do see the syntax similarity.
It's all learning I need to do and you guys have put me on the right track. as it's roughly 6pm here, I'm now only getting a chance to see all this on a pc screen, and I think I'll be tinkering for much of the night perfecting it.

It all sounds much as I had expected - that someone has already tried to do what I am trying to do, and come up with an instruction set to handle that - I just need to find it.

Also I see the millis beginners guide sticky - thankyou for that.

when presented with such a tiny package I instantly assume that the size measn some sacrifices have been made.

Size has nothing to do with it.

Apart from anything else, the Arduino is programmed in C++ so you have the full range of its functions available to you as well as the Arduino specific functions provided by the IDE. If you want to you can dive beneath the surface and use the hardware registers and timers and even program in assembler if you want.

Just a quick update -

Millis was the trick I needed.

I have the randowm on/off timing I wanted in a much more efficient line of code.

times and delays are still placeholders, but things are going quite well now.

It would be nice to have a single instruction to alternate pin 3 - going to try a for loop next.

/*
Doorbell beam code
Attached to long range IR perimiter beam relay.
Designed to trigger wireless doorbell immediately on pin 2
Then trigger external lights and sound with delay,
to disguise the physical position and reaction time of the beam.
*/

const int buttonPin = 0;     // the number of the pushbutton pin
const int ledPin =  1;      // the number of the LED pin
unsigned long startMillis; //start time
unsigned long currentMillis; //current time
unsigned long period = 1000;  //initial light time - 1 second for power on test
String lightState; //lights are on or off?
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  pinMode(ledPin, OUTPUT); //connected to wireless doorbell transmitter
  pinMode(buttonPin, INPUT); //connected to IR beam sensor output
  pinMode(2, OUTPUT); //connected to "Range active" sign
  pinMode(3, OUTPUT); //connected to warning beeper
  pinMode(4, OUTPUT); //connected to external building light (intended to trigger from cycle count)
  //pinMode(5, OUTPUT);
  digitalWrite(2, HIGH); //turn all relays off (High = off, Low = on)
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(1, HIGH);
  currentMillis = millis();
  startMillis = millis();  //initial start time
}

void loop() {
  // read the state of the IR beam relay
  buttonState = digitalRead(buttonPin);

  // check if the IR beam is broken. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    //Trip the doorbell button for 250 miliseconds
    digitalWrite(2, LOW);
    delay(250);
    digitalWrite(2, HIGH);
    //wait two seconds
    delay(2000);
    //turn on a light relay
    digitalWrite(4, LOW);
    //flash "Range active" sign
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);            
    //end flashing sign.
    //turn off light relay
    digitalWrite(4, HIGH);
    lightState = "lightsoff";
    
  } else {
    // make sure doorbell transmitter is not continuously triggered
    digitalWrite(ledPin, HIGH);
    currentMillis = millis();  //get the current "time"
    if (currentMillis - startMillis >= period)
    {
      if (lightState == "lightsoff") 
      {
        digitalWrite(4, LOW);
        lightState = "lightson";
        period = random(1000,5000);
      }
      else
      {
        digitalWrite(4, HIGH);
        lightState = "lightsoff";
      }
      startMillis = millis();  //reset to initial start time
    }
  }
}
String lightState; //lights are on or off?

Its best to avoid using the String class with Arduino with small amounts of ram memory. It can cause memory overflow problems. In your case, you should not be using a string to hold a simple true/false status. That's tremendously wasteful. Just use Bool.

Bool lightsOn = false;
...
if (lightsOn) {
  ...

I have future plans for that string.

However suggestion noted.

So how's this look then?

/*
/*
Doorbell beam code
Attached to long range IR perimiter beam relay.
Designed to trigger wireless doorbell immediately on pin 2
Then trigger external lights and sound with delay,
to disguise the physical position and reaction time of the beam.
*/

const int buttonPin = 0;     // the number of the pushbutton pin
const int ledPin =  1;      // the number of the LED pin
unsigned long startMillis; //start time
unsigned long currentMillis; //current time
unsigned long period = 120000;  //initial light time
bool lightState; //lights are on or off?
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  pinMode(ledPin, OUTPUT); //connected to wireless doorbell transmitter
  pinMode(buttonPin, INPUT); //connected to IR beam sensor output
  pinMode(2, OUTPUT); //connected to "Range active" sign
  pinMode(3, OUTPUT); //connected to warning beeper
  pinMode(4, OUTPUT); //connected to external building light (intended to trigger from cycle count)
  //pinMode(5, OUTPUT);
  digitalWrite(2, HIGH); //turn all relays off (High = off, Low = on)
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(1, HIGH);
  currentMillis = millis();
  startMillis = millis();  //initial start time
}

void loop() {
  // read the state of the IR beam relay
  buttonState = digitalRead(buttonPin);

  // check if the IR beam is broken. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    //Trip the doorbell button for 250 miliseconds
    digitalWrite(2, LOW);
    delay(250);
    digitalWrite(2, HIGH);
    //wait two seconds
    delay(2000);
    //turn on a light relay
    digitalWrite(4, LOW);
    //flash "Range active" sign
    digitalWrite(3, HIGH);
    digitalWrite(1, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    digitalWrite(1, LOW);;
    delay(250);
    digitalWrite(3, HIGH);
    digitalWrite(1, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    digitalWrite(1, LOW);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);
    digitalWrite(3, LOW);
    delay(250);
    digitalWrite(3, HIGH);
    delay(250);            
    //end flashing sign.
    //turn off light relay
    digitalWrite(4, HIGH);
    lightState = false;
    
  } else {
    // make sure doorbell transmitter is not continuously triggered
    digitalWrite(ledPin, HIGH);
    currentMillis = millis();  //get the current "time"
    if (currentMillis - startMillis >= period)
    {
      if (lightState == false) 
      {
        digitalWrite(4, LOW);
        lightState = true;
        period = random(900000,1800000);
      }
      else
      {
        digitalWrite(4, HIGH);
        lightState = false;
      }
      startMillis = millis();  //reset to initial start time
    }
  }
}

You can just say

if (!lightState)

You should give these pins numbers as named constants, like you did with the button & led.

  pinMode(2, OUTPUT); //connected to "Range active" sign
  pinMode(3, OUTPUT); //connected to warning beeper
  pinMode(4, OUTPUT); //connected to external building light

These are just readability tips, I can't see any other errors at the moment.