Fixed MODBUS and seneca drivers, added partial counter reset
This commit is contained in:
@@ -3,7 +3,6 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
|
|
||||||
//#define DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
namespace drivers
|
namespace drivers
|
||||||
@@ -18,6 +17,7 @@ namespace drivers
|
|||||||
LOG_INFO("Init serial port 1");
|
LOG_INFO("Init serial port 1");
|
||||||
// RS485 is hardwired to serial port 1
|
// RS485 is hardwired to serial port 1
|
||||||
m_serial.begin(baud, conf, 18, 17);
|
m_serial.begin(baud, conf, 18, 17);
|
||||||
|
m_serial.setTimeout(1000);
|
||||||
m_serial.flush();
|
m_serial.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
constexpr uint8_t func = 0x06;
|
constexpr uint8_t func = 0x06;
|
||||||
LOG_DEBUG("Write single register: dev[", device, "], reg[", reg, "], value[", value, "]");
|
LOG_DEBUG("Write single register: dev[", device, "], reg[", reg, "], value[", value, "]");
|
||||||
return writeInteger(device, func, reg, {value});
|
return writeInteger(device, func, reg, {value}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Func 0x0F
|
// Func 0x0F
|
||||||
@@ -128,7 +128,7 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
constexpr uint8_t func = 0x10;
|
constexpr uint8_t func = 0x10;
|
||||||
LOG_DEBUG("Write multi registers: dev[", device, "], start[", reg, "], num[", values.size(), "]");
|
LOG_DEBUG("Write multi registers: dev[", device, "], start[", reg, "], num[", values.size(), "]");
|
||||||
return writeInteger(device, func, reg, values);
|
return writeInteger(device, func, reg, values, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
@@ -150,15 +150,15 @@ namespace drivers
|
|||||||
LOG_ERROR("Failed receive readBinary response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
LOG_ERROR("Failed receive readBinary response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
||||||
|
printBytes("readBinary Response", response);
|
||||||
|
#endif
|
||||||
|
|
||||||
// element 2 of response has the response data bytes expected
|
// element 2 of response has the response data bytes expected
|
||||||
const uint8_t actualRespLen(response.at(2));
|
const uint8_t actualRespLen(response.at(2));
|
||||||
if (actualRespLen != nRespDataBytes)
|
if (actualRespLen != nRespDataBytes)
|
||||||
{
|
{
|
||||||
LOG_ERROR("Failed receive, data to short: actual[", actualRespLen, "], expected[", nRespDataBytes, "]");
|
LOG_ERROR("Failed receive, data to short: actual[", actualRespLen, "], expected[", nRespDataBytes, "]");
|
||||||
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
|
||||||
printBytes("readBinary Response", response);
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,11 +198,11 @@ namespace drivers
|
|||||||
if (!readN(expectedRespLen, response))
|
if (!readN(expectedRespLen, response))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Failed receive readInteger response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
LOG_ERROR("Failed receive readInteger response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
||||||
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
|
||||||
printBytes("readInteger Response", response);
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
||||||
|
printBytes("readInteger Response", response);
|
||||||
|
#endif
|
||||||
|
|
||||||
// element 2 of response has the response data bytes expected
|
// element 2 of response has the response data bytes expected
|
||||||
const uint8_t actualRespLen(response.at(2));
|
const uint8_t actualRespLen(response.at(2));
|
||||||
@@ -270,11 +270,11 @@ namespace drivers
|
|||||||
if (!readN(expectedRespLen, response))
|
if (!readN(expectedRespLen, response))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Failed receive writeBinary response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
LOG_ERROR("Failed receive writeBinary response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
||||||
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
|
||||||
printBytes("writeBinary Response", response);
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
||||||
|
printBytes("writeBinary Response", response);
|
||||||
|
#endif
|
||||||
|
|
||||||
// compute crc of current message
|
// compute crc of current message
|
||||||
if (!verifyCrc(response))
|
if (!verifyCrc(response))
|
||||||
@@ -283,10 +283,10 @@ namespace drivers
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool MODBUS::writeInteger(const uint8_t device, const uint8_t func, const uint16_t reg, const std::vector<uint16_t> &in)
|
const bool MODBUS::writeInteger(const uint8_t device, const uint8_t func, const uint16_t reg, const std::vector<uint16_t> &in, const bool multi)
|
||||||
{
|
{
|
||||||
const uint16_t num(in.size());
|
const uint16_t num(in.size());
|
||||||
if (num == 1)
|
if (!multi)
|
||||||
{
|
{
|
||||||
if (!write(singleRequest(device, func, reg, in[0])))
|
if (!write(singleRequest(device, func, reg, in[0])))
|
||||||
{
|
{
|
||||||
@@ -298,14 +298,14 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
// build data vector for request, inverting bytes if necessary
|
// build data vector for request, inverting bytes if necessary
|
||||||
std::vector<uint8_t> requestData;
|
std::vector<uint8_t> requestData;
|
||||||
requestData.resize(in.size() * sizeof(uint16_t));
|
requestData.resize(in.size() * sizeof(uint16_t), 0xff);
|
||||||
auto it=requestData.begin();
|
auto it = requestData.begin();
|
||||||
std::for_each(in.begin(), in.end(), [requestData, &it](auto inV) {
|
for (auto inV : in)
|
||||||
|
{
|
||||||
const uint16_t beV(htobe16(inV));
|
const uint16_t beV(htobe16(inV));
|
||||||
*it=highByte(beV);
|
*(it++) = lowByte(beV);
|
||||||
*(++it)=lowByte(beV);
|
*(it++) = highByte(beV);
|
||||||
});
|
}
|
||||||
|
|
||||||
if (!write(multiRequest(device, func, reg, num, requestData)))
|
if (!write(multiRequest(device, func, reg, num, requestData)))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Failed send writeMultiInteger command");
|
LOG_ERROR("Failed send writeMultiInteger command");
|
||||||
@@ -318,11 +318,11 @@ namespace drivers
|
|||||||
if (!readN(expectedRespLen, response))
|
if (!readN(expectedRespLen, response))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Failed receive writeInteger response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
LOG_ERROR("Failed receive writeInteger response, expected[", expectedRespLen, "], received[", response.size(), "]");
|
||||||
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
|
||||||
printBytes("writeInteger Response", response);
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
||||||
|
printBytes("writeInteger Response", response);
|
||||||
|
#endif
|
||||||
|
|
||||||
// compute crc of current message
|
// compute crc of current message
|
||||||
if (!verifyCrc(response))
|
if (!verifyCrc(response))
|
||||||
@@ -366,7 +366,7 @@ namespace drivers
|
|||||||
header.qty = htobe16(qty);
|
header.qty = htobe16(qty);
|
||||||
header.bytes = data.size(); // 8 bit value
|
header.bytes = data.size(); // 8 bit value
|
||||||
|
|
||||||
//const uint8_t headerBytes(sizeof(req_multi_t)); // sizeof not working because of memory padding
|
// const uint8_t headerBytes(sizeof(req_multi_t)); // sizeof not working because of memory padding
|
||||||
const uint8_t headerBytes(7);
|
const uint8_t headerBytes(7);
|
||||||
const uint8_t dataBytes(data.size());
|
const uint8_t dataBytes(data.size());
|
||||||
const uint8_t crcBytes(sizeof(crc_t));
|
const uint8_t crcBytes(sizeof(crc_t));
|
||||||
@@ -380,7 +380,7 @@ namespace drivers
|
|||||||
std::vector<uint8_t> dataOut;
|
std::vector<uint8_t> dataOut;
|
||||||
dataOut.resize(headerBytes + dataBytes + crcBytes); // header message + data values + crc code
|
dataOut.resize(headerBytes + dataBytes + crcBytes); // header message + data values + crc code
|
||||||
std::memcpy(dataOut.data(), &header, headerBytes); // copy message
|
std::memcpy(dataOut.data(), &header, headerBytes); // copy message
|
||||||
std::memcpy(dataOut.data() + headerBytes, data.data(), dataBytes); // copy data
|
std::memcpy(dataOut.data() + headerBytes, data.data(), dataBytes); // copy data
|
||||||
std::memcpy(dataOut.data() + headerBytes + dataBytes, &crc, crcBytes); // copy crc
|
std::memcpy(dataOut.data() + headerBytes + dataBytes, &crc, crcBytes); // copy crc
|
||||||
|
|
||||||
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
#ifdef DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// #define DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
|
||||||
|
|
||||||
#include <DebugLog.h>
|
#include <DebugLog.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <HardwareSerial.h> // Reference the ESP32 built-in serial port library
|
#include <HardwareSerial.h> // Reference the ESP32 built-in serial port library
|
||||||
@@ -50,7 +52,6 @@ namespace drivers
|
|||||||
typedef uint16_t crc_t;
|
typedef uint16_t crc_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
MODBUS(const uint32_t baud, const SerialConfig conf);
|
MODBUS(const uint32_t baud, const SerialConfig conf);
|
||||||
|
|
||||||
// Func 0x01
|
// Func 0x01
|
||||||
@@ -84,7 +85,7 @@ namespace drivers
|
|||||||
const bool readBinary(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t bits, std::vector<bool> &out);
|
const bool readBinary(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t bits, std::vector<bool> &out);
|
||||||
const bool readInteger(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t num, std::vector<uint16_t> &out);
|
const bool readInteger(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t num, std::vector<uint16_t> &out);
|
||||||
const bool writeBinary(const uint8_t device, const uint8_t func, const uint16_t reg, const std::vector<bool> &in);
|
const bool writeBinary(const uint8_t device, const uint8_t func, const uint16_t reg, const std::vector<bool> &in);
|
||||||
const bool writeInteger(const uint8_t device, const uint8_t func, const uint16_t reg, const std::vector<uint16_t> &in);
|
const bool writeInteger(const uint8_t device, const uint8_t func, const uint16_t reg, const std::vector<uint16_t> &in, const bool multi);
|
||||||
const bool verifyCrc(const std::vector<uint8_t> &data);
|
const bool verifyCrc(const std::vector<uint8_t> &data);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace drivers
|
|||||||
|
|
||||||
const S50140::powerinfo_t S50140::getAll()
|
const S50140::powerinfo_t S50140::getAll()
|
||||||
{
|
{
|
||||||
powerinfo_t info;
|
powerinfo_t info{MAXFLOAT};
|
||||||
info.v = getV();
|
info.v = getV();
|
||||||
info.a = getA();
|
info.a = getA();
|
||||||
info.pAct = getPact();
|
info.pAct = getPact();
|
||||||
@@ -57,19 +57,36 @@ namespace drivers
|
|||||||
return readFloatReg(REG_WhPart);
|
return readFloatReg(REG_WhPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
void S50140::resetTotalCounters()
|
void S50140::delayRequest()
|
||||||
{
|
{
|
||||||
uint8_t retries(0);
|
auto now = millis();
|
||||||
const uint16_t resetAll = 0x0000;
|
if ((now - m_lastRequest) < minDelay)
|
||||||
while (retries++ < maxRetries)
|
{ // minimum 500ms between requests
|
||||||
{
|
delay(now - m_lastRequest);
|
||||||
if (m_bus.writeRegister(m_address, REG_TotCount, resetAll))
|
|
||||||
return;
|
|
||||||
LOG_ERROR("Unable to Reset Powermeter Total Counters, device", m_address);
|
|
||||||
delay(10);
|
|
||||||
}
|
}
|
||||||
return;
|
m_lastRequest = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t S50140::getRegset()
|
||||||
|
{
|
||||||
|
std::vector<uint16_t> value;
|
||||||
|
delayRequest();
|
||||||
|
m_bus.readHoldingRegisters(m_address, REG_Regset, 2, value);
|
||||||
|
if (value.empty())
|
||||||
|
return UINT8_MAX;
|
||||||
|
return value.front() + value.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t S50140::getCounterStatus()
|
||||||
|
{
|
||||||
|
std::vector<uint16_t> value;
|
||||||
|
delayRequest();
|
||||||
|
m_bus.readHoldingRegisters(m_address, REG_PartCount, 2, value);
|
||||||
|
if (value.empty())
|
||||||
|
return UINT16_MAX;
|
||||||
|
return value.front() + value.back();
|
||||||
|
}
|
||||||
|
|
||||||
void S50140::resetPartialCounters()
|
void S50140::resetPartialCounters()
|
||||||
{
|
{
|
||||||
uint8_t retries(0);
|
uint8_t retries(0);
|
||||||
@@ -79,15 +96,18 @@ namespace drivers
|
|||||||
while (retries++ < maxRetries)
|
while (retries++ < maxRetries)
|
||||||
{
|
{
|
||||||
bool ok(true);
|
bool ok(true);
|
||||||
ok &= m_bus.writeRegister(m_address, REG_TotCount, stopAll);
|
delayRequest();
|
||||||
delay(10);
|
LOG_WARN("Powermeter Counter STOP");
|
||||||
ok &= m_bus.writeRegister(m_address, REG_TotCount, resetAll);
|
ok &= m_bus.writeRegisters(m_address, REG_PartCount, {0x0000, stopAll});
|
||||||
delay(10);
|
delayRequest();
|
||||||
ok &= m_bus.writeRegister(m_address, REG_TotCount, startAll);
|
LOG_WARN("Powermeter Counter RESET");
|
||||||
|
ok &= m_bus.writeRegisters(m_address, REG_PartCount, {0x0000, resetAll});
|
||||||
|
delayRequest();
|
||||||
|
LOG_WARN("Powermeter Counter START");
|
||||||
|
ok &= m_bus.writeRegisters(m_address, REG_PartCount, {0x0000, startAll});
|
||||||
if (ok)
|
if (ok)
|
||||||
return;
|
return;
|
||||||
LOG_ERROR("Unable to Reset Powermeter Partial Counters, device", m_address);
|
LOG_ERROR("Unable to Reset Powermeter Partial Counters, device", m_address);
|
||||||
delay(10);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -99,11 +119,12 @@ namespace drivers
|
|||||||
|
|
||||||
while (retries++ < maxRetries)
|
while (retries++ < maxRetries)
|
||||||
{
|
{
|
||||||
|
delayRequest();
|
||||||
if (m_bus.readHoldingRegisters(m_address, reg, dataWords, values) && values.size() == dataWords)
|
if (m_bus.readHoldingRegisters(m_address, reg, dataWords, values) && values.size() == dataWords)
|
||||||
{
|
{
|
||||||
floatval_t fv; // potrebbe essere il contrario, vedremo
|
floatval_t fv; // potrebbe essere il contrario, vedremo
|
||||||
fv.hi = values[0]; // magari va invertita ancora l'endianness
|
fv.words.lo = values[0]; // magari va invertita ancora l'endianness
|
||||||
fv.lo = values[1];
|
fv.words.hi = values[1];
|
||||||
return fv.f;
|
return fv.f;
|
||||||
}
|
}
|
||||||
LOG_ERROR("Unable to Read Powermeter values, device", m_address);
|
LOG_ERROR("Unable to Read Powermeter values, device", m_address);
|
||||||
|
|||||||
@@ -11,24 +11,28 @@ namespace drivers
|
|||||||
private:
|
private:
|
||||||
const uint8_t maxRetries = 5;
|
const uint8_t maxRetries = 5;
|
||||||
const uint8_t dataWords = 2;
|
const uint8_t dataWords = 2;
|
||||||
|
const uint16_t minDelay = 500;
|
||||||
|
|
||||||
const uint16_t REG_V = 0x100C;
|
const uint16_t REG_V = 0x100C;
|
||||||
const uint16_t REG_A = 0x1016;
|
const uint16_t REG_A = 0x1016;
|
||||||
const uint16_t REG_Pact = 0x1026;
|
const uint16_t REG_Pact = 0x1026;
|
||||||
const uint16_t REG_Papp = 0x102E;
|
const uint16_t REG_Papp = 0x102E;
|
||||||
const uint16_t REG_Prea = 0x1036;
|
const uint16_t REG_Prea = 0x1036;
|
||||||
const uint16_t REG_Freq = 0x1036;
|
const uint16_t REG_Freq = 0x1038;
|
||||||
const uint16_t REG_WhTot = 0x1106;
|
const uint16_t REG_WhTot = 0x1106;
|
||||||
const uint16_t REG_WhPart = 0x1400;
|
const uint16_t REG_WhPart = 0x1400;
|
||||||
const uint16_t REG_Serial = 0x0500;
|
const uint16_t REG_Serial = 0x0500;
|
||||||
const uint16_t REG_Regset = 0x0538;
|
const uint16_t REG_Regset = 0x0538;
|
||||||
const uint16_t REG_TotCount = 0x0516;
|
const uint16_t REG_PartCount = 0x0526;
|
||||||
const uint16_t REG_PartCount = 0x0517;
|
|
||||||
|
|
||||||
typedef union
|
typedef union
|
||||||
{
|
{
|
||||||
float_t f;
|
float_t f;
|
||||||
uint16_t hi;
|
struct
|
||||||
uint16_t lo;
|
{
|
||||||
|
uint16_t hi;
|
||||||
|
uint16_t lo;
|
||||||
|
} words;
|
||||||
} floatval_t;
|
} floatval_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -59,14 +63,18 @@ namespace drivers
|
|||||||
const float_t getWhTot();
|
const float_t getWhTot();
|
||||||
const float_t getWhPar();
|
const float_t getWhPar();
|
||||||
|
|
||||||
void resetTotalCounters();
|
const uint8_t getRegset();
|
||||||
|
const uint16_t getCounterStatus();
|
||||||
|
|
||||||
void resetPartialCounters();
|
void resetPartialCounters();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void delayRequest();
|
||||||
float_t readFloatReg(const uint16_t reg);
|
float_t readFloatReg(const uint16_t reg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint8_t m_address;
|
const uint8_t m_address;
|
||||||
drivers::MODBUS &m_bus;
|
drivers::MODBUS &m_bus;
|
||||||
|
uint64_t m_lastRequest = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
18
src/main.cpp
18
src/main.cpp
@@ -59,7 +59,6 @@ void loop()
|
|||||||
auto io = digitalIO(i2c, bus, {relayBoardAddr});
|
auto io = digitalIO(i2c, bus, {relayBoardAddr});
|
||||||
delay(100);
|
delay(100);
|
||||||
auto seneca = drivers::S50140(bus, senecadAddr);
|
auto seneca = drivers::S50140(bus, senecadAddr);
|
||||||
delay(100);
|
|
||||||
|
|
||||||
Network.onEvent([ð](arduino_event_id_t event, arduino_event_info_t info)
|
Network.onEvent([ð](arduino_event_id_t event, arduino_event_info_t info)
|
||||||
{ eth.onEvent(event, info); });
|
{ eth.onEvent(event, info); });
|
||||||
@@ -116,16 +115,23 @@ void loop()
|
|||||||
|
|
||||||
for (auto j(0); j < io.getOutNum(); j++)
|
for (auto j(0); j < io.getOutNum(); j++)
|
||||||
{
|
{
|
||||||
// io.digitalIOWrite(j, true);
|
|
||||||
LOG_INFO("Input", j, io.digitalIORead(j) ? "True" : "False");
|
LOG_INFO("Input", j, io.digitalIORead(j) ? "True" : "False");
|
||||||
delay(500);
|
delay(50);
|
||||||
// io.digitalIOWrite(j, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drivers::S50140::powerinfo_t pinfo = seneca.getAll();
|
drivers::S50140::powerinfo_t pinfo = seneca.getAll();
|
||||||
LOG_INFO("Power Info\nV:", pinfo.v, "\nA:", pinfo.a, "\nW:", pinfo.pAct, "\nWh_t:", pinfo.whTot, "\nWh_p:", pinfo.whPar);
|
LOG_INFO("Power Info ==> V:", pinfo.v, "- A:", pinfo.a, "- W:", pinfo.pAct, "- F:", pinfo.f, "- Wh_t:", pinfo.whTot, "- Wh_p:", pinfo.whPar);
|
||||||
|
|
||||||
delay(5000);
|
if (io.digitalIORead(0))
|
||||||
|
{
|
||||||
|
uint8_t regset(seneca.getRegset());
|
||||||
|
uint16_t countStat(seneca.getCounterStatus());
|
||||||
|
LOG_INFO("Register Set: ", regset);
|
||||||
|
LOG_INFO("Counter Status: ", countStat);
|
||||||
|
seneca.resetPartialCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user