first prototype
This commit is contained in:
parent
40f8799a05
commit
2ca65ec643
3 changed files with 349 additions and 5 deletions
|
|
@ -1,4 +1,238 @@
|
||||||
#include <cud_display.hpp>
|
#include <cud_display.hpp>
|
||||||
|
|
||||||
CUDDisplay::CUDDisplay() : ESPMegaDisplay(&Serial2) {
|
CUDDisplay::CUDDisplay() : ESPMegaDisplay(&Serial2)
|
||||||
}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUDDisplay::begin(std::function<rtctime_t()> getTime, HardwareSerial *serial,
|
||||||
|
DigitalInputCard *inputCard, DigitalOutputCard *outputCard,
|
||||||
|
ClimateCard *climateCard)
|
||||||
|
{
|
||||||
|
ESPMegaDisplay::begin();
|
||||||
|
this->getTime = getTime;
|
||||||
|
this->sendClock();
|
||||||
|
this->inputCard = inputCard;
|
||||||
|
this->outputCard = outputCard;
|
||||||
|
this->climateCard = climateCard;
|
||||||
|
auto bindedHandlePWMChange = std::bind(&CUDDisplay::handlePWMChange, this, std::placeholders::_1, std::placeholders::_2);
|
||||||
|
auto bindedHandleACChange = std::bind(&CUDDisplay::handleACChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||||
|
this->outputCallbackHandle = this->outputCard->registerChangeCallback(bindedHandlePWMChange);
|
||||||
|
this->climateCallbackHandle = this->climateCard->registerChangeCallback(bindedHandleACChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUDDisplay::handleTouch(uint8_t page, uint8_t component, uint8_t event)
|
||||||
|
{
|
||||||
|
if (page != PAGE_DASHBOARD)
|
||||||
|
return;
|
||||||
|
switch (component)
|
||||||
|
{
|
||||||
|
case COMPONENT_LIGHT_TOGGLE_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
toggleLightGroupState();
|
||||||
|
break;
|
||||||
|
case COMPONENT_FAN_TOGGLE_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
toggleFanGroupState();
|
||||||
|
break;
|
||||||
|
case COMPONENT_AIR_PURIFIER_TOGGLE_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->outputCard->setState(7, !this->outputCard->getState(7));
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_TEMP_UP_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setTemperature(this->climateCard->getTemperature() + 1);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_TEMP_DOWN_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setTemperature(this->climateCard->getTemperature() - 1);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_MODE_OFF_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setMode(0);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_MODE_FAN_ONLY_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setMode(1);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_MODE_COOL_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setMode(2);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_FAN_MODE_AUTO_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setFanSpeed(0);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_FAN_MODE_LOW_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setFanSpeed(1);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_FAN_MODE_MEDIUM_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setFanSpeed(2);
|
||||||
|
break;
|
||||||
|
case COMPONENT_AC_FAN_MODE_HIGH_BUTTON:
|
||||||
|
if (event != TOUCH_TYPE_RELEASE)
|
||||||
|
break;
|
||||||
|
this->climateCard->setFanSpeed(3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUDDisplay::handlePWMChange(uint8_t pin, uint8_t value)
|
||||||
|
{
|
||||||
|
// Is the pin a light, fan, or air purifier?
|
||||||
|
if (pin >= 0 && pin <= 3)
|
||||||
|
{
|
||||||
|
// Light
|
||||||
|
updateLightGroupState();
|
||||||
|
}
|
||||||
|
else if (pin >= 4 && pin <= 6)
|
||||||
|
{
|
||||||
|
// Fan
|
||||||
|
updateFanGroupState();
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (pin == 7)
|
||||||
|
{
|
||||||
|
// Air Purifier
|
||||||
|
updateAirPurifierState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUDDisplay::handleACChange(uint8_t mode, uint8_t fan_speed, uint8_t temperature)
|
||||||
|
{
|
||||||
|
// Update AC Controls
|
||||||
|
updateACState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUDDisplay::sendClock()
|
||||||
|
{
|
||||||
|
// Get the time
|
||||||
|
rtctime_t time = this->getTime();
|
||||||
|
// Send the time to the display
|
||||||
|
this->displayAdapter->print("clock.txt=");
|
||||||
|
// We use 24 hour format, HH:MM
|
||||||
|
this->displayAdapter->print(time.hours);
|
||||||
|
this->displayAdapter->print(":");
|
||||||
|
this->displayAdapter->print(time.minutes);
|
||||||
|
this->sendStopBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CUDDisplay::calculateLightGroupState() {
|
||||||
|
// Check if all lights are on
|
||||||
|
bool allLightsOn = true;
|
||||||
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
|
if (this->outputCard->getState(i) == 0) {
|
||||||
|
allLightsOn = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allLightsOn;
|
||||||
|
}
|
||||||
|
bool CUDDisplay::calculateFanGroupState() {
|
||||||
|
// Check if all fans are on
|
||||||
|
bool allFansOn = true;
|
||||||
|
for (uint8_t i = 4; i < 7; i++) {
|
||||||
|
if (this->outputCard->getState(i) == 0) {
|
||||||
|
allFansOn = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allFansOn;
|
||||||
|
}
|
||||||
|
void CUDDisplay::setLightGrouptState(bool state) {
|
||||||
|
// Set all lights to state
|
||||||
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
|
this->outputCard->setState(i, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void CUDDisplay::setFanGroupState(bool state) {
|
||||||
|
// Set all fans to state
|
||||||
|
for (uint8_t i = 4; i < 7; i++) {
|
||||||
|
this->outputCard->setState(i, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void CUDDisplay::toggleLightGroupState() {
|
||||||
|
// Get the current group state
|
||||||
|
bool state = calculateLightGroupState();
|
||||||
|
// Toggle the state
|
||||||
|
state = !state;
|
||||||
|
// Set the state
|
||||||
|
setLightGrouptState(state);
|
||||||
|
}
|
||||||
|
void CUDDisplay::toggleFanGroupState() {
|
||||||
|
// Get the current group state
|
||||||
|
bool state = calculateFanGroupState();
|
||||||
|
// Toggle the state
|
||||||
|
state = !state;
|
||||||
|
// Set the state
|
||||||
|
setFanGroupState(state);
|
||||||
|
}
|
||||||
|
void CUDDisplay::updateLightGroupState() {
|
||||||
|
// Calculate the state
|
||||||
|
bool state = calculateLightGroupState();
|
||||||
|
// Send the state to the display
|
||||||
|
this->displayAdapter->print("LT_BT.pic=");
|
||||||
|
this->displayAdapter->println(state ? COMPONENT_LIGHT_TOGGLE_PIC_ON : COMPONENT_LIGHT_TOGGLE_PIC_OFF);
|
||||||
|
this->sendStopBytes();
|
||||||
|
}
|
||||||
|
void CUDDisplay::updateFanGroupState() {
|
||||||
|
// Calculate the state
|
||||||
|
bool state = calculateFanGroupState();
|
||||||
|
// Send the state to the display
|
||||||
|
this->displayAdapter->print("FAN_BT.pic=");
|
||||||
|
this->displayAdapter->println(state ? COMPONENT_FAN_TOGGLE_PIC_ON : COMPONENT_FAN_TOGGLE_PIC_OFF);
|
||||||
|
this->sendStopBytes();
|
||||||
|
}
|
||||||
|
void CUDDisplay::updateAirPurifierState() {
|
||||||
|
// Get the state
|
||||||
|
bool state = this->outputCard->getState(7);
|
||||||
|
// Send the state to the display
|
||||||
|
this->displayAdapter->print("AIR_BT.pic=");
|
||||||
|
this->displayAdapter->println(state ? COMPONENT_AIR_PURIFIER_TOGGLE_PIC_ON : COMPONENT_AIR_PURIFIER_TOGGLE_PIC_OFF);
|
||||||
|
this->sendStopBytes();
|
||||||
|
}
|
||||||
|
void CUDDisplay::updateACState() {
|
||||||
|
// Get the state
|
||||||
|
uint8_t mode = this->climateCard->getMode();
|
||||||
|
uint8_t fan_speed = this->climateCard->getFanSpeed();
|
||||||
|
uint8_t temperature = this->climateCard->getTemperature();
|
||||||
|
// Send the state to the display
|
||||||
|
this->displayAdapter->print("AC_MODE_OFF.pic=");
|
||||||
|
this->displayAdapter->println(mode == 0 ? COMPONENT_AC_MODE_OFF_PIC_ACTIVE : COMPONENT_AC_MODE_OFF_PIC_INACTIVE);
|
||||||
|
this->sendStopBytes();
|
||||||
|
this->displayAdapter->print("AC_MODE_FAN_ONLY.pic=");
|
||||||
|
this->displayAdapter->println(mode == 1 ? COMPONENT_AC_MODE_FAN_ONLY_PIC_ACTIVE : COMPONENT_AC_MODE_FAN_ONLY_PIC_INACTIVE);
|
||||||
|
this->sendStopBytes();
|
||||||
|
this->displayAdapter->print("AC_MODE_COOL.pic=");
|
||||||
|
this->displayAdapter->println(mode == 2 ? COMPONENT_AC_MODE_COOL_PIC_ACTIVE : COMPONENT_AC_MODE_COOL_PIC_INACTIVE);
|
||||||
|
this->sendStopBytes();
|
||||||
|
this->displayAdapter->print("AC_FAN_MODE_AUTO.pic=");
|
||||||
|
this->displayAdapter->println(fan_speed == 0 ? COMPONENT_AC_FAN_MODE_AUTO_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_AUTO_PIC_INACTIVE);
|
||||||
|
this->sendStopBytes();
|
||||||
|
this->displayAdapter->print("AC_FAN_MODE_HIGH.pic=");
|
||||||
|
this->displayAdapter->println(fan_speed == 1 ? COMPONENT_AC_FAN_MODE_HIGH_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_HIGH_PIC_INACTIVE);
|
||||||
|
this->sendStopBytes();
|
||||||
|
this->displayAdapter->print("AC_FAN_MODE_MEDIUM.pic=");
|
||||||
|
this->displayAdapter->println(fan_speed == 2 ? COMPONENT_AC_FAN_MODE_MEDIUM_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_MEDIUM_PIC_INACTIVE);
|
||||||
|
this->sendStopBytes();
|
||||||
|
this->displayAdapter->print("AC_FAN_MODE_LOW.pic=");
|
||||||
|
this->displayAdapter->println(fan_speed == 3 ? COMPONENT_AC_FAN_MODE_LOW_PIC_ACTIVE : COMPONENT_AC_FAN_MODE_LOW_PIC_INACTIVE);
|
||||||
|
this->sendStopBytes();
|
||||||
|
this->displayAdapter->print("AC_TEMP.val=");
|
||||||
|
this->displayAdapter->println(temperature);
|
||||||
|
this->sendStopBytes();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,89 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <ESPMegaDisplay.hpp>
|
#include <ESPMegaDisplay.hpp>
|
||||||
|
#include <TimeStructure.hpp>
|
||||||
|
#include <DigitalInputCard.hpp>
|
||||||
|
#include <DigitalOutputCard.hpp>
|
||||||
|
#include <ClimateCard.hpp>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Pin Mapping
|
||||||
|
0-3: Lights
|
||||||
|
4-6: Fans
|
||||||
|
7: Air Purifier
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Touch Types
|
||||||
|
#define TOUCH_TYPE_PRESS 0
|
||||||
|
#define TOUCH_TYPE_RELEASE 1
|
||||||
|
|
||||||
|
// Page IDs
|
||||||
|
#define PAGE_BOOT 0
|
||||||
|
#define PAGE_DASHBOARD 1
|
||||||
|
|
||||||
|
// Dashboard Toggle Buttons
|
||||||
|
#define COMPONENT_LIGHT_TOGGLE_BUTTON 1
|
||||||
|
#define COMPONENT_LIGHT_TOGGLE_PIC_ON 11
|
||||||
|
#define COMPONENT_LIGHT_TOGGLE_PIC_OFF 12
|
||||||
|
#define COMPONENT_FAN_TOGGLE_BUTTON 2
|
||||||
|
#define COMPONENT_FAN_TOGGLE_PIC_ON 21
|
||||||
|
#define COMPONENT_FAN_TOGGLE_PIC_OFF 22
|
||||||
|
#define COMPONENT_AIR_PURIFIER_TOGGLE_BUTTON 3
|
||||||
|
#define COMPONENT_AIR_PURIFIER_TOGGLE_PIC_ON 31
|
||||||
|
#define COMPONENT_AIR_PURIFIER_TOGGLE_PIC_OFF 32
|
||||||
|
|
||||||
|
// Dashboard AC Controls
|
||||||
|
#define COMPONENT_AC_TEMP_UP_BUTTON 4
|
||||||
|
#define COMPONENT_AC_TEMP_DOWN_BUTTON 5
|
||||||
|
#define COMPONENT_AC_MODE_OFF_BUTTON 6
|
||||||
|
#define COMPONENT_AC_MODE_OFF_PIC_ACTIVE 61
|
||||||
|
#define COMPONENT_AC_MODE_OFF_PIC_INACTIVE 62
|
||||||
|
#define COMPONENT_AC_MODE_FAN_ONLY_BUTTON 7
|
||||||
|
#define COMPONENT_AC_MODE_FAN_ONLY_PIC_ACTIVE 71
|
||||||
|
#define COMPONENT_AC_MODE_FAN_ONLY_PIC_INACTIVE 72
|
||||||
|
#define COMPONENT_AC_MODE_COOL_BUTTON 8
|
||||||
|
#define COMPONENT_AC_MODE_COOL_PIC_ACTIVE 81
|
||||||
|
#define COMPONENT_AC_MODE_COOL_PIC_INACTIVE 82
|
||||||
|
#define COMPONENT_AC_FAN_MODE_AUTO_BUTTON 9
|
||||||
|
#define COMPONENT_AC_FAN_MODE_AUTO_PIC_ACTIVE 91
|
||||||
|
#define COMPONENT_AC_FAN_MODE_AUTO_PIC_INACTIVE 92
|
||||||
|
#define COMPONENT_AC_FAN_MODE_LOW_BUTTON 10
|
||||||
|
#define COMPONENT_AC_FAN_MODE_LOW_PIC_ACTIVE 101
|
||||||
|
#define COMPONENT_AC_FAN_MODE_LOW_PIC_INACTIVE 102
|
||||||
|
#define COMPONENT_AC_FAN_MODE_MEDIUM_BUTTON 11
|
||||||
|
#define COMPONENT_AC_FAN_MODE_MEDIUM_PIC_ACTIVE 111
|
||||||
|
#define COMPONENT_AC_FAN_MODE_MEDIUM_PIC_INACTIVE 112
|
||||||
|
#define COMPONENT_AC_FAN_MODE_HIGH_BUTTON 12
|
||||||
|
#define COMPONENT_AC_FAN_MODE_HIGH_PIC_ACTIVE 121
|
||||||
|
#define COMPONENT_AC_FAN_MODE_HIGH_PIC_INACTIVE 122
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class for controlling the ESPMegaDisplay.
|
||||||
|
*
|
||||||
|
* Made for Chulalongkorn University Demonstration Seconary School.
|
||||||
|
*/
|
||||||
class CUDDisplay : public ESPMegaDisplay {
|
class CUDDisplay : public ESPMegaDisplay {
|
||||||
public:
|
public:
|
||||||
CUDDisplay();
|
CUDDisplay();
|
||||||
void begin();
|
void begin(std::function<rtctime_t()> getTime, HardwareSerial* serial, DigitalInputCard* inputCard, DigitalOutputCard* outputCard, ClimateCard* climateCard);
|
||||||
void handleTouch(uint8_t page, uint8_t component, uint8_t event);
|
void handleTouch(uint8_t page, uint8_t component, uint8_t event);
|
||||||
void handlePWMChange(uint8_t pin, uint8_t value);
|
void handlePWMChange(uint8_t pin, uint8_t value);
|
||||||
void handleACChange(uint8_t mode, uint8_t fan_speed, uint8_t temperature);
|
void handleACChange(uint8_t mode, uint8_t fan_speed, uint8_t temperature);
|
||||||
void sendClock();
|
void sendClock();
|
||||||
|
private:
|
||||||
|
std::function<rtctime_t()> getTime;
|
||||||
|
DigitalInputCard* inputCard;
|
||||||
|
DigitalOutputCard* outputCard;
|
||||||
|
uint8_t outputCallbackHandle;
|
||||||
|
ClimateCard* climateCard;
|
||||||
|
uint8_t climateCallbackHandle;
|
||||||
|
bool calculateLightGroupState();
|
||||||
|
bool calculateFanGroupState();
|
||||||
|
void setLightGrouptState(bool state);
|
||||||
|
void setFanGroupState(bool state);
|
||||||
|
void toggleLightGroupState();
|
||||||
|
void toggleFanGroupState();
|
||||||
|
void updateLightGroupState();
|
||||||
|
void updateFanGroupState();
|
||||||
|
void updateAirPurifierState();
|
||||||
|
void updateACState();
|
||||||
};
|
};
|
||||||
36
src/main.cpp
36
src/main.cpp
|
|
@ -1,9 +1,21 @@
|
||||||
#include <main.hpp>
|
#include <main.hpp>
|
||||||
#include <cud_display.hpp>
|
#include <cud_display.hpp>
|
||||||
|
|
||||||
ESPMegaPRO espmega = ESPMegaPRO();
|
/***********************************************
|
||||||
CUDDisplay external_display = CUDDisplay();
|
* Begin Configuration *
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
// Display Configuration
|
||||||
|
#define cudDisplayAdapter Serial2
|
||||||
|
#define CUD_DISPLAY_BAUD_RATE 115200
|
||||||
|
#define CUD_DISPLAY_TX_PIN 16
|
||||||
|
#define CUD_DISPLAY_RX_PIN 17
|
||||||
|
|
||||||
|
//Air Conditioner Configuration
|
||||||
|
#define AIR_CONDITIONER_SENSOR_TYPE AC_SENSOR_TYPE_DHT22
|
||||||
|
#define AIR_CONDITIONER_SENSOR_PIN 4
|
||||||
|
#define AIR_CONDITIONER_IR_PIN 5
|
||||||
|
#define AIR_CONDITIONER_RMT_CHANNEL RMT_CHANNEL_0
|
||||||
|
|
||||||
const char *mode_names[] = {"off", "fan_only", "cool"};
|
const char *mode_names[] = {"off", "fan_only", "cool"};
|
||||||
const char *fan_speed_names[] = {"auto", "high", "medium", "low"};
|
const char *fan_speed_names[] = {"auto", "high", "medium", "low"};
|
||||||
|
|
@ -18,6 +30,17 @@ AirConditioner ac = {
|
||||||
.getInfraredCode = &getInfraredCode
|
.getInfraredCode = &getInfraredCode
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/***********************************************
|
||||||
|
* End Configuration *
|
||||||
|
***********************************************/
|
||||||
|
|
||||||
|
ESPMegaPRO espmega = ESPMegaPRO();
|
||||||
|
CUDDisplay cudDisplay = CUDDisplay();
|
||||||
|
|
||||||
|
ClimateCard climateCard = ClimateCard(AIR_CONDITIONER_IR_PIN, ac,
|
||||||
|
AIR_CONDITIONER_SENSOR_TYPE, AIR_CONDITIONER_SENSOR_PIN,
|
||||||
|
AIR_CONDITIONER_RMT_CHANNEL);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
espmega.begin();
|
espmega.begin();
|
||||||
espmega.enableIotModule();
|
espmega.enableIotModule();
|
||||||
|
|
@ -30,6 +53,15 @@ void setup() {
|
||||||
espmega.enableInternalDisplay(&Serial);
|
espmega.enableInternalDisplay(&Serial);
|
||||||
espmega.enableWebServer(80);
|
espmega.enableWebServer(80);
|
||||||
espmega.inputs.registerCallback(on_pin_change);
|
espmega.inputs.registerCallback(on_pin_change);
|
||||||
|
espmega.outputs.setAutoSaveToFRAM(true);
|
||||||
|
espmega.installCard(3, &climateCard);
|
||||||
|
climateCard.bindFRAM(&espmega.fram, 5000);
|
||||||
|
climateCard.loadStateFromFRAM();
|
||||||
|
climateCard.setFRAMAutoSave(true);
|
||||||
|
espmega.iot->registerCard(3);
|
||||||
|
cudDisplayAdapter.begin(CUD_DISPLAY_BAUD_RATE, SERIAL_8N1, CUD_DISPLAY_RX_PIN, CUD_DISPLAY_TX_PIN);
|
||||||
|
auto bindedGetTime = std::bind(&ESPMegaPRO::getTime, &espmega);
|
||||||
|
cudDisplay.begin(bindedGetTime, &cudDisplayAdapter, &espmega.inputs, &espmega.outputs, &climateCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue