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

#define Pin_IC_Latch 12
#define Pin_IC_Data 16
#define Pin_IC_Clock 13
#define Pin_DHT22_SDA 21
#define Pin_TM4D_CLK 35
#define Pin_TM4D_DIO 34
#define Pin_KEYPAD_Itr 11
uint8_t Pin_KEYPAD_Row[4] = {2, 3, 4, 5};
uint8_t Pin_KEYPAD_Col[4] = {6, 7, 10, 11};

int Mode = 0;
int Data[20] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char Weekday[7][12] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char keys[4][4] =
{{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }};
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

RTC_DS1307 RTC;
DHTesp DHT22;
Keypad KEYPAD(makeKeymap(keys), Pin_KEYPAD_Row, Pin_KEYPAD_Col, 4, 4);
Adafruit_SSD1306 OLED(128, 64, &Wire, -1);
TM1637 TM4D(Pin_TM4D_CLK, Pin_TM4D_DIO);

void setup() {
pinMode(Pin_IC_Latch, OUTPUT);
pinMode(Pin_IC_Clock, OUTPUT);
pinMode(Pin_IC_Data, OUTPUT);
Serial.begin(9600);
Wire.begin();
RTC.begin();
OLED.begin(SSD1306_SWITCHCAPVCC, 0x3C);
print_OLED_text(3, "Welcome!! OLED Clock", 0, 2, 0, 0);
TM4D.init();
TM4D.set(BRIGHT_TYPICAL);
DHT22.setup(Pin_DHT22_SDA, DHTesp::DHT22);
delay(3000);
set_RTC_data();
attachInterrupt(Pin_KEYPAD_Itr, input_KEYPAD_selection, HIGH);
}

void loop() {
read_RTC_data();
if (Mode == 1) {show_OLED_clock();} // A
if (Mode == 2) {show_TM4D_time();} // B
if (Mode == 3) {show_OLED_temperature();} // C
if (Mode == 0) {set_SEGMENT_data();} // D
}

void read_RTC_data() {
DateTime now = RTC.now();
TempAndHumidity data = DHT22.getTempAndHumidity();
int hr = now.hour();
int mn = now.minute();
int sc = now.second();
int tp = int(data.temperature);
int hm = 100 * float(data.humidity);
int dy = now.day();
int mo = now.month();
int year = now.year();
Data[1] = (hr/10) % 10;
Data[2] = hr % 10;
Data[3] = (mn/10) % 10;
Data[4] = mn % 10;
Data[5] = (sc/10) % 10;
Data[6] = sc % 10;
Data[7] = (tp/10) % 10;
Data[8] = tp % 10;
Data[9] = (hm/10) % 10;
Data[10] = hm % 10;
Data[11] = (dy/10) % 10;
Data[12] = dy % 10;
Data[13] = (mo/10) % 10;
Data[14] = mo % 10;
Data[15] = (year/1000) % 10;
Data[16] = (year/100) % 10;
Data[17] = (year/10) % 10;
Data[18] = year % 10;
}

void set_SEGMENT_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};
// for (int i = 1; i <= 2; i++) {
N[0] = DigitBin[0][Data[1]];
D[0] = DigitBin[2][1];
show_SEGMENT_data(N[0], D[0]);
N[1] = DigitBin[0][Data[2]];
D[1] = DigitBin[2][2];
show_SEGMENT_data(N[1], D[1]);
N[2] = DigitBin[0][Data[3]];
D[2] = DigitBin[2][3];
show_SEGMENT_data(N[2], D[2]);
N[3] = DigitBin[0][Data[4]];
D[3] = DigitBin[2][4];
show_SEGMENT_data(N[3], D[3]);
N[4] = DigitBin[0][Data[5]];
D[4] = DigitBin[2][5];
show_SEGMENT_data(N[4], D[4]);
N[5] = DigitBin[0][Data[6]];
D[5] = DigitBin[2][6];
show_SEGMENT_data(N[5], D[5]);
N[6] = DigitBin[0][Data[7]];
D[6] = DigitBin[2][7];
show_SEGMENT_data(N[6], D[6]);
N[7] = DigitBin[0][Data[8]];
D[7] = DigitBin[2][8];
show_SEGMENT_data(N[7], D[7]);
}

void show_SEGMENT_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 set_RTC_data() {
print_OLED_text(3, "Set time : (hr:mn)", 0, 2, 0, 0);
input_KEYPAD_data();
int hr = 10*Data[4] + Data[5];
int mn = 10*Data[6] + Data[7];
print_OLED_text(3, "Set date : (dy-mo)", 0, 2, 0, 0);
input_KEYPAD_data();
int dy = 10*Data[4] + Data[5];
int mo = 10*Data[6] + Data[7];
print_OLED_text(3, "Set year :", 0, 2, 0, 0);
input_KEYPAD_data();
int year = 1000*Data[4] + 100*Data[5] + 10*Data[6] + Data[7];
RTC.adjust(DateTime(year, mo, dy, hr, mn, 1));
print_OLED_text(4, "", 0, 0, 0, 0);
}

void input_KEYPAD_selection() {
int j = 0;
while (j < 1) {
char keyin = KEYPAD.getKey();
Data[j] = 0 - 48 + int(keyin);
if (keyin) {
j++;
}}
if (Data[0] == 17) {Mode = 1;}
if (Data[0] == 18) {Mode = 2;}
if (Data[0] == 19) {Mode = 3;}
if (Data[0] == 20) {Mode = 0;}
return;
}

void input_KEYPAD_data() {
int j = 4;
while (j < 8) {
char keyin = KEYPAD.getKey();
Data[j] = 0 - 48 + int(keyin);
if (keyin) {
TM4D.display(0, Data[j - 3]);
TM4D.display(1, Data[j - 2]);
TM4D.display(2, Data[j - 1]);
TM4D.display(3, Data[j - 0]);
j++;
}}}

void show_OLED_clock() {
DateTime now = RTC.now();
OLED.clearDisplay();
print_OLED_text(1, "", now.day(), 2, 0, 0);
print_OLED_text(1, "", now.month(), 2, 40, 0);
print_OLED_text(1, "", now.year(), 2, 80, 0);
print_OLED_text(2, "-", 0, 2, 25, 0);
print_OLED_text(2, "-", 0, 2, 65, 0);
print_OLED_text(2, Weekday[now.dayOfTheWeek()], 0, 2, 0, 20);
print_OLED_text(1, "", Mode, 2, 50, 20);
print_OLED_text(1, "", now.hour(), 3, 0, 40);
print_OLED_text(1, "", now.minute(), 3, 45, 40);
print_OLED_text(1, "", now.second(), 3, 90, 40);
print_OLED_text(2, ":", 0, 3, 32, 40);
print_OLED_text(2, ":", 0, 3, 77, 40);
OLED.display();
}

void print_OLED_text(int type, char* 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 show_OLED_temperature() {
TempAndHumidity data = DHT22.getTempAndHumidity();
OLED.clearDisplay();
OLED.setTextSize(2);
OLED.setCursor(0,0);
OLED.println("DHT22:" );
OLED.setCursor(0,20);
OLED.println(String(data.humidity, 1) + " %");
OLED.setTextSize(3);
OLED.setCursor(0,40);
OLED.println(String(data.temperature, 2) + " C");
OLED.display();
}

void show_TM4D_time() {
TM4D.display(0, Data[1]);
TM4D.display(1, Data[2]);
TM4D.display(2, Data[3]);
TM4D.display(3, Data[4]);
}

void show_TM4D_humidity() {
TM4D.display(0, Data[7]);
TM4D.display(1, Data[8]);
TM4D.display(2, Data[9]);
TM4D.display(3, Data[10]);
}
$abcdeabcde151015202530354045505560fghijfghij
74HC595
74HC595
4-Digit Display
GND5VSDASCLSQWRTCDS1307+