/*=====================================================================================
----------------------------------------------------------------------------------------
Настрока дисплея: 1.8 TFT SPI 128x160 V1.1 на чипе ST7735
Эту конфигурацию необходимо внести в файл настроек /TFT_eSPI/User_Setup.h
#define ST7735_DRIVER
#define TFT_RGB_ORDER TFT_RGB
#define TFT_WIDTH 128
#define TFT_HEIGHT 160
#define TFT_MISO PIN_D6
#define TFT_MOSI PIN_D7
#define TFT_SCLK PIN_D5
#define TFT_CS PIN_D8
#define TFT_DC PIN_D3
#define TFT_RST PIN_D4
#define SMOOTH_FONT
#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
----------------------------------------------------------------------------------------
SDO/MISO --> D6 (or leave disconnected if not reading TFT) - не нужен
LED --> VIN (or 5V, see below) +
SCK --> D5 +
SDI/MOSI/SDA --> D7 +
DC (RS/AO) --> D3 +
RESET --> D4 (or RST, see below) +
CS --> D8 (or GND, see below) +
GND --> GND (0V) +
VCC --> 5V or 3.3V +
----------------------------------------------------------------------------------------
Для WokWi применено ILI9341 320x240 на неполный экран
=====================================================================================*/
#include <Arduino.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <WiFi.h>
// #include <NTPClient.h>
#include <GyverNTP.h>
#include <WiFiUdp.h>
//#include <PZEM004Tv30.h> // Для счетчика
//#include <SoftwareSerial.h> // PZEM004T
#include "Fonts.h"
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
const char *ssid = "Wokwi-GUEST";
const char *password = "";
WiFiUDP ntpUDP;
GyverNTP timeClient(3);
const char *ntpServer = "pool.ntp.org";
// const long gmtOffset_sec = 10800; // Часовой пояс * 3600 , Москва +3 GTM
// const int daylightOffset_sec = 0;
// цвета
unsigned short grays[24];
unsigned short back = TFT_MAGENTA;
unsigned short blue = 0x0250;
#define c1 0xBDD7 // белый
#define c2 0x18C3 // серый
#define c3 0x9986 // красный
#define c4 0x2CAB // зеленый
#define c5 0xBDEF // золотой
// Оттенки серого
uint16_t lines[11] = {0};
uint16_t gra[60] = {0};
// electrical
double KWH;
double todayKWH = 0;
double WH;
int dot = 0;
String lbl[3] = {"Напр.", "Ток", "Част."};
float data[3] = {220.80, 16.84, 49.90};
bool PZEM_online, MQTT_online, TIME_online, WiFi_online = false;
// счетчик
int fromTop = 93;
int left = 50;
int width = 60;
int heigth = 20;
//SoftwareSerial pzemSWSerial(D1, D2); // Для WokWi
//PZEM004Tv30 pzem(pzemSWSerial); // отключим счетчик PZEM
// для показаний
float u1 = 0.0, i1 = 0.0, f1 = 0.0, p1 = 0.0, e1 = 0.0;
float p_max = 0, p_min = 99999999;
float u_avg = 0.0, i_avg = 0.0, f_avg = 0.0, p_avg = 0.0;
int u_count = 0, i_count = 0, f_count, p_count = 0;
// дни недели
String days[7] = {"Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"};
// для таймеров
uint32_t ms, ms1 = 0, ms2 = 0, ms3 = 0, ms4 = 0, ms_ok = 0;
uint32_t tm = 0;
uint32_t t_cur = 0;
long t_correct = 0;
//------------------------------------------------------------------------
void connectToWifi(void);
void define_level_of_gray();
void initSCREEN();
void setTime();
//------------------------------------------------------------------------
void setup()
{
define_level_of_gray();
tft.init();
tft.setRotation(3);
tft.setSwapBytes(true);
tft.fillScreen(TFT_BLACK);
spr.createSprite(160, 128);
spr.setSwapBytes(1);
connectToWifi();
timeClient.begin();
}
void loop()
{
ms = millis();
// Опрос показаний PZEM
if (ms1 == 0 || ms < ms1 || (ms - ms1) > 500)
{
ms1 = ms;
t_cur = ms / 1000;
tm = t_cur + t_correct;
float u2, i2, f2, p2, e2;
for (int i = 0; i < 3; i++)
{
//u2 = pzem.voltage();
u2 = 222;
if (u2 != NAN && u2 >= 0)
{
PZEM_online = true;
u1 = u2;
u_avg += u1;
u_count++;
break;
}
else
{
PZEM_online = false;
};
}
for (int i = 0; i < 3; i++)
{
i2 = 14;
//i2 = pzem.current();
if (i2 != NAN && i2 >= 0)
{
PZEM_online = true;
i1 = i2;
i_avg += i1;
i_count++;
break;
}
else
{
PZEM_online = false;
};
}
for (int i = 0; i < 3; i++)
{
p2 = 22;
//p2 = pzem.power();
if (p2 != NAN && p2 >= 0)
{
PZEM_online = true;
p1 = p2;
p_avg += p1;
p_count++;
break;
}
else
{
PZEM_online = false;
};
}
for (int i = 0; i < 3; i++)
{
f2 = 50.1;
//f2 = pzem.frequency();
if (f2 != NAN && f2 >= 0)
{
PZEM_online = true;
f1 = f2;
f_avg += f1;
f_count++;
break;
}
else
{
PZEM_online = false;
};
}
for (int i = 0; i < 3; i++)
{
e2 = 44;
//e2 = pzem.energy();
if (e2 != NAN && e2 >= 0)
{
PZEM_online = true;
e1 = e2;
break;
}
else
{
PZEM_online = false;
};
}
if (p_max < p1)
p_max = p1;
if (p_min > p1)
p_min = p1;
}
data[0] = u1;
data[1] = i1;
data[2] = f1;
if (!WiFi.isConnected())
WiFi_online = false;
else
WiFi_online = true;
if (!timeClient.synced())
timeClient.updateNow();
timeClient.tick();
initSCREEN();
delay(10);
}
//------------------------------------------------------------------------
void initSCREEN()
{
spr.setTextDatum(0);
spr.fillSprite(blue);
spr.fillSmoothRoundRect(1, 1, 158, 126, 2, TFT_BLACK, blue);
// Центр --- нагрузка
spr.fillSmoothRoundRect(40, 4, 115, 120, 2, grays[19], TFT_BLACK);
spr.loadFont(UbuntuCondensed_Regular_10);
spr.setTextColor(grays[8], grays[19]);
spr.drawString(F("Нагрузка"), 57, 48);
spr.setTextColor(TFT_BLACK);
// подложка под Watt букву
spr.fillRectVGradient(93, 64, 9, 12, TFT_ORANGE, TFT_RED);
spr.drawSmoothRoundRect(91, 62, 2, 3, 12, 14, grays[19], grays[19]);
spr.drawString(F("W"), 94, 65);
spr.setTextColor(grays[8], grays[22]);
// Окна --- напряжение -- ток -- частота
for (int i = 0; i < 3; i++)
{
spr.fillSmoothRoundRect(47 + (i * 36), 19, 30, 28, 2, grays[22], grays[19]);
spr.drawString(lbl[i], 47 + (i * 36) + 3, 20);
}
spr.unloadFont();
spr.loadFont(AndersonSupercar1);
spr.setTextColor(grays[4], grays[22]);
for (int i = 0; i < 3; i++)
{
spr.drawFloat(data[i], 1, 47 + (i * 36) + 3, 34);
}
spr.unloadFont();
// Название титул
spr.loadFont(ComputerPixel7);
spr.setTextColor(TFT_ORANGE, grays[19]);
spr.drawString(F("ЭНЕРГОмеIр"), 47, 6);
// spr.drawFastHLine(95, 6, 27, TFT_ORANGE);
spr.fillRectHGradient(95, 6, 29, 1, TFT_RED, TFT_ORANGE);
spr.unloadFont();
// Середина
spr.fillRect(49, 51, 1, 26, grays[7]);
spr.fillRect(49, 58, 50, 1, grays[7]);
spr.drawSpot(49, 51, 2, grays[7], grays[19]);
spr.drawSpot(49, 51 + 26, 2, grays[7], grays[19]);
spr.drawSpot(49 + 50, 58, 2, grays[7], grays[19]);
// График
spr.fillSmoothRoundRect(107, 50, 43, 30, 2, grays[22], grays[19]);
for (int i = 0; i < 5; i++)
spr.drawFastHLine(109, 58 + (i * 4), 40, grays[16]);
spr.drawFastHLine(108, 78, 41, grays[7]);
spr.drawFastVLine(108, 57, 21, grays[7]);
spr.setTextColor(grays[10], grays[19]);
spr.loadFont(UbuntuCondensed_Regular_10);
spr.drawString(F("За 24 ч."), 115, 49);
spr.setTextColor(grays[10], grays[19]);
spr.drawString(F("Сег:"), 130, 83);
spr.drawLine(130, 92, 151, 92, grays[5]);
spr.setTextColor(grays[4], grays[19]);
spr.drawFloat(e1, 1, 131, 95); // ---------------- ЗНАЧЕНИЕ -----------------
spr.setTextColor(grays[10], grays[19]);
spr.drawString(F("Макс:"), 130, 103);
spr.drawLine(130, 112, 151, 112, grays[5]);
spr.setTextColor(grays[4], grays[19]);
spr.drawFloat(378, 1, 131, 114); // -------------- ЗНАЧЕНИЕ -----------------
spr.unloadFont();
// Текущая мощность
spr.loadFont(AndersonSupercar2);
spr.setTextColor(grays[2], grays[22]);
spr.drawFloat(p1, 1, 54, 64);
spr.unloadFont();
// "Железный" циферблат
spr.setTextDatum(4);
spr.fillSmoothRoundRect(47, 83, 80, 39, 3, c1, grays[19]);
spr.fillSmoothRoundRect(left + 7, fromTop - 4, width - 5, heigth, 3, c2, c1);
spr.fillSmoothRoundRect(left + 8, fromTop - 3, width - 6, heigth, 3, c1, c2);
spr.fillRect(left + 6, fromTop - 1, width - 10, 3, c1);
spr.fillRect(left + 25, fromTop - 4, width - 40, 3, c1);
spr.fillSmoothRoundRect(left, fromTop, width, heigth, 3, blue, c1);
spr.fillSmoothRoundRect(left + 54, fromTop, 21, heigth, 3, c3, c1);
spr.fillRect(left + 54, fromTop, 2, heigth, c1);
spr.fillSmoothCircle(left + 55, fromTop + heigth, 3, c1);
spr.fillSmoothCircle(left + 55, fromTop + heigth, 2, c2);
spr.loadFont(UbuntuCondensed_Regular_10);
spr.setTextColor(c2, c1);
spr.drawString("kWh", left + 36, fromTop - 4);
for (int i = 0; i < 5; i++)
{
spr.fillRectHGradient(left + (3) + (i * 10), fromTop + 4, 8, 12, TFT_BLACK, gra[2]);
for (int j = 0; j < 6; j++)
if (j == 3)
spr.drawLine(left + (9) + (i * 10), fromTop + 4 + (j * 2), left + (10) + (i * 10), fromTop + 4 + (j * 2), lines[j]);
else
spr.drawLine(left + (10) + (i * 10), fromTop + 4 + (j * 2), left + (10) + (i * 10), fromTop + 4 + (j * 2), lines[j]);
}
spr.fillRectHGradient(left + 59, fromTop + 4, 14, 12, TFT_BLACK, gra[2]);
for (int j = 0; j < 6; j++)
if (j == 3)
spr.drawLine(left + 70, fromTop + 4 + (j * 2), left + 71, fromTop + 4 + (j * 2), lines[j]);
else
spr.drawLine(left + 71, fromTop + 4 + (j * 2), left + 71, fromTop + 4 + (j * 2), lines[j]);
spr.drawLine(left + 6, fromTop + 21, left + 6, fromTop + 23, blue);
spr.drawLine(left + 6, fromTop + 23, left + 13, fromTop + 23, blue);
spr.drawLine(left + 47, fromTop + 21, left + 47, fromTop + 23, blue);
spr.drawLine(left + 47, fromTop + 23, left + 40, fromTop + 23, blue);
spr.setTextColor(c2, c1);
spr.drawString("X1", left + 67, fromTop + 25);
spr.setTextColor(blue, c1);
spr.drawString("TOTAL", left + 28, fromTop + 25);
spr.setTextColor(c2, c1);
// Цыфры на табло счётчика
spr.setTextColor(c1, TFT_BLACK);
spr.drawString("0", left + 6, fromTop + 11);
spr.drawString("0", left + 16, fromTop + 11);
spr.drawString("0", left + 26, fromTop + 11);
spr.drawString("0", left + 36, fromTop + 11);
spr.drawString("0", left + 46, fromTop + 11);
spr.drawString("00", left + 64, fromTop + 11);
spr.unloadFont();
// Левая часть ---- время --- дата --- день --- индикаторы
spr.loadFont(AndersonSupercar2);
spr.setTextColor(grays[6], TFT_BLACK);
spr.setTextDatum(0);
if (timeClient.timeString() == "Not sync")
{
spr.drawString("--:--", 6, 6);
}
else
{
spr.drawString(timeClient.timeString().substring(0, 5), 6, 6);
}
spr.unloadFont();
spr.fillRect(8, 22, 1, 20, grays[13]);
spr.fillRect(8, 32, 26, 1, grays[13]);
spr.loadFont(UbuntuCondensed_Regular_10);
spr.setTextColor(grays[10], TFT_BLACK);
if (timeClient.timeString() == "Not sync")
{
spr.drawString("--.--", 12, 23);
spr.drawString("--", 12, 33);
TIME_online = false;
}
else
{
spr.drawString(timeClient.dateString().substring(0, 5), 12, 23); // + "." + String(timeClient.dateString().substring(5, 7))
spr.drawString(days[timeClient.weekDay()], 12, 33);
TIME_online = true;
}
// Индикаторы OnLine
spr.drawString("PZEM", 5, 80);
if (PZEM_online)
spr.drawSpot(35, 84, 2, c4, grays[10]);
else
spr.drawSpot(35, 84, 2, c3, grays[10]);
spr.drawString("MQTT", 5, 89);
if (MQTT_online)
spr.drawSpot(35, 93, 2, c4, grays[10]);
else
spr.drawSpot(35, 93, 2, c3, grays[10]);
spr.drawString("Время", 5, 98);
if (timeClient.synced())
spr.drawSpot(35, 102, 2, c4, grays[10]);
else
spr.drawSpot(35, 102, 2, c3, grays[10]);
spr.drawString("WiFi", 5, 107);
if (WiFi_online)
spr.drawSpot(35, 111, 2, c4, grays[10]);
else
spr.drawSpot(35, 111, 2, c3, grays[10]);
spr.unloadFont();
spr.pushSprite(0, 0);
}
void connectToWifi()
{
tft.loadFont(UbuntuCondensed_Regular_10);
tft.setTextColor(grays[10], TFT_BLACK);
tft.print("Connecting..");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(300);
tft.print(".");
}
tft.println("");
tft.println(" WiFi connected.");
tft.println(" IP address: ");
tft.println(WiFi.localIP());
delay(3200);
tft.fillScreen(TFT_BLACK);
tft.unloadFont();
}
void define_level_of_gray()
{
// Определим оттенки серого
int co = 240;
for (int i = 0; i < 24; i++)
{
grays[i] = tft.color565(co, co, co);
co = co - 10;
}
for (int i = 0; i < 50; i++)
gra[i] = tft.color565(i * 5, i * 5, i * 5);
lines[0] = gra[10];
lines[1] = gra[20];
lines[2] = gra[30];
lines[3] = gra[40];
lines[4] = gra[30];
lines[5] = gra[20];
lines[6] = gra[10];
}