хитрый термометр

Тема в разделе "Флудилка", создана пользователем CYITEP_BAC9I, 29 дек 2017.

  1. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    Доброй всем полуночи. Тут решил сделать игрушку термометр с претензией на приставку умный. на датчике
    DS18B20. но что то у меня затопорилось на моменте индикации тенденции роста или падения температуры.
    реализовывываю это дело так счетчиком проходов Pass_counter цикла с 2 секундным интервалом отсчитываю промежутки времени. на 2 и 6 запиминаю температуру и потом сравниваю полученные значения. И вот нечего не выходит. всякая чушь получается когда температура поднимается пишет что идет вниз и наоборот хаотично. понимаю что не лучший алгоритм выбрал. но как это правильно реализовать не соображу. Скрины постараюсь завтра приложить. если будет время. Сейчас 2 час ночи и уже горшочек не вари совсем(

    ниже кот. он сырой совсем только час назад начал писать. но основыные моменты закомментированы

    Код (C++):
    #include <EEPROM.h>
    #include <OneWire.h>
    #include <DallasTemperature.h>
    #include "TM1637.h" // Подключаем библиотеку
    #define CLK 13 // К этому пину подключаем CLK
    #define DIO 12 // К этому пину подключаем DIO
    //пины светодиодов
    #define  Fever                 10
    #define  Normal_temperature    9
    #define  Low_temperature       8
    #define  Body_temperature      7 //надо проверить какой шим поддерживает
    #define  Simple_thermometer    6
    #define  Battery_charge        5
    //пины кнопок
    #define  IN_memory             4
    #define  FROM_memory           3
    TM1637 disp(CLK,DIO);

    //кнопка верхняя 1023 средняя 511 нижняя 339


    #define ONE_WIRE_BUS 11// Подключение цифрового вывода датчика к 11-му пину Ардуино.
    OneWire oneWire(ONE_WIRE_BUS);// Запуск интерфейса OneWire для подключения OneWire устройств.
    DallasTemperature sensors(&oneWire);// Указание, что устройством oneWire является термодатчик от  Dallas Temperature.

    ///////////////
    bool Clock_generator_1000 =0; //переменная для задания частоты опроса датчика 2 сек
    bool Clock_generator_7000 =0; // задатчик времени сравнения температуры
    bool Counter_switch =0;
    byte Pass_counter =0;
    double Correction = 00.21 ; //поправка для датчика
    double Last_temperature =0; // последняя измеренная температура
    bool Last_temperature_S =0; // для запиминания температуры за 1 проход цикла
    double Current_temperature=0; // текущая температура
    double Current_temperature_M=0; // текущая температура для запоминания
    bool Current_temperature_MS=0; // для запоминани температуры за 1 цикл
    double Temperature_for_display = Last_temperature +Correction; // исправление погрешности моего датчика у каждого своя поправка. Высчитывется по эталону

    void setup() {
    pinMode(Fever, OUTPUT);
    pinMode(Normal_temperature, OUTPUT);
    pinMode(Low_temperature, OUTPUT);
    pinMode(Body_temperature, OUTPUT);
    pinMode(Simple_thermometer, OUTPUT);
    pinMode(Battery_charge, OUTPUT);
    pinMode(A0, INPUT);
    pinMode(IN_memory, INPUT);
    //pinMode(FROM_memory, INPUT);
    //pinMode(Body_temperature, OUTPUT)
    //pinMode(Body_temperature, OUTPUT)
    //pinMode(Body_temperature, OUTPUT)
    digitalWrite(Battery_charge, LOW);
      digitalWrite(Simple_thermometer, LOW);
      digitalWrite(Body_temperature, LOW);
      digitalWrite(Low_temperature, LOW);
      digitalWrite(Normal_temperature, LOW);
      digitalWrite(Fever, LOW);
    disp.set(1);
      disp.init(D4056A);



        Serial.begin(9600);
    sensors.begin();
    }

    void loop() {
      disp.display(Current_temperature+Correction); // вывод температуры на экран
    //задаем частоту опроса датчика 1 сек

    static unsigned long Counter_1_sec = millis();
    //static unsigned long Counter_6_sec = millis();

    if (millis() - Counter_1_sec >= 1000)
    {
      Clock_generator_1000 =!Clock_generator_1000; //генератор 2 секундного интервала
      Counter_1_sec = millis();
    }
    if (Clock_generator_1000 == true)
    {
    if(Counter_switch ==0) //чтобы счетчик сработал один раз запомнив температуру и номер прохода
    {sensors.requestTemperatures(); //запрос на получение температуры
         Pass_counter++;  //считаем количество циклов для сравнения температуры
         Current_temperature = sensors.getTempCByIndex(0); // получение данных о температуре и присваивание переменной
         Counter_switch=1;
    if(Pass_counter >4) //если циклов болше 4 обнуляем счетчик
    {
      Pass_counter =0;
    }
    }
     
    }
    if (Clock_generator_1000 == false)
    {
      Counter_switch=0; //обнуляем переключатель счетчика
    //sensors.requestTemperatures(); //запрос на получение температуры
    }
    Serial.println("Pass_counter");
    Serial.println(Pass_counter);
    //СРАВНЕНИЕ ТЕМПЕРАТУРЫ ЧЕРЕЗ ПРОМЕЖУТОК 6 СЕК
    //Serial.println("Clock_generator_1000");
    // Serial.println(Clock_generator_1000);

    if (Pass_counter ==1 && Last_temperature_S==0) //если 1 двухх секундный цикл то запоминаем текущую температуру
    {
    Last_temperature= (Current_temperature+Correction);
    Last_temperature_S=1; //сделано чтобы температура запомнилась за текущий проход
    Current_temperature_MS =0;
    }
    if (Pass_counter ==4 && Current_temperature_MS ==0  ) // если 4 двухсекундный проход то запоминаем текущюю температуру
    {
    Current_temperature_M = (Current_temperature+Correction);
    Current_temperature_MS =1;
    Last_temperature_S=0;
    }

    if(Current_temperature_M>Last_temperature) //сравниваем температуры за заданный временной интервал
    //если последняя записанная температура больше предидущей то значит температура растет
    {
       digitalWrite(Simple_thermometer, HIGH);
      digitalWrite(Battery_charge, LOW);
      Serial.println("VVERHHH");
    }
    else if(Current_temperature_M==Last_temperature)
    //если температуры равны то значить ничего не происходит и температура устаканилась
    {
       digitalWrite(Simple_thermometer, HIGH);
      digitalWrite(Battery_charge, HIGH);
      Serial.println("RAVNO");
    }
    else //если же последняя записанная температура меньше предидущей значит температура падает
    {
       digitalWrite(Simple_thermometer, LOW);
      digitalWrite(Battery_charge, HIGH);
    Serial.println("VNIZZZ");
    }
    //индикация температуры тела

    //Serial.println("Current_temperature_M");
    // Serial.println(Current_temperature_M);
    //Serial.println("Last_temperature");
    //Serial.println(Last_temperature);
    //Serial.println("Pass_counter");
    ///Serial.println(Pass_counter);
    if((Current_temperature+Correction) <35)  //пониженная температура
    {
      digitalWrite(Low_temperature, HIGH);
      digitalWrite(Normal_temperature, LOW);
      digitalWrite(Fever, LOW);
    }
    else if((Current_temperature+Correction) >35 && (Current_temperature+Correction) <37 ) // нормальная температура
    {
      digitalWrite(Low_temperature, LOW);
      digitalWrite(Normal_temperature, HIGH);
      digitalWrite(Fever, LOW);
    }
    else if((Current_temperature+Correction) >37 && (Current_temperature+Correction) <39 ) // повышенная температура
    {
      digitalWrite(Low_temperature, LOW);
      digitalWrite(Normal_temperature, LOW);
      digitalWrite(Fever, HIGH);
    }
    else if((Current_temperature+Correction) >39) // пипец повышенная температура. Выше 41 пипец
    {
      digitalWrite(Low_temperature, LOW);
      digitalWrite(Normal_temperature, LOW);
      digitalWrite(Fever, HIGH); // сделать мигание
    }
     
  2. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    Если кто знает как правильно реализовать индикацию тенденции температуры напишете пожалуйста в общих чертах. Хочу игрушку доделать до нового года. А я баиньки )
     
  3. ImrDuke

    ImrDuke Гик

     
    CYITEP_BAC9I нравится это.
  4. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    спасибо завтра гляну. спать жутко хочу. а завтра на корпаратив переться. Кстати всех с Началом празднования нового года!
     
  5. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    попробую перестроить алгоритм. буде делать замеры не по циклам а каждые 5 сек. т.е 5, 10, 15, 20,25 итд. а потом сравнивать. это градусник для измерения температуры тела так что пойдет. возможно где то у меня ошибка и переменные меняются местами и получается каша. но это сегодня уже не осилю. баю бай
     
  6. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    всех с наступающим новым годом! долго бы я бился над скетчем пока не обратил внимание на необоснованные скачки температуры и спаленный порт дуни). разобрал датчик и офигел. но вообще китайцы оборзели. все 3 вывода были никак не заизолированны. малейшие движение и замыкание. как он вообще работал.
    [​IMG]
    в общем прощай алиэкспресс здравствуй ебей. может там меньше брака будет. а то китайцы в конец охренели. датчики то не дешевые брал.
    до НГ не успею игрушку доделать. в следующем году доделаю.
    линейная аппроксимация методом наименьших квадратов не очень подходит в моем случае но все равно спасибою
    я придумал как сделать. метод тоже не сахар но проще предидущего метода. заведу массив в 2 ячейками и каждые 5 сек буду засовывать туда температуру с датчика. перед каждым засовыванием буду сдвигать даные с 1 ячейки в 0. таки образом в 1 ячейке всегда будут "свежие" данные а в 0 данные о температуре 5 сек назад. остальное дело техники сравнить эти ячейки и вуаля тренд известен. как то так. думаю получится. После НГ будет известно. Всем добра и здоровья. и квартиру в элитном районе.
     
  7. DIYMan

    DIYMan Гуру

    Вы ошибаетесь про тренд :) На основе двух измерений никак, повторюсь, никак не сделать вывод про тренд - для этого слишком мало измерений. Вот пример, два измерения температуры: 36,6 и 36,61. Что - температура растёт, или это просто флуктуация из-за того, что пахнУло горячим ветерком на датчик?

    Тренд выявляется на более длинной серии измерений, плюс надо отбрасывать флуктуации.
     
    CYITEP_BAC9I нравится это.
  8. ImrDuke

    ImrDuke Гик

    Это зря - там те же самые китайцы продают, только дороже...
     
    CYITEP_BAC9I нравится это.
  9. ostrov

    ostrov Гуру

    В любом случае выводы делаются на основе множества данных, из которых отсеиваются ошибочные (той же медианой) и формируется зависимость. Температура не меняется моментально, поэтому замеров можно наделать море.
     
    CYITEP_BAC9I нравится это.
  10. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    Спасибо всем 33
    да вы правы 2х измерений мало, можно нарваться на ложный сигнал в момент имерения. тогда я сделаю массив из 10 ячеек!(а если щас еще тяпну раствора этилового спирта то и 20. но завтра!:)) каждую секунду по 1 измерению и прошу прощения "паровозиком" сдвигать эти данные в массиве. в 9 ячейки будут самые свежие данные , в 0. 10 (или более секунд назад). затем найду среднее первых 5 ячеек и среднее 5 последних .:):):):):) и :p:p:p:p:p. это среднее сравнивать. думаю что такая выборка избавит от ложных всплесков.
    Но все это завтра!!! (в худшем случае через неделю, это как праздник пройдет)
    А сейчас Всем Отличного праздника!
     
  11. DIYMan

    DIYMan Гуру

    Во-первых - с наступающим! Во-вторых, про алгоритм тренда показаний температуры:

    1. Вводим понятие флуктуации, скажем, в одну десятую градуса;
    2. Читаем показание с датчика, если ошибка чтения - ничего не делаем;
    3. Если предыдущее показание с учётом флуктуации не отличается от текущего - отбрасываем предыдущее из архива;
    4. Сохраняем текущее в архив;
    5. Если в архиве по каким-то признакам (кол-во записей, прошедшее время, пр.) накопилось достаточно информации для анализа, то анализируем тренд: если сумма разниц между соседними показаниями больше нуля - температура растёт, если меньше - падает.

    На примере, в псевдокоде:

    Код (C++):
    array sensorsData; // массив показаний
    #define COUNT_OF_ARCHIVE 5 // кол-во архивных записей, нечётное число
    #define FLUCTUATION .1 // флуктуация в одну десятую градуса

    void loop()
    {
        // читаем температуру
        data sensorData = sensor.getTemperature();
       
        trend currentTrend = trend.Nothing; // текущий тренд - без изменений
       
        // если уже есть данные
        if(sensorsData.size())
        {
                // получаем предыдущее показание
                data previousData = sensorsData[sensorsData.size()-1];
               
                // создаём на его основе интервал с учётом флуктуации
                data beginInterval = previousData - FLUCTUATION;
                data endInterval = previousData + FLUCTUATION;
               
                // если текущее показание вписывается в интервал
                if(sensorData >= beginInterval && sensorData <= endInterval)
                    sensorsData[sensorsData.size()-1] = sensorData; // перезаписываем его вместо последнего
                else
                    sensorsData.push(sensorData);
        }
        else // не было показаний, просто сохраняем
            sensorsData.push(sensorData);
       
        if(sensorsData.size() >= COUNT_OF_ARCHIVE) // если набрали достаточное кол-во показаний, то
        {
            // получаем сумму разниц двух соседних значений
            int sum = 0;
            for(size_t i=1;i<sensorsData.size();i++)
            {
                sum +=  sensorsData[i] - sensorsData[i-1];
            }
           
            if(sum > 0) // если сумма больше нуля - температура растёт
                currentTrend = trend.Ascending;
            else
                if(sum < 0) // если меньше нуля - уменьшается
                    currentTrend = trend.Descending;
           
            sensorsData.remove(0); // удаляем элемент с головы
        }
       
    }
    Писал навскидку, критикуйте ;) Пойнт в том, что подобный подход призван корректно работать на длительном промежутке времени, сглаживая периодические перепады температур, например, на выборке показаний "10 20 10 20 10" алгоритм покажет, что изменений нет. На выборке показаний "10 20 10" и COUNT_OF_ARCHIVE == 3 также покажет, что изменений нет. На выборке показаний "10 20 30" покажет тренд роста, на показаниях "10 15 5" покажет тренд падения.

    Ессно, всё это навскидку писалось, мог где и напороть, но суть, надеюсь, передал верно.
     
    Securbond, ИгорьК и CYITEP_BAC9I нравится это.
  12. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    спасибо !!! С праздником!
    Проверять завтра буду
    Всем замечательно отметить Новый Год!!!
     
  13. ostrov

    ostrov Гуру

    Я бы делал измерения партиями, например по 5 штук с некоторым периодом. Из этих пяти брал медиану, которая тем и хороша, что автоматом отсеивает всплески. И вот из таких медиан уже строил график. Вероятность того, что измерения будут близки к ральным, весьма велика.
     
    ИгорьК и CYITEP_BAC9I нравится это.
  14. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    спасибо всем. сажусь все переваривать.
    DIYMan
    интересный подход. смысл уловил. попробую адаптировать для ардуиновского IDE. пока горошочек только начинает варить но компилятор мне тут ультиматум ставит что float array[] это фигня и дожен быть только типа int. видно мало еще минералки выпил. пойду дальше мозги разминать. Всех с прошедшим НГ !
     
  15. CYITEP_BAC9I

    CYITEP_BAC9I Гик

    DIYMan исче раз спасибо. разобрался с вашим способом. очень оригинальных подход. плохо что компилятор ардуиновский не понимает что такое контейнер #include <queue> (. в оригинальном С++ коде все прекрасно работает даже с float. будет время постараюсь адаптировать к ардуиновскому IDE
     
  16. DIYMan

    DIYMan Гуру

    Не обязательно использовать контейнеры, можно на чистом С++ сделать - динамическими массивами ;) Как-то так, навскидку (на ошибки не проверял, компилировать не пробовал):
    Код (C++):
    float* sensorsData = NULL; // массив показаний
    int countOfData = 0;

    #define COUNT_OF_ARCHIVE 5 // кол-во архивных записей, нечётное число
    #define FLUCTUATION .1 // флуктуация в одну десятую градуса

    void loop()
    {
        // читаем температуру
        data sensorData = sensor.getTemperature();
     
        trend currentTrend = trend.Nothing; // текущий тренд - без изменений
     
        // если уже есть данные
        if(countOfData)
        {
                // получаем предыдущее показание
                float previousData = sensorsData[countOfData-1];
             
                // создаём на его основе интервал с учётом флуктуации
                float beginInterval = previousData - FLUCTUATION;
                float endInterval = previousData + FLUCTUATION;
             
                // если текущее показание вписывается в интервал
                if(sensorData >= beginInterval && sensorData <= endInterval)
                    sensorsData[countOfData-1] = sensorData; // перезаписываем его вместо последнего
                else
                {
                    countOfData++;
                    float* newData = new float[countOfData];
                    if(sensorsData)
                    {
                        memcpy(newData,sensorsData,sizeof(float)*(countOfData-1));
                        delete [] sensorsData;
                    }
                   
                    sensorsData = newData;
                    sensorsData[countOfData-1] = sensorData;
                }
        }
        else // не было показаний, просто сохраняем
        {
            countOfData++;
            float* newData = new float[countOfData];
            if(sensorsData)
            {
                memcpy(newData,sensorsData,sizeof(float)*(countOfData-1));
                delete [] sensorsData;
            }
           
            sensorsData = newData;
            sensorsData[countOfData-1] = sensorData;
        }
     
        if(countOfData >= COUNT_OF_ARCHIVE) // если набрали достаточное кол-во показаний, то
        {
            // получаем сумму разниц двух соседних значений
            int sum = 0;
            for(size_t i=1;i<countOfData;i++)
            {
                sum +=  sensorsData[i] - sensorsData[i-1];
            }
         
            if(sum > 0) // если сумма больше нуля - температура растёт
                currentTrend = trend.Ascending;
            else
                if(sum < 0) // если меньше нуля - уменьшается
                    currentTrend = trend.Descending;
         
            // удаляем элемент с головы
            float* newData = new float[countOfData-1];
            memcpy(newData, &(sensorsData[1]),sizeof(float)*(countOfData-1));
            delete [] sensorsData;
            sensorsData = newData;
            countOfData--;
        }
     
    }
     
    CYITEP_BAC9I нравится это.