Pull to refresh

Comments 65

я ещё только собираюсь брать быка за рога, но тоже смутила перспектива программировать ESP32 из Arduino IDE, как самая рекомендуемая. искал варианты. ещё советуют VS Code, но к поделкам на Electron'е у меня тоже мало доверия, несмотря на то, что VS Code очень хвалят.
хотелось что-то вроде привычных Atmel Studio, Visual Studio.
спасибо вам за содержательную и полезную во многих смыслах статью.

Arduino в Visual Studio? Да легко: есть плагин Visual micro.
Я в процессе, как раз пытаюсь приручить платку TTGO на основе ESP32. В том материале, которым руководствуюсь, используется Эклипс и соответствующий плагин. Говорят, работает, но у меня пока не вышло:)

На сайте espressif исчерпывающий мануал. Рекомендую под линуксом пробовать.

Для vs code есть PlatformIO, очень удобно.

Спасибо за совет, удалось прошить плату из VS Code с PlatformIO каким-то тестовым проектом. Проблемы остались — собирается 5 минут, кодировка в терминале не та, но вектор, похоже, верный
>собирается 5 минут
Собирается или собирается + заливается? У меня только первая сборка только долгая. А дальше у меня собирается секунд за 30.
Ещё стоит посмотреть в сторону конфигурации сборки. Я для себя остановился на
;Build options
build_flags = -std=c++14 -Wno-unknown-pragmas

Посмотрите platformio. Она позволяет генерировать все необходимые настройки для разных ide и редакторов (например clion, vscode начинают просто работать без каких либо расширений), и полезной фичей будет взрослая работа с пакетами ( как pip или npm)

Вместо кучи if/else можно заюзать весьма полезную функцию «map» и менять температуру плавно (если это возможно и целесообразно):
void calculate_water_temp(void *pvParameters)
{
	while (true)
	{
		if (heating_mode != 3)
		{
			max_water_temp = map(temp_outside, -30, -10, 85, 50);
		}
		vTaskDelay(1000 / portTICK_RATE_MS);
	}
}

Спасибо за предложение! Да, вы правы, оно и возможно и целесообразно. И функцию map знакома почти любому ардуинщику по преобразованию AnalogRead в напряжение. Я думал об этом, но потом стало лень вычислять диапазоны для мапинга. Когда ни будь я так и сделаю.

Зачем заводить отдельную задачу для сторожевого таймера — если Вам достаточно контролировать только то, что живет RTOS, что у Вас и реализовано, то сторожевой таймер вполне можно сбрасывать и в idle задаче.

А как к ней обратится? Есть какой то отдельный метод? У нас ведь нет хэндла для idle задачи.

В файле настройки опций RTOS — FreeRTOSConfig.h есть макро #define configUSE_IDLE_HOOK, его надо установить в 1. При этом, когда все задачи находятся в состоянии ожидания, RTOS будет вызывать функцию vApplicationIdleHook(), которая обявлена как weak (__attribute__((weak)) для компиляторов gcc и g++, для других может быть как то по другому) и может быть переопределена на пользовательскую.
Спасибо! Интересная возможность, надо попробовать.

Так таймер должен контролировать не работу Idle, а зависание задач. Каждая критичная (имеющая дело с внешней средой) задача должна сообщить ему, что не зависла в ожидании данных или готовности аппаратуры.

Для умеренно критичных к сбоям применений достаточно контролировать именно Idle: если какая-либо задача зависнет, то она не будет попадать в состояние ожидания и, соответственно, Idle задача не будет вызываться, что приведет к срабатыванию ватчдога. Если же задача застрянет в бесконечном состоянии ожидания, то это уже не сбой, а ошибка в логике программы, а бороться с ошибками программиста с помощью сторожевого таймера — порочный путь. Хочу напомнить, что в оригинале у автора сброс ватчдога осуществляется отдельной задачей и никак не контролирует корректность работы других задач, а лишь работу самой ОС.

Для очень критичных приложений можно организовать контроль времени цикла каждой задачи, но это потребует организации двойного ватчдога — программного, с независимым счетчиком времени для каждой задачи, и аппаратного, контролирующего работу программного.
Вот это все за неделю? Включая разобраться со средой, отладкой на железе???
В любом случае очень здорово.
Пожалуй, только вопросы:
1) Увидев «FreeRTOS» и «Blynk» в одном флаконе, я сначла подумал, что Blynk будет использоваться не как «черный ящик», а через API. Не было нужды, все и так прекрасно работает?
Просто сталкиваюсь с проблемами при пропадании Интернета — реконнекты Блинка блокируют все чуть ли не на пол минуты. Не будет ли глюков, если шедулер будет многократно аварийно завершать такие попытки (при гудках и т.п.)?
Я для отладки просто на роутере блокировал данному хосту Интернет, и через несколько раз наичналась нестабильность.
2) Насколько отладчик экономит время при написании таких не слишком больших задач по сравнению с обычной отладочной печатью в serial?
На бесплатном PlatformIO под VScode, насколько я понял, аппаратная отладка становится доступной только при оформлении платной подписки.
На самом деле неделя рабочего времени это ни мало, ни много, 7*8=56 часов. Занимался я этим по вечерам и выходным, так что месяц то провозился.
По вопросам:
1) Проблем нет, всё просто работает. К API Blynk я обращался из кода, для пробы, там ничего сложного, но зачем? :) Что могу сказать по отсутствию инета и реконнектам Blynk, бывает что я забываю заплатить за инет и плата работает без инета, вроде всё в порядке, но я не проверял в это время сработку сигнализации, что бы таски с гудками и морганием работали. На днях попробую поставить дом на охрану и выключить роутер.
2) Если честно, то не очень то и экономит, может процентов на 10 побыстрее. :) Но сам процесс с использованием отладчика мне нравится больше. Тут дело в том, что в отладке ESP32 средствами VisualGDB не поддерживаются так называемые «живые переменные», это когда можно смотреть значения переменных не останавливая контроллер. Т.е. нужно поставить точку останова, что бы посмотреть, если бы эта фича поддерживалась, было бы прям удобно и быстрее.
Я так делаю, код из обычного настенного выключателя, если blynk или wifi зависает, всё реконнектится автоматически периодичность проверки настраивается (для удобства настройки/отладки вынесено в глобальные переменные), локальная работа выключателя не блокируется. RTOS не использую, есть много удобных механизмов типа диспетчеризации задач типа TimeAlarms. Аптайм несколько лет.

Заголовок спойлера
unsigned long                 //  счетчики таймеров для:
      WifiLoopTime = millis(), // проверки wifi 
      BlynkLoopTime = 0;       // проверки коннекта к серверу blynk

setup()
  WiFi.mode(WIFI_STA);
  WiFi.begin();
  WiFi.setAutoReconnect(true);
  Blynk.config("***************", IPAddress(***********), 8442);
  Blynk.connect(1666); // 5sec

loop()
  if (WiFi.status() == WL_CONNECTED)
    {
    if (Blynk.connected())
      Blynk.run();
    else 
      if (millis()> BlynkLoopTime+20000)
      {
       BlynkLoopTime=millis();
       Blynk.connect(1666);
      }
    httpServer.handleClient();
    }
  else
      {
      if (millis()> WifiLoopTime+20000)
        {
        Serial.println("WI-FI Not connected");
        WiFi.mode(WIFI_OFF);
        Serial.println("WI-FI disconnecting");
        WiFi.begin();
        Serial.println("WI-FI connecting.");
        WifiLoopTime=millis();
        }
       else 
        if (millis()> Seconds+1000)
        {
         Seconds=millis();
         Serial.print(".");
        }
      }

Прошу прощения, я с FreeRTOS пока дела не имел. И, поизучав внимательнее код, удивился, что в smart_home_2019.ino не вижу, к примеру, #include «freertos/FreeRTOS.h». На вид самые обычные инклуды.
Также не нашел, как в smart_home_2019.ino подцепляются таски (tasks_functions.ino).
Или на гитхабе только кусочки проекта? Можете пояснить?
Также не нашел, как в smart_home_2019.ino подцепляются таски (tasks_functions.ino).
В конце функции setup() ряд вызовов функций xTaskCreate()
Да, вызовы вижу. Я не понимаю, как система без #include знает, что они находятся в tasks_functions.ino
Вряд ли это зарезериврованное имя файла.
Ну да ладно, у вас среда разработки другая. У меня ж вообще не .ino, а main.c главный файл…
Все равно много интересного увидел.
Ну да ладно, у вас среда разработки другая.
Это не у меня, а у автора. Я ардуиновскими и околоардуиновскими софтверными инструментами не пользуюсь. Полагаю, что часть «магии» просто скрыта от пользователя, ведь подключить заголовочники можно и через опции командной строки компилятора (для gcc и g++ это -include).
Да, точно. Зависимости же можно и явно в настроечных файлах указывать…
А, вы про это. Скорее всего среда разработки считает, что файлы с разрешением ino в одной папке находятся как бы в одном пространстве имён. Такое же поведение и Arduino IDE.
Но, ссылку на метод для рестарта, который был изначально в из tasks_functions.ino, в smart_home_2019.ino не было видно, пришлось перенести этот метод в smart_home_2019.ino.
На гитхабе весь код, который написал я. В visual GDB это всё как то компилируется без #include «freertos/FreeRTOS.h» и прочего. Какая то чёрная магия, но всё вроде работает :) Таски подцепляются в конце setup().
Для ESP32 в ArduinoIDE FreeRTOS подключена «по умолчанию», #include «freertos/FreeRTOS.h» уже не нужен. Пользовательский скетч для ESP32 — это просто задача FreeRTOS, которая запускается на ядре процессора CORE1, на CORE0 выполняется сама RTOS. Для VisualGDB не знаю, но вероятно, реализовано похожим образом.
Да, точно. Для ESP32 «freertos/FreeRTOS.h» инклудится в самом Arduino.h
А вот ESP8266 такого нет. Там xCreateTask И прочие радости неизвестны, что меня и смутило.
Ох и потратил я на это времени в свое время, прежде чем разобрался как это работает ))) Если быть точным, то в cores\esp32\main.cpp

main.cpp
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "Arduino.h"

TaskHandle_t loopTaskHandle = NULL;

#if CONFIG_AUTOSTART_ARDUINO

bool loopTaskWDTEnabled;

void loopTask(void *pvParameters)
{
setup();
for(;;) {
if(loopTaskWDTEnabled){
esp_task_wdt_reset();
}
loop();
}
}

extern "C" void app_main()
{
loopTaskWDTEnabled = false;
initArduino();
xTaskCreateUniversal(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, CONFIG_ARDUINO_RUNNING_CORE);
}
#endif


Здесь же видно, что для задачи по умолчанию выделяется всего 8192 (байт? слов? — в разных источниках по разному) стека. И это далеко не весь доступный контроллеру стек — у меня на одном контроллере дополнительно запущено еще с десяток задач и все работает.

В общем, при программировании ESP32 под ArduinoIDE без FreeRTOS вообще никак.
А с учетом, что я «не настоящий свращик», вообще мрак :)
Я как раз очень сильно заморачивался, как сделать, чтобы и Blynk использовать, и чтобы определенные действия даже без Блинка (и вообще без Интернета) работали (по расписанию).
Похоже, что использование планировщика FreeRTOS как раз то, что мне нужно.
Для ESP32 размер стека именно в байтах. А для STM32 вроде как в словах.
UFO just landed and posted this here
Ну так есть модель дома и его теплоотдачи. Обогреватель в стабильном режиме должен вкачивать столько же джоулей, сколько уходит сквозь стены. А это зависит от уличной температуры. Через температуру в доме это можно мерить, но кажется с большими волнами — чтобы получить управляющий сигнал нужно получить изменение температуры в доме. То есть снизить комфорт жителей.
Могу предположить, что если сделать все время низкую температуру теплоносителя, в холод ее будет не хватать. Скажем, если мощность котла недостаточна.
Но зачем такая точность, почему все время зимой не держать 85 градусов (а летом 70) — тоже не очень понимаю.

Еще можно аналогию провести с «почему инверторный кндиционер лучше обычного».
Одно дело большой гистерезис (разница температур между включением и выключением), а другое — плавное регулирование мощности.
Возможно здесь изменение температуры — такой аналог управления мощностью?
Но котел все равно ведь вкл/выкл сам делает…

Ну т.е. разве что избежать слишком частых включений/выключений, что имеет некоторый смысл.

Если честно, я придумал этот кусок кода год назад, и сейчас уже не помню, почему мне тогда не понравилась высокая температура в трубах. В этом году просто перенёс на esp32. По сути да, можно и без этого.

UFO just landed and posted this here
Проблема подобных комментариев в том, что никто и никогда не дает ссылку на готовый продукт.
А человеку, может, было интересно сделать из говна и палок термостат, да еще и иметь возможность получить некую инфографику. При этом, он не знает, что ему далее может потребоваться, что будет лишним, а что он захочет добавить.
UFO just landed and posted this here
Гуглить тут, конечно, каждый умеет. Но речь то шла о сравнении поделки автора с «обыкновенными программируемыми термостатами». Да, у меня самого есть пара таких, один копеечный с алиекспресса сейчас подогревает компрессор морозилки стоящей в не отапливаемом помещении. Второй теплым полом в санузле управляет. Но, черт возьми, ни один из них не умеет понижать температуру по расписанию.
Почему автор измеряет температуру на улице — а почему нет? Если у него получается регулировать так температуру, то и хорошо. Хотя, как по мне, задача подогнать параметры регулятора так гораздо сложнее.
Да и ваша «инфографика» по счетам за отопления — гораздо менее показательна. У нас тут зима на зиму не приходится. Прошлая была очень теплой, раза три снег всего выпадал. А вот три года назад, как выпал, так и не стаял. Как бы это на счетах отразилось?
Вообще, статья то про RTOS на esp32. Крутилка для котла тут как пример.
А если внимательно прочесть сначала, то можно увидеть что у автора не было средств на прграммируемые термостаты и пр., и он просто сделал «конфетку» из того, что у него на тот момент было. Ориентируется он на собственный кошелёк, а не на ваш. Моё почтение автору статьи.
При подключении отладчика Windows устанавливает на него неподходящие драйвера, которые необходимо поменять с помощью программы Zadig, там всё просто, не буду описывать.

Странно у меня все из коробки заработало с этой платой. Вам пришлось переустанавливать драйвера?

Спасибо за статью. Очень вовремя!
Не более как неделю запустил в ещё пока строящемся доме отопление от котла. Котел умеет как eBUS, так и простой вход З/Р.
Установщики предлагали систему EktoControl (11к), но я в ней не увидел необходимости.
Имеется в наличии пара wemos d1 mini pro (v1.1), один ds18b20, решил как раз на этих выходных попробовать сделать управление для котла.
На си писал давно, но тулинг меня начал убивать :) может я просто зря решил не хватать Ардуино, а попытаться использовать NonOS / RTOS SDK.
Попробовал по их инструкциям, надо сделать в Windows кучу софта, ок. Не завелось. Попробовал CLion + esp8266 plugin. Ну вроде что-то работало, но подсказки кода нет.
Сейчас рабочий вариант нашел только VSCode + PlatformIO на основе NonOS. Но расстраивает свежее сообщение в гитхабе, что NonOS всё.

VSCode + PlatformIO на основе NonOS

А почему не VSCode + PlatformIO на основе Ардуино?
[env:ttgo-t1]
platform = espressif32
board = ttgo-t1
framework = arduino
Удобного шедулера в NonOS все равно нет. В чем ее прелесть? А так — куча ардуионовских библиотек подходит…
Оправдаюсь тем, что не знаю в полной мере, что такое Ардуино и вся это железячство. А так как на Си писал, решил, что этот путь может оказаться прямее.

Сейчас вижу, что, скорее всего, был неправ.

А с ограничением на таски пока не столкнулся. В целом хочу сделать достаточно простую вещь — временами опрашивать 18b20, по формуле считать когда вкл/выкл котёл, дёргать реле, а заодно отправлять всё измеренное и произошедшее по mqtt.

Так как в доме не живу, а зимой даже планирую некоторое время его не посещать, хотелось бы знать, что там всё ОКъ — скорее всего куплю самый-самый дешёвый 3g-модем (покрытие норм) с раздачей Wi-Fi.
Можно ещё датчик движения повесить или концевик на дверь, что бы знать что никто не залез внутрь. Ну и дальше как время/желание позволит — датчик дыма, датчик протечки :) код на все эти вещи несложный. Главное что бы датчики работали норм, иначе долгий дебаг и всё такое.
к сведению
Время создания: Sun, 08 Dec 2019 05:17:43 -0800
Тема: [platformio/platform-espressif8266] Support for ESP-IDF style ESP8266_RTOS_SDK

Several days ago Espressif has deprecated NonOS SDK
Support Policy for ESP8266 NonOS
Starting from December 2019,
We will not add any new features to the ESP8266 NonOS SDK.
We will only fix critical bugs in the ESP8266 NonOS SDK.

Самодельный Nest получился.
А зачем днём поддерживать +20 если дома никого нет?
ИМХО +16 достаточно, и повышать к желаемой температуре ко времени прихода домой.

Это можно поменять, в тёплую погоду обычно ставлю меньше. Но вообще, на 3 градуса дом разогревается, почти в любой мороз, за пару часов. Если выставить меньше, то бывает холодновато по приезду.

Тоже присматриваюсь freertos на esp32. Сейчас работает похожий проект (для гаража сделал), связка: дома — esp32+LoRa и srm32+lora Переодически вылазят какие-то фантомы в данных (раз в сутки или в 2-e), все сделано через loop. Спасибо за реальный пример rtos — сяду перелопачивать!

а что за фантомы в данных, можно по конкретнее? от какого устройства приходят?
В общем между двумя lora (ra-01), поднял собственный протокол типа запрос/ответ, с адресацией (на будущее), контролем доставки, контроль ошибок, метками времени (esp с по ntp получает, тоже задел на будущее). Создал 2 абстрактных объекта сервер и клиент (не привязывая к пердааемым данным), все через систему событий. Грешил на датчик dht22, он начинал через сутки после запуска присылать какие-то дикие значения, с непонятной переодичностью (раз в 2-4). Но вроде нашёл корень зла — в событийной системе для глобальных переменных установил volatile :) и STM через некоторое время при использовании промежуточных буферов (использовал для расчёта crc и формировании отправки пакета) и работе в полевых условиях (в гараже порой до — 20) memcpy подмешивал шнягу. Побщем х/з сам первый раз с таким встретился. Разобрался с указателями и ссылками — убрал все стековые буфера.
После этого фантомные данные пропали? Я тоже делал подобное на esp32, но у меня аппаратное crc включено и все равно иногда приходят фантомные данные если устройства находятся достаточно близко. И в документации на sx1276 встречал что возможны фантомные данные в режиме неявного заголовка.

В домашних условиях уже сутки без косяков. Сейчас код вылизываю — есть еще куда стремиться. И буду портировать на atmega16 (наковырял кучу, валяются без дела)

Смотрите в сторону crc-16 modbus, заточено по мк. Зашил все в свой протокол, появилась возможность вычислять "уровень сигнала" и ошибок в данных ноль (за неделю). Модули на родных пружинках через 3 ж/б стены (80 байт данных), в среднем 10-15 раз в минуту в обе стороны на 300 метров, без проблем!

Как то однажды мне тоже надоело разглядывать лапшу из arduino кода при добавлении очередного датчика. По этому, вспомнив, что я все-таки программист, написал событийную систему, где каждое устройство, это некий объект, способный передавать сигналы другим объектам. В итоге, получилась довольно легко расширяемая система, в которой отдельно пишутся объекты (датчики или устройства), а отдельно задаются связи между ними. В моем проекте, это делалось при старте, но можно было и на лету.
Freertos у меня не было, потому как в загашнике валялись только платы arduino uno.
Реализовал на этом проект поливалки с парой датчиков влажности, дисплеем, кнопками для задания уровней срабатывания датчиков. Потом добавил часы реального времени и управление досветкой растений в зависимости от длинны суток, включалась она утром и вечером. Хотел даже написать статью, но писать статьи для меня сложнее чем писать код. Если вдруг кому то интересно, то проект лежит здесь.
И да, писать в arduino ide — сущий ад, проще один раз прикрутить qt creator.
Итак, при использовании FreeRTOS функция setup играет роль функции main, точки входа в приложение, в ней создаются FreeRTOS tasks (далее таски)...

Я правильно понимаю, что вы пользуетесь тем, что arduino-esp32 фреймворк запускает одновременно функции setup() и loop() как единственную задачу во FreeRTOS, судя по коду main.cpp фреймворка?
Часть кода mail.cpp
void loopTask(void *pvParameters)
{
    setup();
    for(;;) {
        if(loopTaskWDTEnabled){
            esp_task_wdt_reset();
        }
        loop();
        if (serialEventRun) serialEventRun();
    }
}

extern "C" void app_main()
{
    loopTaskWDTEnabled = false;
    initArduino();
    xTaskCreateUniversal(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, CONFIG_ARDUINO_RUNNING_CORE);
}


Причем эта единственная задача имеет по умолчанию стек 8192.
Правильно ли я далее понимаю, что новые таски, которые вы создаете в setup(), расходуют ресурсы головного таска? Или во FreeRTOS не может быть подтасков, и задачи созданные в любом месте и из любой задачи уравниваются друг с другом и расходуют только единые ресурсы процессора?

Я прошу заранее простить, если вопрос непрофессионален. Только начинаю разбираться с ESP32, и вообще пытаюсь настроить окружение, чтобы использовать Arduino.h как компоненту для ESP-IDF, но тут еще те проблемы…
Никто не ответит? Автор ушел?
Или во FreeRTOS не может быть подтасков, и задачи созданные в любом месте и из любой задачи уравниваются друг с другом и расходуют только единые ресурсы процессора
— это происходит именно так. Таск можно запустить из другого таска, он будет «равноценный» всем остальным, созданным в setup. Ресурсы у него будут свои, стек и процессорное время.
Интересно вы залезли в main, я там даже не был. Не ожидал что там так просто всё реализовано.
Занимаюсь как хобби похожими проектами.
Отвечу на вопрос Глюки ESP32:
Проблема в том что при включении проседает питание (как пример висит конденсатор)
Решаеться очень просто: Надо прицепить конденсатор ~10uf между EN и GND.
Будет загружаться стабильно

Подружить Arduino и Esp-IDF, смотри пример github.com/Yurik72/esphapcontroller
уже подружено :)

Ну или для любителей Arduino IDE visual studio IDE
Пример библиотеки для Arduino но все задачи написаны через Free RTOS
github.com/Yurik72/ESPHap

Подзадачи и процессорное время:
Тут могу добавить что у ESP32 два ядра процессора (0 и 1)
Как правило на 1 работает Wifi и 0 по умолчанию работает для того же Arduino
(loop)

Но можно вешать свои задачи на любое ядро через
xTaskCreatePinnedToCore… и задавать приоритеты

На разных ядрах действительно выполняються задачи паралельно
Примеры можете глянуть в ссылках выше

> Как всякое устройство с Aliexpress, моя ESP32 плата имела свой, нигде не описанный глюк. Когда она пришла, я запитывал от платы кое какую периферию, работавшую по I2C, но по прошествии какого то времени, плата стала перезагружаться, если к ноге +5V было прицеплено любое потребляющее оборудование или даже просто кондесатор. Почему так — совершенно непонятно.

Помониторь линию 3V3, от нее ESP32 питается. Тоже ловил подобный глюк. Он происходит из-за того, что происходит просадка по питанию (оказывается, для питания ESP32 AMS1117-3.3 слабоват), и ESP32 перезагружается из-за Brownout detector was triggered. Частично решить проблему поможет установка дополнительного конденсатора по линии 3V3, перманентно — питание подавать непосредственно на 3V3 через импульсную (к примеру, MP1584 или Mini360, последнюю лучше выбрать с набором напряжений) понижайку, настроенную на соответствующее напряжение.

И это, такую систему я бы посоветовал подключать через Ethernet, было бы классно, если бы и Blynk через него работал. Единственное — RMII для ethernet-модуля использует 9 пинов, из них 6 нельзя переназначить.
Спасибо за подсказку по питанию! Попробую понижайку.
А какие плюсы у Ethernet? Вроде и через WiFi работает достаточно хорошо + проводов не надо от маршрутизатора к ESP, можно ставить где захочешь.

Плюс у Ethernet в надежности канала связи. При подключении WiFi возможны отвалы соединения по разным причинам (помехи, много клиентов, зашумленность эфира, да и тупо точка доступа уставшая). Если роутер/точка доступа для таких устройств в подвале, под них отдельная сетка и сигнал достаточно хороший приходит, тогда норм. Ну или если устройство перемещается. В остальных случаях лучше Ethernet, не пожалеть портов и пинов)))
А ты мониторишь отвалы устройства?

Sign up to leave a comment.

Articles