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

Для желающих повторить подразумевается как возможность сборки из модулей, так и нормальная железка. Но устройство в общем очень даже универсальное, полностью совместимое с Arduino IDE, достаточно воткнуть USB и можно шить. Порог вхождения минимальный. А почему универсальное- только изменением кода можно парсить что угодно с любого сайта.

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

API хабра

... которого нет :(

Тут я задумался как получать данные. Поддержка сказала что АПИ задача не приоритетная, но где‑то в планах существует. Значит придется парсить по якорям. То‑есть ищем кусок уникальных данных перед нужным значением, и ориентируясь по нему извлекаем нужные.

Собственно код

Весь код написан в среде ArduinoIDE. Основная функция, конечно‑ получение значений. Голый код страницы профиля пользователя весит около 120кБ. Хранить его можно разве что в файловой системе, но особого смысла нет. Для класса Stream в Arduino IDE есть отличная функция find(), которой мы и воспользуемся.

Код функции парсинга с комментариями
 if ((WiFi.status() == WL_CONNECTED)) {  //Если есть подключение к Wifi
    http.begin(client, SURL + USER + "/");  //Открываем HTTP соединение
delay(10);
    int httpCode = http.GET(); //Производим GET запрос
    delay(10);
Serial.print("httpCode");
    Serial.println(httpCode);
    if (httpCode==200) {   //Если ответ 200

        WiFiClient* stream = http.getStreamPtr();  //Пребразуем данные в поток Stream

        if (stream->available()) { //Если поток доступен

          //----------------карма
          stream->find(R"rawliteral(karma__votes_positive">)rawliteral");   //Ищем якорь
          for (int i = 0; i < 5; i++) {       //Отступ от якоря
            stream->read();
          }

          for (byte i = 0; i < 5; i++) {     //Читаем нужное количество символов
            KARMA[i] = stream->read();
          }

          //----------------рейтинг

          stream->find(R"rawliteral(tm-rating__counter">)rawliteral");

          for (byte i = 0; i < 7; i++) {
            RATING[i] = stream->read();
          }

          //----------------позиция

          stream->find("В рейтинге");
          for (int i = 0; i < 118; i++) {
            stream->read();
          }

          for (byte i = 0; i < 4; i++) {
            RatingPos[i] = stream->read();
          }


          Serial.println(KARMA);
          Serial.println(RATING);
          Serial.println(RatingPos);
          Serial.println("END");
        }
        delay(10);


        Serial.println();
        Serial.print("[HTTP] connection closed or file end.\n");
      
    } else {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end();
    delay(10);
  }

и

Весь код
Главный




String USER = "ENGIN33RRR";  //Имя пользователя
const char ssid[] = "Eng";   //SSID
const char password[] = "123456789h";  //Пароль от WiFi

const char* ntpServer1 = "pool.ntp.org";  //Первый сервер времени
const char* ntpServer2 = "time.nist.gov"; //Второй сервер времени
const long gmtOffset_sec = 21600;         //Часовой пояс в секундах


String SURL = "https://habr.com/ru/users/"; //Начало адреса до страницы пользователя




//LОбъявляем Дисплей
#include <GxEPD2_BW.h>
#define USE_VSPI_FOR_EPD
#define GxEPD2_DISPLAY_CLASS GxEPD2_BW
#define MAX_DISPLAY_BUFFER_SIZE 65536ul       
#define GxEPD2_DRIVER_CLASS GxEPD2_290_T94_V2  
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/5, /*DC=*/17, /*RST=*/16, /*BUSY=*/4));




//Шрифты
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
#include <Fonts/FreeMonoBold18pt7b.h>
#include <Fonts/FreeMonoBold24pt7b.h>
#include <Fonts/FreeSerifBoldItalic18pt7b.h>














//Библиотеки Wifi, HTTP и времени
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
WiFiClientSecure client;

#include <HTTPClient.h>
HTTPClient http;


#include "time.h"
#include "sntp.h"





//Библиотека для датчика темпреатуры/влажности
#include <Adafruit_Sensor.h>
#include <DHT.h>

#define DHTPIN 27     
#define DHTTYPE DHT22  
DHT dht(DHTPIN, DHTTYPE);





//Переменные для хранения данных и фиксации изменений
String KARMA = "000";
String RATING = "000.0";
String RatingPos = "999";

String KARMA1;
String RATING1;
String RatingPos1;

float Temp;
float Hum;

float HumR;
float TempR;


char TimeDisp[9];

byte count;
bool flag;

long ms;
long ms1;
bool blink;
bool noWiFi;
byte WS;


void setup() {

  xTaskCreatePinnedToCore(
    Graph,   //Функция потока
    "Task2", //Название потока
    16000,   //Стек потока
    NULL,    //Параметры потока
    1,       //Приоритет потока
    NULL,  //Идентифкатор потока
    0);      //Ядро для выполнения потока


  delay(500);

  xTaskCreatePinnedToCore(
    FileUpdate, //Функция потока
    "Task1",    //Название потока
    10000,      //Стек потока
    NULL,       //Параметры потока
    2,          //Приоритет потока
    NULL,     //Идентифкатор потока
    1);         //Ядро для выполнения потока

  delay(500);
}




void FileUpdate(void* pvParameters) {
 Serial.begin(115200);             //Инициализация UART
  //http.setReuse(true);            
  http.setTimeout(3000);
 http.setReuse(true);

 Connect();  //Подключаемся к WiFi

  client.setInsecure(); //Игнорируем сертификаты HTTPS

  for (;;) //Цикл потока
  {

    if (WiFi.status() == WL_CONNECTED) {  //Если есть подключение

      if (WiFi.RSSI() > -60) {    //переводим уровень сигнала для значка
        WS = 2;
      } else if (WiFi.RSSI() > -70) {
        WS = 1;
      } else {
        WS = 0;
      }
      noWiFi = 0;
     
      findVAR();  //Функция поиска значений
    } else {
      Reconnect();   //Переподключить Wifi
      noWiFi = 1;     
    }



    if (!KARMA1.equals(KARMA) || !RATING1.equals(RATING) || !RatingPos1.equals(RatingPos)) {  //Детектируем изменения в переменных
      KARMA1 = KARMA;
      RATING1 = RATING;
      RatingPos1 = RatingPos;
      flag = 1;             //И поднимаем флаг
    }

    vTaskDelay(20000);    //Пауза 20 секунд
  }
}



void Graph(void* pvParameters) {     //Поток отрисовки на дисплей

  configTime(gmtOffset_sec, 0, ntpServer1, ntpServer2); //Инициализируем службу времени
  dht.begin();       //Инициализируем датчик температуры
  display.init();    //Инициализируем дисплей
  display.setRotation(3); 
  display.clearScreen(); //Очистка экрана
  display.setTextColor(GxEPD_BLACK);
  display.fillScreen(GxEPD_WHITE);
  Static(); // Отрисовка статического изображения
  display.display(false);  //Полный вывод на дисплей


  for (;;) {   //Цикл потока отрисовки на дисплей

    updLocalTime(); //Обновляем время в переменной

    if (millis() > ms + 1000) {  //Обновляем показания датчика раз в секунду
 
      ms = millis();
      HumR = dht.readHumidity();
      TempR = dht.readTemperature();
    }

    if (!isnan(HumR) || !isnan(TempR)) { //Если значения не NAN, копируем в перменные
      Hum = HumR;
      Temp = TempR;
    }

if (millis() > ms1 + 1000) {  //Моргалка для потери WiFi
  ms1=millis();
      blink = !blink;
    }

  
   Dynamic(); // Функция отрисовки меняющихся данных
    
  }
}






void loop() { //Не используется
}

Работа с WiFi




void Connect(void) {


 WiFi.mode(WIFI_STA);
 delay(10);
  WiFi.begin(ssid, password);
delay(10);
  while (WiFi.status() != WL_CONNECTED && count < 15) {
    count++;
    delay(500);
  }

  delay(10);

}


void Reconnect(void) {

KARMA = "000";
RATING = "000.0";
RatingPos = "999";

  WiFi.disconnect();
  vTaskDelay(1000);
  WiFi.begin(ssid, password);
  count = 0;
  while (WiFi.status() != WL_CONNECTED && count < 15 ) {
    count++;
    delay(500);
   
  }




}

Графика

void Static() {

  display.setFont(&FreeMonoBold12pt7b);
  display.fillRect(0, 0, 296, 20, GxEPD_BLACK);
  display.setTextColor(GxEPD_WHITE);
  display.setCursor(10, 16);
  display.print("HabraTab");
 display.fillRect(10, 80, 276, 2, GxEPD_BLACK);
display.fillRect(15, 50, 73, 2, GxEPD_BLACK);
display.fillRect(103, 50, 90, 2, GxEPD_BLACK);
display.fillRect(208, 50, 73, 2, GxEPD_BLACK);
  display.setTextColor(GxEPD_BLACK);
  display.setCursor(18, 72);
  display.print("Karma");
  display.setCursor(115, 72);
  display.print("Score");
  display.setCursor(215, 72);
 display.print("R No");
 display.setFont(&FreeSerifBoldItalic18pt7b);
 display.fillRect(15, 22, 100, 26, GxEPD_WHITE);
  display.setCursor(15, 45);
  display.print(KARMA.toInt());
  display.fillRect(110, 22, 100, 26, GxEPD_WHITE);
  display.setCursor(110, 45);
  display.print(RATING.toFloat(), 1);
  display.fillRect(220, 22, 85, 26, GxEPD_WHITE);
  display.setCursor(220, 45);
  display.print(RatingPos.toInt());
display.setFont(&FreeMonoBold12pt7b);
  display.setCursor(5, 100);
  display.print("@");
  display.print(USER);
  display.setTextColor(GxEPD_BLACK);
display.fillRect(0, 108, 296, 20, GxEPD_BLACK);

}


void Dynamic() {


 display.setFont(&FreeMonoBold12pt7b);
    display.fillRect(145, 0, 150, 20, GxEPD_BLACK);



    

    if (!noWiFi || blink) {
      display.fillCircle(148, 9, 3, GxEPD_WHITE);
      display.fillRect(156, 6, 2, 8, GxEPD_WHITE);

      if (WS == 1) {

        display.fillRect(162, 4, 2, 12, GxEPD_WHITE);
      }
      if (WS == 2) {
        display.fillRect(162, 4, 2, 12, GxEPD_WHITE);
        display.fillRect(168, 2, 2, 16, GxEPD_WHITE);
      }
    }
    display.setTextColor(GxEPD_WHITE);
    display.setCursor(175, 16);
    display.print(TimeDisp);
    vTaskDelay(1);


    if (flag) {

      display.setFont(&FreeSerifBoldItalic18pt7b);
      display.setTextColor(GxEPD_BLACK);
      display.fillRect(15, 22, 95, 26, GxEPD_WHITE);
      display.setCursor(15, 45);
      display.print(KARMA.toInt());
      display.fillRect(110, 22, 95, 26, GxEPD_WHITE);
      display.setCursor(110, 45);
      display.print(RATING.toFloat(), 1);
      display.fillRect(220, 22, 76, 26, GxEPD_WHITE);
      display.setCursor(220, 45);
      display.print(RatingPos.toInt());
      flag = 0;
    }

    display.setFont(&FreeMonoBold12pt7b);
    display.fillRect(0, 108, 200, 20, GxEPD_BLACK);
    display.setTextColor(GxEPD_WHITE);
    display.setCursor(10, 124);
    vTaskDelay(1);
    display.print("T");
   
      display.print(Temp, 1);
    
    display.setFont(&FreeMonoBold9pt7b);
    display.setCursor(82, 118);
    display.print("o");
    display.setFont(&FreeMonoBold12pt7b);
    display.setCursor(120, 124);
    display.print("H");
  
      display.print(Hum, 1);
      display.print("%");
    
    vTaskDelay(10);
    display.display(true);
    vTaskDelay(10);


}

Время

void setTime (){

  sntp_set_time_sync_notification_cb(timeavailable);

 configTime(gmtOffset_sec, 0, ntpServer1, ntpServer2);
  
}


void updLocalTime()
{
  struct tm timeinfo;
getLocalTime(&timeinfo);
strftime(TimeDisp,9, "%H:%M:%S", &timeinfo);
}

// Callback function (get's called when time adjusts via NTP)
void timeavailable(struct timeval *t)
{

}

Все остальное не так интересно‑ работа с дисплеем, датчиком температуры и влажности, время и подключение/пере подключение к WiFi. Ну разве что пара слов о FreeRTOS.

Так как время хочется видеть актуальное, вплоть до секунды, чтобы обращение к серверу ему не мешало‑ отрисовка на дисплей вынесена в отдельный поток. Так у нас все что касается дисплея исполняется на одном ядре, а все что касается сети‑ на другом.

Используемые библиотеки:

Библиотеки для работы с WiFi и HTTP уже есть в ядре ESP32 для Arduino IDE.

Железо

Собираем из модулей



Хотел было поставить TFT на 3.5 дюйма, но что-то лень рисовать новую плату, а из старых проектов особо ничего не подгонишь. Вспомнил что есть у меня E-Ink дисплеи, которые я еще нигде не использовал. А тут как раз- и светится по ночам не будет, и данные часто обновлять не обязательно. Выбор пал на небольшой дисплей диагональю 2.9 дюйма c разрешением 296х128 пикселей. Но версия с TFT конечно будет, и даже будет аватар показывать, но позже.





Сердцем конечно будет ESP32. Для 8266 данных многовато будет, работа со строками и файлами занимает много ресурсов, да и второе ядро выделенное только на работу с сетью уменьшает вероятность глюков. К тому-же на будущее планируется TFT дисплей и графика, а это требует ресурсов. Здесь подойдет любая отладочная плата с ESP32 Wroom на борту.

Подключение

Тут ничего особенного, у модуля 4 проводной SPI + 2 контакта. К ESP32 цепляется просто:

// BUSY -> 4, RST -> 16, DC -> 17, CS -> SS(5), CLK -> SCK(18), DIN -> MOSI(23), GND -> GND, 3.3V -> 3.3V

Датчик DHT22 подключается выходом на 27 ногу ESP32, естественно еще питание.

Железная версия

Для законченной версии была нарисована схема:

Тут стандартная для ESP32 обвязка и UART мост на CH340. По питанию стоит 1117 на 3.3В. Обвязка дисплея стандартная из даташита.

И плата:

Корпуса как такового не подразумевается, плата будет стоять на ножке из металла:






Ножка вырезана на лазере из нержавеющей стали толщиной 1мм. Метал достаточно тонкий и нижняя часть сгибается просто плоскогубцами. Чертеж в формате DXF будет в файлах.
На нижние грани рекомендую наклеить резину или вспененный уплотнитель на самоклеящейся основе.

Вид сзади
Вид сзади

Навесные сопли на фото выше указывают на мою забывчивость. Первоначально развел плату под CH340C, и уже при сборке оказалось что они у меня кончились, пришлось ставить CH340G и навешивать кварц. Куда делась крышка ESP32 даже не спрашивайте:)

Файлы

Файлы печатной платы и схемы в DipTrace + исходник в Arduino IDE:

https://github.com/ENGIN33RRR/HabraTab

Вопрос к читателям

Предлагаю пройти небольшой опрос, да и ваше мнение в комментариях будет интересно.

Нужен ли такой девайс хабровчанам? Какой функционал хотелось бы увидеть? Может какие уведомления, или данные с сайта для читателей, но не писателей? В общем предлагайте, а я буду пилить софт и выкладывать. Устройство прописалось на столе возле монитора и всегда подключено к компьютеру, так что залить новый код дело одной минуты.

После публикации буду особенно часто поглядывать на табло, показания которого полностью зависят от ваших плюсов ;)

Комментарии (46)


  1. Exosphere
    02.02.2023 12:53
    +15

    После выхода статьи двое суток можно выводить рейтинг, просмотры и счётчик комментариев. Это всегда очень волнительно!


  1. Boomburum
    02.02.2023 13:22
    +7

    Сделать корпус посимпатишней и уже на продажу можно )) Очень здорово!


    1. Nansch
      02.02.2023 15:34
      +11

      Надо скооперироваться со знакомыми китайцами и прикрутить АПИ социального рейтинга. Вот это будет более чем волнительно!


      1. vvzvlad
        03.02.2023 18:08

        Интересно, а вы знаете как китайцы реагирует на вопросы о социальном рейтинге? :)


    1. aminought
      03.02.2023 12:03
      +1

      А мне нравится корпус, он такой гиковский и лаконичный.


  1. tormozedison
    02.02.2023 14:02

    Девайс нужен, но он будет ещё лучше с возможностью заранее выбрать несколько хабов, например, diy, history и antikvariat, и как только там выходит статья, пищать и показывать заголовок.


  1. Vsevo10d
    02.02.2023 15:06

    Аж свою ардуиновую юность вспомнил 15-летней давности.

    За девайс зачет, идея кита "собрать из модулей" - обеими руками за.


    1. ENGIN33RRR Автор
      02.02.2023 15:17
      +1

      Идея кита- заводская печатная плата и пакетик радиоэлементов. Собрать из модулей предлагается в смысле- отладочная плата плюс модуль дисплея.


      1. Vsevo10d
        02.02.2023 15:34

        Мне в принципе и так, и так нравится. Хотя пакетик рассыпухи лучше - хоть вспомню, как паять.


  1. YMA
    02.02.2023 15:18
    +3

    Для настоящего кармадр.. ценителя рейтингов стоит еще график кармы добавить и маленький динамик, чтобы при смене показателя девайс мог "Вау!" или "Фууу!" сказать.


    1. ENGIN33RRR Автор
      02.02.2023 15:27
      +1

      На самом деле я работал со звуком на ESP32, правда ставил хороший I2S ЦАП в режиме 24 бита и 2х полосную акустику- с замахом на аудиофильность. Для "Вау" думаю хватит и встроенного ЦАПа.


    1. diogen4212
      02.02.2023 23:09

      лучше, чтобы током било при каждом отрицательном голосе(


  1. Shelsneg
    02.02.2023 15:37
    +2

    Крутая штуковина, хороший получился бы мерч


  1. Dart55
    02.02.2023 15:59

    Девайс классный, но я бы предпочёл виджет на телефон.


  1. Nezdanchick
    02.02.2023 16:51

    Аж захотелось достать свою ардуинку и что-то на ней сваять


  1. vconst
    02.02.2023 18:09
    +1

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

    Рекомендую BME680. Бош веников не вяжет


    1. Javian
      02.02.2023 20:35

      BME живут от 1 до 3 лет на улице в зависимости от, предположительно, загрязненности воздуха — московские меняю раз в год-полтора, в Крыму — на год реже. Сначала «плывёт» влажность — сильно завышается, становится «дискретной»: 15-70-100%, позже отваливается температура (а от неё зависит всё остальное). Немного помогает перенос такого датчика «на работу» в помещение, но влажность дохнет навсегда.

      habr.com/ru/post/525140/#comment_22252464


      1. vconst
        03.02.2023 10:49
        +1

        BME живут от 1 до 3 лет на улице
        У автора девайс — чисто комнатного типа. Причем DHT22 /11 вообще не уличного исполнения, в принципе, а бошевские датчики (не обязательно 680) есть в корпусе-капсуле. Точность DHT22 ± 2-3 градуса, у бошевских от 0,5 и до 0,25.


  1. vconst
    02.02.2023 18:15

    Сообществу вопрос, раз уж тут еспшники собрались.

    Как оптимально запитать платку от 18650? Платку для зарядки акка найти не проблема, но они все выдают 5 вольт, как правило. Есть что-то, понижающее с 5 до 3, но доступное не с Али через месяц, а в условном чипедипе и через пару дней?


    1. ENGIN33RRR Автор
      02.02.2023 18:20

      Оптимально сепик, но это собирать надо. Не совсем оптимально есть платки mini360, в любом ардуиномагазине.


      1. vconst
        02.02.2023 18:38

        А, не… Ошибся
        Выход с контроллера лития 3,7, как с самого аккп (TP4056). Вот в чем засада

        5 вольт чем угодно можно понизить, а вот платка, принимающая 3,7 и отдающая 3 — это уже не так просто оказалось ((

        Хочется обойтись минимумом железок, чтобы не майстрячить трехслойный бутерброд только для того, чтобы взять от акк 3,7 и отдать в есп 3 ровно. Ардуинка у меня питается всего двумя платками размером чуть больше ногтя, потому что не такая разборчивая и берет на вход от 4 до 12 (даже до 20, если случайно перекрутил подстроечный резистор), как таким же набором запитать есп32?


        1. sbw
          02.02.2023 22:37

          Можно включить кремниевый диод последовательно с батареей, падение напряжения на диоде 0.6 В, итого на выходе после диода будет 3.1 В. Потери примерно 16%. Интересно, сравнимо ли с потерями китайских импульсных преобразователей 5 В ->3 В ?


          1. vconst
            03.02.2023 10:50

            Любопытно, можно попробовать


        1. EfogHD
          03.02.2023 14:34

          А что мешает взять любой линейный стабилизатор в ТО-220? Там токи мизерные


    1. semen-pro
      03.02.2023 14:50

      Перейти на LIFEPO4, там нет проблем с напряжением.  18650 для есп так себе вариант.


      1. vconst
        03.02.2023 15:35
        -1

        Беглое гугление показало, что там те же 3,7, большинство в форм-факторе 18650. Может я просто не знаю в куда смотреть?


  1. MaFrance351
    02.02.2023 19:58

    Прекрасный девайс! Обязательно попробую собрать. Как раз от одного из старых проектов осталась плата на ESP32, как раз будет, куда применить. Только портировать для своего дисплея надо будет.


  1. YemSalat
    02.02.2023 20:45

    Обожаю тот факт что он еще температуру и влажность воздуха показывает :)


  1. 76popugaev
    02.02.2023 20:59

    Для _реально_ зависимых, надо трекер прикрутить и кнопку, синтегрированной с браузером и по нажатии которой переходит на следующий пункт в трекере.

    Плюс аларм на новые статьи из выбранных хабов или от пользователей на которых подписан.

    А для _реально_реально_ зависимых, ещё бы кнопку которая добавляет в файервол правило которое блокирует хабр, на два часа.


  1. smart_alex
    02.02.2023 21:34
    +2

    Проверил — работает.

    На мой взгляд привязываться к конкретному железу (дисплей, сенсор) не очень правильно, поскольку у пользователей может не быть такого дисплея или им не нужна температура на шильдике про Хабр. Поэтому первым делом выкинул из кода всё, что связано с дисплеем и сенсором. Теперь это можно прикрутить к любому дисплею.

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

    В целом, скетч — отличный пример для разбора начинающими и повышения своей квалификации в программировании.

    Автор — молодец.

    P.S.

    Немного фантазии и на этом «движке» можно много чего интересного сделать.


    1. MaFrance351
      02.02.2023 22:00

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


      1. smart_alex
        02.02.2023 22:09
        +1

        Не обязательно просто отображение на дисплее - так можно брать данные с сайтов в интернете для вашей "умной системы".


    1. ENGIN33RRR Автор
      02.02.2023 22:14

      Где то в функциях отрисовки, которые вы выбросили, были строчки типа:

      display.print(KARMA.toInt());

      Эта функция извлекает число и отсеивает мусор. Там дальше правда .toFloat().

      Это простейшее решение по фильтрации, ибо значение может быть от 1 до 5 знаков, и в случае короткого значения парсер захватывает мусор вроде кусков HTML тегов. Например после кармы почему то присутствует знак переноса строки.


      1. smart_alex
        02.02.2023 22:20

        @ENGIN33RRR, вы плохо обо мне думаете :) когда я выбрасывал «лишний» код, то делал это достаточно квалифицированно и отделял «мух от котлет» — .toInt и прочее я оставил, заменив только вывод на дисплей выводом в Serial.


      1. smart_alex
        03.02.2023 11:07

        Экспериментирую далее... Возникло 2 вопроса:

        1. Почему инициализация Serial находится в функции FileUpdate(), а не как обычно в setup()?

        2. Почему в коде:

          client.setInsecure(); // игнорируем сертификаты HTTPS
          
          Serial.println(F("FileUpdate1..."));
          
          for (;;) { // цикл потока
        
            Serial.println(F("FileUpdate2..."));
            
            if (WiFi.status() == WL_CONNECTED) {

        cтрока «FileUpdate2...» выводится всегда, а строка «FileUpdate1...» только при первом вызове функции FileUpdate()?


        1. ENGIN33RRR Автор
          04.02.2023 10:51

          У нас тут Rtos, соответственно у каждого потока типа своего Setup, который вызывается один раз, а потом свой Loop, который For, который крутится в цикле. Serial я инициализирую внутри одного потока. Если использовать UART в двух потоках без мьютексов- будут глюки.


          1. smart_alex
            04.02.2023 10:58

            Да, пока ждал ответ, сам понял как это работает. Просто интуитивно ожидал немного другой логики работы.

            Кстати, с получением значений всё в порядке - глюки были связаны с моим выведением в Serial.


          1. smart_alex
            04.02.2023 11:12

            Кстати, ещё вопрос: а как Хабр и прочие сервера отнесутся к постоянным периодическим запросам, особенно если они будут массовыми? Не сочтут за атаку и не будут блокировать?


  1. AlexanderS
    02.02.2023 21:48
    +1

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

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


    1. ENGIN33RRR Автор
      02.02.2023 22:00

      А какая должна быть влажность? Это большое рабочее помещение около 70 квадратов, к тому же теплый пол.


      1. AlexanderS
        02.02.2023 22:28
        +2

        По санитарным нормам 40-50%. 20% — это подсыхание слизистой, стягивание кожи, общее самочувствие хуже, сонливость, усталость. Опять же накопление статики в помещении. Поэтому с приходом зимы я достаю увлажнитель и несмотря на то, что в воздух «выливается» 5 л/сут у меня влажность в квартире выше 35% не поднимается. Однако и такое улучшение я чувствую.


        1. 76popugaev
          02.02.2023 22:31

          несмотря на то, что в воздух «выливается» 5 л/сут у меня влажность в квартире выше 35% не поднимается
          Если расположить увлажнитель достаточно близко, то вполне можно локально повысить до нормальных значений. Чтобы всё помещение увлажнить, тут надо постараться.


          1. AlexanderS
            02.02.2023 22:49

            Верно говорите. Поэтому увлажнитель работает у диванов, так как большую часть времени в квартирах мы спим. Это, конечно, если не по удалёнке работать)

            Поднять влажность я думаю мог бы и выше. Я специально выбирал модель с относительно большой производительностью, но минимальным уровнем шума на первой скорости вентилятора под ночной режим. На высокой скорости он дует гораздо сильнее, но шум от него никому не нужен даже днём, поэтому он на минимальной скорости весь сезон и отрабатывает.


  1. RV3EFE
    03.02.2023 09:49

    Класс. Респект за питание без микросхемы, но на россыпи! Хорошо когда люди видят такие решения.


  1. ASPtr
    03.02.2023 14:37
    +5

    Есть готовые модули ESP32 + e-ink. Искать на алиэкспресс: "LILYGO E-paper". Их несколько видов.


  1. marimero
    04.02.2023 05:35

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