Hello World,
I modified the blink without delay sketch in an effort to blink 4 different LED in a cycle for 2 seconds each. I am using the modulo operator with millis() function to achieve this. ultimately I want to be able to use this to display sensor readings on an OLED screen without freezing the program up with delays. The problem I am running into is this code doesnt seem to work. I am using the tinkercad simulator and it just lights up one of the LED's without cycling.
https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
*/
// constants won't change. Used here to set a pin number:
const int ledPin1 = 13;// the number of the LED pin
const int ledPin2 = 12;
const int ledPin3 = 11;
const int ledPin4 = 10;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change:
const long interval = 8000; // interval for modula operator
unsigned long currentMillis;
int remainder = 0;
void setup() {
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
pinMode(ledPin4, OUTPUT);
}
void loop() {
currentMillis = millis();
remainder = currentMillis % interval;
if (remainder == 0) {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin4, LOW);}
else if (remainder == 2){
digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin1, LOW);}
else if (remainder == 4){
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin2, LOW);}
else if (remainder == 6){
digitalWrite(ledPin4, HIGH);
digitalWrite(ledPin3, LOW);}
}
So I added the serial.print to see what the remainder value was and it is cycling through 1-8000. So I just fixed the code to run off 2000, 4000, 6000, and 0. There is still and issue though, the first LED only lights up.
// https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
// constants won't change. Used here to set a pin number:
const int ledPin1 = 13;// the number of the LED pin
const int ledPin2 = 12;
const int ledPin3 = 11;
const int ledPin4 = 10;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change:
const long interval = 8000; // interval for modula operator
unsigned long currentMillis;
int remainder = 0;
void setup() {
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
pinMode(ledPin4, OUTPUT);
Serial.begin(38400);
}
void loop() {
currentMillis = millis();
remainder = currentMillis % interval;
Serial.print("remainder");
Serial.println(remainder);
if (remainder >= 0) {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin4, LOW);}
else if (remainder >= 2000){
digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin1, LOW);}
else if (remainder >= 4000){
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin2, LOW);}
else(remainder >= 6000){
digitalWrite(ledPin4, HIGH);
digitalWrite(ledPin3, LOW);}
}
Ok here is the working code, in case anyone has the same issue..
// https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
// constants won't change. Used here to set a pin number:
const int ledPin1 = 2;// the number of the LED pin
const int ledPin2 = 3;
const int ledPin3 = 4;
const int ledPin4 = 5;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change:
const long interval = 8000; // interval for modula operator
unsigned long currentMillis;
int remainder = 0;
void setup() {
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
pinMode(ledPin4, OUTPUT);
Serial.begin(38400);
}
void loop() {
currentMillis = millis();
remainder = currentMillis % interval;
Serial.print("remainder");
Serial.println(remainder);
if (remainder >= 0 && remainder <2000) {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin4, LOW);}
else if (remainder >= 2000 && remainder <4000){
digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin1, LOW);}
else if (remainder >= 4000 && remainder <6000){
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin2, LOW);}
else {
digitalWrite(ledPin4, HIGH);
digitalWrite(ledPin3, LOW);}
}
Horribly inefficient. Running the fairly CPU intensive '%' operation and all the digital writes every time through loop(). You only have actionable requirements every few seconds, so code it that way.
const int ledPin1 = 2;// the number of the LED pin
const int ledPin2 = 3;
const int ledPin3 = 4;
const int ledPin4 = 5;
const long interval = 2000; // interval for LED change
unsigned long currentMillis;
int ledCounter;
void setup() {
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
pinMode(ledPin4, OUTPUT);
Serial.begin(38400);
}
void loop() {
currentMillis = millis();
if (currentMillis - lastMillis >= interval)
{
currentMillis += interval;
Serial.print("ledCounter");
Serial.println(ledCounter);
switch (ledCounter) {
case 0:
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin4, LOW);
break;
case 1:
digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin1, LOW);
break;
case 2:
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin2, LOW);
break;
case 3:
digitalWrite(ledPin4, HIGH);
digitalWrite(ledPin3, LOW);
}
ledCounter++;
if (ledCounter >= 4) ledCounter = 0;
}
}
Also, every 4.3(?) days, the modulo method will "hiccup" with a bad timing if the interval is not a perfect power of two.
It's a valid perspective that is only true for a single sketch seen in isolation. Yes, both sketches use all the available cycles, and effectively "throw away" the cycles that they don't need (in idle time). But if you consider the code as a functional module that you intend to use with additional code, the idle time cycles aren't gratuitous any more, instead they are a shared resource. The more idle time is provided by a module, the more CPU time will be available to additional modules, and it might affect either or both, latency and throughput.
It is hard to predict in advance, what combinations of code modules might be needed for different projects. So it is much easier to just build in whatever efficiency you can, so long as it is relatively painless (easy to code) and doesn't compromise performance.
I'm not sure this code works, lastMillis isnt defined and the currentMillis += interval would just increase the currentMillis so it would never satisfy the if statement.
I left it as an exercise to the student to find the mistakes.
Two fixes:
define lastMillis
change currentMillis =+ interval to lastMillis += interval
Now are you happy?
Also it's possible to replace the '%' operation with a cumulative update that preserves the 'remainder' approach and also fixes the rollover issue, but uses the same code:
// https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
// constants won't change. Used here to set a pin number:
const int ledPin1 = 2;// the number of the LED pin
const int ledPin2 = 3;
const int ledPin3 = 4;
const int ledPin4 = 5;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
// constants won't change:
const int interval = 8000; // interval for (simulated) modula operator
unsigned long currentMillis;
unsigned long lastMillis;
int remainder = 0;
void setup() {
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
pinMode(ledPin4, OUTPUT);
Serial.begin(38400);
}
void loop() {
currentMillis = millis();
// replace this computationally expensive operation
// and make it rollover safe:
// remainder = currentMillis % interval;
remainder = currentMillis - lastMillis;
if (remainder >= interval)
{
lastMillis += interval;
}
Serial.print("remainder");
Serial.println(remainder);
if (remainder >= 0 && remainder < 2000) {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin4, LOW);
}
else if (remainder >= 2000 && remainder < 4000) {
digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin1, LOW);
}
else if (remainder >= 4000 && remainder < 6000) {
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin2, LOW);
}
else {
digitalWrite(ledPin4, HIGH);
digitalWrite(ledPin3, LOW);
}
}