#include <Wire.h>
#include "SPI.h"
#include "DHTesp.h"
#include <TM1637.h>
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"
#include <Keypad.h>
#include "SD.h"
#include <Adafruit_SSD1306.h>

#define Pin_DHT22_SDA 15
#define Pin_TM4D_CLK 25
#define Pin_TM4D_DIO 26
#define Pin_IC_Latch 32
#define Pin_IC_Data 13
#define Pin_IC_Clock 33
uint8_t Pin_KEYPAD_Row[4] = {17, 16, 4, 2};
uint8_t Pin_KEYPAD_Col[4] = {1, 3, 14, 27};

const uint8_t DigitBin[3][10] =
{{0b10000001, 0b11110011, 0b01001001, 0b01100001,
0b00110011, 0b00100101, 0b00000101, 0b11110001,
0b00000001, 0b00100001}, // 0123456789
{0b10000000, 0b11110010, 0b01001000, 0b01100000,
0b00110010, 0b00100100, 0b00000100, 0b11110000,
0b00000000, 0b00100000}, // 0.1.2.3.4.5.6.7.8.9.
{0b00000000, 0b10000000, 0b01000000, 0b00100000,
0b00010000, 0b00001000, 0b00000100, 0b00000010,
0b00000001, 0b11111111}}; // D0,D1,D2,D3,D4,D5,D6,D7,D8,Dall
const char Keys[4][4] =
{{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }};
char* Weekday[] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
String Text[] = {"", "", ""};
// Default values (Servo, Ultrasonic, Loadcell, Temperature, Gyro, ...)
int Set[4][16] = {
{0, 500, 343, 0, 3026, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1900, 2, 5, 330, 120, 120, 120, 9807, 9807, 9807, 0, 255, 0, 0, 0},
{0, 4500, 10, 2100, 788, 7860, 7860, 7860, 4096, 4096, 4096, 4096, 4095, 0, 0, 0},
{0, 4095, 0, 0, 6120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
int Mode[8] = {0, 0, 0, 0, 0, 0, 0, 0};
int Data[5][16] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
float SData[5][16] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};

DHTesp DHT22;
TM1637 TM4D(Pin_TM4D_CLK, Pin_TM4D_DIO);
LiquidCrystal_I2C LCD(0x27,16,2);
Adafruit_SSD1306 OLED(128, 64, &Wire, -1);
RTC_DS1307 RTC; // ** Change to (DS1307 RTC;)
Keypad KEYPAD(makeKeymap(Keys), Pin_KEYPAD_Row, Pin_KEYPAD_Col, 4, 4);
File file, root;

void setup() {
pinMode(Pin_TM4D_CLK, OUTPUT);
pinMode(Pin_TM4D_DIO, OUTPUT);
pinMode(Pin_IC_Latch, OUTPUT);
pinMode(Pin_IC_Clock, OUTPUT);
pinMode(Pin_IC_Data, OUTPUT);
Serial.begin(9600);
RTC.begin();
LCD_start();
OLED_start();
I2C_start();
SDCARD_start();
TM4D.init();
TM4D.set(BRIGHT_TYPICAL);
DHT22.setup(Pin_DHT22_SDA, DHTesp::DHT22);
Mode[0] = 21;
}

void loop() {
char keyin = KEYPAD.getKey();
if (keyin == '#') {KEYPAD_get_option();}
switch (Mode[0]) {
case 10: RTC_set_data(); break;
case 21: MODE_21(); break;
case 22: MODE_22(); break;
case 23: MODE_23(); break;
case 24: MODE_24(); break;
case 25: MODE_25(); break;
case 31: MODE_31(); break;
case 32: MODE_32(); break;
}}

void MODE_21() {
RTC_read_data();
SEGMENT_set_data();
}

void MODE_22() {
RTC_read_data();
TM4D_print_data(Data[1][3], Data[2][3], Data[1][2], Data[2][2]);
}

void MODE_23() {
RTC_read_data();
TM4D_print_data(Data[1][4], Data[2][4], Data[1][5], Data[2][5]);
}

void MODE_24() {
RTC_read_data();
LCD_string_RTC();
LCD_print_text(2, Text[0], 0, 0, 0);
LCD_print_text(2, Text[1], 0, 0, 1);
TM4D_print_data(Data[1][4], Data[2][4], Data[1][5], Data[2][5]);
}

void MODE_25() {
RTC_read_data();
OLED_show_clock();
}

void MODE_31() {
LCD_print_text(3, "Saving DHT data", 0, 0, 0);
SDCARD_save_data("Create", "/DHT.txt", "DHT Data Collection\n", 0);
SDCARD_save_data("Write", "/DHT.txt", "Data are as follow :\n", 0);
for (int j = 0; j < 16; j++) {
RTC_read_data();
SData[0][j] = Data[0][7]/10;
SData[1][j] = Data[0][8]/10;
SDCARD_save_data("Write", "/DHT.txt", "", SData[0][j]);
SDCARD_save_data("Write", "/DHT.txt", "", SData[1][j]);
LCD_print_text(2, ">", 0, j, 1);
delay(990);
}
Mode[0] = 21;
}

void MODE_32() {
SDCARD_load_data("/DHT.txt");
Mode[0] = 21;
}

void LCD_start() {
LCD.init();
LCD.clear();
LCD.backlight();
LCD_print_text(2, "ESP32 RTC", 0, 6, 0);
LCD_print_text(2, "Starting >", 0, 6, 1);
for (int i = 0; i <= 3; i++) {
LCD_print_text(1, "", 3 - i, 15, 1);
delay(990);
}
LCD_print_text(6, "", 0, 0, 0);
}

void LCD_string_RTC() {
String Syr = String(Data[0][1]);
String Smo = String(Data[0][2]);
String Sdy = String(Data[0][3]);
String Shr = String(Data[0][4]);
String Smn = String(Data[0][5]);
String Ssc = String(Data[0][6]);
String Stp = String(int(SData[0][7]));
String Shm = String(int(SData[0][8]));
String Swk = Text[2];
Text[0] = String(Sdy + '-' + Smo + '-' + Syr + " (" + Swk + ")  ");
Text[1] = String(Shr + ':' + Smn + ':' + Ssc + '|' + Stp + 'C' + '|' + Shm + '%');
}

void LCD_print_text(int type, String text, byte value, int col, int row) {
LCD.display();
LCD.setCursor(col, row);
switch (type) {
case 1: LCD.println(value, DEC); break;
case 2: LCD.println(text); break;
case 3: LCD.clear(); LCD.println(text); break;
case 4: LCD.clear(); LCD.println(value); break;
case 5: LCD.println(value, HEX); break;
case 6: LCD.clear(); LCD.noDisplay();
}}

void OLED_start() {
OLED.begin(SSD1306_SWITCHCAPVCC, 0x3C);
OLED_print_text(3, "Welcome!! OLED Clock", 0, 2, 0, 0);
delay(2000);
OLED_print_text(4, "", 0, 0, 0, 0);
}

void OLED_print_text(int type, String text, int value, int size, int col, int row) {
OLED.setTextColor(WHITE);
OLED.setTextSize(size);
OLED.setCursor(col, row);
switch (type) {
case 1: OLED.println(value, DEC); break;
case 2: OLED.println(text); break;
case 3: OLED.clearDisplay(); OLED.println(text); OLED.display(); break;
case 4: OLED.clearDisplay(); OLED.display(); break;
}}

void I2C_start() {
Wire.begin();
LCD_print_text(3, "I2C scanning ...", 0, 0, 0);
delay(2000);
for (byte j = 8; j < 120; j++) {
Wire.beginTransmission (j);
if (Wire.endTransmission () == 0) {
LCD_print_text(2, "Addr :", 0, 0, 1);
LCD_print_text(1, "", j, 6, 1);
LCD_print_text(2, " (0x", 0, 9, 1);
LCD_print_text(5, "", j, 13, 1);
LCD_print_text(2, ")", 0, 15, 1);
delay(2000);
}}
LCD_print_text(6, "", 0, 0, 0);
}

void TM4D_print_data(int D1, int D2, int D3, int D4) {
TM4D.display(0, D1);
TM4D.display(1, D2);
TM4D.display(2, D3);
TM4D.display(3, D4);
}

void RTC_read_data() {
DateTime now = RTC.now();
TempAndHumidity sense = DHT22.getTempAndHumidity();
int year = now.year();
Data[0][1] = year;
Data[1][1] = (year/1000) % 10;
Data[2][1] = (year/100) % 10;
Data[3][1] = (year/10) % 10;
Data[4][1] = year % 10;
int mo = now.month();
Data[0][2] = mo;
Data[1][2] = (mo/10) % 10;
Data[2][2] = mo % 10;
int dy = now.day();
Data[0][3] = dy;
Data[1][3] = (dy/10) % 10;
Data[2][3] = dy % 10;
int hr = now.hour();
Data[0][4] = hr;
Data[1][4] = (hr/10) % 10;
Data[2][4] = hr % 10;
int mn = now.minute();
Data[0][5] = mn;
Data[1][5] = (mn/10) % 10;
Data[2][5] = mn % 10;
int sc = now.second();
Data[0][6] = sc;
Data[1][6] = (sc/10) % 10;
Data[2][6] = sc % 10;
float tp = float(sense.temperature);
SData[0][7] = tp;
Data[0][7] = int(tp);
Data[1][7] = (Data[0][7]/10) % 10;
Data[2][7] = Data[0][7] % 10;
float hm = float(sense.humidity);
SData[0][8] = hm;
Data[0][8] = int(hm);
Data[1][8] = (Data[0][8]/10) % 10;
Data[2][8] = Data[0][8] % 10;
char* wk = Weekday[now.dayOfTheWeek()]; // Change to (now.dayOfWeek)
Text[2] = wk;
}

void RTC_set_data() {
LCD_print_text(2, "Set time (hr:mn)", 0, 0, 0);
KEYPAD_get_input(4);
int hr = 10*Data[0][0] + Data[1][0];
int mn = 10*Data[2][0] + Data[3][0];
LCD_print_text(2, "Set date (dy:mo)", 0, 0, 0);
KEYPAD_get_input(4);
int dy = 10*Data[0][0] + Data[1][0];
int mo = 10*Data[2][0] + Data[3][0];
LCD_print_text(2, "Set year (*year)", 0, 0, 0);
KEYPAD_get_input(4);
int year = 1000*Data[0][0] + 100*Data[1][0] + 10*Data[2][0] + Data[3][0];
RTC.adjust(DateTime(year, mo, dy, hr, mn, 1));
LCD_print_text(6, "", 0, 0, 0);
Mode[0] = 21;
}

void KEYPAD_get_input(int i) {
int j = 0;
String text = "";
Data[0][0] = 0; Data[1][0] = 0; Data[2][0] = 0; Data[3][0] = 0;
while (text.length() < i) {
char keyin = KEYPAD.getKey();
if (keyin >= '0' && keyin <= '9') {
Data[j][0] = 0 - 48 + int(keyin);
LCD_print_text(1, "", Data[0][0], 12, 1);
LCD_print_text(1, "", Data[1][0], 13, 1);
LCD_print_text(1, "", Data[2][0], 14, 1);
LCD_print_text(1, "", Data[3][0], 15, 1);
text += keyin; j++;
}}
LCD_print_text(6, "", 0, 0, 0);
OLED_print_text(4, "", 0, 0, 0, 0);
}

void KEYPAD_get_option() {
LCD_print_text(3, "Operating Mode", 0, 0, 0);
LCD_print_text(2, "Enter 10-99>", 0, 0, 1);
KEYPAD_get_input(2);
Mode[0] = 10 * Data[0][0] + Data[1][0];
}

void SDCARD_start() {
if (!SD.begin(5)) {Serial.println("SD card reader error"); return;}
uint8_t cardType = SD.cardType();
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
if (cardType == CARD_NONE) {Serial.println("No SD card"); return;}
Serial.print("Storage : ");
if (cardType == CARD_MMC) {Serial.println("MMC");}
else if (cardType == CARD_SD) {Serial.println("SDSC");}
else if (cardType == CARD_SDHC) {Serial.println("SDHC");}
else {Serial.println("UNKNOWN");}
Serial.printf("Size : %lluMB\n", cardSize);
Serial.printf("Space : %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used : %lluMB\n", SD.usedBytes() / (1024 * 1024));
SDCARD_write_file("Add", "/Folder", "");
SDCARD_read_file("/", 0);
SDCARD_write_file("Remove", "/Folder", "");
SDCARD_read_file("/", 0);
SDCARD_save_data("Create", "/TestFile1.txt", "Test", 0);
SDCARD_write_file("Rename", "/TestFile1.txt", "/TestFile2.txt");
SDCARD_write_file("Delete", "/TestFile2.txt", "");
}

void SDCARD_read_file(const char* path, uint8_t levels) {
Serial.printf("Listing directory: %s\n", path);
root = SD.open(path);
if (!root) {Serial.println("ERROR: Open dir failed"); return;}
if (!root.isDirectory()) {Serial.println("Not a directory"); return;}
file = root.openNextFile();
while(file) {
if (file.isDirectory()) {Serial.println(file.name());
if (levels) {SDCARD_read_file(file.name(), levels -1);}}
else {
Serial.print(file.name());
Serial.print("\t");
Serial.print(file.size());
Serial.println(" kB");
}
file = root.openNextFile();
// file.read(static uint8_t Var1[512], size_t Var2);
// file.write(static uint8_t Var1[512], 512);
}}

void SDCARD_write_file(String type, char* path1, char* path2) {
int j = 0;
if (type == "Rename") {if (SD.rename(path1, path2)) {j = 1;}}
if (type == "Delete") {if (SD.remove(path1)) {j = 1;}}
if (type == "Remove") {if (SD.rmdir(path1)) {j = 1;}}
if (type == "Add") {if (SD.mkdir(path1)) {j = 1;}}
Serial.printf("%s ", type);
Serial.printf(": %s ", path1);
Serial.printf("> %s", path2);
if (j == 1) {Serial.println("... Done");}
else {Serial.println("... ERROR");}
}

void SDCARD_load_data(char* path) {
file = SD.open(path);
Serial.printf("Read : %s", path);
if (!file) {Serial.println("... No file"); return;}
Serial.println("... Complete");
while (file.available()) {Serial.write(file.read());}
file.close();
}

void SDCARD_save_data(String type, char* path, char* text, int data) {
if (type == "Create") {file = SD.open(path, FILE_WRITE);}
if (type == "Write") {file = SD.open(path, FILE_APPEND);}
Serial.printf("%s ", type);
Serial.printf(": %s ", path);
if (!file) {Serial.println("... No file"); return;}
if (file.print(text)) {Serial.println("... Saved");}
else if (file.print(data)) {file.print(",\n"); Serial.println("... Input");}
else {Serial.println("... ERROR");}
file.close();
}

void SEGMENT_set_data() {
uint8_t N[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint8_t D[8] = {0, 0, 0, 0, 0, 0, 0, 0};
N[0] = DigitBin[0][Data[1][4]];
D[0] = DigitBin[2][1];
SEGMENT_show_data(N[0], D[0]);
N[1] = DigitBin[0][Data[2][4]];
D[1] = DigitBin[2][2];
SEGMENT_show_data(N[1], D[1]);
N[2] = DigitBin[0][Data[1][5]];
D[2] = DigitBin[2][3];
SEGMENT_show_data(N[2], D[2]);
N[3] = DigitBin[0][Data[2][5]];
D[3] = DigitBin[2][4];
SEGMENT_show_data(N[3], D[3]);
N[4] = DigitBin[0][Data[1][6]];
D[4] = DigitBin[2][5];
SEGMENT_show_data(N[4], D[4]);
N[5] = DigitBin[0][Data[2][6]];
D[5] = DigitBin[2][6];
SEGMENT_show_data(N[5], D[5]);
N[6] = DigitBin[0][Data[1][7]];
D[6] = DigitBin[2][7];
SEGMENT_show_data(N[6], D[6]);
N[7] = DigitBin[0][Data[2][7]];
D[7] = DigitBin[2][8];
SEGMENT_show_data(N[7], D[7]);
}

void SEGMENT_show_data(uint8_t N, uint8_t D) {
digitalWrite(Pin_IC_Latch, LOW);
shiftOut(Pin_IC_Data, Pin_IC_Clock, MSBFIRST, D);
shiftOut(Pin_IC_Data, Pin_IC_Clock, MSBFIRST, N);
digitalWrite(Pin_IC_Latch, HIGH);
}

void OLED_show_clock() {
OLED.clearDisplay();
OLED_print_text(1, "", Data[0][3], 2, 0, 0);
OLED_print_text(1, "", Data[0][2], 2, 40, 0);
OLED_print_text(1, "", Data[0][1], 2, 80, 0);
OLED_print_text(2, "-", 0, 2, 25, 0);
OLED_print_text(2, "-", 0, 2, 65, 0);
OLED_print_text(2, Text[2], 0, 2, 0, 20);
OLED_print_text(1, "", Data[0][7], 2, 50, 20);
OLED_print_text(1, "", Data[0][8], 2, 90, 20);
OLED_print_text(2, "C", 0, 2, 75, 20);
OLED_print_text(2, "%", 0, 2, 115, 20);
OLED_print_text(1, "", Data[0][4], 3, 0, 40);
OLED_print_text(1, "", Data[0][5], 3, 45, 40);
OLED_print_text(1, "", Data[0][6], 3, 90, 40);
OLED_print_text(2, ":", 0, 3, 32, 40);
OLED_print_text(2, ":", 0, 3, 77, 40);
OLED.display();
}
74HC595
74HC595
GND5VSDASCLSQWRTCDS1307+
4-Digit Display
Loading
ssd1306