#include <IRremoteESP8266.h>
#include <LiquidCrystal_I2C.h>
#include "max7219func.h"
#include <IRrecv.h>
#include <AHT20.h>
#include "dsrtc.h"
#include <WiFiClientSecure.h>
#include <ESPAsyncWebServer.h>
#include "OTA.h"
#include <SPIFFS.h>
#include <esp_sntp.h>
#include <SD.h>
#define PIN_RECEIVER 4 // Signal Pin of IR receiver
#define MAX_MESG 50
AsyncWebServer server(80);
const char* PARAM_MESSAGE = "message";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
//NTPClient timeClient(ntpUDP);
String SSID = "Wokwi-GUEST"; //!!!!!!!!!!!!!!!!Change!!!!!!!!!!!!!!!!!!!
String passw = ""; //!!!!!!!!!!!!!!!!Change!!!!!!!!!!!!!!!!!!!
IRrecv receiver(PIN_RECEIVER);
LiquidCrystal_I2C lcd(0x27, 16, 2);
AHT20 aht20;
#if defined(DS3231)
uEEPROMLib eeprom(0x57);
#endif
utf8_t szMesg[MAX_MESG + 1];
bool setT,showText, servOn;
bool updtemp, setconf, getconf, ntpUpdated, updateTemp, loadConf, saveConf;
// RTC_DSRTC rtc;
// RTC_Millis rtcmill;
time_t setdt;
uint32_t ntpUpdatePeriod = 15000;
uint8_t intensity = 2, nvramstr[56];
enum states { Start, TimeStart, ShowTime, ShowTempLocText, ShowTempOut, ShowTempInText, ShowTempIn, ShowHumText, ShowHum, ShowMes} state, nextState;
enum tmpsetget {GetTemp, LoadConf, SaveConf};
uint32_t idleTime, showTimeMs = 3000000, showTimeSetMs = 5000000, showValuesMs = 1000000, blinkMs = 300000, animIdleTime = 15000;
uint32_t lastDsUpdate, tmpUpdateMs = 1*5*1000, lastTmpUpdate;
String dataService, ntpServer, locQueryStr = "datasun", settlmnt, tmp, genErrMsg, netErrMsg, timeZone;
WiFiClientSecure client;
uint16_t respCode = 0;
float dsRTCdrift;
decode_results results;
#define animIdleTimeOffs 0
#define tmpUpdateMsOffs 1
#define showValuesMsOffs 2
#define showTimeSetMsOffs 3
#define showTimeMsOffs 4
#define dsRTCdriftOffs 5
#define lastDsUpdateOffs 9
#define locQueryStrOffs 13
#define dataServiceOffs 43
#define ntpServerOffs 63
void translateIR();
//--------------------------------Hashing functions------------------------------------------------
constexpr inline uint32_t hash(char const * str, int h = 0)
{
return (!str[h] ? 1 : hash(str, h+1) ^ str[h]);
}
constexpr inline uint32_t operator "" _(char const * p, size_t) { return hash(p); }
inline uint32_t hash(String const & s) { return hash (s.c_str()); }
//--------------------------------- File read write -----------------------------------------------
String load_from_file(String file_name,bool lastline=true) {
String result = "";
File file = SD.open(file_name, "r");
if(file){
while(file.available())
result = lastline?file.readStringUntil('\n'):result + file.readStringUntil('\n');
file.close();
}
return result;
}
bool write_to_file(String file_name, String contents, bool append = false) {
File file = SD.open(file_name, append?"a":"w");
if (file) {
int bytesWritten = file.print(contents);
if (bytesWritten == 0) { // write failed
return false;
}
else{
file.close();
return true;
}
}
else return false;
}
float load_float_from_file(String file_name) {
String res = load_from_file(file_name);
float flt;
memcpy(&flt,res.c_str(),4);
return flt;
}
bool write_float_to_file(String file_name, float f, bool append = false){
byte buff[5] = {0,0,0,0,0};
*(float*)buff = f;
return write_to_file(file_name, (char*)buff, append);
}
//--------------------------------------- Stop animation ------------------------------------------
void stopAnim(){
idleTime = 0;
animState = 0;
animEnd = true;
}
//------------------------------------------ Websocket --------------------------------------------
//------------------------------------------ Time to string conversion ----------------------------
void timeToString()
{
char dummyfont;
if (numFont == Font3x8) dummyfont = 1;
else if (numFont == Font4x8) dummyfont = 2;
else if (numFont == Font5x8) dummyfont = 3;
time_t dt = setT?setdt:time(nullptr);
tm ltime = *localtime(&dt);
sprintf((char*)szMesg, "%02d%s%02d%c\x9a%02d\x9a", ltime.tm_hour, ltime.tm_sec % 2 ? (numFont == Font5x8 ? "\x9c" : " ") : (numFont == Font5x8 ? "\x9b" : ":"), ltime.tm_min, numFont == Font5x8 ? '\0' : ' ', ltime.tm_sec);
if (ltime.tm_hour < 10) *szMesg = dummyfont;
//if(numFont == Font5x8 ) psz[1 < 10 ? 4 : 5] = '\x0';
}
//------------------------------------------ UTF string conversion --------------------------------
utf8_t* utfStrConv(utf8_t *str)
{
bool utfFound;
while (*str != '\0') {
switch (*str) {
case 0xC3: *str = *(str + 1) + 64; utfFound = true; break;
case 0xC5:
utfFound = true;
switch (*(str + 1)) {
case 0x90: case 0x91: *str = *(str + 1) + 5; break; // Ő ő
case 0xB0: case 0xB1: *str = *(str + 1) - 25; // Ű ű
}
default: utfFound = false;
}
str++;
if (utfFound)
for (utf8_t *tmp = str; *tmp != '\0'; tmp++)
*tmp = *(tmp + 1);
}
return str;
}
//-------------------------------------------- Show message ---------------------------------------
void showMessage(String msgs, states nextSt){
stopAnim();
showText = true;
nextState = nextSt;
state = ShowHum;
sprintf((char *)szMesg, msgs.c_str());
}
//------------------------------------------ Data retrieve functions ------------------------------
uint8_t updateStlmtTempTimeData(tmpsetget choice){
bool success = false;
static uint8_t conState;
static String setl = "";
String qstr;
IPAddress srv((uint32_t)0);
switch (conState){
case 0:
setl = netErrMsg = "";
ntpUpdated = false;
if(choice == GetTemp) updtemp = true;
else if(choice == SaveConf) setconf = true;
else getconf = true;
if(!WiFi.isConnected())
if(!SSID.isEmpty()) WiFi.begin(SSID.c_str(),passw.c_str(), 6);
else netErrMsg = F("SSID/Pwd not set");
conState = 1;
break;
case 1:
if(WiFi.isConnected()) {
elapsed(0,4);
if(dataService.isEmpty()){
netErrMsg = F("Data service isn't set");
success = true; break;;
}
conState = 2;
}
else if(elapsed(5000,4)){
netErrMsg = F("Wifi connection failed");
break;
}
else break;
// Send query
case 2: Serial.println("State 2"); // Waiting for the server to response
if(!client.connect(dataService.c_str(),443,300)){
netErrMsg = dataService + F(" not connecting");
break;
}
if(updtemp) qstr = "GET /?loc=" + locQueryStr;
else if(getconf)qstr = "GET /getconf";
else qstr = "GET /setconf?tsrv="+ntpServer+"&lqs="+locQueryStr+"&lds="+lastDsUpdate+"&drf="+
dsRTCdrift+"&stm="+showTimeMs+"&stsm="+showTimeSetMs+"&svm="+showValuesMs+"&tum="+tmpUpdateMs+
"&ait="+animIdleTime+"&tzo="+timeZone;
client.print(qstr + F(" HTTP/1.1\r\n"
"Host:") + dataService + F("\r\n"
"User-Agent: Mozilla/5.0\r\n\r\n"));
conState = 3;
case 3:Serial.println("State 3");
if(!elapsed(1000,5))break;
Serial.println("State 3");
if(client.available()) conState = 4;
else if(!elapsed(15000,4)){Serial.print("."); break;}
else {netErrMsg = F("No response"); break;}
case 4: Serial.println("State 4"); // Check header, get temp
String resp = client.readStringUntil(' ');
String val;
if(resp.startsWith("HTTP")){Serial.println("Response body");
respCode = client.readStringUntil(' ').toInt();
Serial.printf("REsponse: %d", respCode);
if(respCode == 200){
if(!client.findUntil("app","\r\n\r\n")){Serial.println("Missing app header");
netErrMsg = F("Missing 'app' header");
break;
}
if(setconf) {Serial.println("success");success = true;break;}
client.find("\r\n\r\n");
if(updtemp){
setl = client.readStringUntil(' ');
tmp = client.readStringUntil('\n');
if(setl == "e"){
if(tmp == "nf") netErrMsg = F("Settlement not found");
else if(tmp == "se") netErrMsg = F("Data server error");
break;
}
settlmnt = setl + ":";
tmp+="\x90\x43";
}else{
while (client.available())
{
resp = client.readStringUntil(',');
val = client.readStringUntil('\n');
switch (hash(resp))
{
case "tsrv"_: ntpServer = val;break;
case "lqs"_: locQueryStr = val;break;
case "tzo"_: timeZone = val;break;
case "lds"_: lastDsUpdate = val.toInt();break;
case "drf"_: dsRTCdrift = val.toFloat();break;
case "stm"_: showTimeMs = val.toInt();break;
case "stsm"_: showTimeSetMs = val.toInt();break;
case "svm"_: showValuesMs = val.toInt();break;
case "tum"_: tmpUpdateMs = val.toInt();break;
case "ait"_: animIdleTime = val.toInt();break;
default:
break;
}
}
}
success = true;
netErrMsg = "";
lastTmpUpdate =esp_timer_get_time();
}else netErrMsg = F("Server error");
}else netErrMsg = F("Response is not HTTP");
}
bool err = !netErrMsg.isEmpty();
if(success || err){
Serial.printf("TEST: %d %d %d\r\n",success,err,ntpUpdated);
conState = respCode = 0;
getconf = setconf = updtemp = success = false;
client.stop();
if(ntpUpdated && !servOn) WiFi.disconnect();
return err?-1:1;
}
return 0;
}
void getTemp(){
sprintf((char *)szMesg, "%.1f\x90\x43", aht20.getTemperature());
}
void getHumidityText(){
sprintf((char *)szMesg, "RH:");
utfStrConv(szMesg);
}
void getHumidity(){
sprintf((char *)szMesg, "%.0f%%", aht20.getHumidity());
}
//----------------------------------------------------------------------------------------------
//--------------------------------------------- DSRTC NVRAM -----------------------------------
#if defined(DS1307)
void readNVRAMsets(){
dsrtcReadNVRAM(nvramstr,56);
locQueryStr = (char*)(nvramstr+14);
lastDsUpdate = *(uint32_t*)nvramstr;
dsRTCdrift = *(uint32_t*)(nvramstr+1);
showTimeMs = nvramstr[8]?nvramstr[8]*100:showTimeMs;
showTimeSetMs = nvramstr[9]?nvramstr[9]*100:showTimeSetMs;
showValuesMs = nvramstr[10]?nvramstr[10]*100:showValuesMs;
tmpUpdateMs = nvramstr[11]?nvramstr[11]*100:tmpUpdateMs;
animIdleTime = nvramstr[12];
intensity = nvramstr[13];
}
void writeNVRAMsets(){
String temp = locQueryStr + '\0';
*(uint32_t*)nvramstr = lastDsUpdate;
*(uint32_t*)(nvramstr+1) = dsRTCdrift;
nvramstr[8] = uint8_t(showTimeMs / 100);
nvramstr[9] = uint8_t(showTimeSetMs / 100);
nvramstr[10] = uint8_t(showValuesMs / 100);
nvramstr[11] = uint8_t(tmpUpdateMs / 100);
nvramstr[12] = animIdleTime;
nvramstr[13] = intensity;
for(uint8_t i = 0; i < temp.length(); i++)
nvramstr[i+14] = temp.charAt(i);
dsrtcWriteNVRAM(nvramstr,56);
}
#endif
//---------------------------------------- Adjust Time -------------------------------------------
const timeval getDSRTCCompensTime(){
Serial.printf("DRIFT: %d\r\n", (int)round(((DSRTCgetTimeStamp() - lastDsUpdate)*dsRTCdrift)));
return {DSRTCgetTimeStamp() + (int)round(((DSRTCgetTimeStamp() - lastDsUpdate)*dsRTCdrift))};
}
//------------------------------------------ Setup ------------------------------------------------
void setup()
{
Serial.begin(115200);
DSRTCbegin();
client.setInsecure();
// if(!SPIFFS.begin(true)) Serial.println(F("Formatting"));
//---------------------------------------------------------
/* SPIClass hspi = SPIClass(HSPI);
pinMode(15, OUTPUT);
if (!SD.begin(15,hspi)) {
Serial.println("Card initialization failed!");
while (true);
}
File f = SD.open("/serv","a");
f.println("tfi.glitch.me");
f.close();
f = SD.open("/drift","a");
f.close(); */
//---------------------------------------------------------
//dataService = load_from_file("/serv");
//Serial.println(dataService);
dataService = "tfi.glitch.me";//!!!!!!!!!!!!!!!!!!!!!delete
if (aht20.begin() == false)
genErrMsg = F("AHT20 not available");
sntp_set_time_sync_notification_cb([](timeval *v){
Serial.println("SNTP Sync");
ntpUpdated = true;
const time_t tsec = time(nullptr);
if(tsec - lastDsUpdate > 3600*24*365){
Serial.println("DSUPDATE");
DSRTCsetTime(tsec);
lastDsUpdate = tsec;
if(dsRTCdrift != 0);
//write_to_file("/drift", String(dsRTCdrift)+"\n", true);
}
if(lastDsUpdate && time(nullptr)>lastDsUpdate){
Serial.printf("Time: %d dsRTCtime: %d lastdsupdate: %d\r\n",time(nullptr),DSRTCgetTimeStamp(),lastDsUpdate);
dsRTCdrift = float(time(nullptr)- DSRTCgetTimeStamp())/(time(nullptr) - lastDsUpdate);
#if defined(DS1307)
writeNVRAMsets();
#endif
Serial.printf("Drift: %f\r\n", dsRTCdrift);
}
Serial.println(localtime(&tsec), "%H:%M:%S");
});
sntp_set_sync_interval(ntpUpdatePeriod);
configTzTime("CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00","europe.pool.ntp.org");
uint8_t isSet = 0;
#if defined(DS1307)
dsrtcReadNVRAM(&isSet,1,8);
if(isSet)
readNVRAMsets();
else writeNVRAMsets();
#elif defined(DS3231)
isSet = eeprom.eeprom_read(0);
if(isSet){
eeprom.eeprom_read(animIdleTimeOffs,&animIdleTime);
eeprom.eeprom_read(tmpUpdateMsOffs,&tmpUpdateMs);
eeprom.eeprom_read(showValuesMsOffs,&showValuesMs);
eeprom.eeprom_read(showTimeSetMsOffs,&showTimeSetMs);
eeprom.eeprom_read(showTimeMsOffs,&showTimeMs);
eeprom.eeprom_read(dsRTCdriftOffs,&dsRTCdrift);
eeprom.eeprom_read(lastDsUpdateOffs,&lastDsUpdate);
eeprom.eeprom_read(locQueryStrOffs,&locQueryStr);
eeprom.eeprom_read(dataServiceOffs,&dataService);
eeprom.eeprom_read(ntpServerOffs,&ntpServer);
}
else{
eeprom.eeprom_write(animIdleTimeOffs,animIdleTime);
eeprom.eeprom_write(tmpUpdateMsOffs,tmpUpdateMs);
eeprom.eeprom_write(showValuesMsOffs,showValuesMs);
eeprom.eeprom_write(showTimeSetMsOffs,showTimeSetMs);
eeprom.eeprom_write(showTimeMsOffs,showTimeMs);
eeprom.eeprom_write(dsRTCdriftOffs,dsRTCdrift);
eeprom.eeprom_write(lastDsUpdateOffs,lastDsUpdate);
eeprom.eeprom_write(locQueryStrOffs,locQueryStr);
eeprom.eeprom_write(dataServiceOffs,dataService);
eeprom.eeprom_write(ntpServerOffs,ntpServer);
}
#endif
Serial.printf("Data service: %s\r\n", dataService.c_str());
WiFi.persistent(true);
WiFi.onEvent([](arduino_event_id_t event, arduino_event_info_t info){
switch (event)
{
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
Serial.println((char*)info.wifi_sta_connected.ssid);
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
Serial.println("Disconnected");
default:
break;
}
},ARDUINO_EVENT_MAX);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Hello, world");
});
// Send a GET request to <IP>/get?message=<message>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String message;
if (request->hasParam(PARAM_MESSAGE)) {
message = request->getParam(PARAM_MESSAGE)->value();
} else {
message = "No message sent";
}
request->send(200, "text/plain", "Hello, GET: " + message);
});
// Send a POST request to <IP>/post with a form field message set to <message>
server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
String message;
if (request->hasParam(PARAM_MESSAGE, true)) {
message = request->getParam(PARAM_MESSAGE, true)->value();
} else {
message = "No message sent";
}
request->send(200, "text/plain", "Hello, POST: " + message);
});
server.onNotFound(notFound);
OTA.begin(&server);
mx.begin();
lcd.init(); //!!!!!!!!!!!!!!!!!!!DELETE!!!!!!!!!!!!!!!!!!!!!!
lcd.print("<press a button>"); //!!!!!!!!!!!!!!!!!!!DELETE!!!!!!!!!!!!!!!!!!!!!!
receiver.enableIRIn(); // Start the receiver
Serial.printf("PIN_RECEIVER: %d\r\n", PIN_RECEIVER);
numFont = numFonts[numFontIndex];
mx.control(MD_MAX72XX::INTENSITY, intensity);
lastTmpUpdate = esp_timer_get_time();
//WiFi.disconnect();
}
//char animStr[] = {'P','L','U'};
enum animTypes { Pr, LR, UD };
uint8_t animType, lastSec;
bool lock = false;
timeval tv;
//------------------------------------------ Loop -------------------------------------------------
void loop() {
uint iters = 0;
//----------------------------------------------------------------
while(1){
if (elapsed(idleTime)) {
if (animEnd) {
//animEnd = false;
lr = random(2);
updown = random(2);
animType = random(3);
if (lock && (state == ShowTime || state == ShowTempOut || state == ShowHum)) {
animType = Pr;
state = states((int)state - 1);
}
if(state == ShowTime){
if(elapsed(setT ? showTimeSetMs : showTimeMs,1)){
if(setT)numFont= curFont;
state = ShowTempLocText;
setT = false;
}
}
else if (state == ShowHum && !showText) state = TimeStart;
else if (showText) state = ShowMes;
else if (state == ShowMes) state = nextState;
else state = states((int)state + 1);
switch (state) {
case TimeStart:
tv = getDSRTCCompensTime();
settimeofday(&tv, nullptr);
timeToString();
break;
case ShowTempLocText:
lr = true;
sprintf((char *)szMesg, settlmnt.c_str());
utfStrConv(szMesg);
break;
case ShowTempOut:
if(!tmp.isEmpty())sprintf((char *)szMesg, tmp.c_str());
else stopAnim();
break;
case ShowTempInText:
sprintf((char *)szMesg, F("Szoba:")); break;
case ShowTempIn: getTemp(); break;
case ShowHumText: getHumidityText(); break;
case ShowHum: getHumidity(); break;
case ShowMes:
lr = true;
showText = false;
}
}
if (state == ShowTempLocText){
if(updtemp) showMessage(F("Updating..."), ShowTempInText);
else if(!netErrMsg.isEmpty())showMessage(F("Error, press +"),ShowTempInText);
else if(settlmnt.isEmpty())showMessage(F("No temp data"),ShowTempInText);
}
if(!showText)
switch(state){
case ShowTime: {
uint8_t seconds = time(nullptr)%60;
uint64_t nextRun = setT?esp_timer_get_time()+500000:esp_timer_get_time() + 1000000;
timeToString();
printText(szMesg);
if (numFont == Font5x8) {
seconds <= 30 ? mx.setPoint(7, seconds == 0 ? 30 : 31 - seconds, true) : mx.setPoint(7, seconds == 31 ? 1 : seconds - 30, true);
if (seconds > 55)mx.setColumn(30, 4 << seconds % 5);
if (seconds > 25 && seconds < 31) mx.setColumn(1, 4 << (seconds < 30 ? seconds % 5 : 5));
}
if(setT && elapsed(blinkMs,6))mx.clear();
idleTime = nextRun - esp_timer_get_time();
break;
}
default:
switch (animType) {
case Pr: printText(szMesg); break;
case LR: TSLR(szMesg); break;
case UD: TSUD(szMesg);
}
if (animEnd && (state != TimeStart || state != ShowTime))
idleTime = showValuesMs;
else idleTime = animIdleTime;
}
}
// else { // Do other tasks
// if (receiver.decode(&results)) {
// translateIR();
// receiver.resume(); // Receive the next value
// }
// if(elapsed(tmpUpdateMs,3) && state > ShowTempLocText && state < ShowMes)
// updateTemp = true;
// if(updateTemp && !setconf && !getconf){
// if(updateStlmtTempTimeData(GetTemp)) {elapsed(tmpUpdateMs,3); updateTemp = false;}
// }
// else if(saveConf && !updtemp && !getconf){Serial.print("saveConf");
// if(updateStlmtTempTimeData(SaveConf))saveConf = false;
// }
// else if(loadConf && !updtemp && !setconf){Serial.print("saveConf");
// if(updateStlmtTempTimeData(LoadConf))loadConf = false;
// }
// }
iters++;
if(iters = 255){
iters = 0;
yield();
}
}
// if (curState == ShowTime) state = ShowTime;
}
//----------------------------------------Print to LCD---------------------------------------------
void lcdPrint(const char* text)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("button pressed:");
lcd.setCursor(0, 1);
lcd.print(text);
lcd.print(" code: ");
lcd.print(results.command);
}
//------------------------------------------Set time-----------------------------------------------
void setTime(){
stopAnim();
lastSec = 0;
startTime[1] = esp_timer_get_time();
state = TimeStart;
//printText(szMesg);
}
void translateIR()
{
// Takes command based on IR code received
switch (results.command) {
case 162:
lcdPrint("POWER");
servOn = !servOn;
if(servOn){
if(!WiFi.isConnected())WiFi.begin();
server.begin();
("Server on: " + WiFi.localIP().toString(), ShowTime);
}
else {
server.end();
showMessage("Server off", ShowTime);
}
break;
case 226:
lcdPrint("MENU");
lock = !lock;
break;
case 34:
lcdPrint("TEST");
stopAnim();
mx.clear();
break;
case 2:
lcdPrint("PLUS");
if(!netErrMsg.isEmpty() || !genErrMsg.isEmpty())
showMessage(netErrMsg + " " + genErrMsg,ShowTime);
break;
case 194:
lcdPrint("BACK");
break;
case 224:
lcdPrint("PREV.");
break;
case 168:
lcdPrint("PLAY");
break;
case 144:
lcdPrint("NEXT");
break;
case 152:
lcdPrint("MINUS");
break;
case 176:
lcdPrint("key: C");
stopAnim();
numFontIndex = (numFontIndex + 1) % 3;
numFont = numFonts[numFontIndex];
lastSec = 0;
startTime[1] = esp_timer_get_time();
state = TimeStart;
break;
case 104: // 0
setT = !setT;
if(setT) {
// setdt = rtcmill.now();
setdt = time(nullptr);
curFont = numFont;
numFont = Font4x8;
showMessage(F("Time Set"),ShowTime);
} else {
// rtcmill.adjust(setdt);
const timeval tv = {.tv_sec = time(nullptr)};
settimeofday(&tv, nullptr);
startTime[1] = 0;
numFont = curFont;
}
return;
case 48: // 1
setdt += 3600;
break;
case 24: // 2
setdt += 60;
break;
case 122: // 3
setdt += 1;
break;
case 16: // 4
setdt -= 3600;
break;
case 56: // 5
setdt -= 60;
break;
case 90: // 6
setdt -= 1;
break;
case 66: // 7
break;
case 74: // 8
break;
case 82: // 9
break;
default:
lcd.clear();
lcd.print(results.command);
lcd.print(" other button");
}
if(setT && state == ShowTime) setTime();
}