mirror of
https://github.com/zzzzDev4/IAS-Better-Tea.git
synced 2025-04-21 07:31:20 +02:00
basic lifecycle done (no data sync yet)
This commit is contained in:
parent
a6c5005eb0
commit
bb936e60e4
6 changed files with 203 additions and 109 deletions
|
@ -5,61 +5,86 @@ SmartDisplay::SmartDisplay(Adafruit_SSD1306 *display)
|
||||||
m_display = display;
|
m_display = display;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmartDisplay::printTeaConfigScreen(String teaName, int steepingSeconds, int editDir)
|
void SmartDisplay::printTeaConfigScreen(TeaData tea)
|
||||||
{
|
{
|
||||||
m_display->clearDisplay();
|
|
||||||
m_display->setTextColor(WHITE);
|
|
||||||
m_display->setTextSize(1);
|
|
||||||
m_display->setCursor(0, 0);
|
|
||||||
|
|
||||||
m_display->println(teaName);
|
resetDisplay();
|
||||||
m_display->println(" ");
|
|
||||||
|
m_display->println(tea.m_teaName);
|
||||||
|
m_display->print(tea.m_waterTemp);
|
||||||
|
m_display->println(" C");
|
||||||
|
|
||||||
m_display->setTextSize(2);
|
m_display->setTextSize(2);
|
||||||
m_display->print(steepingSeconds);
|
m_display->print(tea.m_steepingSeconds);
|
||||||
m_display->print(" ");
|
m_display->print(" ");
|
||||||
|
|
||||||
if (editDir > 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < editDir; i++)
|
|
||||||
{
|
|
||||||
m_display->print("+");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (editDir < 0)
|
|
||||||
{
|
|
||||||
for (int i = editDir; i < 0; i++)
|
|
||||||
{
|
|
||||||
m_display->print("-");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_display->display();
|
m_display->display();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmartDisplay::printTeaSteepingProgressScreen(String teaName, int steepingSeconds)
|
void SmartDisplay::printTeaSteepingProgressScreen(TeaData tea, int remainingSeconds)
|
||||||
{
|
{
|
||||||
m_display->clearDisplay();
|
resetDisplay();
|
||||||
m_display->setTextColor(WHITE);
|
|
||||||
m_display->setTextSize(1);
|
|
||||||
m_display->setCursor(0, 0);
|
|
||||||
|
|
||||||
m_display->println(teaName);
|
m_display->println(tea.m_teaName);
|
||||||
m_display->println("(Steeping)");
|
m_display->println(" (Steeping)");
|
||||||
|
|
||||||
m_display->setTextSize(2);
|
m_display->setTextSize(2);
|
||||||
m_display->println(steepingSeconds);
|
m_display->println(remainingSeconds);
|
||||||
|
|
||||||
m_display->display();
|
m_display->display();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SmartDisplay::printWaitForRFIDScreen()
|
void SmartDisplay::printWaitForRFIDScreen()
|
||||||
|
{
|
||||||
|
resetDisplay();
|
||||||
|
m_display->println("Waiting for RFID scan...");
|
||||||
|
|
||||||
|
m_display->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmartDisplay::printRequestFeedbackScreen(TeaData teaData)
|
||||||
|
{
|
||||||
|
resetDisplay();
|
||||||
|
|
||||||
|
m_display->println("Feedback on brew intensity");
|
||||||
|
m_display->println();
|
||||||
|
m_display->println("|less| perfect |more|");
|
||||||
|
|
||||||
|
m_display->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmartDisplay::printAddNewTeaConfigScreen(TeaData teaData)
|
||||||
|
{
|
||||||
|
resetDisplay();
|
||||||
|
|
||||||
|
m_display->println("Add initial config for new tea");
|
||||||
|
m_display->println();
|
||||||
|
m_display->print("Steeping time:");
|
||||||
|
m_display->print(teaData.m_steepingSeconds);
|
||||||
|
m_display->println("s");
|
||||||
|
|
||||||
|
m_display->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmartDisplay::printMsg(const String &s)
|
||||||
|
{
|
||||||
|
resetDisplay();
|
||||||
|
m_display->setCursor(0, 10);
|
||||||
|
m_display->println(s);
|
||||||
|
m_display->display();
|
||||||
|
}
|
||||||
|
void SmartDisplay::printMsg(unsigned long l)
|
||||||
|
{
|
||||||
|
resetDisplay();
|
||||||
|
m_display->setCursor(0, 10);
|
||||||
|
m_display->println(l);
|
||||||
|
m_display->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmartDisplay::resetDisplay()
|
||||||
{
|
{
|
||||||
m_display->clearDisplay();
|
m_display->clearDisplay();
|
||||||
m_display->setTextColor(WHITE);
|
m_display->setTextColor(WHITE);
|
||||||
m_display->setTextSize(1);
|
m_display->setTextSize(1);
|
||||||
m_display->setCursor(0, 0);
|
m_display->setCursor(0, 0);
|
||||||
|
|
||||||
m_display->println("Waiting for RFID scan...");
|
|
||||||
|
|
||||||
m_display->display();
|
|
||||||
}
|
}
|
|
@ -1,14 +1,30 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Adafruit_GFX.h>
|
#include <Adafruit_GFX.h>
|
||||||
#include <Adafruit_SSD1306.h>
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include "TeaData.hpp"
|
||||||
|
|
||||||
class SmartDisplay {
|
class SmartDisplay
|
||||||
public:
|
{
|
||||||
SmartDisplay(Adafruit_SSD1306 *display);
|
public:
|
||||||
void printTeaConfigScreen(String teaName, int steepingSeconds, int editDir);
|
SmartDisplay(Adafruit_SSD1306 *display);
|
||||||
void printAddNewTeaScreen();
|
|
||||||
void printTeaSteepingProgressScreen(String teaName, int steepingSeconds);
|
public:
|
||||||
void printWaitForRFIDScreen();
|
// lifecycle screens
|
||||||
private:
|
void printWaitForRFIDScreen();
|
||||||
Adafruit_SSD1306 *m_display;
|
void printTeaConfigScreen(TeaData teaData);
|
||||||
|
void printTeaSteepingProgressScreen(TeaData teaData, int remainingSeconds);
|
||||||
|
void printRequestFeedbackScreen(TeaData teaData);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// special screens
|
||||||
|
void printAddNewTeaConfigScreen(TeaData teaData);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// helper methods
|
||||||
|
void printMsg(const String &s);
|
||||||
|
void printMsg(unsigned long l);
|
||||||
|
void resetDisplay();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Adafruit_SSD1306 *m_display;
|
||||||
};
|
};
|
|
@ -18,15 +18,14 @@ void SmartRFID::init()
|
||||||
Serial.println("PN5 chip found!");
|
Serial.println("PN5 chip found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
String SmartRFID::readTag()
|
String SmartRFID::readTag(uint16_t timeoutMillis)
|
||||||
{
|
{
|
||||||
m_nfc->SAMConfig();
|
m_nfc->SAMConfig();
|
||||||
Serial.println("Waiting for ISO14443A chip...");
|
|
||||||
|
|
||||||
uint8_t success;
|
uint8_t success;
|
||||||
uint8_t uid[] = {0, 0, 0, 0, 0, 0, 0}; // buffer for UID
|
uint8_t uid[] = {0, 0, 0, 0, 0, 0, 0}; // buffer for UID
|
||||||
uint8_t uidLength; // UID length (4 or 7 bytes depending on ISO14443A Card/Chip type)
|
uint8_t uidLength; // UID length (4 or 7 bytes depending on ISO14443A Card/Chip type)
|
||||||
success = m_nfc->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
|
success = m_nfc->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, timeoutMillis);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
Serial.println("Found an ISO14443A card");
|
Serial.println("Found an ISO14443A card");
|
||||||
|
|
|
@ -8,7 +8,7 @@ class SmartRFID
|
||||||
public:
|
public:
|
||||||
SmartRFID(Adafruit_PN532 *nfc);
|
SmartRFID(Adafruit_PN532 *nfc);
|
||||||
void init();
|
void init();
|
||||||
String readTag();
|
String readTag(uint16_t timeoutMillis);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String string_dump_byte_array(byte *buffer, byte bufferSize);
|
String string_dump_byte_array(byte *buffer, byte bufferSize);
|
||||||
|
|
174
src/main.cpp
174
src/main.cpp
|
@ -43,14 +43,22 @@ SmartButton buttonRight(TACTILE_BTN_RIGHT);
|
||||||
// networking
|
// networking
|
||||||
String shuttleServer = "https://ias-tea-axum.shuttleapp.rs";
|
String shuttleServer = "https://ias-tea-axum.shuttleapp.rs";
|
||||||
|
|
||||||
TeaData currentTea("Teafault", "c0derfid", 100, 30);
|
TeaData currentTea("Teafault", "c0derfid", -1, 30);
|
||||||
|
String currentUid;
|
||||||
|
|
||||||
enum State
|
enum State
|
||||||
{
|
{
|
||||||
|
// lifecycle states
|
||||||
WAIT_FOR_RFID_SCAN,
|
WAIT_FOR_RFID_SCAN,
|
||||||
|
PROCESS_RFID_SCAN,
|
||||||
DISPLAY_SCANNED_TEA_CONFIG,
|
DISPLAY_SCANNED_TEA_CONFIG,
|
||||||
ADD_NEW_TEA_CONFIG,
|
|
||||||
STEEPING_IN_PROGRESS,
|
STEEPING_IN_PROGRESS,
|
||||||
|
REQUEST_FEEDBACK,
|
||||||
|
|
||||||
|
// special states
|
||||||
|
DISPLAY_ADD_NEW_TEA_CONFIG,
|
||||||
|
|
||||||
|
// debugging states
|
||||||
NETWORKING_DEBUG,
|
NETWORKING_DEBUG,
|
||||||
};
|
};
|
||||||
State state = State::WAIT_FOR_RFID_SCAN;
|
State state = State::WAIT_FOR_RFID_SCAN;
|
||||||
|
@ -63,19 +71,19 @@ void setup()
|
||||||
FastLED.addLeds<WS2812B, LEDSTRIPE_PIN, RGB>(leds, PIXELCOUNT);
|
FastLED.addLeds<WS2812B, LEDSTRIPE_PIN, RGB>(leds, PIXELCOUNT);
|
||||||
FastLED.clear(true);
|
FastLED.clear(true);
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
delay(2000);
|
delay(500);
|
||||||
|
|
||||||
leds[0].setRGB(2, 0, 0);
|
leds[0].setRGB(2, 0, 0);
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
delay(1000);
|
delay(500);
|
||||||
|
|
||||||
leds[10].setRGB(0, 2, 0);
|
leds[10].setRGB(0, 2, 0);
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
delay(1000);
|
delay(500);
|
||||||
|
|
||||||
leds[19].setRGB(0, 0, 2);
|
leds[19].setRGB(0, 0, 2);
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
delay(1000);
|
delay(500);
|
||||||
|
|
||||||
teaNet.init(ssid, password);
|
teaNet.init(ssid, password);
|
||||||
|
|
||||||
|
@ -83,9 +91,9 @@ void setup()
|
||||||
|
|
||||||
Serial.println("Initializing OLED...");
|
Serial.println("Initializing OLED...");
|
||||||
display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false); // Address 0x3C for 128x32
|
display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false); // Address 0x3C for 128x32
|
||||||
printMsg("Tea is Great!");
|
smartDisplay.printMsg("Tea is Great!");
|
||||||
|
|
||||||
delay(1000);
|
delay(500);
|
||||||
|
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
pinMode(TOUCH_BTN_PIN, INPUT);
|
pinMode(TOUCH_BTN_PIN, INPUT);
|
||||||
|
@ -96,7 +104,6 @@ void setup()
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
|
|
||||||
contServo.tick();
|
contServo.tick();
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
|
@ -123,38 +130,57 @@ void loop()
|
||||||
{
|
{
|
||||||
smartDisplay.printWaitForRFIDScreen();
|
smartDisplay.printWaitForRFIDScreen();
|
||||||
|
|
||||||
// this blocks execution until a tag has been detected
|
// this blocks execution until a tag has been detected (5s timeout)
|
||||||
String uid = smartRFID.readTag();
|
String uid = smartRFID.readTag(5000);
|
||||||
|
if (uid != "no_tag_detected")
|
||||||
|
{
|
||||||
|
currentUid = uid;
|
||||||
|
state = PROCESS_RFID_SCAN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PROCESS_RFID_SCAN:
|
||||||
|
{
|
||||||
|
|
||||||
display.println(uid);
|
display.println(currentUid);
|
||||||
display.display();
|
|
||||||
delay(1000);
|
|
||||||
|
|
||||||
display.println("Success!");
|
|
||||||
display.display();
|
display.display();
|
||||||
delay(500);
|
delay(500);
|
||||||
|
|
||||||
JSONVar result = teaNet.httpsGETRequest(shuttleServer + "/teabyrfid/" + uid);
|
display.println("Success!");
|
||||||
|
display.display();
|
||||||
|
delay(250);
|
||||||
|
|
||||||
|
JSONVar result = teaNet.httpsGETRequest(shuttleServer + "/teabyrfid/" + currentUid);
|
||||||
Serial.print("Result:");
|
Serial.print("Result:");
|
||||||
Serial.println(result);
|
Serial.println(result);
|
||||||
|
|
||||||
if (JSONVar::stringify(result) == "null")
|
if (JSONVar::stringify(result) == "null")
|
||||||
{
|
{
|
||||||
Serial.print("TEA DOES NOT EXIST YET!");
|
Serial.print("TEA DOES NOT EXIST YET!");
|
||||||
String payload = teaNet.createTeaData("Unnamed Tea", uid, 90, 180);
|
String payload = teaNet.createTeaData("Unnamed Tea", currentUid, 90, 180);
|
||||||
teaNet.httpsPOSTRequest("https://ias-tea-axum.shuttleapp.rs/addtea", payload);
|
teaNet.httpsPOSTRequest(shuttleServer + "/addtea", payload);
|
||||||
result = teaNet.httpsGETRequest(shuttleServer + "/teabyrfid/" + uid);
|
result = teaNet.httpsGETRequest(shuttleServer + "/teabyrfid/" + currentUid);
|
||||||
|
state = State::DISPLAY_ADD_NEW_TEA_CONFIG;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = State::DISPLAY_SCANNED_TEA_CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTea = TeaData(result["teaname"], result["rfidcode"], result["watertemp"], result["steepingseconds"]);
|
currentTea = TeaData(result["teaname"], result["rfidcode"], result["watertemp"], result["steepingseconds"]);
|
||||||
|
|
||||||
state = State::DISPLAY_SCANNED_TEA_CONFIG;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DISPLAY_SCANNED_TEA_CONFIG:
|
case DISPLAY_SCANNED_TEA_CONFIG:
|
||||||
{
|
{
|
||||||
// int dir = smartDial.smoothEdit(¤tTea.m_steepingSeconds, 0, 6000);
|
|
||||||
|
String uid = smartRFID.readTag(50); // just enough time to detect tag
|
||||||
|
if (uid != "no_tag_detected")
|
||||||
|
{
|
||||||
|
currentUid = uid;
|
||||||
|
smartDisplay.resetDisplay();
|
||||||
|
state = PROCESS_RFID_SCAN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (buttonLeft.isJustPressed())
|
if (buttonLeft.isJustPressed())
|
||||||
{
|
{
|
||||||
|
@ -164,7 +190,7 @@ void loop()
|
||||||
{
|
{
|
||||||
currentTea.m_steepingSeconds += 10;
|
currentTea.m_steepingSeconds += 10;
|
||||||
}
|
}
|
||||||
smartDisplay.printTeaConfigScreen(currentTea.m_teaName, currentTea.m_steepingSeconds, 0);
|
smartDisplay.printTeaConfigScreen(currentTea);
|
||||||
|
|
||||||
if (isBtnPressed() && currentTea.m_steepingSeconds <= 0)
|
if (isBtnPressed() && currentTea.m_steepingSeconds <= 0)
|
||||||
{
|
{
|
||||||
|
@ -177,62 +203,94 @@ void loop()
|
||||||
// change state if start pressed
|
// change state if start pressed
|
||||||
if (isBtnPressed())
|
if (isBtnPressed())
|
||||||
{
|
{
|
||||||
teaTimer.beginSteeping(10000);
|
teaTimer.beginSteeping(5000);
|
||||||
state = State::STEEPING_IN_PROGRESS;
|
state = State::STEEPING_IN_PROGRESS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ADD_NEW_TEA_CONFIG:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case STEEPING_IN_PROGRESS:
|
case STEEPING_IN_PROGRESS:
|
||||||
{
|
{
|
||||||
teaTimer.tick();
|
teaTimer.tick();
|
||||||
if (teaTimer.isSteeping())
|
if (teaTimer.isSteeping())
|
||||||
{
|
{
|
||||||
auto r = teaTimer.remainingSteepTimeSeconds();
|
auto r = teaTimer.remainingSteepTimeSeconds();
|
||||||
smartDisplay.printTeaSteepingProgressScreen(currentTea.m_teaName, r);
|
smartDisplay.printTeaSteepingProgressScreen(currentTea, r);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printMsg("DONE");
|
smartDisplay.printMsg("DONE");
|
||||||
if (!contServo.isTurning())
|
if (!contServo.isTurning())
|
||||||
{
|
{
|
||||||
delay(2000);
|
delay(1000);
|
||||||
state = State::WAIT_FOR_RFID_SCAN;
|
state = State::REQUEST_FEEDBACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case REQUEST_FEEDBACK:
|
||||||
|
{
|
||||||
|
smartDisplay.printRequestFeedbackScreen(currentTea);
|
||||||
|
|
||||||
|
String uid = smartRFID.readTag(50); // just enough time to detect tag
|
||||||
|
if (uid != "no_tag_detected")
|
||||||
|
{
|
||||||
|
currentUid = uid;
|
||||||
|
smartDisplay.resetDisplay();
|
||||||
|
state = PROCESS_RFID_SCAN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonLeft.isJustPressed())
|
||||||
|
{
|
||||||
|
// decrease steeping time for next brew
|
||||||
|
smartDisplay.printMsg("Feedback accepted (-)");
|
||||||
|
delay(3000);
|
||||||
|
state = State::WAIT_FOR_RFID_SCAN;
|
||||||
|
}
|
||||||
|
if (buttonRight.isJustPressed())
|
||||||
|
{
|
||||||
|
// increase steeping time for next brew
|
||||||
|
smartDisplay.printMsg("Feedback accepted (+)");
|
||||||
|
delay(3000);
|
||||||
|
state = State::WAIT_FOR_RFID_SCAN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DISPLAY_ADD_NEW_TEA_CONFIG:
|
||||||
|
{
|
||||||
|
smartDisplay.printAddNewTeaConfigScreen(currentTea);
|
||||||
|
|
||||||
|
String uid = smartRFID.readTag(50); // just enough time to detect tag
|
||||||
|
if (uid != "no_tag_detected")
|
||||||
|
{
|
||||||
|
currentUid = uid;
|
||||||
|
smartDisplay.resetDisplay();
|
||||||
|
state = PROCESS_RFID_SCAN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonLeft.isJustPressed())
|
||||||
|
{
|
||||||
|
currentTea.m_steepingSeconds -= 10;
|
||||||
|
}
|
||||||
|
if (buttonRight.isJustPressed())
|
||||||
|
{
|
||||||
|
currentTea.m_steepingSeconds += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBtnPressed())
|
||||||
|
{
|
||||||
|
smartDisplay.printMsg("Accepted");
|
||||||
|
delay(1000);
|
||||||
|
state = State::DISPLAY_SCANNED_TEA_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggle_led()
|
|
||||||
{
|
|
||||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isBtnPressed()
|
bool isBtnPressed()
|
||||||
{
|
{
|
||||||
return digitalRead(TOUCH_BTN_PIN) == HIGH;
|
return digitalRead(TOUCH_BTN_PIN) == HIGH;
|
||||||
}
|
|
||||||
|
|
||||||
void printMsg(const String &s)
|
|
||||||
{
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setTextColor(WHITE);
|
|
||||||
display.setTextSize(1);
|
|
||||||
display.setCursor(0, 10);
|
|
||||||
display.println(s);
|
|
||||||
display.display();
|
|
||||||
}
|
|
||||||
void printMsg(unsigned long l)
|
|
||||||
{
|
|
||||||
display.clearDisplay();
|
|
||||||
display.setTextColor(WHITE);
|
|
||||||
display.setTextSize(1);
|
|
||||||
display.setCursor(0, 10);
|
|
||||||
display.println(l);
|
|
||||||
display.display();
|
|
||||||
}
|
}
|
|
@ -19,8 +19,4 @@
|
||||||
#include "SmartButton.hpp"
|
#include "SmartButton.hpp"
|
||||||
#include "SmartSound.hpp"
|
#include "SmartSound.hpp"
|
||||||
|
|
||||||
void playSong();
|
|
||||||
bool toggle_led(void *);
|
|
||||||
void printMsg(const String &s);
|
|
||||||
void printMsg(unsigned long l);
|
|
||||||
bool isBtnPressed();
|
bool isBtnPressed();
|
Loading…
Add table
Reference in a new issue