Hello, trying to give as much info as i can:
I am making a scoreboard for a basketball team with wired controller (matrix). For the countdown i have 228 LED's (ws2812b) connected to a single pin. For the "period", "faults" and "scores" i have another pin connected, but all of that code is not important right now (and not ready yet), so i will only post the code that matters for now that works.
For controlling i use an Elegoo Mega R3 Mega clone
I have read a lot about using delay, millli's, RTC DS 3231, zero error 1 second timing , but for me it's difficult!
With the following code, without a delay of 130ms, the countdown goes too fast! When i set the countdown for 10 minutes and compare it with real time, real time has 11 seconds left when the countdown is at 0.
What i first did, as you can see in the code, was adding a delay of 130ms which works, but the button press (offcourse) would allso have the same delay.
Is there a way i can "fool" the countdown timer to get more accurate without using this delay?
My personal guess is that the Elegoo clone crystal is the big problem. But i (in the end) need 19 pins to controll all functions for the scoreboard. The Mega has the proper amount of pins to handle all of my needs.
Could be that i am missing another board which is more reliable with the same amount of pins, but could not find them.
Code is :
#include <FastLED.h>
#define DATA_PIN 25
#define NUM_LEDS2 228
#define NUM_LEDS_PER_DIGIT2 56
#define DISPLAY_MMSS
#define BRIGHTNESS 20
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define ROWS 6
#define COLS 5
#include <Bounce2.h>
#include "Adafruit_Keypad.h"
#include <Keypad.h>
uint8_t rowPins[ROWS] = {22, 24, 26, 28, 30, 32};
uint8_t colPins[COLS] = {23, 8, 27, 29, 31};
char keys[ROWS][COLS] = {
{'1','2','3','4','5'},
{'6','7','8','9','0'},
{'a','b','c','d','e'},
{'f','g','h','i','j'},
{'k','l','m','n','o'},
{'p','q','r','s','t'}
};
bool lit[ROWS*COLS] = {0};
CRGB leds2[NUM_LEDS2];
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);
const uint64_t digits[10] = {
0b0000000011111111111111111111111100000000111111111111111111111111, // 0
0b0000000011111111000000000000000000000000111111110000000000000000, // 1
0b0000000000000000111111111111111111111111111111111111111100000000, // 2
0b0000000011111111111111110000000011111111111111111111111100000000, // 3
0b0000000011111111000000000000000011111111111111110000000011111111, // 4
0b0000000011111111111111110000000011111111000000001111111111111111, // 5
0b0000000011111111111111111111111111111111000000001111111111111111, // 6
0b0000000011111111000000000000000000000000111111111111111100000000, // 7
0b0000000011111111111111111111111111111111111111111111111111111111, // 8
0b0000000011111111111111110000000011111111111111111111111111111111, // 9
};
// The time at which the counter was (most recently) started
unsigned long startTime;
// Duration is specified in ms. So 1000 = 1 second, 60000 = 1 minute, etc.
unsigned long timerDuration = 0;
// Keep track of elapsed time from previous start/stop cycles
unsigned long cumulativeElapsedTime;
// Keep track of the current state of the device
enum State {Inactive, Active};
State state = State::Inactive;
// Count direction
enum Mode {CountUp, CountDown};
Mode mode = Mode::CountUp;
// FUNCTIONS
// Set the values in the LED strip corresponding to a particular display/value
void setDigit(int display, int val, CHSV colour){
for(int i=0;i<NUM_LEDS_PER_DIGIT2; i++){
colour.v = bitRead(digits[val], i) * 255;
leds2[display*NUM_LEDS_PER_DIGIT2 + i] = colour;
}
}
void Start(){
Serial.println(F("Timer activated!"));
state = State::Active;
startTime = millis();
}
void Stop() {
Serial.println(F("Timer stopped"));
// Add the length of time elapsed since the timer was last started to the total time elapsed
cumulativeElapsedTime += (millis() - startTime);
state = State::Inactive;
}
void Reset(){
Serial.println(F("Timer reset"));
cumulativeElapsedTime = 0;
state = State::Inactive;
}
// This function runs once when the program first starts
void setup() {
// Initialise a serial connection, used only for debugging
Serial.begin(115200);
Serial.println(__FILE__ __DATE__);
// Initialise the LED strip
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds2, NUM_LEDS2);
FastLED.setBrightness( BRIGHTNESS ); // Set overall brightness
}
// This function runs over and over
void loop() {
// Grab the current timestamp
unsigned long currentTime = millis();
// Calculate the value to be displayed
static long timeValue = 0;
// The colour hue in which the time will be displayed
int timeHue = 100;
// What to do next depends on the current state of the device
if(state == State::Active) {
if(mode == Mode::CountDown) {
// The time remaining is the total game duration, less the time spent during the
// current period of play, less the time elapsed during any previous sessions
// or other deductions
timeValue = timerDuration - (currentTime - startTime) - cumulativeElapsedTime;
// Map colour hue from green -> red based on fraction of time remaining
if(timeValue <= 31000){
timeHue = 0;
// Countdown has reached zero
if(timeValue <= 0) {
timeValue = 0;
state = State::Inactive;
}
}
}
else if(mode == Mode::CountUp) {
// Time is however long since we started counting, plus any previous existing time
timeValue = (currentTime - startTime) + cumulativeElapsedTime;
// Constant colour
timeHue = 0;
}
}
else if(state == State::Inactive){
// Cycle colour hue while paused (BPM, from_value, to_value)
if(timeValue >31000){
timeHue = 100;
}
else if(timeValue <=31000){
timeHue = 0;
}
}
char key = keypad.getKey();
if (key=='m'){
mode = Mode::CountDown;
if (state==Inactive){
Start();
}
}
if (key=='o'){
if (mode = Mode::CountDown){
if (state==Active){
Stop();
}
}
}
if (key=='n'){
if (mode = Mode::CountDown){
if (state==Inactive){
timerDuration = 0;
timeValue = 0;
Reset();
}
}
}
if (key=='h'){
timerDuration+=10000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='i'){
timerDuration+=1000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='k'){
timerDuration+=600000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='l'){
timerDuration+=60000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='p'){
timerDuration+=1200000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='q'){
timerDuration+=900000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='r'){
timerDuration+=600000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='s'){
timerDuration+=300000;
timeValue = timerDuration - cumulativeElapsedTime;
}
if (key=='t'){
timerDuration+=120000;
timeValue = timerDuration - cumulativeElapsedTime;
}
// Display as mm:ss
#ifdef DISPLAY_MMSS
// Use modulo to calculate "remainder" seconds
int seconds = (timeValue / 1000) % 60;
int minutes = timeValue / 60000;
// Units
setDigit(3, seconds%10, CHSV(timeHue, 255, 255));
// Tens
setDigit(2,(seconds/10)%10, CHSV(timeHue, 255, 255));
// Hundreds
setDigit(1, minutes%10, CHSV(timeHue, 255, 255));
// Thousands
setDigit(0,(minutes/10)%10, CHSV(timeHue, 255, 255));
// Display in seconds
#else
// Units
setDigit(3, (timeValue / 1000) % 10, CHSV(timeHue, 255, 255));
// Tens
setDigit(2, (timeValue / 10000) % 10, CHSV(timeHue, 255, 255));
// Hundreds
setDigit(1, (timeValue / 100000) % 10, CHSV(timeHue, 255, 255));
// Thousands
setDigit(0, (timeValue / 1000000) % 10, CHSV(timeHue, 255, 255));
#endif
// Send the updated values to the LED strip
delay(130);
FastLED.show();
}
So with the delay at the end, it works perfect. But the buttons allso having the same delay, which is a bit of a problem to say the least for a basketball game.
I am thinking of using a RTC module to solve this, but all i understand, is that it is used in combination with a actual clock. Am i missing something here to only use it for a countdown?
I am really triggered to learn, but i am doing this by try and error....
Can someone give me a hint to what to change or where to start?
I am not looking for a direct solution, want to learn, but if it gets too technical a little help would be great.
Thanks!