low memory version of arduino meeting-clock code, suitable for the arduino nano

This commit is contained in:
Zechert, Frank (EXTERN: Capgemini) 2020-12-08 04:03:56 +01:00
parent 222e748fc4
commit 695f738010
6 changed files with 967 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

262
include/fonts.h Normal file
View File

@ -0,0 +1,262 @@
#pragma once
#include <MD_MAX72xx.h>
MD_MAX72XX::fontType_t font_fw_3x5[] PROGMEM =
{
0, // 0
0, // 1
0, // 2
0, // 3
0, // 4
0, // 5
0, // 6
0, // 7
0, // 8
0, // 9
0, // 10
0, // 11
0, // 12
0, // 13
0, // 14
0, // 15
0, // 16
0, // 17
0, // 18
0, // 19
0, // 20
0, // 21
0, // 22
0, // 23
0, // 24
0, // 25
0, // 26
0, // 27
0, // 28
0, // 29
0, // 30
0, // 31
1, 0, // 32
0, // 33
0, // 34
0, // 35
0, // 36
3, 25, 4, 19, // 37
0, // 38
1, 3, // 39
3, 0, 14, 17, // 40
3, 17, 14, 0, // 41
3, 5, 2, 5, // 42
3, 4, 14, 4, // 43
1, 24, // 44
3, 4, 4, 4, // 45
1, 16, // 46
3, 24, 4, 3, // 47
3, 31, 17, 31, // 48
3, 0, 0, 31, // 49
3, 29, 21, 23, // 50
3, 21, 21, 31, // 51
3, 7, 4, 31, // 52
3, 23, 21, 29, // 53
3, 31, 21, 29, // 54
3, 1, 1, 31, // 55
3, 31, 21, 31, // 56
3, 23, 21, 31, // 57
1, 10, // 58
1, 26, // 59
0, // 60
0, // 61
0, // 62
0, // 63
0, // 64
0, // 65
0, // 66
3, 31, 17, 17, // 67
0, // 68
0, // 69
3, 31, 5, 1, // 70
0, // 71
0, // 72
0, // 73
0, // 74
0, // 75
0, // 76
0, // 77
0, // 78
0, // 79
0, // 80
0, // 81
0, // 82
0, // 83
0, // 84
0, // 85
0, // 86
0, // 87
0, // 88
0, // 89
0, // 90
0, // 91
0, // 92
0, // 93
0, // 94
0, // 95
0, // 96
0, // 97
0, // 98
0, // 99
0, // 100
0, // 101
0, // 102
0, // 103
0, // 104
0, // 105
0, // 106
0, // 107
0, // 108
0, // 109
0, // 110
0, // 111
0, // 112
0, // 113
3, 28, 4, 4, // 114
0, // 115
3, 16, 16, 16, // 116
0, // 117
3, 12, 16, 12, // 118
0, // 119
0, // 120
0, // 121
2, 3, 3, // 122
0, // 123
0, // 124
0, // 125
0, // 126
0, // 127
0, // 128
0, // 129
0, // 130
0, // 131
0, // 132
0, // 133
0, // 134
0, // 135
0, // 136
0, // 137
0, // 138
0, // 139
0, // 140
0, // 141
0, // 142
0, // 143
0, // 144
0, // 145
0, // 146
0, // 147
0, // 148
0, // 149
0, // 150
0, // 151
0, // 152
0, // 153
0, // 154
0, // 155
0, // 156
0, // 157
0, // 158
0, // 159
0, // 160
0, // 161
0, // 162
0, // 163
0, // 164
0, // 165
0, // 166
0, // 167
0, // 168
0, // 169
0, // 170
0, // 171
0, // 172
0, // 173
0, // 174
0, // 175
2, 3, 3, // 176
0, // 177
0, // 178
0, // 179
0, // 180
0, // 181
0, // 182
0, // 183
0, // 184
0, // 185
0, // 186
0, // 187
0, // 188
0, // 189
0, // 190
0, // 191
0, // 192
0, // 193
0, // 194
0, // 195
0, // 196
0, // 197
0, // 198
0, // 199
0, // 200
0, // 201
0, // 202
0, // 203
0, // 204
0, // 205
0, // 206
0, // 207
0, // 208
0, // 209
0, // 210
0, // 211
0, // 212
0, // 213
0, // 214
0, // 215
0, // 216
0, // 217
0, // 218
0, // 219
0, // 220
0, // 221
0, // 222
0, // 223
0, // 224
0, // 225
0, // 226
0, // 227
0, // 228
0, // 229
0, // 230
0, // 231
0, // 232
0, // 233
0, // 234
0, // 235
0, // 236
0, // 237
0, // 238
0, // 239
0, // 240
0, // 241
0, // 242
0, // 243
0, // 244
0, // 245
0, // 246
0, // 247
0, // 248
0, // 249
0, // 250
0, // 251
0, // 252
0, // 253
0, // 254
0, // 255
};

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

17
platformio.ini Normal file
View File

@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nanoatmega328]
platform = atmelavr
board = nanoatmega328
framework = arduino
lib_deps =
majicdesigns/MD_MAX72XX@^3.2.3
adafruit/RTClib@^1.12.4

630
src/main.cpp Normal file
View File

@ -0,0 +1,630 @@
#include <Arduino.h>
#include <SoftwareSerial.h>
#include <MD_MAX72xx.h>
#include <EEPROM.h>
#include <RTClib.h>
#include "fonts.h"
#define VERSION F("v. 1.0.0")
#define SERIAL_SPEED 9600
#define SERIAL_BT_SPEED 9600
#define SERIAL_BT_STATE_PIN 3
#define SERIAL_BT_TX_PIN 4
#define SERIAL_BT_RX_PIN 5
#define LED_HW_TYPE MD_MAX72XX::FC16_HW
#define LED_CS_PIN 6
#define LED_MODULES 4
#define STATE_TIME 0
#define STATE_DATE 1
#define STATE_TEMP 2
// config addresses
#define ADDR_CHECKSUM 0x0
#define ADDR_INTENSITY 0x1
#define ADDR_TIME_ENABLED_INTERVAL 0x2
#define ADDR_DATE_ENABLED_INTERVAL 0x3
#define ADDR_TEMP_ENABLED_INTERVAL 0x4
#define ADDR_DATE_ENABLED 0x5
#define ADDR_TEMP_ENABLED 0x6
#define ADDR_SCROLL_SPEED 0x7
#define ADDR_SCROLL_WAIT 0x8
// config default values
#define CFG_CHECKSUM 0x2
#define CFG_INTENSITY_DEFAULT 0xf
#define CFG_TIME_ENABLED_INTERVAL_DEFAULT 35
#define CFG_DATE_ENABLED_INTERVAL_DEFAULT 3
#define CFG_TEMP_ENABLED_INTERVAL_DEFAULT 3
#define CFG_DATE_ENABLED_DEFAULT 1
#define CFG_TEMP_ENABLED_DEFAULT 1
#define CFG_SCROLL_SPEED_DEFAULT 5
#define CFG_SCROLL_WAIT_DEFAULT 40
// config shortcut macros
#define CFG_INTENSITY EEPROM.read(ADDR_INTENSITY)
#define CFG_TIME_ENABLED_INTERVAL (((uint16_t)EEPROM.read(ADDR_TIME_ENABLED_INTERVAL)) * 1000)
#define CFG_DATE_ENABLED_INTERVAL (((uint16_t)EEPROM.read(ADDR_DATE_ENABLED_INTERVAL)) * 1000)
#define CFG_TEMP_ENABLED_INTERVAL (((uint16_t)EEPROM.read(ADDR_TEMP_ENABLED_INTERVAL)) * 1000)
#define CFG_DATE_ENABLED EEPROM.read(ADDR_DATE_ENABLED)
#define CFG_TEMP_ENABLED EEPROM.read(ADDR_TEMP_ENABLED)
#define CFG_SCROLL_SPEED (((uint16_t)EEPROM.read(ADDR_SCROLL_SPEED)) * 10)
#define CFG_SCROLL_WAIT (((uint16_t)EEPROM.read(ADDR_SCROLL_WAIT)) * 100)
// global variables
SoftwareSerial btSerial(SERIAL_BT_RX_PIN, SERIAL_BT_TX_PIN);
MD_MAX72XX led(LED_HW_TYPE, LED_CS_PIN, LED_MODULES);
RTC_DS3231 rtc;
#define BUFFER_SIZE 20
char buffer[BUFFER_SIZE];
uint8_t state = STATE_TIME;
unsigned long lastClockCall = 0;
unsigned long lastScrollCall = 0;
// functions
uint8_t conv2d(const char *p, uint8_t maxLen)
{
uint8_t v = 0;
for (uint8_t i = 0; *(p + i) != '\0' && maxLen-- > 0; i++)
{
v = (v * 10) + (*(p + i) - '0');
}
return v;
}
void setup()
{
// setup pins
pinMode(SERIAL_BT_RX_PIN, INPUT);
pinMode(SERIAL_BT_TX_PIN, OUTPUT);
pinMode(LED_CS_PIN, OUTPUT);
// setup serial
Serial.begin(SERIAL_SPEED);
// setup bluetooth serial
btSerial.begin(SERIAL_BT_SPEED);
// setup RTC
rtc.begin();
// setup led matrix
led.begin();
// clear flash
if (EEPROM.read(ADDR_CHECKSUM) != CFG_CHECKSUM)
{
EEPROM.write(ADDR_CHECKSUM, CFG_CHECKSUM);
EEPROM.write(ADDR_INTENSITY, CFG_INTENSITY_DEFAULT);
EEPROM.write(ADDR_TIME_ENABLED_INTERVAL, CFG_TIME_ENABLED_INTERVAL_DEFAULT);
EEPROM.write(ADDR_DATE_ENABLED_INTERVAL, CFG_DATE_ENABLED_INTERVAL_DEFAULT);
EEPROM.write(ADDR_TEMP_ENABLED_INTERVAL, CFG_TEMP_ENABLED_INTERVAL_DEFAULT);
EEPROM.write(ADDR_DATE_ENABLED, CFG_DATE_ENABLED_DEFAULT);
EEPROM.write(ADDR_TEMP_ENABLED, CFG_TEMP_ENABLED_DEFAULT);
EEPROM.write(ADDR_SCROLL_SPEED, CFG_SCROLL_SPEED_DEFAULT);
EEPROM.write(ADDR_SCROLL_WAIT, CFG_SCROLL_WAIT_DEFAULT);
}
// clear led
led.clear();
led.control(MD_MAX72XX::INTENSITY, CFG_INTENSITY);
led.update(MD_MAX72XX::OFF); // no automatic updates wanted
lastClockCall = lastScrollCall = millis();
}
// command processing
#define COMMAND_SIZE 255
char command[COMMAND_SIZE];
bool clearCommand = true;
char *readCommand()
{
// if clearCommand flag was set, clear the command buffer before we receive new data
if (clearCommand)
{
clearCommand = false;
memset(command, 0, sizeof(command) / sizeof(command[0]));
}
// current length of the command
uint8_t commandLength = strnlen(command, COMMAND_SIZE);
// receive available bytes
while (btSerial.available() > 0)
{
// check for command overflow
if (commandLength == COMMAND_SIZE)
{
clearCommand = true; // clear the command buffer and pointers next time
return NULL; // no command is available for processing
}
command[commandLength] = btSerial.read(); // receive one byte
if (command[commandLength] == '\r' || command[commandLength] == '\n') // the received byte is end-of-command identifier
{
// end of command was received, null terminate the string, the \r or \n is not needed as part of the command
command[commandLength] = '\0';
if (strnlen(command, COMMAND_SIZE) == 0) // if the command length is 0, we did not receive anything else but the \r or \n, ignore
{
clearCommand = true;
return NULL;
}
// first 3 bytes are the command (e.g. SET, ADJ, ), then 0 terminate, this is just for convenience
// possible arguments to the command will be from command[4] onwards. Maybe all of command[4] and onwards will be NULL if the command
// had no parameters. This needs to be checked in code consuming the command
command[3] = '\0';
// next time we need to start with a fresh command buffer
clearCommand = true;
// return that we received a command
return command;
}
commandLength++; // the command buffer is now one character longer
}
// no complete command was received yet
return NULL;
}
uint8_t charBuf[8];
#define SCROLL_BUFF_SIZE 255
char scrollBuf[SCROLL_BUFF_SIZE];
bool showText(char *text, MD_MAX72XX::fontType_t *font, uint8_t tsd, uint16_t scrollSkip = 0)
{
led.setFont(font);
uint16_t position = led.getColumnCount() - 1;
bool center = scrollSkip == 0;
// render the text
while (*text != '\0')
{
uint8_t width = led.getChar((uint8_t)*text, 8, charBuf);
for (uint8_t i = 0; i < width; i++)
{
if (scrollSkip > 0)
{
// if we are still about to skip columns, reduce the scrollSkip and do nothing else
scrollSkip--;
}
else
{
// we are not skipping characters to scroll, so now we can actually draw
led.setColumn(position, charBuf[i]);
if (position == 0)
{
break; // there is no more space on the led matrix
}
else
{
position--; // move to the next column
}
}
}
if (position == 0)
{
break; // there is no more space on the led matrix
}
text++; // go to next char
if (scrollSkip > 0)
{
scrollSkip--;
}
else
{
position--; // move to the next column (one column space between characters)
}
}
if (scrollSkip > 0)
{
// we are done scrolling, but we had not enough text to draw anything (text completely scrolled out of the screen)
return true;
}
// center the text if it is shorter than the display (centering is only enabled when not scrolling)
while (position > 0 && center)
{
led.transform(MD_MAX72XX::TSR);
position = min(position - 1, position - 2); // avoid underflow of position
}
// translate downwards if requested
while (tsd-- > 0)
{
led.transform(MD_MAX72XX::TSD);
}
return false;
}
void showClock()
{
if (rtc.lostPower())
{
buffer[0] = buffer[1] = buffer[2] = buffer[3] = '-';
buffer[4] = '\0';
showText(buffer, NULL, 0);
lastClockCall = millis();
return;
}
unsigned long diff = millis() - lastClockCall;
DateTime now = rtc.now();
if (state == STATE_TIME) // display the time
{
snprintf_P(buffer, BUFFER_SIZE, PSTR("%02d:%02d:%02d"), now.hour(), now.minute(), now.second());
showText(buffer, font_fw_3x5, 1);
if (diff >= CFG_TIME_ENABLED_INTERVAL)
{
lastClockCall = millis();
if (CFG_DATE_ENABLED)
{
state = STATE_DATE;
}
else if (CFG_TEMP_ENABLED)
{
state = STATE_TEMP;
}
else
{
state = STATE_TIME;
}
}
}
else if (state == STATE_DATE) // display the date
{
snprintf_P(buffer, BUFFER_SIZE, PSTR("%02d.%02d."), now.day(), now.month());
showText(buffer, font_fw_3x5, 1);
if (diff >= CFG_DATE_ENABLED_INTERVAL)
{
lastClockCall = millis();
if (CFG_TEMP_ENABLED)
{
state = STATE_TEMP;
}
else
{
state = STATE_TIME;
}
}
}
else if (state == STATE_TEMP) // display the temperature
{
snprintf_P(buffer, BUFFER_SIZE, PSTR("%02d °C"), (int)rtc.getTemperature());
showText(buffer, font_fw_3x5, 1);
if (diff >= CFG_TEMP_ENABLED_INTERVAL)
{
lastClockCall = millis();
state = STATE_TIME;
}
}
}
bool scrollEnabled = false;
uint16_t scrollSkip;
uint16_t scrollWait;
void beginScrollText(const char *txt)
{
strncpy(scrollBuf, txt, SCROLL_BUFF_SIZE);
scrollEnabled = true;
scrollSkip = 0;
scrollWait = CFG_SCROLL_WAIT;
lastScrollCall = millis();
showText(scrollBuf, NULL, 0);
}
void endScrollText()
{
scrollEnabled = false;
memset(scrollBuf, 0, sizeof(scrollBuf) / sizeof(scrollBuf[0]));
}
void scrollText()
{
if (!scrollEnabled)
{
return;
}
unsigned long diff = millis() - lastScrollCall;
if (scrollWait > 0)
{
lastScrollCall = millis();
scrollWait = scrollWait - min(scrollWait, diff);
if (scrollWait > 0)
{
showText(scrollBuf, NULL, 0, scrollSkip);
return;
}
}
if (diff >= CFG_SCROLL_SPEED)
{
bool scrollDone = showText(scrollBuf, NULL, 0, scrollSkip++);
if (scrollDone)
{
scrollWait = CFG_SCROLL_WAIT;
scrollSkip = 0;
showText(scrollBuf, NULL, 0);
}
lastScrollCall = millis();
}
else
{
showText(scrollBuf, NULL, 0, scrollSkip);
}
}
bool blink = false;
bool blinkState = false;
unsigned long lastBlinkCall;
void startBlink()
{
if (blink == false)
{
blink = true;
lastBlinkCall = millis();
}
}
void stopBlink()
{
blink = false;
}
void doBlink()
{
if (!blink)
{
return;
}
if (blinkState)
{
led.transform(MD_MAX72XX::TINV);
}
unsigned long diff = millis() - lastBlinkCall;
if (diff > 1000)
{
blinkState = !blinkState;
lastBlinkCall = millis();
}
}
DateTime *nextTime = 0;
void loop()
{
// clear the output
led.clear();
if (!digitalRead(SERIAL_BT_STATE_PIN)) // BT is not connected
{
showClock();
// indicate that there is no bluetooth connection
led.setPoint(7, 0, true);
}
else // BT is connected
{
// read command
char *cmd = readCommand();
if (cmd != NULL) // command received?
{
if (strncmp_P(cmd, PSTR("MTC"), 3) == 0)
{
btSerial.print(F("MTC "));
btSerial.println(VERSION);
}
else if (strncmp_P(cmd, PSTR("ADJ"), 3) == 0)
{
// Adjust the rtc clock, arguments from position 4 "2000-01-01T00:00:00"
cmd[24] = '\0';
rtc.adjust(DateTime(&cmd[4]));
btSerial.println(F("ADJ"));
}
else if (strncmp_P(cmd, PSTR("SET"), 3) == 0) // SET a config value
{
// Set a setting value, arguments:
// - form position 4 to 5 contains the setting name (6 is the \0 byte)
// - from position 7 the new value begins
cmd[6] = '\0';
if (strncmp_P(&cmd[4], PSTR("IN"), 2) == 0) // intensity
{
uint8_t intensity = conv2d(&cmd[7], 2);
intensity = (intensity == 0) ? intensity = 1 : ((intensity > MAX_INTENSITY) ? MAX_INTENSITY : intensity);
EEPROM.update(ADDR_INTENSITY, intensity);
led.control(MD_MAX72XX::INTENSITY, intensity);
btSerial.println(F("SET IN"));
}
else if (strncmp_P(&cmd[4], PSTR("TI"), 2) == 0) // time interval
{
uint8_t interval = conv2d(&cmd[7], 3);
EEPROM.update(ADDR_TIME_ENABLED_INTERVAL, interval);
btSerial.println(F("SET TI"));
}
else if (strncmp_P(&cmd[4], PSTR("DI"), 2) == 0) // date interval
{
uint8_t interval = conv2d(&cmd[7], 3);
EEPROM.update(ADDR_DATE_ENABLED_INTERVAL, interval);
btSerial.println(F("SET TI"));
}
else if (strncmp_P(&cmd[4], PSTR("CI"), 2) == 0) // temperature interval
{
uint8_t interval = conv2d(&cmd[7], 3);
EEPROM.update(ADDR_TEMP_ENABLED_INTERVAL, interval);
btSerial.println(F("SET CI"));
}
else if (strncmp_P(&cmd[4], PSTR("DE"), 2) == 0) // date enabled
{
EEPROM.update(ADDR_DATE_ENABLED, cmd[7] == '1');
btSerial.println(F("SET DE"));
}
else if (strncmp_P(&cmd[4], PSTR("CE"), 2) == 0) // temperature enabled
{
EEPROM.update(ADDR_TEMP_ENABLED, cmd[7] == '1');
btSerial.println(F("SET CE"));
}
else if (strncmp_P(&cmd[4], PSTR("SS"), 2) == 0) // scroll speed
{
uint8_t speed = conv2d(&cmd[7], 3);
EEPROM.update(ADDR_SCROLL_SPEED, speed);
btSerial.println(F("SET SS"));
}
else if (strncmp_P(&cmd[4], PSTR("SW"), 2) == 0) // scroll wait
{
uint8_t interval = conv2d(&cmd[7], 3);
EEPROM.update(ADDR_SCROLL_WAIT, interval);
btSerial.println(F("SET SW"));
}
else
{
btSerial.println(F("ERR"));
}
}
else if (strncmp_P(cmd, PSTR("GET"), 3) == 0) // GET a config value
{
// Get a setting value, arguments:
// - form position 4 to 5 contains the setting name (6 is the \0 byte)
cmd[6] = '\0';
if (strncmp_P(&cmd[4], PSTR("IN"), 2) == 0) // intensity
{
btSerial.print(F("GET IN "));
btSerial.println(CFG_INTENSITY);
}
else if (strncmp_P(&cmd[4], PSTR("TI"), 2) == 0) // time interval
{
btSerial.print(F("GET TI "));
btSerial.println(CFG_TIME_ENABLED_INTERVAL / 1000);
}
else if (strncmp_P(&cmd[4], PSTR("DI"), 2) == 0) // date interval
{
btSerial.print(F("GET DI "));
btSerial.println(CFG_DATE_ENABLED_INTERVAL / 1000);
}
else if (strncmp_P(&cmd[4], PSTR("CI"), 2) == 0) // temperature interval
{
btSerial.print(F("GET CI "));
btSerial.println(CFG_TEMP_ENABLED_INTERVAL / 1000);
}
else if (strncmp_P(&cmd[4], PSTR("DE"), 2) == 0) // date enabled
{
btSerial.print(F("GET DE "));
btSerial.println(CFG_DATE_ENABLED);
}
else if (strncmp_P(&cmd[4], PSTR("CE"), 2) == 0) // temperature enabled
{
btSerial.print(F("GET CE "));
btSerial.println(CFG_TEMP_ENABLED);
}
else if (strncmp_P(&cmd[4], PSTR("SS"), 2) == 0) // scroll speed
{
btSerial.print(F("GET SS "));
btSerial.println(CFG_SCROLL_SPEED / 10);
}
else if (strncmp_P(&cmd[4], PSTR("SW"), 2) == 0) // scroll wait
{
btSerial.print(F("GET SW "));
btSerial.println(CFG_TEMP_ENABLED_INTERVAL / 100);
}
else
{
btSerial.println(F("ERR"));
}
}
else if (strncmp_P(cmd, PSTR("SCR"), 3) == 0) // Scroll Text
{
// position 4 is set to 0 and from position 5 the text to scroll starts
// it might be empty when we want to stop the scroll
if (cmd[4] == '\0')
{
endScrollText(); // do not scroll text anymore
}
else
{
beginScrollText(&cmd[4]);
}
btSerial.println(F("SCR"));
}
else if (strncmp_P(cmd, PSTR("NXT"), 3) == 0) // Next Time
{
// position 4 is set to 0 and from position 5 the time will start
// Next appointmen starts at this time. Format "2000-01-01T00:00:00"
if (nextTime != 0)
{
delete nextTime;
}
cmd[24] = '\0';
nextTime = new DateTime(&cmd[4]);
btSerial.println(F("NXT"));
}
else // everything else is unknown (ERR)
{
btSerial.println(F("ERR"));
}
}
if (strnlen(scrollBuf, SCROLL_BUFF_SIZE) > 0)
{
scrollText();
}
else
{
showClock();
}
if (nextTime != 0)
{
TimeSpan ts = *nextTime - rtc.now();
uint8_t devices = led.getDeviceCount();
if (ts.totalseconds() < -30)
{
// time is up
// 30 seconds past the time, stop blinking and delete time
delete nextTime;
nextTime = 0;
stopBlink();
// also now we can stop scrolling
memset(scrollBuf, 0, sizeof(scrollBuf) / sizeof(scrollBuf[0]));
}
else if (ts.totalseconds() < 30)
{
// only 30 seconds left, alarm by blinking
startBlink();
}
else if (ts.totalseconds() < ((devices + 1) * 60))
{
// only some minutes left, start warning
for (uint16_t device = 0; device < devices; device++)
{
if (ts.totalseconds() < (device + 1) * 60)
{
led.transform(device, device, MD_MAX72XX::TINV);
}
}
}
}
}
doBlink();
led.update();
delay(5); // sleep some tme
}