I´m using an Arduino Nano to count parts at a vibrating conveyer. The parts fall through a light bridge which is connected to the arduino via an octocoupler(The pin on the arduino is in input_Pullup mode). The counter is displayed over a lcd display with I2C backpack. If no part was registered within a certain time, a lamp,also controlled via an octocoupler, turns on. When a new part is registered or the counter is resetted(by pressing the "reset counter" button) the lamp turns off.
My problem is, that the arduino chrashes after a, seemingly, random time(Sometimes it runs for hours). When this happens, the display shows weird characters and the lamp won´t shut off neither when a part drops through the light barrier nor when the "reset counter" button is pressed. The only way to "free" the arduino, is disconnecting the power supply.
I´m thankful for any help, even if it is just a suggestion how to improve the code.
The code is originaly written in german, but I added english comments and most of the variable names should be understandable.
/*
Programm Zähleranlage
V 2.1.0
Program Counting System
V2.1.0
Projektbeschreibung:
Dieses Programm steuert den Micro-Controller einer Zählanlage, die an einer Rüttelstrecke montiert ist. Die Zählung erfolgt
über eine Lichtschranke, die Anzeige über ein LCD-Display. Das Rücksetzen des Zählers erfolgt über einen, am Gehäuse verbauten Taster.
Kommt nach der eingestellten Zeit(s. Nutzerhinweise) kein Zählsignal, wird ein Relais angesteuert, welches eine Warnleuchte aufleuchten
lässt.
Project description:
This program controls the micro-controller of a counting system which is mounted on a vibrating conveyor. The counting is done by a
light barrier, which connected to the arduino via an optocoupler, the indication by a LCD display. The counter is reset via a button
installed on the housing. If no counting signal is received after the set time, a relay is activated which lights up a warning light.
*/
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args) write(args);
#else
#define printByte(args) print(args, BYTE);
#endif
/*
Pin-Belegung/Used Pins:
Variablenname/Variable name : Pin : Funktion/Function : Pin mode
Lichtschranke/Optokoppler : D3 : Lichtschranke zur Teileerkennung; Optokoppler zur Umwandlung von 24 V zu 5V/Light barrier;octocoupler : INPUT_Pullup
CounterResetButton : D2 : Taster zum Zurücksetzen des Zählers /button for resetting the counter : INPUT_Pullup
Leuchte : D8 : Optokoppler zur Ansteuerung der Alarmleuchte/ lamp : OUTPUT
lcd : A4 A5 : LCD-Display zur Anzeige von Informationen(Zähler, Zeit) /LCD :(Output
*/
//Input Variablen
//Input Variables
const byte Lichtschranke = 3;
const byte CounterResetButton = 2;
//Output Variablen
//Output Variables
const byte Leuchte = 8;
LiquidCrystal_I2C lcd(0x27, 20, 4);
//Globale Variablen
//Global Variables
unsigned long counter = 0; //Zähler //Counter
bool prevSensor = 1; //Speicherung des vorherigen Sensorwertes //Stores the sensor value for the next cycle
bool prevReset = 1; //Speicherung des vorherige Wertes des Reset-Tasters //Stores the value of the "reset"-button for the next cycle
unsigned long timeAtLastPart; //Zeit beim letzten Teil //Time at the last part
unsigned long prevTime; //Die Zeit beim letzten Programmzyklus //Stores the time at the last cycle
const int timeToError = 20000; //Zeit in ms bevor der Alarm auslöst; Zahl bearbeiten um die Zeit bis Alarm zu ändern (Standard = 20 s = 20000 ms) //The time in ms until the warning light lights up. Change the number (default = 20 s = 20000 ms)
//Deklarierung der Funktionen
void count();
void setupDisplay();
void displayCounter();
//Setup-Funktion (Läuft einmal bei Einschalten)
void setup()
{
//Einschalten des LCD
//Turning on the LCD
lcd.init();
lcd.backlight();
//Display in Ausgangszustand
//Set LCD to Initial-State
setupDisplay();
//Definieren der Pin-Modi
//Set the pinModes
pinMode(Lichtschranke, INPUT_PULLUP);
pinMode(CounterResetButton, INPUT_PULLUP);
pinMode(Leuchte, OUTPUT);
}
//Loop-Funktion (Wiederholt sich immer wieder)
void loop()
{
unsigned long time = millis();
//Auswerten des Sensorwertes
//Ist der Sensorwert anders als beim letzten Durchgang und ist aktiv (-> positive Flanke), wird die Zählfunktion aufgerufen.
//If the sensor state is different than in the last cycle and HIGH the counting function is called
bool sensor = digitalRead(Lichtschranke);
if ((sensor != prevSensor) && sensor)
{
count();
}
prevSensor = sensor; //Der aktuelle Sensorwert wird als vorhergehender Sensorwert für den nächsten Durchlauf gespeichert //The value of the sensor is stored for the next cycle
//Auswerten der Zeit
//Error-Check
bool error = false;
unsigned long timeSinceLastPart = time - timeAtLastPart; //Die Zeit seit dem letzten Teil wird errechnet //The time since the last part is calculated
if (timeSinceLastPart >= timeToError) //Wenn die Zeit größer als die Vorgabezeit ist, wird die Error-Variable gesetzt //If the time since the last part is greater than the configured time, the "Error"-variable is set to true.
{
error = true;
}
digitalWrite(Leuchte, error); //Die Leuchte wird auf den Fehlerwert gesetzt (error = 1 > Lampe an) //The warning light is set to the value of "Error" (error = true -> light on)
//Rücksetzen des Zählers bei Betätigung des Reset-Tasters
//Resetting of the counter if the "Reset"-button is pressed
bool reset = !digitalRead(CounterResetButton);
if (reset && (reset = !prevReset))
{
counter = 0; //Zähler wird auf 0 gesetzt //Counter is set to 0
setupDisplay(); //Die Anzeige wird in den Ausgangszustand versetzt //The Display is set to the initial state
timeAtLastPart = time; //Die Zeit bei dem letzten Teil wird auf die aktuelle Zeit gesetzt //The time a the last part is set to the new time
}
prevReset = reset; //Der aktuelle Wert des Reset-Tasters wird als vorhergehender Wert für den nächsten Durchlauf gespeichert. //The value of the "Reset"-button is stored for the next cycle
//Falls ein Fehler vorlieggt, wird die Stillstandzeit angezeigt, ansonsten die Zeit seit dem letzten Teil
//If "Error" = true, the time, how long error is true is displayed, else the time since the last part is Displayed
if (time / 1000 > prevTime / 1000)
{
if (error)
{
lcd.setCursor(0, 2);
delay(3);
lcd.print("Sillstandzeit: ");
delay(3);
lcd.setCursor(0, 3);
delay(3);
lcd.print((timeSinceLastPart - time) / 1000);
delay(3);
lcd.print(" s ");
}
else
{
lcd.setCursor(0, 2);
delay(3);
lcd.print("Zeit seit Teil: ");
delay(3);
lcd.setCursor(0, 3);
delay(3);
lcd.print((timeSinceLastPart) / 1000);
delay(3);
lcd.print(" s ");
}
}
prevTime = time;
}
//Funktion die aufgerufen wird wenn ein neues Teil erkannt wird.
//Function, which gets called if a part is detected
void count()
{
counter++; //Zähler wird um eins erhöht. //Counter + 1
timeAtLastPart = time; // Die Zeit beim letzten Teil wird mit der aktuellen Zeit ersetzt. //The time a the last part is set to the new time
displayCounter(); //Die Anzeige wird akualisiert //The counter is displayed
}
//Funktion die das Display in den Ausgangszustand versetzt:
//Function for the initial state of the display
/*
Anzahl Teile:
<Zähler>
*/
void setupDisplay()
{
lcd.clear(); //Die Anzeige wird geleert //The Display is cleared
delay(3);
lcd.print("Anzahl Teile: "); //Das Display wir beschrieben (s.o.)
delay(3);
lcd.setCursor(0, 1);
delay(3);
lcd.print(counter);
}
//Funktion die den aktuellen Zählerwert auf das Display schreibt.
//writing the counter value to the display
void displayCounter()
{
lcd.setCursor(0, 1);
delay(3);
lcd.print(counter);
delay(3);
lcd.print(" ");
}