• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Делюсь опытом MAX31855, датчик к-термопары с компенсацией температуры холодного спая

enjoynering

Well-known member
Библиотека для MAX31855 от Maxim Integrated. На борту два АЦП. 14-bit для термопары и 12-bit для холодного спая. Время преобразования около 100 миллисекунд или 10 измерений в секунду. По мимо температуры термопары умеет выдавать температуру холодного спая. Обладает улучшенными диагностическими функциями - замыкание термопары на землю, замыкание на питание, обрыв термопары. Для уменьшения наводок на входе производитель советует допаять параллельно "-T" и "+T" конденсатор на 10nF/0.01mF. Ребята из Adafruit по мимо этого еще и ферритовые бусинки на холодный спай одевают.

Библиотека поддерживает все плюшки сенсора. Работает с железным SPI или эмулирует последовательный протокол ногодрыгом/bitbang.

Забирать тут.
 

Jaeger

New member
Либа неплохая. Отлично прокомментирована. Но тупо ждать в лупе 100 мс завершения конверсии и ни чего не делать, как то не кошерно. Я имею ввиду: delay(MAX31855_CONVERSION_TIME) в функции readRawData.
 

Jaeger

New member
Ну я типа такого сколхозил, заменил delay стандартным способом, у меня работает:
Код:
int32_t MAX31855::readRawData(void)
{
int32_t rawData = 0;
uint32_t previousMillis = 0;

digitalWrite(_cs, LOW);  //stop  measurement/conversion
delay(1);               //4MHz  is 0.25usec, do we need it???
digitalWrite(_cs, HIGH); //start measurement/conversion

if(millis() - previousMillis > MAX31855_CONVERSION_TIME) {
        previousMillis = millis(); 
        ........

      return rawData;
}
     return false;
}
В примере:
Код:
if(myMAX31855.readRawData()); { //если конверсия закончилась, получаем температуру
   c = myMAX31855.getTemperature(rawData);
}
 

enjoynering

Well-known member
Да можно, но осторожно. Дело в том, что вы можете словить о-о-о-очень редкий глюк, когда mills() переполнился и стал например 10, а previousMillis все еще за 4-е миллиона и у вас будет отрицельный результат, который вызовет краш модуля. Чтоб этого не произошло используйте функцию abs().

Кстати у вас ошибка, тк вы обявляте previousMillis внутри функции, она всегда будет 0 при вычитании из mills(). Ее надо объявлять как глобальную, или уж если не хотите захламлять код то можно оставить переменную внутри функции, но объявить ее как static uint32_t. Тогда она станет глобальной, но будет доступна только той функции в которой объявлена.

Изначально данная либа писалась под avr для фена с PID регулировкой. Мне была важна точность поддержания температуры, а не скорость работы камня. Отсюда и delay().
 
Последнее редактирование:

CodeNameHawk

Moderator
Команда форума
о-о-о-очень редкий глюк, когда mills() переполнился и стал например 10, а previousMillis все еще за 4-е миллиона и у вас будет отрицельный результат, который вызовет краш модуля.
IMHO так как все переменные беззнаковые, отрицательного результата не может быть (просто получиться переполнение, пропадет один бит и результат получиться "малым") и краша тоже не будет.
Раз в примерно 50 суток, может быть неточно выдержанный интервал задержки.
 
Последнее редактирование:

enjoynering

Well-known member
получиться переполнение, пропадет один бит и результат получиться "малым") и краша тоже не будет.
Вы уверенны? Проверяли?

Я только показал как правильно, а дальше как хотите.

Можно и знак поворота не показывать на дороге, но настанет после дождичка в четверг вас нехило накажет.
 
Последнее редактирование:

CodeNameHawk

Moderator
Команда форума
Вы уверенны? Проверяли?
Стал бы я тут распинаться...
Код:
unsigned long old_Value = UINT64_MAX;
unsigned long delay_value = 10;

unsigned long replace_millis_function(void)
{
    return 5;
}


void setup()
{

  /* add setup code here */
    Serial.begin(115200);
   
    Serial.println();
    Serial.println("Start.");

    Serial.print("old_Value ");
    Serial.println(old_Value);

    Serial.print("replace_millis_function ");
    Serial.println(replace_millis_function());

    Serial.print("delay_value ");
    Serial.println(delay_value);

    Serial.print("Result ");
    Serial.println(replace_millis_function() - old_Value);
   

    if ((replace_millis_function() - old_Value) > delay_value)
    {
        Serial.println("True.");
    }
    else Serial.println("False.");
}

void loop()
{

  /* add main program code here */

}
Код:
Start.
old_Value 4294967295
replace_millis_function 5
delay_value 10
Result 6
False.
Вам не показалось странным, что почти все используют код без проверки переполнения?
 

enjoynering

Well-known member
Нет, не показалось. Ардунщики на 90% копипастеры. Уже не раз в этом убеждался. Поэтому очень редко пользуюсь чужими библиотеками.
 
Последнее редактирование:

CodeNameHawk

Moderator
Команда форума
Вся суть ардуино, взять куски готового и добавить немного своего кода.
 

Jaeger

New member
Изначально данная либа писалась под avr для фена с PID регулировкой. Мне была важна точность поддержания температуры, а не скорость работы камня. Отсюда и delay().
Во, у меня такая же сейчас задача, соорудить контроллер для китайского термофена. Планирую на ардуине про мини + OLED 128х64 желто-синий, энкодер, max31855, шим на вентилятор, ардуиновский PID с выходом на твердотельное реле. Вот только мне не очень понятно, как delay() влияет на точность регулирования PID? PID тоже ваш?
 

enjoynering

Well-known member
моя логика была такая - с delay(MAX31855_CONVERSION_TIME) я гарантированно получаю температуру СРАЗУ ПОСЛЕ ИЗМЕРЕНИЯ. с (millis() - previousMillis > MAX31855_CONVERSION_TIME непонятно когда это случится. через 100 или 150, или 200мс?

PID не мой. еще только в планах осилить а пока самым популярным пользуюсь (по мне так неплохо написан).
 

Jaeger

New member
enjoynering, каким образом с (millis() - previousMillis > MAX31855_CONVERSION_TIME время конверсии может быть меньше MAX31855_CONVERSION_TIME?
 

Jaeger

New member
Больше не меньше, в данном случае это не повредит результату. Застрять можно где угодно, все зависит от кодера.
Я просто хочу выяснить, delay(200) в лупе - это нормально?
И еще немного конструктивной критики:
Функция getTemperature(int32_t rawValue)возвращает число с двумя знаками после запятой, на дисплей выводим
целое число, на "месте" бы округлять, да и вообще отказаться бы от операций с "плавающей точкой", уж больно жрет до хрена памяти.
 

enjoynering

Well-known member
Я люблю по точнее. в память никогда не упирался. откажитесь от oled. одни недостатки: шрифты, он же массив символов висящий в памяти (он просто огромен по сравнению с одним float из моей либы), выгорает со временем, маааленький и читать неудобно. по мне так - Универсальная библиотека LiquidCrystal_I2C на базе расширителя портов PCF8574 самое то для таких поделок и дешевле.

ну и в догонку вам - MAX31855, датчик к-термопары с компенсацией температуры холодного спая

если упираетесь в размер скеча, а не памяти можно поменять загрузчик - Optiboot загрузчик для Arduino даст лишних 1.5kb!!!
 
Последнее редактирование:

pvvx

Активный участник сообщества
Да можно, но осторожно. Дело в том, что вы можете словить о-о-о-очень редкий глюк, когда mills() переполнился и стал например 10, а previousMillis все еще за 4-е миллиона и у вас будет отрицельный результат, который вызовет краш модуля.
(unsigned)0x0000000a - (unsigned)0xffffffff = 0x0000000b и ? Где отрицательное? Значение константы с которым сравнивается? Но оно же не больше 2147483647 микросекунд (35 минут) и указывать тип unsigned нет смысла.
millis считывает и выдает младшие 32 бита 64-х аппаратного счетчика микросекунд.
То, что переменная не глобальная и не инициализирована - это ошибка, т.к. желательно ей присвоить значение millis() в процедурах инициализации проекта, перед запросами данной функции, а не в setup().
Но это всё безразлично, т.к. Arduino на ESP8266 не рассчитано работу без глюков в течении десятка минут :)
 
Последнее редактирование:

pvvx

Активный участник сообщества
Я просто хочу выяснить, delay(200) в лупе - это нормально?
А в чем беда?
Всё в ESP8266 работает по событиям. Эмуляция поллинга в loop() - это чуждо её системе и сделано для тех, кто не понимает основ программирования кроме как в линеечку...
Системе и нормальному коду всё равно, даже если loop() содержит всего одну функцию delay(100500). WiFi, работа TCP стека и все прерывания только в этом случае работают правильно, не нарушая спецификаций WiFi и прочих RFC (рекомендаций).
И еще немного конструктивной критики:
Функция getTemperature(int32_t rawValue)возвращает число с двумя знаками после запятой, на дисплей выводим
целое число, на "месте" бы округлять, да и вообще отказаться бы от операций с "плавающей точкой", уж больно жрет до хрена памяти.
Плавающая точка жрет всего несколько блоков с инициализацией библиотеки в RAM, а остальной код находится в Flash (XIP). XIP у вас 1 МегаБайт и не понятно, что туда не влезает при правильном подходе и разработке приложения. Константы, скрипты и даже код могут подгружаться на время исполнения, к примеру как оверлеи, из всей доступной области одного чипа Flash на 16 МБайт. А можно и несколько чипов Flash или внешний сервер, включающий оплату за загружаемый код в IRAM и исполняемый код у пользователя :). По этому ограничений нет. Тем более для ваших приложений, где какие либо события или опрос происходят и производятся не чаще 1 раза в несколько минут. Даже если переписывать flash для запуска разного кода, всё равно за время использования ESP8266 дырки там не будет, т.к. все процессы у вас очень медленные... а все задачи в loop() сводятся к разбивке на малые блоки кода по времени исполнения для быстрейшего ухода в delay(), чтобы система работала. Проще всё эти куски повесить на "калбэки" уже имеющиеся в системе у LwIP, да совт-таймерные "калбэки" и аппаратные прерывания... Код у вас сократиться на никому ненужный "поллинг" - т.е. loop() станет пустым, с одной строчкой - delay(100500). Ну и т.к. все события в ESP8266 обрабатываются по раздельности, с нулевой глубиной стека (вызываются из ets_run()), то каждое событие имеет возможность на время обработки использовать всю память ESP8266, по выходу предоставив пару байт ответа и новые назначенные события для последующей обработки, да с указанием уровня приоритета, чтобы не мешать работать системе.
Arduino - это атавизм, созданный для того, чтобы вы не смогли работать с современными мультизадачными системами в последующем.
 
Последнее редактирование:

Jaeger

New member
Все мои вопросы были относительно ардуины на AVR. С ESP8266 я еще не работал (хотя и есть несколько штук) поэтому информацию из ваших постов я, к сожалению, ни хрена не понял.

если упираетесь в размер скеча, а не памяти можно поменять загрузчик - Optiboot загрузчик для Arduino даст лишних 1.5kb!!!
Давно уже поставлен. Более того, можно обойтись вообще без бутлодыря.
 
Сверху Снизу