Converting code written with delay to millis() instead - c++

I have a basic code I wrote on Arduino, however, I need to change the delay to Millis instead.
Whatever I do I can't get it to work, it's always getting stuck at a red light and won't ever turn green.
I'm posting the original delay code as code I wrote using Millis seems useless and may confuse what I'm trying to do.
const int redPin = 2;
const int yellowPin = 3;
const int greenPin = 4;
int redDuration = 10000;
int greenDuration = 5000;
void setup() {
pinMode(redPin, OUTPUT);
pinMode(yellowPin, OUTPUT);
pinMode(greenPin, OUTPUT);
}
void loop() {
setTrafficLight(1,0,0);
delay(redDuration);
setTrafficLight(1,1,0);
delay(2000);
setTrafficLight(0,0,1);
delay(greenDuration);
setTrafficLight(0,1,0);
delay(2000);
}
void setTrafficLight(int redState, int yellowState, int greenState) {
digitalWrite(redPin, redState);
digitalWrite(yellowPin, yellowState);
digitalWrite(greenPin, greenState);
}

Save the time when it started waiting.
If the difference of current time and the start time become the time to wait, proceed to the next status.
const int redPin = 2;
const int yellowPin = 3;
const int greenPin = 4;
int redDuration = 10000;
int greenDuration = 5000;
unsigned long startTime;
int status = 0; // using enum may be better
void setup() {
pinMode(redPin, OUTPUT);
pinMode(yellowPin, OUTPUT);
pinMode(greenPin, OUTPUT);
startTime = millis();
status = 0;
}
void loop() {
unsigned long currentTime = millis();
switch (status) {
case 0: // initial state
setTrafficLight(1, 0, 0);
status = 1;
break;
case 1: // waiting instead of delay(redDuration)
if (currentTime - startTime >= redDuration) {
setTrafficLight(1, 1, 0);
startTime = currentTime;
status = 2;
}
break;
case 2: // waiting instead of first delay(2000)
if (currentTime - startTime >= 2000) {
setTrafficLight(0, 0, 1);
startTime = currentTime;
status = 3;
}
break;
case 3: // waiting instead if delay(greenDuration)
if (currentTime - startTime >= greenDuration) {
setTrafficLight(0, 1, 0);
startTime = currentTime;
status = 4;
}
break;
case 4: // waiting instead of second delay(2000)
if (currentTime - startTime >= 2000) {
startTime = currentTime;
status = 0;
}
break;
default: // for in-case safety
status = 0;
break;
}
}
void setTrafficLight(int redState, int yellowState, int greenState) {
digitalWrite(redPin, redState);
digitalWrite(yellowPin, yellowState);
digitalWrite(greenPin, greenState);
}

Related

Fade leds with serial communication Arduino

I am getting serial communication and trying to make an led fade effect ,
This is my function for leds which is facing latency issues , obviously the for loop . Can anyone suggest a better logic or solution to approach this without getting latency in leds?
void loop() {
// serial communication
while(Serial.available() > 0 ){
int inByte = Serial.read();
Serial.print(inByte);
if (inByte > 0) {
// if byte is 255, then the next 3 bytes will be new rgb value
if (inByte == 255) {
setColors = true;
colorSetCounter = 0;
} else if (setColors) {
switch (colorSetCounter) {
case 0:
redVal = inByte;
break;
case 1:
greenVal = inByte;
break;
case 2:
blueVal = inByte;
setColors = false;
receiveNotes = true;
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
break;
}
colorSetCounter++;
} else if (receiveNotes) {
controlLeds(inByte);
}
}
}
}
void controlLeds (int note) {
note -= 1;
if (!leds[note]) {
leds[note].red = redVal;
leds[note].green = greenVal;
leds[note].blue = blueVal;
}
else {
for(int i =0; i<=255; i++){
leds[note].fadeToBlackBy(i);
FastLED.show();
if(!leds[note]){
break;
}
}
}
FastLED.show();
}
You need to write non-blocking code as Scheff mentioned. Instead of a global variable, you can use a static variable in a function. It remembers its value for each call of that function.
Here is an example how you could do it, using millis(). My code fades an LED on and off if some serialEvent happens and it does not block the rest of the code:
const int ledPin = 13;
int setValue = 0;
unsigned long lastTime = 0;
const unsigned long refreshTime = 200;
char buffer1[8];
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
if (millis() - lastTime > refreshTime)
{
lastTime = millis();
fadeLED(setValue);
}
}
void fadeLED(int fadeValue)
{
static int currentValue = 0;
if (fadeValue > currentValue) {
currentValue++;
}
if (fadeValue < currentValue) {
currentValue--;
}
analogWrite(ledPin, currentValue);
}
void serialEvent()
{ Serial.readBytesUntil('\n', buffer1, 8);
switch (setValue)
{
case 0:
setValue = 255;
break;
case 255:
setValue = 0;
break;
default:
break;
}
}

Ardunio UNO solve multiple overlapping timer loops

I'm stuck in writing two overlapping loops for switching a pump relay. If the timer "delayPump" ends (LOW) the timer "runnningPump" (HIGH) should start.
i guess some math madness, to be honest this loop is already making me mad, cause it should be easy!!!!
Any clue??
#define pumpSwitch_1 8
int delayPump = 10000; //delay time
int runnningPump = 5000; // running Timer
bool pumpState = LOW;
unsigned long TimerPump = 0;
unsigned long TimerDelay = 0;
void setup() {
pinMode(pumpSwitch_1, OUTPUT);
digitalWrite(pumpSwitch_1, LOW);
TimerPump = millis();
TimerDelay = millis();
}
void loop() {
digitalWrite(pumpSwitch_1, pumpState);
if (pumpState == HIGH){
if((millis() - TimerPump) >= runnningPump){
pumpState = LOW;
TimerPump = millis() + delayPump;
}
}else {
if((millis() - TimerDelay) >= delayPump){
pumpState = HIGH;
TimerDelay = millis() + runnningPump;
}
}
}
I think this is what you need:
You just need a single Timer variable, and each time you toggle the state, set it to current time, i.e. millis().
#define pumpSwitch_1 8
int delayPump = 10000; //delay time
int runnningPump = 5000; // running Timer
bool pumpState = LOW;
unsigned long Timer = 0;
void setup() {
pinMode(pumpSwitch_1, OUTPUT);
digitalWrite(pumpSwitch_1, LOW);
Timer = millis();
}
void loop() {
digitalWrite(pumpSwitch_1, pumpState);
if (pumpState == HIGH) {
if ((millis() - Timer) >= runnningPump) {
pumpState = LOW;
Timer = millis();
}
} else {
if ((millis() - Timer) >= delayPump) {
pumpState = HIGH;
Timer = millis();
}
}
}

Arduino LCD Display showing jumbled letters

When switching between states, the lines get jumbled and the characters get mixed up. Nothing I've seen online helps and example code in the library works just fine. The main issue I think comes from when the LCD is wiped clean, but then I don't know where it should be wiped. I've moved it from loop() to the cases multiple times, and delays don't help.
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
#include "RTClib.h"
RTC_DS1307 rtc;
const int hourButton = 2; // Interrupt Pin 0 -- TOP
const int minuteButton = 3; // Interrupt Pin 1 -- 2nd
const int displayStateButton = 18; // Interrupt Pin 5 -- 3rd
const int alarmButton = 19; // Interrupt Pin 4 -- BOTTOM
int buttonState = LOW;
int redPin = 4;
int greenPin = 5; // RGB LED Pins
int bluePin = 6;
int alarmPin = 13; // Alarm Pin
enum DeviceDisplayState {CLOCK, ALARM, DATE, YEAR}; // All different states
DeviceDisplayState displayState = CLOCK; // Initially in Clock State
#ifdef DEBOUNCE
long lastDebounceTime = 0;
long debounceDelay = 60;
#endif
void setup() {
lcd.begin(16, 2);
Serial.begin(57600);
// Set the time:: //
const int hourInit = 1;
const int minuteInit = 2;
const int secondInit = 1;
const int dayInit = 3;
const int monthInit = 4;
const int yearInit = 2020;
rtc.adjust(DateTime(yearInit, monthInit, dayInit, hourInit , minuteInit, secondInit));
pinMode(hourButton, INPUT_PULLUP);
pinMode(minuteButton, INPUT_PULLUP);
pinMode(displayStateButton, INPUT_PULLUP);
attachInterrupt(0, increaseHour, FALLING);
attachInterrupt(1, increaseMinute, FALLING);
attachInterrupt(5, SwitchToNextDisplayState, FALLING);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(alarmPin, OUTPUT);
SwitchToClockState();
};
void RGB_color(int red_light_value, int green_light_value, int blue_light_value)
{
analogWrite(redPin, red_light_value);
analogWrite(greenPin, green_light_value);
analogWrite(bluePin, blue_light_value);
}
void increaseHour()
{
DateTime dt = rtc.now();
Serial.print("Previous Time: " + dt.hour());
if (dt.hour() < 23)
{
TimeSpan ts(3600);
dt = dt + ts;
}
else // do not roll over the day by upping the hour, go back to zero hours
{
TimeSpan ts(3600 * 23);
dt = dt - ts;
}
Serial.print("Changed Time: " + dt.hour());
Serial.println();
rtc.adjust(dt);
}
void increaseMinute()
{
DateTime dt = rtc.now();
if (dt.minute() < 59)
{
TimeSpan ts(60);
dt = dt + ts;
}
else // Don't roll over the minutes into the hours
{
TimeSpan ts(60 * 59);
dt = dt - ts;
}
rtc.adjust(dt);
}
void SwitchToClockState()
{
displayState = CLOCK;
RGB_color(255, 0, 0);
}
void SwitchToAlarmState()
{
displayState = ALARM;
RGB_color(255, 125, 0);
}
void SwitchToDateState()
{
displayState = DATE;
RGB_color(0, 255, 0);
}
void SwitchToYearState()
{
displayState = YEAR;
RGB_color(0, 0, 255);
}
void SwitchToNextDisplayState()
{
switch (displayState) {
case CLOCK:
SwitchToAlarmState();
Serial.print("Switching to Alarm State...");
Serial.println();
lcd.clear();
break;
case ALARM:
SwitchToDateState();
Serial.print("Switching to Date State...");
Serial.println();
lcd.clear();
break;
case DATE:
SwitchToYearState();
Serial.print("Switching to Year State...");
Serial.println();
lcd.clear();
break;
case YEAR:
SwitchToClockState();
Serial.print("Switching to Clock State...");
Serial.println();
lcd.clear();
break;
default:
// assert()
digitalWrite(redPin, LOW);
digitalWrite(greenPin, LOW);
digitalWrite(bluePin, LOW);
break;
}
}
String WithLeadingZeros(int number)
{
if (number < 10)
{
return "0" + String(number);
}
else
{
return String(number);
}
}
void loop() {
DateTime now = rtc.now();
int yearInt = now.year();
int monthInt = now.month();
int dayInt = now.day();
int hourInt = now.hour();
int minuteInt = now.minute();
int secondInt = now.second();
switch (displayState) {
case CLOCK:
lcd.print("Robot Slave");
lcd.setCursor(0, 1);
lcd.print("Time> " + WithLeadingZeros(now.hour()) + ":" + WithLeadingZeros(now.minute()) + ":" + WithLeadingZeros(now.second()));
break;
case ALARM:
lcd.print("Robot Slave");
case DATE:
lcd.print("Robot Slave");
lcd.setCursor(0, 1);
lcd.print("Date> " + WithLeadingZeros(now.month()) + " - " + WithLeadingZeros(now.day()));
break;
//case YEAR:
lcd.print("Robot Slave");
lcd.setCursor(0, 1);
lcd.print("Year> " + String(now.year()));
break;
}
}
You're creating nonsense instructions for your LCD if you execute commands in an ISR while already executing instructions in your normal program.
Let's say the serial command to write letter A is "WRITEA" and the command for clearing the display is "CLEAR".
Now while sending the letter A to your display you push the button, your display will receive something like "WRCLEARTEB" which it cannot make sense of. Or maybe it receives "WRITECLEARA" and it will write C instead of A.
Please note that this is just to give you an idea what is going on. Of course the data sent to the display is different.
But you're creating a mess by interleaving commands.
Update your display in a loop and use ISRs only to update variables that are then used in the next frame. Clocks with second precision are usually updated once per second.

Arduino (C++) sketc periodically freezes or resets: suspected overflow

I'm having trouble identifying the cause of a recurrent issue with some arduino code. The code below reads two temperature sensors, sends the result to a PID library, and uses the output to control some relays on a fridge (adding accurate temperature control to a fridge, basically).
The code freezes or the Arduino resets periodically. This happens periodically, but the period changes - it freezes every minimum 30 minutes, maximum about 30 hours.
I suspect that there's an overflow or that I'm writing beyond the range of an array, but I can't find the issue. It's very unlikely that there's a power issue - the arduino is on a 10A 12v supply with a dedicated 5v regulator, so I doubt it.
I'm fairly new to all this and would be very grateful for any pointers or advice - even some tips on how to troubleshoot this unpredictable error would be very appreciated!
Here's the code:
Setup and main loop, also checks an analog input for the set temperature:
// Call libraries for display, sensor, I2C, and memory. Library setup included as well.
#include <avr/pgmspace.h>
char buffer[20];
#include <Time.h>
#include <TimeLib.h>
#include <OneWire.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f,20,4);
#include <DallasTemperature.h>
#include <PID_v1.h>
// Special Characters for the display and display animations
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args) write(args);
#else
#define printByte(args) print(args,BYTE);
#endif
#define ONE_WIRE_BUS 5 //DS18S20 Signal pin on digital 2
uint8_t heart[8] = { 0x0,0xa,0x1f,0x1f,0xe,0x4,0x0};
uint8_t deg[8] = { 0x1c,0x14,0x1c,0x0,0x3,0x4,0x4,0x3};
uint8_t Pv[8] = { 0x1c,0x14,0x1c,0x10,0x10,0x5,0x5,0x2};
uint8_t Sv[8] = { 0xc,0x10,0x8,0x4,0x18,0x5,0x5,0x2};
// end special chars
//************* Begin Variables Setup ***************//
//Sensors (ds18s20 needs additional chatter)
byte addr1[8]= {0x28, 0x3F, 0xB5, 0x3C, 0x05, 0x00, 0x00, 0x25};
byte addr2[8]= {0x28, 0xC7, 0xCD, 0x4C, 0x05, 0x00, 0x00, 0x0D};
byte data1[12];
byte data2[12];
byte MSB = 0;
byte LSB = 0;
float tempRead = 0;
float TemperatureSum = 0;
OneWire ds(ONE_WIRE_BUS);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
//controller outputs
int ControlCpin = 6; // control to fridge
int ControlTpin = 8; // control to temperature/heater (get aquarium heater)
int ControlLpin = 7; // control to light
int ControlApin = 9; // control to airflow
//operational vars (the button)
//int buttonPushCounter = 0; // counter for the number of button presses DEPRACATED
int buttonState = 0; // current state of the button
int lastButtonState = 0; // previous state of the button
boolean buttonstate = false; // calculastate of the button (includes timer delay. use this in menus)
int buttontime = 0; // press length measure
int buttontimeon = 0; // necessary for press length measure
//operational vars (sensors and timing)
unsigned int sensorInterval = 20000; // time between readings
unsigned long int sensorTime = 0; // current time
unsigned long int sensorTime2 = 0; // time of last sensor reading
// fans, lights, and timers
unsigned long int fanONmillis = 0;
unsigned long int fanOFFmillis = 0;
byte fanON = 0;
byte fanOFF = 0;
boolean fanstate = false;
unsigned long int Time = 0;
unsigned long int TimeAdjust = 0;
unsigned long int LightON = 0;
unsigned long int LightOFF = 0;
unsigned int Hours = 0;
unsigned int Minutes = 0;
unsigned int Days = 0;
byte daysAdj = 0; //not implemented yet
float tempDiff = 0;
//key var storage
float PvH = 0;
double PvT = 0;
float SvH = 0;
double SvT = 12;
float SvTdisplay = 5.5;
float SvTdisplayOld = 5.5;
float Temp1; //Current readings
float Temp2; //Current readings
float Temp3; //Current readings
// Fridge limits
unsigned int safetyRest = 5; // off this long every hour (minimum) to let the compressor rest in minutes
int minCool = 10; // minimum cooling period in minutes
int coolStart = 0;
byte coolON = 0; // PID attached to this
// Heat limits
byte heatON = 0; // PID attached to this
//cool
double Kp = 0.5;
double Ki = 0.5;
double Kd = 0.5;
double Output;
PID coolPID(&PvT, &Output, &SvT ,Kp,Ki,Kd, REVERSE);
unsigned coolWindowSize = 600; // minutes*10
unsigned long coolWindowStartTime;
unsigned long coolOffElapsed = 0;
long unsigned PIDpos = 0;
unsigned long Outputx = 0;
unsigned long PIDposx = 0;
unsigned long safetyRestx = 0;
// ensure setpoint, input, and outpit are defined
//************* End Variables Setup ***************//
void setup(){
//Sensor start
sensors.begin();
//Pin declarations
pinMode(ControlTpin, OUTPUT); //set outputs
pinMode(ControlLpin, OUTPUT);
pinMode(ControlApin, OUTPUT);
pinMode(ControlCpin, OUTPUT);
digitalWrite(ControlTpin, HIGH); // write outputs HIGH (off in this case) FIRST to prevent startup jitters.
digitalWrite(ControlLpin, HIGH);
digitalWrite(ControlApin, HIGH);
digitalWrite(ControlCpin, HIGH);
//LCD and special chars
Serial.begin(9600);
lcd.begin();
lcd.backlight();
lcd.createChar(0, heart);
lcd.createChar(1, deg);
lcd.createChar(2, Pv);
lcd.createChar(3, Sv);
lcd.clear();
LoadScreen();
HomeSetup();
//PID setup
coolPID.SetOutputLimits(0, coolWindowSize);
coolPID.SetMode(AUTOMATIC);
coolOffElapsed = millis();
}
void loop(){
//if interval has passed, check the sensors, update the triggers, and update the screen
if (millis() - sensorTime2 > sensorInterval){
sensorTime2 = millis();
SensorCheck();
Triggers();
HomeSetup ();
}
SvTdisplay = (float)analogRead(A0);
SvTdisplay = SvTdisplay/40+5;
if(abs(SvTdisplay-SvTdisplayOld) > 0.2){
SvTdisplayOld = SvTdisplay;
lcd.setCursor(2,0);
lcd.print(SvTdisplayOld,1); //svt
lcd.printByte(1);
lcd.print(" ");
SvT = analogRead(A0)/4+50;
}
PIDpos = ((millis()/60000) % (coolWindowSize/10));
}
The following codes a loading screen and updates the screen with current values:
void LoadScreen (){
lcd.clear();
lcd.home();
lcd.setCursor(0,0);
lcd.print(" LaggerLogger ");
lcd.printByte(0);
lcd.setCursor(0,1);
lcd.print(" V2.0 Beepboop!");
delay(3000);
lcd.clear();
}
//write the home screen to the LCD with current data
void HomeSetup(){
lcd.setCursor(0,0);
lcd.printByte(3);
lcd.print(" ");
lcd.print(SvTdisplayOld,1); //svt
lcd.printByte(1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.printByte(2);
lcd.print(" ");
lcd.print(PvT/10,1); //pvt
lcd.printByte(1);
lcd.print(" ");
lcd.setCursor(8,1);
lcd.print(day()-1);
lcd.print("/");
lcd.print(hour());
lcd.print(":");
lcd.print(minute());
lcd.print(" ");
lcd.setCursor(8,0);
lcd.print(Output/10,1);
lcd.print("m/h ");
}
The following checks output values and 'triggers' the relay if its appropriate to do so:
void Triggers () {
coolPID.Compute();
// Check PID
if ((Output/10) > (coolWindowSize/10-PIDpos) && PIDpos > safetyRest ) { //
coolON = 1;
coolStart = millis();
}
else if ((millis() - coolStart) > (minCool * 60000)){
coolON = 0;
}
else {}
// Write to temp relay pins
if (coolON == 1) {
digitalWrite(ControlCpin, LOW);
}
else {
digitalWrite(ControlCpin, HIGH);
}
// Control fans
if (coolON == 1 || heatON == 1 || tempDiff > 1) {
fanOFFmillis = millis();
fanONmillis = millis();
fanstate = true;
digitalWrite(ControlApin, LOW);
}
else {
fanstate = false;
digitalWrite(ControlApin, HIGH);
}
}
The following checks the temperature sensors and does some clock calculations:
void SensorCheck(){
Temp1 = getTemp1();
Temp2 = getTemp2();
//average readings and note the difference
if(Temp1 > 0 && Temp2 >0) {
PvT = (Temp1 + Temp2) / .2;
tempDiff = abs(Temp1 - Temp2);
}
//... unless there's only one thermometer...
else if (Temp1 > 0){
PvT = Temp1*10;
tempDiff = 0;
}
else {
PvT = 999;
tempDiff = 0;
}
//clock update
Time = millis() + TimeAdjust;
Hours = hour();
Minutes = minute();
Days = day();
}
float getTemp1(){
sensors.requestTemperatures();
float z = sensors.getTempCByIndex(0);
return z;
}
float getTemp2(){
sensors.requestTemperatures();
float z = sensors.getTempCByIndex(1);
return z;
}
The problem was that an integer (declared as int coolStart) was later updated to hold the value of millis().
Millis() can be much larger than the 16 bits available to ints - creating an overflow.
Changing the variable declaration to 'unsigned long coolStart = 0;' appears to have solved the problem.
Thanks to everyone for the troubleshooting advice.

Simple SDL_GetTimer() usage - why isn't it working?

I've tried to use SDL_GetTimer() to make action after passing 1000ms using this piece of code:
while(1)
{
int tajmer;
int czas = SDL_GetTicks();
tajmer = SDL_GetTicks() - czas;
if(tajmer > 1000)
{
MoveUp();
czas = SDL_GetTicks();
}
}
But it causes my program to crash. Any ideas why, or how to implement simple timer correctly?
Every time the loop runs, czas is updated to the current time.
Solution: Move it out of the loop.
int czas = SDL_GetTicks();
while(1)
{
int tajmer;
tajmer = SDL_GetTicks() - czas;
if(tajmer > 1000)
{
MoveUp();
czas = SDL_GetTicks();
}
}
However what you are trying to accomplish could possibly be done in a better way using built in timers:
http://wiki.libsdl.org/SDL_AddTimer
Edit:
Example using SDL_AddTimer.
Uint32 my_callbackfunc(Uint32 interval, void *param);
int main() {
... // don't forget to SDL_Init
...
SDL_AddTimer(1000, my_callbackfunc, NULL);
...
}
Uint32 my_callbackfunc(Uint32 interval, void *param)
{
MoveUp();
return(1000); // or however long to wait before my_callbackfunc should run.
}
If you are currently using classes and want to call a class's method called MoveUp() then perhaps:
class example {
...
void start_moving() {
SDL_AddTimer(1000, my_callbackfunc, (void*)this);
}
...
public void MoveUp() {
...
}
}
Uint32 my_callbackfunc(Uint32 interval, void *param) {
((example*)param)->MoveUp();
return (1000);
}
Continuing on Yujin Wus answer you can also do something like this.
int timer = 0;
int ticks = SDL_GetTicks();
while(true)
{
timer += SDL_GetTicks() - ticks;
ticks = SDL_GetTicks();
if(timer >= 1000)
{
timer -= 1000;
MoveUp();
}
}
Or something like this
const int DELAY = 1000;
int timer = SDL_GetTicks() + DELAY;
while(true)
{
if(timer - SDL_GetTicks() <= 0)
{
timer += DELAY;
MoveUp();
}
}