x = auto_intensity_value; //uses the last light sensor intensity settings, prevents display from constantly flicking between global and light sensor value
}
else {
x = intensity;
}
//fade from global intensity to 1
for (byte i = x; i > 0; i--) {
for (byte address = 0; address < 4; address++) {
lc.setIntensity(address, i);
}
delay(30); //change this to change fade down speed
}
clear_display(); //clear display completely (off)
//reset intentsity to global val
for (byte address = 0; address < 4; address++) {
lc.setIntensity(address, x);
}
}
//power up led test & display software version number
void printver() {
byte i = 0;
const char ver_a[] = "Vers 1.1";
const char ver_b[] = " Ratti3 ";
//test all leds.
for (byte x = 0; x <= 31; x++) {
for (byte y = 0; y <= 7; y++) {
plot(x, y, 1);
}
}
delay(500);
fade_down();
while (ver_a[i]) {
puttinychar((i * 4), 1, ver_a[i]);
delay(35);
i++;
}
delay(700);
fade_down();
i = 0;
while (ver_b[i]) {
puttinychar((i * 4), 1, ver_b[i]);
delay(35);
i++;
}
delay(700);
fade_down();
}
// Copy a 3x5 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
// This is unoptimized and simply uses plot() to draw each dot.
void puttinychar(byte x, byte y, char c) {
byte dots;
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
//show the time in small 3x5 characters with seconds display
void small_mode() {
char textchar[8]; // the 16 characters on the display
byte mins = 100; //mins
byte secs = rtc[0]; //seconds
byte old_secs = secs; //holds old seconds value - from last time seconds were updated o display - used to check if seconds have changed
cls();
//run clock main loop as long as run_mode returns true
while (run_mode()) {
//Check light levels for turning on/off matrix
if (light_count > 100) {
light();
light_count = 0;
}
light_count++;
get_time();
//check for button press
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
return;
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
//if secs changed then update them on the display
secs = rtc[0];
if (secs != old_secs) {
//secs
char buffer[3];
itoa(secs, buffer, 10);
//fix - as otherwise if num has leading zero, e.g. "03" secs, itoa converts this to chars with space "3 ".
if (secs < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
puttinychar( 20, 1, ':'); //seconds colon
puttinychar( 24, 1, buffer[0]); //seconds
puttinychar( 28, 1, buffer[1]); //seconds
old_secs = secs;
}
//if minute changes change time
if (mins != rtc[1]) {
//reset these for comparison next time
mins = rtc[1];
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//set characters
char buffer[3];
itoa(hours, buffer, 10);
//fix - as otherwise if num has leading zero, e.g. "03" hours, itoa converts this to chars with space "3 ".
if (hours < 10) {
buffer[1] = buffer[0];
//if we are in 12 hour mode blank the leading zero.
if (ampm) {
buffer[0] = ' ';
}
else {
buffer[0] = '0';
}
}
//set hours chars
textchar[0] = buffer[0];
textchar[1] = buffer[1];
textchar[2] = ':';
itoa (mins, buffer, 10);
if (mins < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//set mins characters
textchar[3] = buffer[0];
textchar[4] = buffer[1];
//do seconds
textchar[5] = ':';
secs = rtc[0];
itoa(secs, buffer, 10);
//fix - as otherwise if num has leading zero, e.g. "03" secs, itoa converts this to chars with space "3 ".
if (secs < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//set seconds
textchar[6] = buffer[0];
textchar[7] = buffer[1];
//print each char
for (byte x = 0; x < 6 ; x++) {
puttinychar( x * 4, 1, textchar[x]);
}
}
delay(50);
}
fade_down();
}
// show the time in 5x7 characters
void basic_mode() {
cls();
char buffer[3]; //for int to char conversion to turn rtc values into chars we can print on screen
byte offset = 0; //used to offset the x postition of the digits and centre the display when we are in 12 hour mode and the clock shows only 3 digits. e.g. 3:21
//do 12/24 hour conversion if ampm set to 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//do offset conversion
if (ampm && hours < 10) {
offset = 2;
}
//set the next minute we show the date at
//set_next_date();
// initially set mins to value 100 - so it wll never equal rtc[1] on the first loop of the clock, meaning we draw the clock display when we enter the function
byte secs = 100;
byte mins = 100;
int count = 0;
//run clock main loop as long as run_mode returns true
while (run_mode()) {
//Check light levels for turning on/off matrix
if (light_count > 4000) {
light();
light_count = 0;
}
light_count++;
//get the time from the clock chip
get_time();
//check for button press
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
return;
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
//check whether it's time to automatically display the date
//check_show_date();
//draw the flashing : as on if the secs have changed.
if (secs != rtc[0]) {
//update secs with new value
secs = rtc[0];
//draw :
plot (15 - offset, 2, 1); //top point
plot (15 - offset, 5, 1); //bottom point
count = 400;
}
//if count has run out, turn off the :
if (count == 0) {
plot (15 - offset, 2, 0); //top point
plot (15 - offset, 5, 0); //bottom point
}
else {
count--;
}
//re draw the display if button pressed or if mins != rtc[1] i.e. if the time has changed from what we had stored in mins, (also trigggered on first entering function when mins is 100)
if (mins != rtc[1]) {
//update mins and hours with the new values
mins = rtc[1];
hours = rtc[2];
//adjust hours of ampm set to 12 hour mode
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
itoa(hours, buffer, 10);
//if hours < 10 the num e.g. "3" hours, itoa coverts this to chars with space "3 " which we dont want
if (hours < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//print hours
//if we in 12 hour mode and hours < 10, then don't print the leading zero, and set the offset so we centre the display with 3 digits.
if (ampm && hours < 10) {
offset = 2;
//if the time is 1:00am clear the entire display as the offset changes at this time and we need to blank out the old 12:59
if ((hours == 1 && mins == 0) ) {
cls();
}
}
else {
//else no offset and print hours tens digit
offset = 0;
//if the time is 10:00am clear the entire display as the offset changes at this time and we need to blank out the old 9:59
byte digits_old[4] = {99, 99, 99, 99}; //old values we store time in. Set to somthing that will never match the time initially so all digits get drawn wnen the mode starts
byte digits_new[4]; //new digits time will slide to reveal
byte digits_x_pos[4] = {25, 19, 7, 1}; //x pos for which to draw each digit at
char old_char[2]; //used when we use itoa to transpose the current digit (type byte) into a char to pass to the animation function
char new_char[2]; //used when we use itoa to transpose the new digit (type byte) into a char to pass to the animation function
//old_chars - stores the 5 day and date suffix chars on the display. e.g. "mon" and "st". We feed these into the slide animation as the current char when these chars are updated.
//We sent them as A initially, which are used when the clocl enters the mode and no last chars are stored.
//char old_chars[6] = "AAAAA";
//plot the clock colon on the display
cls();
putnormalchar(13, 0, ':', font_style, font_cols);
byte old_secs = rtc[0]; //store seconds in old_secs. We compare secs and old secs. WHen they are different we redraw the display
//run clock main loop as long as run_mode returns true
while (run_mode()) {
//Check light levels for turning on/offf matrix
if (light_count > 5000) {
light();
light_count = 0;
}
light_count++;
get_time();
//check for button press
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
return;
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
//if secs have changed then update the display
if (rtc[0] != old_secs) {
old_secs = rtc[0];
//do 12/24 hour conversion if ampm set to 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//split all date and time into individual digits - stick in digits_new array
//draw initial screen of all chars. After this we just draw the changes.
//compare digits 0 to 3 (mins and hours)
for (byte i = 0; i <= 3; i++) {
//see if digit has changed...
if (digits_old[i] != digits_new[i]) {
//run 9 step animation sequence for each in turn
for (byte seq = 0; seq <= 8 ; seq++) {
//convert digit to string
itoa(digits_old[i], old_char, 10);
itoa(digits_new[i], new_char, 10);
//if set to 12 hour mode and we're on digit 2 (hours tens mode) then check to see if this is a zero. If it is, blank it instead so we get 2.00pm not 02.00pm
//save digita array tol old for comparison next loop
for (byte i = 0; i <= 3; i++) {
digits_old[i] = digits_new[i];
}
}//secs/oldsecs
}//while loop
fade_down();
}
//called by slide
//this draws the animation of one char sliding on and the other sliding off. There are 8 steps in the animation, we call the function to draw one of the steps from 0-7
//inputs are are char x and y, animation frame sequence (0-7) and the current and new chars being drawn.
// To slide one char off and another on we need 9 steps or frames in sequence...
// seq# 0123456 <-rows of the display
// | |||||||
// seq0 0123456 START - all rows of the display 0-6 show the current characters rows 0-6
// seq1 012345 current char moves down one row on the display. We only see it's rows 0-5. There are at display positions 1-6 There is a blank row inserted at the top
// seq2 6 01234 current char moves down 2 rows. we now only see rows 0-4 at display rows 2-6 on the display. Row 1 of the display is blank. Row 0 shows row 6 of the new char
// seq3 56 0123
// seq4 456 012 half old / half new char
// seq5 3456 01
// seq6 23456 0
// seq7 123456
// seq8 0123456 END - all rows show the new char
//from above we can see...
//currentchar runs 0-6 then 0-5 then 0-4 all the way to 0. starting Y position increases by 1 row each time.
//new char runs 6 then 5-6 then 4-6 then 3-6. starting Y position increases by 1 row each time.
//if sequence number is below 7, we need to draw the current char
plot(x + col, y + start_y, 0); //else plot led off
}
start_y++;//add one to y so we draw next row one down
}
}
//draw a blank line between the characters if sequence is between 1 and 7. If we don't do this we get the remnants of the current chars last position left on the display
if (sequence >= 1 && sequence <= 8) {
for (byte col = 0; col < 5; col++) {
plot(x + col, y + (sequence - 1), 0); //the y position to draw the line is equivalent to the sequence number - 1
}
}
//if sequence is above 2, we also need to start drawing the new char
if (sequence >= 2) {
//work out char
byte dots;
if (new_c >= 'A' && new_c <= 'Z' ) {
new_c = (new_c - 'A') + 27; // A-Z maps to 27-52
}
else if (new_c >= 'a' && new_c <= 'z') {
new_c &= 0x1F; // a-z maps to 1-26
}
else if (new_c >= '0' && new_c <= '9') {
new_c = (new_c - '0') + 59; // 0-9 maps to 59-68
}
else if (new_c == ' ') {
new_c = 0; // space
}
else if (new_c == '.') {
new_c = 27; // full stop
}
else if (new_c == '\'') {
new_c = 28; // single quote mark
}
else if (new_c == ':') {
new_c = 29; // colon
}
else if (new_c == '>') {
new_c = 30; // >
}
byte newcharrowmin = 6 - (sequence - 2); //minimumm row num to draw for new char - this generates an output of 6 to 0 when fed sequence numbers 2-8. This is the minimum row to draw for the new char
byte start_y = 0; //y position to start at - is same as sequence number. we inc it each row
//plot each row up from row minimum (calculated by sequence number) up to 6
for (byte newcharrow = newcharrowmin; newcharrow <= 6; newcharrow++) {
for (byte col = 0; col < 5; col++) {
dots = pgm_read_byte_near(&myfont[new_c][col]);
if (dots & (64 >> newcharrow))
plot(x + col, y + start_y, 1); //plot led on
else
plot(x + col, y + start_y, 0); //else plot led off
}
start_y++;//add one to y so we draw next row one down
}
}
}
//print a clock using words rather than numbers
void word_clock() {
cls();
//potentially 3 lines to display
char str_a[8];
char str_b[9];
char str_c[8];
//byte hours_y, mins_y; //hours and mins and positions for hours and mins lines
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
get_time(); //get the time from the clock chip
byte old_mins = 100; //store mins in old_mins. We compare mins and old mins & when they are different we redraw the display. Set this to 100 initially so display is drawn when mode starts.
byte mins;
//run clock main loop as long as run_mode returns true
while (run_mode()) {
//check for button press
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
get_time(); //get the time from the clock chip
mins = rtc[1]; //get mins
//if mins is different from old_mins - redraw display
if (mins != old_mins) {
//update old_mins with current mins value
old_mins = mins;
//reset these for comparison next time
mins = rtc[1];
hours = rtc[2];
//make hours into 12 hour format
if (hours > 12) {
hours = hours - 12;
}
if (hours == 0) {
hours = 12;
}
//split mins value up into two separate digits
int minsdigit = rtc[1] % 10;
byte minsdigitten = (rtc[1] / 10) % 10;
const char past[] = "PAST";
const char to[] = "TO";
const char half[] = "HALF";
const char quar[] = "QUARTER";
const char oclk[] = "O'CLOCK";
//if both mins are zero, i.e. it is on the hour, the top line reads "hours" and bottom line reads "o'clock"
if (minsdigitten == 0 && minsdigit == 0 ) {
progmem_numbers(0, hours - 1);
strcpy (str_a, words);
strcpy (str_b, oclk);
strcpy (str_c, "");
}
//if mins <= 10 , then top line has to read "minsdigti past" and bottom line reads hours
else if (mins < 10) {
progmem_numbers(0, minsdigit - 1);
strcpy (str_a, words);
strcpy (str_b, past);
progmem_numbers(0, hours - 1);
strcpy (str_c, words);
}
//if mins = 10, cant use minsdigit as above, so soecial case to print 10 past /n hour.
if (mins == 10) {
progmem_numbers(0, 9);
strcpy (str_a, words);
strcpy (str_b, past);
progmem_numbers(0, hours - 1);
strcpy (str_c, words);
}
else if (mins == 15) {
strcpy (str_a, quar);
strcpy (str_b, past);
progmem_numbers(0, hours - 1);
strcpy (str_c, words);
}
else if (mins == 20) {
progmem_numbers(1, minsdigitten - 1);
strcpy (str_a, words);
strcpy (str_b, past);
progmem_numbers(0, hours - 1);
strcpy (str_c, words);
}
else if (mins == 30) {
strcpy (str_a, half);
strcpy (str_b, past);
progmem_numbers(0, hours - 1);
strcpy (str_c, words);
}
else if (mins == 40) {
progmem_numbers(1, 1);
strcpy (str_a, words);
strcpy (str_b, to);
if (hours == 12) {
progmem_numbers(0, hours - hours);
}
else {
progmem_numbers(0, hours);
}
strcpy (str_c, words);
}
else if (mins == 50) {
progmem_numbers(0, 9);
strcpy (str_a, words);
strcpy (str_b, to);
if (hours == 12) {
progmem_numbers(0, hours - hours);
}
else {
progmem_numbers(0, hours);
}
strcpy (str_c, words);
}
else if (mins == 45) {
strcpy (str_a, quar);
strcpy (str_b, to);
if (hours == 12) {
progmem_numbers(0, hours - hours);
}
else {
progmem_numbers(0, hours);
}
strcpy (str_c, words);
}
//if time is not on the hour - i.e. both mins digits are not zero,
//then make first line read "hours" and 2 & 3rd lines read "minstens" "mins" e.g. "three /n twenty /n one"
else if (minsdigitten != 0 && minsdigit != 0) {
progmem_numbers(0, hours - 1);
strcpy (str_a, words);
//if mins is in the teens, use teens from the numbers array for the 2nd line, e.g. "fifteen"
if (mins >= 11 && mins <= 19) {
progmem_numbers(0, mins - 1);
strcpy (str_a, words);
strcpy (str_b, past);
progmem_numbers(0, hours - 1);
strcpy (str_c, words);
}
else if (mins > 50) {
progmem_numbers(0, 60 - (mins + 1));
strcpy (str_a, words);
strcpy (str_b, to);
if (hours == 12) {
progmem_numbers(0, hours - 12);
strcpy (str_c, words);
}
else {
progmem_numbers(0, hours);
strcpy (str_c, words);
}
}
else {
progmem_numbers(1, minsdigitten - 1);
strcpy (str_b, words);
progmem_numbers(0, minsdigit - 1);
strcpy (str_c, words);
}
}
}//end working out time
//run in a loop
//print line a "twelve"
byte len = 0;
while (str_a[len]) {
len++;
}; //get length of message
byte offset_top = (31 - ((len - 1) * 4)) / 2; //
//plot hours line
byte i = 0;
while (str_a[i]) {
puttinychar((i * 4) + offset_top, 1, str_a[i]);
i++;
}
//hold display but check for button presses
int counter = 1000;
while (counter > 0) {
//check for button press
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
delay(1);
counter--;
}
fade_down();
//print line b
len = 0;
while (str_b[len]) {
len++;
}; //get length of message
offset_top = (31 - ((len - 1) * 4)) / 2;
i = 0;
while (str_b[i]) {
puttinychar((i * 4) + offset_top, 1, str_b[i]);
i++;
}
//hold display but check for button presses
counter = 1000;
while (counter > 0){
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
delay(1);
counter--;
}
fade_down();
//print line c if there.
len = 0;
while (str_c[len]) {
len++;
}; //get length of message
offset_top = (31 - ((len - 1) * 4)) / 2;
i = 0;
while (str_c[i]) {
puttinychar((i * 4) + offset_top, 1, str_c[i]);
i++;
}
counter = 1000;
while (counter > 0){
//check for button press
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
delay(1);
counter--;
}
fade_down();
//hold display blank but check for button presses before starting again.
counter = 1000;
while (counter > 0) {
//check for button press
if (buttonA.uniquePress()) {
switch_mode();
return;
}
if (buttonB.uniquePress()) {
display_date();
}
if (buttonC.uniquePress()) {
display_thp();
return;
}
delay(1);
counter--;
//Check light levels for turning on/off matrix
if (light_count > 2000) {
light();
light_count = 0;
}
light_count++;
}
}
fade_down();
}
//used by word mode to retrieve words from progmem, m : 0 = numbers, 1 = numberstens. i = index
set_devices(false, 0); //Call sleep routine to turn on matrix, applies when light is high enough
}
//this runs if auto_intensity is true and display is not off, it defines the intensity based on the light sensor and calls set_devices to set intensity.
if (auto_intensity && !shut) {
switch(lx) {
case 0:
auto_intensity_value = 0;
break;
case 2:
auto_intensity_value = 1;
break;
case 3 ... 4:
auto_intensity_value = 2;
break;
case 5 ... 6:
auto_intensity_value = 3;
break;
case 7 ... 10:
auto_intensity_value = 4;
break;
case 11 ... 20:
auto_intensity_value = 5;
break;
case 21 ... 40:
auto_intensity_value = 6;
break;
case 41 ... 60:
auto_intensity_value = 7;
break;
case 61 ... 100:
auto_intensity_value = 8;
break;
case 101 ... 150:
auto_intensity_value = 9;
break;
case 151 ... 200:
auto_intensity_value = 10;
break;
case 201 ... 250:
auto_intensity_value = 11;
break;
case 251 ... 300:
auto_intensity_value = 12;
break;
case 301 ... 350:
auto_intensity_value = 13;
break;
case 351 ... 400:
auto_intensity_value = 14;
break;
case 401 ... 65535:
auto_intensity_value = 15;
break;
}
set_devices(true, auto_intensity_value);
//this is useful for help setting the values above
//Serial.println(lx);
}
}
//Routine called by light() to turn on/off matrix and by auto light intensity to adjust device intensity. bool m = true (light intensity), false (matrix on/off), byte i = intensity
void set_devices(bool m, byte i) {
int devices = lc.getDeviceCount();
for (int address = 0; address < devices; address++) {
if (!m) {
//turns on/off matrix
lc.shutdown(address, shut);
}
else {
//sets matrix intensity
lc.setIntensity(address, i);
}
}
}
//Routine to set display on/off options (0 = normal, 1 = always on, 2 - 4 = from specific time)