#include <SD.h> // sd card
#include "RTClib.h" // real time clock
#include "HT16K33.h" // 4 digit displays vvv
#include <PololuLedStrip.h> // led array
#include <Adafruit_CAP1188.h> // touch sensors vvv
#include <Wire.h>
#include <SPI.h>
#include <Arduino.h> // encoder vvv (new and shiny vers!)
#include <BasicEncoder.h>
#include <TimerOne.h>
int sdCardPin = 53; // sd card, cs pin
File file; // sd card [get file] name
RTC_Millis rtc; // real time clock time set?
HT16K33 leftDigits(0x71); // 4 digit i2c address & name define
HT16K33 rightDigits(0x70); // 4 digit i2c address & name define
uint32_t counter = 0; // 4 digit ?
#define CAP1188_SENSITIVITY 0x1F
#define CAP1188_RESET 9 // pin 9, run this line down all 3 sensors
Adafruit_CAP1188 cap = Adafruit_CAP1188(CAP1188_RESET); // touch sensors vvv
Adafruit_CAP1188 touchSensor1(0x28); // i2c addresses + name defines vvv
Adafruit_CAP1188 touchSensor2(0x29);
Adafruit_CAP1188 touchSensor3(0x2B);
BasicEncoder encoder(3, 2); // rotary pins
#define SW 4 // roatary sw pin
unsigned long rotaryPressTime; // counter/timer for how long it's held down
bool rotaryWaitForReset = false; // if it's stopped being held down
int encoderPrev = 0; // resets itsself to 0
const int writeLockPin = 7; // write/lock switch
bool writeMode = false; // bool for lock/unlock
bool touchButtons = false; // diff from writeMode to prevent 'overwrite day w/ data already'
const int dayPlusPin = 5; // day +-1 buttons
bool dayPlusState; // as in prev state, for tracking press and un-press
const int dayMinusPin = 6; // day +-1 buttons
bool dayMinusState;
int displayDayPrev = 0;
int displayDay = 0; // first day (starting at 0)
void timer_service() { // timer for rotary enc initiation
encoder.service();
}
PololuLedStrip<12> ledStrip; // led input pin (only others 5v + gnd)
#define ledCount 24 // len(array)
rgb_color colors[ledCount]; //
char colorLetter[26] = "bbbbbbbbbbbbbbbbbbbbbbbb" // 24 of them
"\0"; // keeps it from saving gibberish afterwards
char colorTouching[26] = "000000000000000000000000" // current log state
"\0";
char colorTouchingPrev[26] = "000000000000000000000000" // checking if state has changed
"\0";
bool touchJustOn = false;
void setup() {
Serial.begin(9600);
Serial.println("cool squares");
Wire.begin();
Wire.setClock(100000); // 4 digits begin vvv
leftDigits.begin();
rightDigits.begin();
leftDigits.displayOn();
rightDigits.displayOn();
pinMode(writeLockPin, INPUT); // switch (button)
pinMode(dayPlusPin, INPUT); // button
pinMode(dayMinusPin, INPUT); // button
pinMode(SW, INPUT_PULLUP); // rotary sw
Timer1.initialize(1000); // !!!!!how fast u can scroll on rotary!
Timer1.attachInterrupt(timer_service); // ^^^
file = SD.open("FILE6.txt", FILE_WRITE); // open "file.txt" to write data
pinMode(sdCardPin, OUTPUT); // sd card debug messages vvv
if (!SD.begin(sdCardPin)) {
Serial.println("Could not initialize SD card.");
} else {
Serial.println("yup there's an sd card");
}
#ifndef ESP8266 // rtc vvv
while (!Serial)
; // wait for serial port to connect. Needed for native USB
#endif
rtc.begin(DateTime(F(__DATE__), F(__TIME__))); // January 21, 2014 at 3am == rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
DateTime future(1600560000); // 9/20/20 (+ displayDay * 86400L)
DateTime dt0(2020, 9, 20, 0, 0, 0);
int maxDay = (rtc.now().unixtime() - 1600560000) / 86400L; // days from 09/20/2020 --> [current day]
Serial.print("Currently this many days: ");
Serial.println(maxDay);
if (!touchSensor1.begin(0x28)) { // error messages- have to be here in some form for them to work?
Serial.println("28 CAP1188 not found");
touchSensor1.writeRegister(CAP1188_SENSITIVITY, 0x1F); // 2x sensitivity THIS SEEMS TO WORK THE BEST FOR 3.5" plate sensors
} else {
Serial.println("28 CAP1188 found!");
}
if (!touchSensor2.begin(0x29)) {
Serial.println("29 CAP1188 not found");
touchSensor2.writeRegister(CAP1188_SENSITIVITY, 0x1F);
} else {
Serial.println("29 CAP1188 found!");
}
if (!touchSensor3.begin(0x2B)) {
Serial.println("2b CAP1188 not found");
touchSensor2.writeRegister(CAP1188_SENSITIVITY, 0x1F);
} else {
Serial.println("2b CAP1188 found!");
}
if (!findDaysData()) {
turnAllGrey();
}
digitsDisplayDay(0); // digits to correct date
updateLEDs();
}
void loop() {
//finding out displayDate (scrollers always on) vvv
bool dayPlusPrev = dayPlusState; // day +1 button
dayPlusState = digitalRead(dayPlusPin); // const updating reads of
if (dayPlusState == LOW && dayPlusPrev == HIGH && displayDay >= 0) { // +1 button has just been held & released
displayDay += 1;
}
bool dayMinusPrev = dayMinusState; // day -1 button
dayMinusState = digitalRead(dayMinusPin);
if (dayMinusState == LOW && dayMinusPrev == HIGH && displayDay > 0) { // -1 button has just been held and released
displayDay -= 1;
}
if (encoder.get_change()) { // encoder & rotary same thing. if rotated...
int encoderNow = encoder.get_count(); // gives const read of encoder position
if (encoderNow > encoderPrev) { // new date greater than prev & not gonna take us above max
displayDay += 1; // only using increments of 1 bc sometimes it'd skip
} else if (displayDay > 0) { //new date less than prev & not gonna take us below zero
displayDay -= 1;
}
encoderPrev = encoderNow;
}
// so now if any of those have been updated.... vvv
if (displayDay != displayDayPrev) {
printDate(displayDay); // serial
digitsDisplayDay(displayDay); // updates 4 dig displays. not dependant on lock/unlock or data/noData
if (!findDaysData()) { // if NoData -- returns true or false if data
turnAllGrey();
if (writeMode) { // if noData & we ARE writing, then buttons on (call on dayChange & writeLock change)
touchButtons = true;
touchJustOn = true;
}
} else {
touchButtons = false; // else theres data sorry :( press button to overwrite first (called in button press func)
}
}
displayDayPrev = displayDay;
if (digitalRead(writeLockPin) == HIGH) { // HIGH is at top of breadboard and == unlocked
if (!writeMode && !findDaysData()) { // just switched from ! to on
touchButtons = true; // call on writeLock change (& day change)
touchJustOn = true;
}
writeMode = true;
if (touchButtons == true) { // butons had to get turned on from somewhere first...
touchButtonsOn(); // gets to this everytime in loop
}
} else { // if LOW
if (writeMode && !findDaysData()) { // just switched from on to !
touchButtons = false;
turnAllGrey();
} else if (writeMode && findDaysData()) {
}
writeMode = false; // it just got locked
}
// vvv this just for rotary press (must b unlocked)!!!
int currentRotaryState = digitalRead(SW); // read of rotary encoder to see if it's being pressed, 1=up, 0=down
if (!currentRotaryState && !rotaryWaitForReset) { // if held down && not waiting to reset (Just been pressed, reset initialized false)
rotaryPressTime = millis(); // timer start
rotaryWaitForReset = true; // reset start waiting
}
if (currentRotaryState && rotaryWaitForReset) { // if not held down & it Is waiting for reset,
rotaryWaitForReset = false; // it was just held and released. Exit func
if (millis() - rotaryPressTime > 1500 && !writeMode) {
printFile(); // optional for final- read out the file into serial
}
if (millis() - rotaryPressTime > 1500 && writeMode && findDaysData()) { // LONG
overWriteDay(); // there was data. if noData, no need for this
} else if (millis() - rotaryPressTime > 50 && writeMode && !findDaysData() && colorLetter[0] != 'g') { // SHORT
saveDay(); // there's no data & i want to save
touchButtons = false;
}
}
delay(5);
}
void updateLEDs() {
ledStrip.write(colors, ledCount);
}
void digitsDisplayDay(int displayDay) { // displays regradless of lockBool & data/noData
DateTime future(1600560000 + displayDay * 86400L); // 09/20/20 + numb days since * len(day) in seconds
leftDigits.displayColon(true);
leftDigits.displayInt(future.month() * 100 + future.day());
rightDigits.displayInt(future.year());
}
bool findDaysData() { // func call to READ FILE + FIND CORRECT TEXT LINE (from displayDay != displayDayPrev)
file = SD.open("FILE6.txt", FILE_READ);
int lenFile = file.size();
int lineNumb = 0;
while (lineNumb < lenFile) { // loop each line of file
file.seek(lineNumb); // find start index of line
char character; // resetting vars...
int checkNumb = 0;
int deciPlace = 1000;
for (unsigned int i = 0; i < 4; i++) { // loop the first 4 indexs of each line
character = file.read();
if (character != 48) { // if not zero (leaving in a fun bug for 2060 where checkNumb == 14456 (xxxx))
checkNumb += (character - 48) * deciPlace; // ascii translations
}
deciPlace = deciPlace / 10; // just getting rid of extra '0's. char --> int
}
if (checkNumb == displayDay) { // THIS DAY HAS DATA!
showDayDataOnLEDs(lineNumb); // new func call to init LEDs read from file
file.close();
return true;
}
lineNumb += 42; // go to start of next line. (idk if lineNumb & i could be same var?)
}
Serial.println("no data for day :(");
file.close();
return false;
}
void showDayDataOnLEDs(int lineNumb) { // func call to UPDATE LEDS READ FROM FILE
file.seek(lineNumb + 16); // move from start of line to where b&w array starts
char character;
for (unsigned int i = 0; i < 24; i++) { // next 24 is the b&w array
character = file.read();
colorLetter[i] = character;
}
char tmp2[26] = "bbbbbbbbbbbbbbbbbbbbbbbb"
"\0";
for (uint8_t i = 0; i < 24; i++) { // strrev()??
tmp2[i] = colorLetter[i]; // I PUT THE LED STRIP IN BACKWARDS P SURE NOO
} // buttons & led array line up but colorLetter is reverse of those
int z2 = 23; // this just reverses colorLetter right b4 the save
for (uint8_t i = 0; i < 24; i++) { // :(
colorLetter[i] = tmp2[z2];
z2 -= 1;
}
for (unsigned int i = 0; i < 24; i++) {
if (colorLetter[i] == 'w') { // section bc colors is a weird pololulu thing :( vvv
colors[i] = rgb_color(50, 50, 50);
} else if (colorLetter[i] == 'b') {
colors[i] = rgb_color(0, 0, 0);
}
}
updateLEDs();
Serial.println(colorLetter);
return;
}
void turnAllGrey() { // func call for whenever all LEDS DISPLAY GREY
for (unsigned int i = 0; i < 24; i++) {
colorLetter[i] = 'g';
colors[i] = rgb_color(1, 1, 1);
}
updateLEDs();
}
void touchButtonsOn() {
rgb_color w;
w.red = 50;
w.green = 50;
w.blue = 50;
rgb_color b; // ideally want to declare these outside the loop- moving later xx
b.red = 0;
b.green = 0;
b.blue = 0;
rgb_color g;
g.red = 1;
g.green = 1;
g.blue = 1;
uint8_t touched1 = touchSensor1.touched();
uint8_t touched2 = touchSensor2.touched();
uint8_t touched3 = touchSensor3.touched();
for (uint8_t i = 0; i < 8; i++) { // for the 8 inputs...
if (touched1 & (1 << i)) { // if it's touched && shift i to i^2 ?
colorTouching[i] = '1'; // then it should register as "touching" const while being touched.
} else {
colorTouching[i] = '0'; // const registers as not if not
}
}
for (uint8_t i = 0; i < 8; i++) { // 0-7, 8-15, 16-23
if (touched2 & (1 << i)) {
colorTouching[i + 8] = '1';
} else {
colorTouching[i + 8] = '0';
}
}
for (uint8_t i = 0; i < 8; i++) {
if (touched3 & (1 << i)) {
colorTouching[i + 16] = '1';
} else {
colorTouching[i + 16] = '0';
}
}
for (uint8_t i = 0; i < 24; i++) { // for the 24 inputs...
if (colorTouching[i] != colorTouchingPrev[i] && colorTouching[i] == '1' && !touchJustOn) { // its touchState has changed, & that state is To BeingTouched
if (colorLetter[i] == 'w') { // if it was white now it's black
colorLetter[i] = 'b';
colors[i] = b;
} else if (colorLetter[i] == 'b') { // if it was black now its white
colorLetter[i] = 'w';
colors[i] = w;
} else if (colorLetter[i] == 'g') { // if it was grey: currently touching = b, all others = w
for (uint8_t k = 0; k < 24; k++) {
colorLetter[k] = 'w';
colors[k] = w;
}
colorLetter[i] = 'b';
colors[i] = b;
}
}
}
if (touchJustOn) {
touchJustOn = false;
}
for (uint8_t i = 0; i < 24; i++) {
colorTouchingPrev[i] = colorTouching[i]; // now log this state to b able 2 check next time if its changed
}
updateLEDs();
}
void overWriteDay() { // longPress && writeMode (&& dayData == true)
file = SD.open("FILE6.txt", FILE_READ); // stolen from findDaysData... perhaps could call this from there but eh idc
int lenFile = file.size();
int lineNumb = 0;
while (lineNumb < lenFile) { // loop each line of file
file.seek(lineNumb); // find start index of line
char character; // resetting vars...
int checkNumb = 0;
int deciPlace = 1000;
for (unsigned int i = 0; i < 4; i++) { // loop the first 4 indexs of each line
character = file.read();
if (character != 48) { // if not zero
checkNumb += (character - 48) * deciPlace; // ascii translations
}
deciPlace = deciPlace / 10; // just getting rid of extra '0's. char --> int
}
if (checkNumb == displayDay) { // FOUND THE CORRECT DAY
file.close();
file = SD.open("FILE6.txt", O_RDWR); // not sure, but this is "overwrite" bc FILE_WRITE doesn't do it
file.seek(lineNumb);
for (unsigned int i = 0; i < 40; i++) { // the "i"'s move the file's current read position apparentley
file.print("x"); // can't figure out how to delete! but this overwrites it fine
} // .txt file could be easily scrubbed for this later
file.close();
turnAllGrey();
touchButtons = true;
touchJustOn = true;
return;
}
lineNumb += 42;
}
}
void saveDay() {
char tmp[26] = "bbbbbbbbbbbbbbbbbbbbbbbb"
"\0";
for (uint8_t i = 0; i < 24; i++) { // strrev()??
tmp[i] = colorLetter[i]; // I PUT THE LED STRIP IN BACKWARDS P SURE NOO
} // buttons & led array line up but colorLetter is reverse of those
int z = 23; // this just reverses colorLetter right b4 the save
for (uint8_t i = 0; i < 24; i++) { // :(
colorLetter[i] = tmp[z];
z -= 1;
}
DateTime future(1600560000 + displayDay * 86400L); // 09/20/20 + numb days since * len(day) in seconds
file = SD.open("FILE6.txt", FILE_WRITE);
if (file) {
if (displayDay < 10) { // 0001 vvv, print actual numb
file.print("000");
} else if (displayDay < 100) {
file.print("00");
} else if (displayDay < 1000) {
file.print("0");
}
file.print(displayDay); // mo/dy/year with zeros vvv
file.print(" ");
if (future.month() < 10) {
file.print('0');
}
file.print(future.month(), DEC);
file.print('/');
if (future.day() < 10) {
file.print('0');
}
file.print(future.day(), DEC);
file.print('/');
file.print(future.year(), DEC);
file.print(" ");
file.println(colorLetter); // 24 array
file.close();
} else {
Serial.println("Could not open file (writing).");
}
Serial.print("Saved!");
Serial.println(colorLetter);
}
void printDate(int displayDay) { // func call for SERIAL PRINT
if (displayDay < 10) {
Serial.print("000");
} else if (displayDay < 100) {
Serial.print("00");
} else if (displayDay < 1000) {
Serial.print("0");
}
Serial.print(displayDay); // this func is just for serial monitor. not saving to SD.
Serial.print(" ");
DateTime future(1600560000 + displayDay * 86400L); // 09/20/20 + numb days since * len(day) in seconds
if (future.month() < 10) {
Serial.print('0');
}
Serial.print(future.month(), DEC);
Serial.print('/');
if (future.day() < 10) {
Serial.print('0');
}
Serial.print(future.day(), DEC);
Serial.print('/');
Serial.print(future.year(), DEC);
Serial.print(' ');
}
void printFile() {
if (writeMode) {
DateTime future(1600560000 + displayDay * 86400L); // 09/20/20 + numb days since * len(day) in seconds
file = SD.open("FILE6.txt", FILE_WRITE);
if (file) {
if (displayDay < 10) { // 0001 vvv, print actual numb
file.print("000");
} else if (displayDay < 100) {
file.print("00");
} else if (displayDay < 1000) {
file.print("0");
}
file.print(displayDay); // mo/dy/year with zeros vvv
file.print(" ");
if (future.month() < 10) {
file.print('0');
}
file.print(future.month(), DEC);
file.print('/');
if (future.day() < 10) {
file.print('0');
}
file.print(future.day(), DEC);
file.print('/');
file.print(future.year(), DEC);
file.print(" ");
file.println(colorLetter); // 24 array
file.close();
} else {
Serial.println("Could not open file (writing).");
}
}
file = SD.open("FILE6.txt", FILE_READ); // this is entirely for Serial Print
if (file) {
Serial.println("--- Reading start ---");
char character;
while ((character = file.read()) != -1) { // this while loop reads data stored in "file.txt" and prints it to serial monitor
Serial.print(character);
}
file.close();
} else {
Serial.println("Could not open file (reading).");
}
}