Прошу помощи в отладке Arduino Mega 2560

Я прошу прощения за своё непонимание. Но, что значит “отделить его от основного блока”?

Тут тоже не понимаю - что значит “отдельно датчики как модули”?

В этом месте можно разместить логику для обработки в случае прихода команды. В этом месте доступны значения датчиков.

Если надо разместить дополнительную логику, то в цикле loop() после анализа “пришла ли команда” можно разместить логику работы исходя из показаний датчиков. Логику можно вставить и до анализа “пришла ли команда”, если требуется.

Итогом - весь остальной код нужен для передачи на дисплей и приема команд. В этом модуле есть свой WEB, где можно смотреть показания датчиков. Это на тот случай, если второй блок отключен.

Поскольку модули esp имеют WIFI подключение, то можно сделать отдельный модуль для управления соленоидом. Это позволяет подвести только питание к модулю соленоида, а все управление пойдет по WIFI.

Опять же. Через WIFI можно сделать модули с esp01 чипом, чтобы укоротить провода до датчиков температуры, а их значения передавать по WIFI. Питание для каждого датчика+модуля можно взять с простых зарядок к телефону. Это позволить передвигать датчики и не перетягивать провода до точек установки.

извините я пропустил, или не было просто, вы хотите что бы он проверил просто на любой esp ? (если она у него уже есть сейчас)
или на каком то промышленном решении ? просто напрямую подключил esp к реле ? без дополнительных модулей…
или тут все таки передача только переменных…

в целом вы хотите что бы вообще плата была 1 - esp32 ?

Пока общаемся по основной плате управления и возможной модификации, чтобы была возможность удобной замены и унификации подключения датчиков. Расставил датчики, подвел к ним питание и все заработало. Если датчик сломался, то ремонтируем блок или меняем, предварительно прошив его нужной прошивкой.

Теперь доработанный код для модуля с дисплеем.

#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <FS.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>
// --- Пины Дисплея и Тачскрина ---
#define TOUCH_CS_PIN     21
#define TOUCH_IRQ_PIN    22
// --- Инициализация оборудования ---
TFT_eSPI tft = TFT_eSPI();
XPT2046_Touchscreen ts(TOUCH_CS_PIN, TOUCH_IRQ_PIN);
WebServer server(80);
// --- НАСТРОЙКА WI-FI ---
const char* ssid = "ИМЯ_ВАШЕЙ_СЕТИ";
const char* password = "ПАРОЛЬ_ОТ_WI-FI";
// ==========================================
// НАСТРОЙКА АДРЕСА ПРИНИМАЮЩЕГО БЛОКА (МОДУЛЬ 1)
// ==========================================
const String targetIP = "192.168.1.101"; 
// IP-адрес Модуля 1const String targetPort = "80";          
// Порт Модуля 1
// ==========================================
// --- Переменные состояния и данных ---
float sensors[10] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
bool relayStates[7] = {false, false, false, false, false, false, false}; 
// 0: ТЭН 1, 1: ТЭН 2, 2: НАСОС 1, 3: НАСОС 2, 4: НАСОС 3, 5: BABOS 4, 6: Вентилятор
// --- ФЛАГИ ДЛЯ РАЗДЕЛЕНИЯ ЛОГИКИ И ОТРИСОВКИ ---
volatile bool needGuiUpdate = false;      
// Флаг обновления экрана при получении данных от Модуля 1
volatile bool needLocalGuiUpdate = false; 
// Флаг обновления экрана при нажатии на тачскрин или веб-кликом
// --- HTML + JS код для Web-интерфейса (AJAX/Fetch Poll) ---
const char HTML_INDEX[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ESP32 Панель Ввода</title>
    <style>
        body { font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 10px; background: #121214; color: #fff; text-align: center; }
        .container { max-width: 600px; margin: 0 auto; }
        h3 { margin: 15px 0 5px 0; color: #00d2ff; text-align: left; font-size: 16px; text-transform: uppercase;}
        .grid-sensors { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 8px; }
        .card-sensor { background: #1a1a24; padding: 10px; border-radius: 8px; border-left: 3px solid #00d2ff; text-align: left; }
        .label { font-size: 11px; color: #aaa; }
        .val { font-size: 18px; font-weight: bold; margin-top: 3px; }
        .grid-controls { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
        .grid-pumps { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
        .control-block { background: #1a1a24; padding: 12px; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); }
        .btn { padding: 12px; font-size: 15px; font-weight: bold; color: white; border: none; border-radius: 6px; cursor: pointer; transition: 0.2s; width: 100%; margin-top: 5px; }
        .on { background: #00b4d8; box-shadow: 0 2px 8px rgba(0,180,216,0.4); }
        .off { background: #3a3a4a; color: #ccc; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Информационная Панель</h2>
        <h3>Датчики температуры</h3>
        <div class="grid-sensors" id="sensors-grid"></div>
        <h3>Статус нагревателей и климата</h3>
        <div class="grid-controls">
            <div class="control-block">
                <div class="label">Нагреватели</div>
                <button class="btn off" id="btn0" onclick="sendToggle(0)">ТЭН 1: ВЫКЛ</button>
                <button class="btn off" id="btn1" onclick="sendToggle(1)">ТЭН 2: ВЫКЛ</button>
            </div>
            <div class="control-block">
                <div class="label">Климат</div>
                <button class="btn off" id="btn6" onclick="sendToggle(6)" style="height: calc(100% - 20px);">ВЕНТИЛЯТОР: ВЫКЛ</button>
            </div>
        </div>
        <h3>Статус насосов</h3>
        <div class="grid-pumps control-block">
            <button class="btn off" id="btn2" onclick="sendToggle(2)">НАСОС 1: ВЫКЛ</button>
            <button class="btn off" id="btn3" onclick="sendToggle(3)">НАСОС 2: ВЫКЛ</button>
            <button class="btn off" id="btn4" onclick="sendToggle(4)">НАСОС 3: ВЫКЛ</button>
            <button class="btn off" id="btn5" onclick="sendToggle(5)">BABOS 4: ВЫКЛ</button>
        </div>
    </div>
    <script>
        const grid = document.getElementById('sensors-grid');
        for(let i=1; i<=10; i++) {
            grid.innerHTML += `<div class="card-sensor"><div class="label">Датчик ${i}</div><div class="val" id="s${i}">--.- °C</div></div>`;
        }

        function parseAndApplyData(text) {
            const parts = text.split(',');
            if(parts.length >= 17) {
                for(let i=0; i<10; i++) {
                    document.getElementById('s' + (i+1)).innerText = parseFloat(parts[i]).toFixed(1) + ' °C';
                }
                const names = ["ТЭН 1", "ТЭН 2", "НАСОС 1", "НАСОС 2", "НАСОС 3", "BABOS 4", "ВЕНТИЛЯТОР"];
                for(let i=0; i<7; i++) {
                    const btn = document.getElementById('btn' + i);
                    const state = parts[10 + i] === "1";
                    btn.className = state ? "btn on" : "btn off";
                    btn.innerText = names[i] + (state ? ": ВКЛ" : ": ВЫКЛ");
                }
            }
        }

        function requestStatus() {
            fetch('/status')
            .then(res => res.text())
            .then(text => parseAndApplyData(text))
            .catch(err => console.log("Ошибка обновления:", err));
        }
        setInterval(requestStatus, 2000);
        window.onload = requestStatus;

        function sendToggle(id) {
            fetch('/btn-click?id=' + id)
            .then(res => res.text())
            .then(text => parseAndApplyData(text));
        }
    </script>
</body>
</html>
)rawliteral";
// --- Вспомогательные функции сборки строк Plain Text ---
String buildPlainString() {
  String dataString = "";
  for(int i=0; i<10; i++) { dataString += String(sensors[i], 1) + ","; }
  for(int i=0; i<7; i++) { dataString += String(relayStates[i] ? "1" : "0") + (i == 6 ? "" : ","); }
  return dataString;
}
String buildButtonsOnlyString() {
  String dataString = "";
  for(int i=0; i<7; i++) { dataString += String(relayStates[i] ? "1" : "0") + (i == 6 ? "" : ","); }
  return dataString;
}
// --- ФУНКЦИЯ ОТПРАВКИ КНОПОК НА ДРУГОЙ БЛОК ---
void sendButtonsToTarget() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    String url = "http://" + targetIP + ":" + targetPort + "/update"; 
    http.begin(url);
    http.addHeader("Content-Type", "text/plain");
    
    String payload = buildButtonsOnlyString(); 
    int httpResponseCode = http.POST(payload);
    
    if (httpResponseCode > 0) {
      Serial.print("Реле отправлены. Ответ исполнителя: "); Serial.println(httpResponseCode);
    } else {
      Serial.print("Ошибка отправки реле: "); Serial.println(http.errorToString(httpResponseCode).c_str());
    }
    http.end();
  }
}
// --- Статический интерфейс дисплея ---
void drawStaticUI() {
  tft.fillScreen(TFT_BLACK);
  tft.fillRect(0, 0, 240, 25, TFT_DARKCYAN);
  tft.setTextColor(TFT_WHITE); tft.setTextSize(1);
  tft.setCursor(8, 8); tft.print("IP: "); tft.print(WiFi.localIP().toString());

  for (int i = 0; i < 10; i++) {
    int col = i % 2; int row = i / 2;
    int x = 5 + (col * 116); int y = 30 + (row * 20);
    tft.drawRect(x, y, 112, 18, TFT_NAVY);
    tft.setTextColor(TFT_LIGHTGREY); tft.setCursor(x + 4, y + 5);
    tft.print("S"); tft.print(i + 1); tft.print(":");
  }

  tft.drawRoundRect(5, 135, 112, 40, 4, TFT_DARKGREY);
  tft.drawRoundRect(121, 135, 114, 40, 4, TFT_DARKGREY);
  tft.drawRoundRect(5, 180, 112, 40, 4, TFT_DARKGREY);
  tft.drawRoundRect(121, 180, 114, 40, 4, TFT_DARKGREY);
  tft.drawRoundRect(5, 225, 112, 40, 4, TFT_DARKGREY);
  tft.drawRoundRect(121, 225, 114, 40, 4, TFT_DARKGREY);
  tft.drawRoundRect(5, 270, 230, 40, 4, TFT_DARKGREY);
}
// --- Обновление динамических данных на TFT ---
void updateDynamicData() {
  tft.setTextSize(1);
  for (int i = 0; i < 10; i++) {
    int col = i % 2; int row = i / 2;
    int x = 5 + (col * 116); int y = 30 + (row * 20);
    tft.fillRect(x + 40, y + 2, 65, 14, TFT_BLACK);
    tft.setTextColor(sensors[i] > 28.0 ? TFT_RED : TFT_CYAN);
    tft.setCursor(x + 45, y + 5); tft.print(sensors[i], 1); tft.print(" C");
  }

  auto drawBtn = [](int x, int y, int w, int h, String name, bool state) {
    tft.fillRoundRect(x+2, y+2, w-4, h-4, 3, state ? TFT_GREEN : TFT_MARO);
    tft.setTextColor(state ? TFT_BLACK : TFT_WHITE);
    tft.setCursor(x + (w / 2) - (name.length() * 3), y + (h / 2) - 3);
    tft.print(name);
  };

  drawBtn(5, 135, 112, 40, "HEATER 1", relayStates[0]);
  drawBtn(121, 135, 114, 40, "HEATER 2", relayStates[1]);
  drawBtn(5, 180, 112, 40, "PUMP 1", relayStates[2]);
  drawBtn(121, 180, 114, 40, "PUMP 2", relayStates[3]);
  drawBtn(5, 225, 112, 40, "PUMP 3", relayStates[4]);
  drawBtn(121, 225, 114, 40, "BABOS 4", relayStates[5]); 
  drawBtn(5, 270, 230, 40, "FAN (VENTILATOR)", relayStates[6]);
}
// --- ПРИЕМ ТЕМПЕРАТУР ОТ ВНЕШНЕГО БЛОКА (POST /update) ---
void handlePostData() {
  if (!server.hasArg("plain")) { server.send(400, "text/plain", "Error: Body empty"); return; }
  String body = server.arg("plain"); body.trim();
  
  int sensorIdx = 0; int startIdx = 0;
  while (sensorIdx < 10) {
    int nextComma = body.indexOf(',', startIdx);
    if (nextComma == -1) {
      if (startIdx < body.length()) { sensors[sensorIdx++] = body.substring(startIdx).toFloat(); }
      break;
    }
    sensors[sensorIdx++] = body.substring(startIdx, nextComma).toFloat();
    startIdx = nextComma + 1;
  }
  
  // Ранее вызывалась updateDynamicData(), что приводило к задержкам веб-сервера.
  // Теперь просто взводим флаг для обработки в loop().
  needGuiUpdate = true; 
  
  server.send(200, "text/plain", "OK");
}
// --- ОТДАЧА ПОЛНОГО СТАТУСА В WEB (GET /status) ---
void handleGetStatus() {
  server.send(200, "text/plain", buildPlainString());
}


// --- Обработка клика по кнопке ---
void toggleButton(int id) {
if (id >= 0 && id < 7) {
relayStates[id] = !relayStates[id];
needLocalGuiUpdate = true; // Взводим флаг локального обновления для экрана
sendButtonsToTarget(); // Инициативная отправка изменений внешнему блоку
}
}
// --- ОБРАБОТКА НАЖАТИЙ ИЗ WEB-ИНТЕРФЕЙСА (GET /btn-click?id=X) ---
void handleWebClick() {
if (server.hasArg("id")) {
int id = server.arg("id").toInt();
toggleButton(id);
}
server.send(200, "text/plain", buildPlainString());
}
void setup() {
Serial.begin(115200);
tft.init(); tft.setRotation(0); tft.fillScreen(TFT_BLACK);
tft.setCursor(10, 50); tft.setTextSize(2); tft.setTextColor(TFT_WHITE); tft.print("Connecting WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); }
SPI.begin(); ts.begin(); ts.setRotation(0);
// Маршрутизация встроенного HTTP веб-сервера
server.on("/", HTTP_GET, { server.send_P(200, "text/html", HTML_INDEX); });
server.on("/update", HTTP_POST, handlePostData);
server.on("/status", HTTP_GET, handleGetStatus);
server.on("/btn-click", HTTP_GET, handleWebClick);
server.begin();
drawStaticUI();
updateDynamicData();
}
void loop() {
server.handleClient();
// вызов функции обновления интерфейса при изменении сетевых или локальных параметров
if (needGuiUpdate || needLocalGuiUpdate) {
needGuiUpdate = false;
needLocalGuiUpdate = false;
updateDynamicData(); // Спокойно и безопасно отрисовываем экран в loop
}
// Обработка нажатий на дисплей панели
if (ts.touched()) {
TS_Point p = ts.getPoint();
int x = map(p.x, 200, 3800, 0, 240);
int y = map(p.y, 200, 3800, 0, 320);
if (y >= 135 && y <= 175) {
if (x >= 5 && x <= 117) toggleButton(0);
else if (x >= 121 && x <= 235) toggleButton(1);
delay(300);
}
else if (y >= 180 && y <= 220) {
if (x >= 5 && x <= 117) toggleButton(2);
else if (x >= 121 && x <= 235) toggleButton(3);
delay(300);
}
else if (y >= 225 && y <= 265) {
if (x >= 5 && x <= 117) toggleButton(4);
else if (x >= 121 && x <= 235) toggleButton(5);
delay(300);
}
else if (y >= 270 && y <= 310) {
if (x >= 5 && x <= 235) toggleButton(6);
delay(300);
}
}
}

Я посмотрел, конечно, код. Единственное что мне понятно - это что там три шины для датчиков температур.
Diskless, откровенно говоря - чем больше Вы вникаете и уточняете код - тем больше я отклоняюсь от идеи пытаться переходить на подобный вариант. И самое неприятное - что мне всё больше кажется - что Вы обидитесь, когда в итоге я сообщу - что решил остаться на nano, Mega…
Повторюсь - единственная для меня причина переходить на предлагаемый вариант (ESP32, как понимаю) - это если я сам для себя смогу доказать, что этот вариант для меня лучше.
BABOS расписал выше (спасибо ему) преимущества. Но, “больше памяти на перспективу, при увеличении умного дома” - это для меня не аргумент, совсем.

Хотелось бы подробностей - о каких модулях (которые не надо собирать в случае с ESP32) идёт речь?

То есть - nano остаётся в любом случае?

Вот это - близко для меня. Здесь я склонен согласиться и прислушаться.

отдельная работа теплового насоса на отдельной ардуине с датчиками чисто для насоса только рассматривали ?)))
если работает отдельно, и не виснет, можно сделать передачу этих показаний на ту плату, которая стабильно работает… если я все правильно понял, вполне простое решение…

например экран, кнопки подключать не надо

наверное… я по крайней мере не могу сказать, возьмите вот это, и оно точно поведет себя нормально для силовой электроники… я зарекся давать советы, тут все надо собирать и смотреть, а то часто в конце всплывают нюансы о которых даже не подумал в начале…

p.s. при этом для себя бы если делал, определенно взял бы esp! не справился бы, или возникли какие либо проблемы, переделал бы…

Рассматриваю. Но сначала - нужно убедиться, что объединённая система не работает. Надо собрать объединённую. Выявить причины, почему не работает (где сбой). Потом искать решение (отдельная работа теплового насоса на отдельной ардуине).
Ведь по факту - нет ничего. От чего оттолкнуться.

esp модули - это модули с wifi клиентом и точкой доступа. Из них можно собрать локальную сеть - подключить их к центральному блоку управления, который тоже на esp32. Если вы решаете конкретную задачу и сделаете без сети, то столкнетесь далее, что при замене датчика - этот датчик придется подбирать в конкретное место. Я же предлагаю сделать схему - как в моторе свечи. Ранее был распределитель зажигания, а сейчас это все подключено в шине и катушка стоит прямо на свече. Длина контактного провода у всех свечей одинакова - это аналог длины провода до датчика. Можно сделать блок с датчиком и модулем, который будет иметь стандартные характеристики и его можно переносить, если потребуется. Тоже самое можно делать с исполнительными устройствами. Если нельзя его удобно подключить прямо к блоку, то можно воспользоваться модулем esp01 с реле (такие есть в продаже с питанием 5 вольт).

Еще одно преимущество - если что то ломается, то получается модульная замена, а не копание общей схемы. Я всегда проигрываю ситуацию, что через несколько лет придется таки лезть и что то менять, поэтому модульный подход намного удобнее, чем хардкорный с центральным ядром.

если есть слабое понимание кода, разделенный вариант очень даже хорош, потому что с ардуиной может пойти все что угодно не так, и делать объединенную систему не очень хороший вариант, в целом все может работать, а потом датчик один выйдет из строя, а аварийной остановки нет…
мало вероятно, но случаи разные быть могут, и чем дольше используете тем опаснее…

но при этом rx tx esp32 без резисторов тоже незя подключать к уно, но мне очень хочется, и я готов к тому что плата просто сгорит, лентяй я… и люблю авось, но вам лучше так не делать!)))

Тю, пристал как банный лист со своим вайфаем. Ну не хочет человек, нет у него до зимы времени.

Если ты работал с esp, то поймешь. Можно взять пачку esp01 с реле, сделать проставку между модулем и релюшкой с датчиком температуры и сделать универсальную прошивку, которую один раз шьешь, а потом через com или по WIFI просто устанавливаешь его параметры, которые будут определять его на исполнительном модуле. Таких блоков можно нашить много и менять по мере выхода из строя реле или датчика - просто выставив его параметры. Эти же релюшки можно ставить прямо к esp32. И сигнальные провода не надо тянуть, и к исполнительным устройствам тоже можно сократить число проводов.

Diskless а если очень много собирали проектов, поймете что вероятность успеха 50 на 50, все надо собирать и проверять!)))
ваш проект радио как там ? собрал же больше 50к ? или вы просто там циферки рисуете)))

я чего спрашиваю, деньги нужны, может вы мне это… 50к, на существование! и запилим какой то проект совместный ?)))
за 10к можем заняться созданием русского репа!

не обещаю что он будет успешный, особенно если основа будет не айс, и без примеров… но могу попытаться сделать все возможное!

Сейчас
ps afx | grep mqtt | grep -c Client
322
Сейчас - 322 шлюза подключено.

и мне

Посчитай - сколько я должен продавать устройств и по какой цене, чтобы заплатить тебе. С налогами и т.д.

Фигасе! Не похож на диванного ыкспЭрда, =респектоz.

Я-ж тебе предлагал. Будет том 50к и больше. Только они так просто не даются = к ним идти надо, а ты не хочешь… Вот и не получаеццА)

Добавить датчик температуры Dallas (DS18B20) к готовому модулю ESP-01 с реле вполне реально, но есть важный нюанс: у ESP-01 всего два свободных контакта ввода-вывода (GPIO0 и GPIO2), и один из них уже жестко занят управлением реле (обычно это GPIO0, реже GPIO2). [1, 2]

Вам нужно задействовать второй, оставшийся свободным пин. [1]


Подключение датчика (Аппаратная часть)

Датчик DS18B20 имеет 3 контакта: VCC (питание), GND (земля) и DATA (данные).

  1. Питание датчика:
    • GND датчика припаяйте к любому контакту GND на плате.
    • VCC датчика подключите к питанию 3.3V модуля ESP-01. Не подключайте к 5V, чтобы не сжечь пины контроллера.
  2. Линия данных (DATA):
    • Припаяйте провод данных датчика к свободному пину ESP-01 (если реле управляется через GPIO0, то датчик подключайте к GPIO2, и наоборот). [1, 2]
  3. Резистор подтяжки (Обязательно!):
    • Между контактами VCC (3.3V) и DATA датчика необходимо установить резистор номиналом 4.7 кОм (в крайнем случае от 2.2 до 10 кОм). Без него датчик будет выдавать ошибку или значение -127. [3]

:warning: Важное предупреждение о GPIO0 / GPIO2:
Пины GPIO0 и GPIO2 участвуют в выборе режима загрузки ESP8266 при включении питания. Если в момент старта GPIO0 притянут к земле, чип уйдет в режим прошивки.
Поскольку датчик Dallas через подтягивающий резистор тянет пин к 3.3V (HIGH), это идеальное условие для нормального старта. Поэтому подключать DATA к свободному пину безопасно.


Альтернативный вариант (без пайки к чипу)

Если на плате реле-щита (Relay Shield) выведены пины интерфейса UART (пины TX / GPIO1 и RX / GPIO3), датчик Dallas можно подключить к пину RX (GPIO3). Протокол 1-Wire отлично работает на этом контакте, оставляя GPIO2 полностью свободным.


Программная настройка

В зависимости от того, какую прошивку вы используете, укажите правильный GPIO для датчика Dallas:

1. Если используете ESPHome (Home Assistant) [4]

Добавьте в конфигурационный YAML-файл следующий код (пример для датчика на GPIO2):

one_wire:
  - pin: GPIO2

sensor:
  - platform: dallas_txt
    name: "Температура на датчике"

2. Если пишете код в Arduino IDE

Вам понадобятся библиотеки OneWire и DallasTemperature. В коде инициализируйте датчик на свободном пине:

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2 // Укажите 2 для GPIO2 или 0 для GPIO0

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

void setup() {
  sensors.begin();
}

void loop() {
  sensors.requestTemperatures(); 
  float tempC = sensors.getTempCByIndex(0);
  // Далее отправка данных по MQTT / HTTP
}

Чтобы я помог точнее, подскажите:

  • Какая именно модель реле-платы у вас используется (черная или синяя)?
  • Какую прошивку вы планируете использовать (Arduino IDE, ESPHome, Tasmota, WiFi-IoT)?

[1] https://esp8266.ru

[2] https://www.drive2.ru

[3] https://arduino.ru

[4] https://psenyukov.ru