Arduino LCD Display showing jumbled letters - c++

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.

Related

Converting code written with delay to millis() instead

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);
}

Connecting an Arduino ESP32 microcontroller to javafx via PacketSender

We have built a drone controller using C++ and an Arduino ESP32 module. We have achieved connection to a Tello Drone and can control it successfully. However, now we want to connect our controller to a javafx program that receives input and then draws on a canvas in scene builder.
Meanwhile the problem is that we can't seem to connect our controller through PacketSender. We have attached our code, including the previous built that enabled connection with a drone.
The big question is how to send the messages between two programs and initiate our virtual depiction of our drone on a canvas - instead of the actual physical one.
The issue seems to be in case 1, line 190, where we connected controller to actual drone, but now have to write up a new connection somehow.
#include <Arduino.h>
#include "WiFi.h"
#include "AsyncUDP.h"
const char * ssid = "OnePlus 7 Pro";
const char * password = "hej12345";
//Connect button variables (Command)
int inPinLand = 18;
int valLand = 0;
//Ultra sonic sensor variables
#define trigPin 2
#define echoPin 21
//Land button variables (Land)
int inPin = 25;
int val = 0;
//Instantiate specific drone
const char * networkName = "TELLO-59F484";
const char * networkPswd = "";
//IP address to send UDP data to:
// either use the ip address of the server or
// a network broadcast address
const char * udpAddress = "10.60.0.227";
const int udpPort = 7000;
boolean connected = false;
char fromTello[256];
unsigned long timer;
//static const byte glyph[] = { B00010000, B00110100, B00110000, B00110100, B00010000 };
//static PCD8544 lcd;
uint8_t state = 0;
//Controller movement variables
int pitch = 0;
int roll = 0;
int yaw = 0;
int throttle = 0;
char cmd[256];
AsyncUDP udp;
void setup() {
Serial.begin(9600);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed");
while (1) {
delay(1000);
}
}
pinMode(5, OUTPUT);
digitalWrite(5, HIGH);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(inPin, INPUT);
pinMode(inPinLand, INPUT);
//LCD screen initialization
//lcd.begin(84, 48);
Serial.begin(9600);
//pinMode(trigPin, OUTPUT);
//pinMode(echoPin, INPUT);
//pinMode(BL, OUTPUT);
//digitalWrite(BL, HIGH);
if (udp.listen(7000)) {
Serial.print("UDP Listening on IP: ");
Serial.println(WiFi.localIP());
udp.onPacket([](AsyncUDPPacket packet) {
Serial.print("UDP Packet Type: ");
Serial.print(packet.isBroadcast()
? "Broadcast"
: packet.isMulticast() ? "Multicast" : "Unicast");
Serial.print(", From: ");
Serial.print(packet.remoteIP());
Serial.print(":");
Serial.print(packet.remotePort());
Serial.print(", To: ");
Serial.print(packet.localIP());
Serial.print(":");
Serial.print(packet.localPort());
Serial.print(", Length: ");
Serial.print(packet.length());
Serial.print(", Data: ");
Serial.write(packet.data(), packet.length());
Serial.println();
// reply to the client/sender
packet.printf("Got %u bytes of data", packet.length());
});
}
// Send unicast
// udp.print("Hello Server!");
// udp.
}
//WiFi connection function
void connectToWiFi(const char * ssid, const char * pwd) {
Serial.println("Connecting to WiFi network: " + String(ssid));
// delete old config
WiFi.disconnect(true);
//Initiate connection
WiFi.begin(ssid, pwd);
Serial.println("Waiting for WIFI connection...");
}
//Drone connection function
void TelloCommand(char *cmd) {
//only send data when connected
if (connected) {
//Send a packet
//udp.beginPacket(udpAddress, udpPort); OUTDATED has new name with ASync
udp.printf(cmd);
//udp.endPacket(); OUTDATED has new name with ASync
Serial.printf("Send [%s] to Tello.\n", cmd);
}
}
void sendMessage(String msg){
udp.writeTo((const uint8_t *)msg.c_str(), msg.length(),
IPAddress(169, 254, 107, 16), 4000);
}
void loop() {
delay(5000);
// Send broadcast on port 4000
udp.broadcastTo("Anyone here?", 4000);
// Serial.println("waiting for udp message...");
int x = 100;
int y = 100;
sendMessage("init " + String(x) + " " + String(y));
}
void loop() {
long duration, distance;
val = digitalRead(inPin); // read input value
//Ultra sonic sensor
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration / 2) / 29.1;
//LCD screen line 2
// lcd.setCursor(0, 1);
// lcd.print(distance, DEC);
//State machine that connects the drone to WiFi and the controller
switch (state)
{
case 0: //Idle not connected
//LCD screen line 1
//lcd.setCursor(0, 0);
//lcd.print("Controller");
if (val == HIGH)
{
state = 1;
connectToWiFi(networkName, networkPswd);
timer = millis() + 5000;
}
break;
case 1: //Trying to connect
if (WiFi.status() == WL_CONNECTED)
{
Serial.print("Connected to: ");
Serial.println(networkName);
udp.begin(WiFi.localIP(), udpPort);
connected = true;
TelloCommand("command");
timer = millis() + 2000;
state = 2;
}
if (millis() > timer)
{
state = 0;
}
break;
case 2: //Connected on ground
//lcd.setCursor(0, 0);
//lcd.print("Connected ");
if (WiFi.status() != WL_CONNECTED)
{
WiFi.disconnect(true);
Serial.println("Disconnected");
state = 0;
}
if (distance < 10)
{
TelloCommand("takeoff");
timer = millis() + 1000;
state = 3;
Serial.println("takeoff");
}
break;
case 3: //In air
//lcd.setCursor(0, 0);
//lcd.print("In air ");
if (millis() > timer)
{
timer = millis() + 20;
pitch = map(analogRead(34) - 1890, -2000, 2000, -100, 100);
roll = map(analogRead(35) - 1910, -2000, 2000, -100, 100);
throttle = map(analogRead(33) - 1910, -2000, 2000, -100, 100);
yaw = map(analogRead(32) - 1910, -2000, 2000, -100, 100);
sprintf(cmd, "rc %d %d %d %d", roll, pitch, throttle, yaw);
TelloCommand(cmd);
}
if (val == HIGH) {
TelloCommand("land");
timer = millis() + 1000;
state = 0;
Serial.println("land");
}
break;
}
delay(200);
}

using arduino tone function on two pin at the same time

I'm trying to make an smart car with Arduino Mega, and I need to turn both of the back wheels on for an specific time sometimes. I've been told that I can set a "digital HIGH" time using tone, But as I need them to work in a same time, Is there a way to set tone for two pins in one line or something to do instead?
Thanks for your help.
#include <Servo.h>
/////////////////////
Servo servo;
/////////////////////
int trig = 12;
int echo = 13;
long duration;
int distance;
int dist_right;
int dist_left;
int ang = 90;
unsigned int value = 255;
unsigned long tone_time = 3000;
float forward_time;
/////////////////////
int ena = 35;
int in1 = 7;
int in2 = 6;
int in3 = 5;
int in4 = 4;
int enb = 47;
/////////////////////
void setup()
{
Serial.begin(9600);
servo.attach(22);
pinMode(trig, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
pinMode(ena, OUTPUT);
pinMode(enb, OUTPUT);
pinMode(echo, INPUT);
digitalWrite(ena, HIGH);
digitalWrite(enb, HIGH);
}
void loop() {
servo.write(90);
distance = dist();
if(distance<=15)
{
for(ang;ang>=0;ang-=2)
{
servo.write(ang);
delay(30);
}
dist_right = dist();
Serial.println(dist_right);
for(ang;ang<=180;ang+=2)
{
servo.write(ang);
delay(30);
}
dist_left = dist();
Serial.println(dist_left);
for(ang;ang>=90;ang-=2)
{
servo.write(ang);
delay(30);
}
if(dist_right>=dist_left)
{
tone(in3, value, tone_time);
}
else if(dist_right<dist_left)
{
tone(in1, value, tone_time);
}
servo.write(90);
ang=90;
}
else{
forward_time=distance/25;
tone((in1,in3), value, forward_time);
}
}
int dist(){
digitalWrite(trig, LOW);
delayMicroseconds(2);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
duration = pulseIn(echo, HIGH);
distance= duration*0.034/2;
return distance;
}
I'm 99.99% sure, that your motors will not feel the time differencce if you turn them one by one. Try simplest case and you will see.
// Define your wheel control pins (use same as in your mega)
const int motor1Pin = 5;
const int motor1Pin = 6;
// somewhere in setup method
outputMode(motor1Pin, OUTPUT);
outputMode(motor2Pin, OUTPUT);
// Create function to turn motors and remember the time
unsigned long turnMotorsOn(int seconds) {
// turn motors ant return time when they should be stopped
return millis() + seconds * 1000;
}
// In you code check if it is time to turn off
if (millis() > timeWhenTurnMotorsOff) {
// turn them off
}

Running a timer code which gets stuck at zero

OK so firstly, I am VERY new to C++ as you will undoubtedly see from the code. I have been struggling with this to get it to where it is now so my apologies if it's not very good.
Ok here is what SHOULD happen.
Button 2 (downButton) is pressed to select the correct program (that works).
Button 3 (startButton) is used to start the timer for that program (that works)
The timer is supposed to count down (also working) and when it reaches zero, display a message and then go back to the start of the switch case.
This is what I am struggling with. Firstly the timer gets to zero and does nothing else. I have tried return, goto etc without any success.
I commented out the end message function, which does work BUT still gets stuck there without exiting the function.
What I am looking for is a little help to make the timer jump back to where it was called from.
Here is the code:
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int hours = 0; // start hours
int minutes = 00; //start min
int seconds = 05; //start seconds
const int buttonPin = 9; // the pin that the pushbutton is attached to
const int ledPin = A0; // the pin that the LED is attached to
const int ledPin2 = A1; // the pin that the LED is attached to
int buttonState = 0; // current state of the button
int WhichScreen = 0; // This variable stores the current Screen number
boolean hasChanged = true;
const int upButton = 8; // the number of the select pin
const int downButton = 10; // the number of the select pin
const int startButton = 9; // the number of the start pin
int selectState; // the current reading from the select pin
int lastSelectState = LOW; // the previous reading from the select pin
int backState; // the current reading from the select pin
int lastBackState = LOW; // the previous reading from the select pin
int startState; // the current reading from the start pin
int lastStartState = LOW; // the previous reading from the start pin
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
void setup()
{
Serial.begin(9600);
pinMode(buttonPin, INPUT); // initialize the button pin as a input
pinMode(ledPin, OUTPUT); // initialize the button pin as a output
pinMode(ledPin2, OUTPUT); // initialize the button pin as a output
lcd.begin(16, 2);
pinMode(upButton, INPUT);
pinMode(startButton, INPUT);
pinMode(downButton, INPUT);
pinMode(A0, OUTPUT); // LED 1
pinMode(A1, OUTPUT); // LED 2
pinMode(A2, OUTPUT); // LED 3
}
void loop()
{
start:
// Read the SELECT pin
if (WhichScreen == 1) {
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
digitalWrite(ledPin, buttonState);
delay(2000);
digitalWrite(ledPin, LOW);
}
}
if (WhichScreen == 4) {
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
// digitalWrite(ledPin2, buttonState);
// delay(5000);
// digitalWrite(ledPin2, LOW);
lcd.begin(16, 2);
minutes = 00; //start min
seconds = 05; //start seconds
digitalWrite(ledPin, buttonState);
lcd.print("P1-AMBER");
timer();
// Function SHOULD return to here when reaches zero
digitalWrite(ledPin, LOW);
minutes = 00; //start min
seconds = 05; //start seconds
lcd.print("P1-DARK RED");
timer();
}
}
if (hasChanged == true) {
switch (WhichScreen) {
case 0:
{
lcd.clear();
lcd.setCursor(2, 0); // Column, line
lcd.print("LIGHT THERAPY");
lcd.setCursor(2, 1);
lcd.print("PANEL V1.01");
delay(2500); // will be removed once relays are installed
lcd.clear();
lcd.setCursor(4, 0); // Column, line
lcd.print("WELCOME");
lcd.setCursor(1, 1);
lcd.print("GARY & TRACEY");
delay(2500); // will be removed once relays are installed
WhichScreen++;
program1();
}
break;
case 1:
{
program1();
}
break;
case 2:
{
program2();
}
break;
case 3:
{
program3();
}
break;
case 4:
{
program4();
}
break;
case 5:
{
program5();
}
break;
case 6:
{
program6();
}
break;
case 7:
{
program7();
}
break;
case 8:
{
program8();
}
break;
case 9:
{
program9();
}
break;
}
}
//-------------------------------
// BEGIN of the switch debouncing code
int reading = digitalRead(upButton);
if (reading != lastSelectState) {
// reset the debouncing timer
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
// if the button state has changed:
if (reading != selectState) {
selectState = reading;
// only toggle the LED if the new button state is HIGH
if (selectState == HIGH) {
hasChanged = true;
WhichScreen++;
}
} else {
hasChanged = false;
}
}
lastSelectState = reading;
// END of the switch Debouncing code
// --------------------------------------
if (WhichScreen > 9) {
WhichScreen = 1;
}
}
void program1()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 1 (P1)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program2()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 2 (P2)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program3()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 3 (P3)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program4()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 4 (P4)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program5()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 5 (P5)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program6()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 6 (P6)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program7()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 7 (P7)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program8()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 8 (P8)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program9()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 9 (P9)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void stepDown() {
if (seconds > 0) {
seconds -= 1;
} else {
if (minutes > 0) {
seconds = 59;
minutes -= 1;
} else {
if (hours > 0) {
seconds = 59;
minutes = 59;
hours -= 1;
} else {
//trigger();
}
}
}
}
void timer() {
delay(150);
while (hours > 0 || minutes > 0 || seconds >= 0) {
lcd.setCursor(0, 2);
//(hours < 10) ? lcd.print("0") : NULL;
// lcd.print(hours);
//lcd.print(":");
lcd.print("TIME LEFT: ");
(minutes < 10) ? lcd.print("0") : NULL;
lcd.print(minutes);
lcd.print(":");
(seconds < 10) ? lcd.print("0") : NULL;
lcd.print(seconds);
lcd.display();
stepDown();
delay(1000);
}
}
void trigger() {
lcd.clear(); // clears the screen and buffer
lcd.setCursor(6, 0); // set timer position on lcd for end.
lcd.print("TIMES UP");
lcd.setCursor(6, 1); // set timer position on lcd for end.
lcd.print("NUMBER 4");
delay(1000);
lcd.display();
}
Sorry it's a bit messy and all over the place, I'm also sure it's not flowing very well either but it works mostly lol.
Any and all help much appreciated, and thank you in advance.
In your timer function. You have a loop condition hours > 0 || minutes > 0 || seconds >= 0. Since your seconds is always greater than 0, you have a infinite loop in timer.
As you can see, seconds does not decrease seconds == 0.
void stepDown() {
if (seconds > 0) {
seconds -= 1;
} else {
if (minutes > 0) {
seconds = 59;
minutes -= 1;
} else {
if (hours > 0) {
seconds = 59;
minutes = 59;
hours -= 1;
} else {
//trigger();
}
}
}
}

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.