Форум самогонщиков Сайт Барахолка Магазин Помощь солдатам

Автоматика Самовар

Форум самогонщиков Вопросы по электр(он)ике
1 ... 166 167 168 169 170 171 172 ... 179 169
dranek Доктор наук Минск 518 467
Отв.3360  07 Авг. 25, 01:27
Edit.htm тоже получилось пожать. И поправил поведение выпадающего меню.
edit.htm.gz
Кроме того убрал логин с паролем чтоб параноик гуглохром открывал.
SPIFFSEditor.zip
В FS.ino при компиляции выскочит ошибка, там надо из вызова функции пользователя и пароль убрать.
сообщения удалены (2)
dranek Доктор наук Минск 518 467
Отв.3361  08 Авг. 25, 15:28
Нашел удобный сервис составления кастомной разметки разделов https://thelastoutpostworkshop.github.io/...rtitionbuilder/
Полученный файл достаточно положить в папку с прошивкой перед загрузкой Arduino IDE/
Вот пример разметки для ESP32-S3 c 16 мБ флэша. В итоге для LittleFS выделяется 13,4 МБ, для разделов ОТА с программой по 1408 кБ, увеличенные, полная прошивка займет около 92%.
partitions.csv
В первый раз шить надо по шнурку, желательно с очисткой флэша.

Добавлено через 1ч. 7мин.:

Во вчерашнем файле я допустил ошибку, что приводило к неправильной загрузке несжатого edit.htm
Вот исправленный.
SPIFFSEditor.zip
сообщения удалены (9)
lkosoj Куратор Видное 612 676
Отв.3362  09 Авг. 25, 22:16
Выложил 6.25. Добавлены предложения dranek, обновлены библиотеки, дополнительные незначительные улучшения.
dranek Доктор наук Минск 518 467
Отв.3363  10 Авг. 25, 12:23
lkosoj, Почитал документацию и порылся в скетче, есть несколько вопросов, не пытаюсь обидеть, просто мне непонятно.

1 Зачем в начале setup() такая конструкция? Ведь если при старте на нулевом пине 1 модуль должен перейти в режим перепрошивки и скетч выполняться не должен.
pinMode(0, INPUT);
vTaskDelay(600 / portTICK_PERIOD_MS);
if (digitalRead(0) == LOW) {
WiFi.mode(WIFI_STA); // cannot erase if not in STA mode !
WiFi.persistent(true);
WiFi.disconnect(true, true);
WiFi.persistent(false);
}

2 Запуск.
Хорошо, проделали нечто предварительно с WiFi, запустили таски, распределили таймеры,
по странному условию прединициализировали, но не весь SamSetup:
if (SamSetup.flag > 250) { -eeprom еще не считан чему flag равен только бог знает.
SamSetup.flag = 2; и т.д. - не проще было это сделать при объявлении структуры?
наконец прочитали EEPROM read_config(); ,
далее инициализируем пины, меню дисплея, вспомнили про WiFi esp_wifi_set_ps( WIFI_PS_NONE );-на всю мощь, ... подключились, Блинк, Телеграм, ОТА, sensor_init();,
непонятно зачем startService();,
samovar_reset(); -чистим дисплей,
WebServerInit();:
почему-то внутри неё FS_init(); ,
допустим, объявили часть эндпоинтов и неожиданно
load_profile();- прочитали если нашли файл профиля согласно режиму, а если не нашли создали save_profile();
зачем то перезаписали EEPROM? EEPROM.put(0, SamSetup); EEPROM.commit(); и опять его прочитали? read_config();,
и продолжили объявлять эндпоинты и запускать сервер.

Странная последовательность.
ИМХО датчики правильней инициализировать раньше WiFi, вместе с пинами,
FS_init(); запускать из setup() перед WIFI, потому что запускать WiFi без ФС бессмысленно, в нем в случае отсутствия Index.htm или переинициализации ФС устанавливать флаг FS_broken и потом после подключения к точке WiFi скачивать файлы.
Ну и чтение бинарных профилей с мученьем EEPROM посреди WebServerInit(); совсем непонятно.

3 Резервируем аппаратные таймеры так понимаю
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
Под шим серво, ШД насоса, шим воды, что то еще? какое вообще распределение таймеров? Серво и шим воды, я так понял, сидят на одном через LEDC?

4 Не понимаю как работает библиотека чтения DS18B20.
В DS_getvalue() во первых в отличие от документации у вас в строках 174-178 сначала считываются значения датчиков ss = correctT + sensors.getTempC(SteamSensor.Sensor);, и только затем в строке 205 инициализируется их чтение по шине sensors.requestTemperatures();. То в общем не беда, будет просто задержка, обновления значений.
Датчики инициализируются в scan_ds_adress() перебором 6 раз sensors.getAddress(DSAddr[dc], dc), при этом устанавливается еще и разрешение каждому sensors.setResolution(DSAddr[dc], 12); , я так понимаю библиотека их тоже запоминает\регистрирует, иначе как это возможно? (Кстати почему-то у меня нулевому датчику разрядность не устанавливается, в консоль выводит 0)
Далее мистика, если запустить Самовар с подключенными датчиками (Измерителем) он их находит, регистрирует и если они в SamSetup присутствуют норамально выводит в интерфейс. Если при запуске Самовар не обнаружит датчики или их подключить позже он всё равно их читает и выводит, но как sensors.requestTemperatures(); о них узнаёт? Значит всё таки чтение типа sensors.getTempC(SteamSensor.Sensor); тоже их регистрирует? Смысл тогда в sensors.setResolution(DSAddr[dc], 12); ?
lkosoj Куратор Видное 612 676
Отв.3364  11 Авг. 25, 00:29
1 Зачем в начале setup() такая конструкция?dranek, 10 Авг. 25, 12:23
GPIO0-блок в начале setup() — это сброс настроек Wi‑Fi, можно при старте нажать кнопку boot и Самовар сбросит настройки сети. Сделано давно, никто про это не знает, я про это не помню Улыбающийся

2 Запуск.dranek, 10 Авг. 25, 12:23
Почему так сделано:
• Профили — “истина” по режимам в SPIFFS. Идёт синхронизация в EEPROM, затем read_config() расписывает всё по рабочим структурам (адреса DS, задержки, PID и т.д.).
• change_samovar_mode() нужен до load_profile(), чтобы выбрать правильный файл профиля под текущий UI/режим.
• Да, двойное чтение/запись кажется избыточным: ранний read_config() из EEPROM до монтирования FS потом перезатирается load_profile().

Каждый код периодически требует рефакторинга (пересмотра). Если проект активно развивается, код обычно не пересматривают (некогда). А потом уже незачем, оно и так работает Улыбающийся
Это не проблема, но некая избыточность действий при инициализации, можно улучшить. Записал, со временем улучшу.

3 Резервируем аппаратные таймерыdranek, 10 Авг. 25, 12:23
ESP32PWM::allocateTimer(0..3) — резерв LEDC‑таймеров для PWM. Ни к чему не обязывает, они все равно ни на что больше не выделяются.
Серво: 50 Гц, отдельный LEDC‑таймер/канал через библиотеку ESP32Servo.
Насос воды (pump_pwm.attachPin(..., freq, 10)): 15 Гц, другой LEDC‑таймер/канал.
В LEDC один таймер задаёт частоту для всех каналов, привязанных к нему. Т.к. частоты 50 Гц и 15 Гц разные, библиотека разведёт их по разным LEDC‑таймерам. Планировал шаговик тоже перевести на такой же таймер, это эффективнее, чем существующее решение, не будет работать через прерывание - иногда видно, как шаговик подтормаживает, когда идет общение по сети и еще что-то считается. Но пока осталось в планах, не очень тривиальная задача.

4 Не понимаю как работает библиотека чтения DS18B20.dranek, 10 Авг. 25, 12:23
Порядок “сначала getTempC, затем requestTemperatures” — асинхронная схема: читаем предыдущую выборку, в конце запускаем новую конверсию для следующего цикла.
Почему датчики появляются, даже если подключены позже: requestTemperatures() шлёт широковещательный SKIP ROM/CONVERT T; когда датчик подключится, он начнёт конверсию. Если чтение идет по конкретному адресу (из SamSetup.*Adress), то как только физически этот адрес появился на шине — getTempC(address) начнёт возвращать значение без необходимости “регистрировать” его заново. Повторный scan_ds_adress() нужен чтобы обновить список индексов DSAddr[...], чтобы можно было на горячую подключить новый/заменить существующий датчик.
setResolution(addr, 12) записывает конфигурацию в сам датчик (в его scratchpad, и при необходимости — в энергонезависимую память датчика). Либа не “запоминает” это где-то у себя для адреса, это свойство датчика. Если у нулевого датчика не ставится разрешение — проверьте:
наличие нормальной подтяжки на линии,
отсутствие паразитного питания (для записи конфигурации нужен нормальный ток, возможен таймаут на сильную подтяжку).
dranek Доктор наук Минск 518 467
Отв.3365  11 Авг. 25, 06:32
lkosoj, Мне удалось перевести setup.htm на json в обе стороны (идея этого и помощь на начальном этапе ais77). При этом изменился файл стиля и формат текстового сохранения. Под сериализацию/ десериализацию использовал arduinojson.h. Соответственно скетч подрос на 1%. Вчера закончил, больше ошибок не нашел. Мой файл больше за счет настроек wifi и второго насоса над царгой пастеризации.
Интегрировал второй перистальтический насос и добавил его в ректификацию для поддержки ЦП. При соответствующей галке в setup htm он включается вместо основного на отборе голов, а на отборе тела дополнительно со скоростью из отдельного поля в setup.htm. Предполагается, что второй насос близнец первого.
Разработал плату и корпус для esp32-S3, перераспределил пины. Подобрал рабочую библиотеку для серво под ESP32S3.
При этом сделал много "плохого".
Убрал все датчики АД кроме bmp180. Заменил дисплей на asoled (наполнение вывода в процессе, в основном он нужен под сервисную информацию), убрал меню и энкодер. Убрал WiFiManager и профили (не видел пока отказов eeprom). Добавил в Samovar_ini.h исходные настройки wifi, адреса датчиков под Измеритель, включение поддержки второго перист насоса, может что забыл.
Выдача json в сторону сервера сделана в текстовом формате, иначе тот не хотел принимать, принимаемый и отправляемый json отличаются в части датчиков температуры и форматов. Файл сохранения это снимок json.
Почему esp32s3. HEAP на старте 190кБ, вместо 126 у нас. Есть PSRAM, можно попробовать задействовать под те же lua скрипты, изначально думал под замену плейсхолдеров, однако отпало. Есть варианты с флэшем аж жо 32 мб. Попросторней и побыстрее при той же цене. Луа пинов будет аж 3. И еще пины свободные есть (на плате развёл на будущее). В общем то основные узлы проверены и работают. Есть вопросы к wifi модулю, у него странное поведение, к новой точке как то со скрипом подключается, приходится даже антенну наращивать, потом держится за нее до последнего.
Так вот уезжаю в отпуск. Интересует? Поделиться этим сырцом?
lkosoj Куратор Видное 612 676
Отв.3366  11 Авг. 25, 07:58
Разработал плату и корпус для esp32-S3, перераспределил пины. Подобрал рабочую библиотеку для серво под ESP32S3.dranek, 11 Авг. 25, 06:32

Да, ais77 говорил. Пины, корпус и библиотеку пришлите пожалуйста.

Сильно пришлось менять то, что у меня было под S3 по пинам?
dranek Доктор наук Минск 518 467
Отв.3367  11 Авг. 25, 09:34
Сильно, много нюансов было. Например аналоговое чтение возможно только на первых 10 пинах, поэтому реально работающие LUA1-3. LUA_PIN4 будет использован под цифровой входвыход. На распиновку также сильно повлияла разводка платы. В целом старался разводить так, чтоб потом некоторые пины вроде LUA можно было легко переназначить, поскольку не знал заработает или нет.
Пины.#if BOARD == ESP32S3
//#pragma message ("ESP32S3")
// поставил пины которые нельзя использовать при загрузке

#define STEPPER_STEP 16
#define STEPPER_DIR 0 // загрузочный
#define STEPPER_EN 15

#define STEPPER2_STEP 7
#define STEPPER2_DIR 3 // загрузочный
#define STEPPER2_EN 6

#define RELE_CHANNEL1 2 //используется для пускателя, который включает нагреватель
#define RELE_CHANNEL2 42 //в режиме "Пиво" используется для включения мешалки
#define RELE_CHANNEL3 41 //используется для клапана, открывающего/закрывающего воду охлаждения
#define RELE_CHANNEL4 40 //если не используется регулятор с управлением, выход используется для управления разгоном

#define ONE_WIRE_BUS 12

#define SERVO_PIN 10

#define BTN_PIN 47

#define LUA_PIN 4
#define LUA_PIN2 5
#define LUA_PIN3 1
#define LUA_PIN4 20

#define ALARM_BTN_PIN 48

#define WATERSENSOR_PIN 13

#define WHEAD_LEVEL_SENSOR_PIN 19

#define LCD_SDA 8
#define LCD_SCL 9

#define RXD2 18
#define TXD2 17

#define WATER_PUMP_PIN 11

#define BZZ_PIN 39
#define BARDA_LEVEL_UP 21 //Пины регулятора уровня барды
#define BARDA_LEVEL_DWN 14 // Светодиод
На плате DIR для ШД не разведены, вместо этого используются джамперы, но есть возможность подключить и к пину с помощью проводов с дюпоинтами. Однако пины я под это завел загрузочные 0 и 3. Не факт что стартанет, особенно с 0, не пробовал, мне джамперы нравятся, завел потому что библиотека их требует, а 0 и 3 не жалко. Поменять в распиновке недолго, есть другие разведенные но не используемые.
Плата.
Корпус. box.zip
Библиотека серво. ESP32S3servo.zip
Инициализация.

#if defined(ARDUINO_ESP32S3_DEV)
servo.attach(SERVO_PIN,7,0,180,400,2000); // В таком варианте коромысло ходит правильно
//attach(int pin, int channel , int min_angle, int max_angle, int min_pulse, int max_pulse)
#endif


Добавлено через 4ч. 6мин.:

lkosoj, почему при смене режима Самовар должен перезагружаться?
Из-за регистрации эндпоинта с подменой index.htm и чтения бинарного профиля?
Что-то ещё?
lkosoj Куратор Видное 612 676
Отв.3368  11 Авг. 25, 14:57
Из-за регистрации эндпоинта с подменой index.htmdranek, 11 Авг. 25, 09:34
Две причины. Первая эта, но можно было не менять эндпойнт. Вторая - чтобы не разбираться, в какой момент нужно перейти на другой профиль. Сменил режим - перегрузили.
dranek Доктор наук Минск 518 467
Отв.3369  11 Авг. 25, 18:34
lkosoj, Почему бы не сделать так?

const char* ModeFileName[] = {"/index.htm", "/distiller.htm", "/bk.htm", "/beer.htm", "/nbk.htm"};

...

void WebServerInit(void) {

...

 server.on("/index.htm", HTTP_GET, [](AsyncWebServerRequest *request) {
   request->send(SPIFFS, ModeFileName[Samovar_Mode], "text/html", false, indexKeyProcessor);
 });

...
}

void handleSave(AsyncWebServerRequest *request) {

...

ValidateSamSetup(); //Проверка на допустимость пределов значений, часть кода read_config();
  if (request->hasArg("mode")) {
    if (SamSetup.Mode != request->arg("mode").toInt()) {
      if (SamovarStatusInt != 0)  {
        SendMsg(F("Попытка сменить режим при запущенном процессе! Отмена смены режима."), ALARM_MSG);
          } else {
          SamSetup.Mode = request->arg("mode").toInt();
            String f;
            f = get_prf_name();
            if (SPIFFS.exists(f)) {
              File file = SPIFFS.open(f, FILE_READ);
              file.setTimeout(0);
              file.read((uint8_t *)&SamSetup, sizeof(SamSetup));
              file.close();
          }
        }
    }
  }
 EEPROM.put(0, SamSetup);
 EEPROM.commit();
 File file = SPIFFS.open(get_prf_name(), FILE_WRITE);
 file.write((uint8_t *)&SamSetup, sizeof(SamSetup));
 file.close();
 AsyncWebServerResponse *response = request->beginResponse(301);
 response->addHeader("Location", "/");
 response->addHeader("Cache-Control", "no-cache");
 request->send(response);
}

И не нужна перезагрузка.

Добавлено через 4мин.:

P.S. чутка подправил логику.
lkosoj Куратор Видное 612 676
Отв.3370  11 Авг. 25, 20:43
Вторая - чтобы не разбираться, в какой момент нужно перейти на другой профиль. Сменил режим - перегрузили.lkosoj, 11 Авг. 25, 14:57
Поэтому Улыбающийся
dranek Доктор наук Минск 518 467
Отв.3371  11 Авг. 25, 22:45
lkosoj,
Слово догма в русском языке обозначает положение, утверждение, принимаемое за истину без доказательств, часто в рамках определенной системы взглядов или вероучения.
У меня плохие новости, а может хорошие, как посмотреть.
Захотел я проверить свои экзерсисы, внедрив их в 6.25.
Прошил ESP32 с очисткой флэша для чистоты эксперимента.
Для начала ваш любимчик ESPAsyncWiFiManager опять показал мне фигу, не беда, как ему абструкцию сделать я уже знаю. ESPAsyncWiFiManager.zip
Потом ни скрипт загрузки, ни сам Самовар записывать файлы в SPIFFS отказался.
Выяснилось что в таблице разделов не хватало буквы s в конце названия последнего раздела spiff. Возможно я случайно выложил не тот файл, из предыдущих экспериментов. Вот правильный partitions.zip В конце отсутствует раздел для дампа при сбоях и Самовар при загрузке ругается в ком порт об этом. На работу повлиять не должно, зато файловая система больше.
Исправил, Скетч использует 1177413 байт (89%) памяти устройства. Всего доступно 1310720 байт, а должно быть 1408 кБ, нестыковка. Скрипт загрузки сработал.
index.htm не грузится, вачдоги, моё предложение не работает. Пока не работает.

И очередной вопрос. Зачем переменная
String jsonstr; // Строка, содержащая json ответ для страницы
объявлена глобальной?
lkosoj Куратор Видное 612 676
Отв.3372  11 Авг. 25, 23:17 (через 32 мин)
Зачем переменная
String jsonstr; // Строка, содержащая json ответ для страницы
объявлена глобальной?dranek, 11 Авг. 25, 22:45
Для экономии памяти
dranek Доктор наук Минск 518 467
Отв.3373  11 Авг. 25, 23:22 (через 6 мин)
Для экономии памятиlkosoj, 11 Авг. 25, 23:17
Это совершенно не соответствует моему пониманию распределения памяти в ESP32.

Добавлено через 13ч. 46мин.:

Её ведь можно объявить внутри server.on("/ajax", HTTP_GET, [](AsyncWebServerRequest *request) { и там же наполнить перед отправкой.
И всё, она больше не занимает память постоянно.
lkosoj Куратор Видное 612 676
Отв.3374  12 Авг. 25, 13:21
Есть такая проблема: фрагментация памяти. Если переменная большая (а эта переменная очень большая) при каждом выделении памяти ей будет выделяться большой участок памяти, потом освобождаться, и так много много раз (раз в три секунды за 12 часов это очень много). Рано или поздно это приведёт к тому, что из-за фрагментации памяти в очередной раз ей не хватит места (не будет достаточно сплошного куска памяти, куда она сможет влезть целиком) и в итоге это кончится печально.
Есть ещё несколько проблем, связанных с выделением памяти и производительности.
dranek Доктор наук Минск 518 467
Отв.3375  12 Авг. 25, 16:05
Это было бы так если бы не тип String. У неё переменный размер, который периодически скачкообразно увеличивается из-за передаваемых мессаг. В промежутке сразу за ней может вдруг оказаться другая переменная. Скачет она по куче как кузнечик в общем. Так что да, фрагментация проблема, из-за таких переменных.
lkosoj Куратор Видное 612 676
Отв.3376  12 Авг. 25, 17:18
У неё переменный размер, который периодически скачкообразно увеличивается из-за передаваемых мессаг.dranek, 12 Авг. 25, 16:05
Не совсем верно. Предлагаю подумать и почитать доку Улыбающийся
Volume Доктор наук Уфа 632 351
Отв.3377  13 Авг. 25, 09:22
У неё переменный размер, который периодически скачкообразно увеличивается из-за передаваемых мессаг.dranek, 12 Авг. 25, 16:05
при каждом выделении памяти ей будет выделяться большой участок памяти, потом освобождатьсяlkosoj, 12 Авг. 25, 13:21
коллеги, вы вроде как об одном и том же говорите - о проблеме сегментации кучи. Применительно к строкам это решается объявлением массива байт фиксированного размера, заведомого больше чем максимальный размер строки+1(на терминальный ноль)
Хотя, при использовании сторонних библиотек это не дает никакой гарантии - если библиотеки копируют переданную в аргументе строку в свои локальные переменные

Такой же трюк со статичным выделением памяти объявлением глобального массива рекомендуется делать для стека выделяемого постоянно работающей задаче FreeRTOS (создавать задачу через xTaskCreateStaticPinnedToCore)
dranek Доктор наук Минск 518 467
Отв.3378  13 Авг. 25, 10:07 (через 46 мин)
Volume, да нет, Алексей похоже прав. Я как бы и не спорил, это для меня новая область, живу старыми понятиями. Все глобальное в кучу и стек не попадает. Под это отдельная область оперативки. Непонятно как с переменной длиной строк freeRROS там управляется. Явно не самым оптимальным образом. А насчет фрагментации, если она есть, то при очередном вэб запросе asyncwebserver создаст несколько длинных строк в куче и отдельный таск. Тут то и вачдог. Массив pchar конечно безопасней звучит. Возможно стоит попробовать потоковую передачу данных. Типа буфер pcar закончился, передаём, клиент увидел, что нет признака конца посылки, делает дозапрос, и так пока всё не получит.
Volume Доктор наук Уфа 632 351
Отв.3379  13 Авг. 25, 11:03 (через 56 мин)
dranek, freeRTOS это ж микроОС, памятью переменных не управляет, только задачами. С переменными и распределением их по памяти разбирается компилятор C или C++, тут все как обычно. const во флешке, static "прибиты гвоздями" в памяти, локальные - в куче или в регистрах и т.д.
буфер pcar закончился, передаём, клиент увидел, что нет признака конца посылки, делает дозапрос, и так пока всё не получитdranek, 13 Авг. 25, 10:07
правильное решение. Сам так делал (на либе Libesphttpd) для передачи логов (порядка мегабайта-двух)