Сломал мозг: теряю 0.1 градус

Тема в разделе "Arduino & Shields", создана пользователем ИгорьК, 3 мар 2016.

  1. ИгорьК

    ИгорьК Гуру

    Задача: управлять температурой в пределах 37-39 градусов.
    Условия: питание устройства может иногда теряться.

    Решаю. При изменении управляемой температуры пишу ее в EEPROM, если питание пропадает - при включении считываю из EEPROM.

    Код.
    Код (C++):
    // на входе есть double переменная setTemp = 37.7

    double tsetTemp = ((setTemp - 30.0)*10.0);
    Serial.print("tsetTemp = ");
    Serial.println(tsetTemp);
         
    byte ttsetTemp = (byte)tsetTemp;
    Serial.print("ttsetTemp = ");
    Serial.println(ttsetTemp);
    EEPROM.update(1,ttsetTemp);

    // раньше был вот этот код, но я его "разложил" на тот что сверху      
    // EEPROM.update(1,(byte)((setTemp - 30.0)*10.0));
    А теперь картинка как это работает:
    11334422.jpg

    При старте EEPROM читается:
    Код (C++):
    setpoint = ((double)(EEPROM.read(1)))/10.0+30.0;
    и в нем, естественно, температура на 0.1 градус меньше.

    Видно, что единица теряется при преобразовании:
    Код (C++):
    byte ttsetTemp = (byte)tsetTemp;
    Почему?????????
     
  2. geher

    geher Гуру

    Особенности работы с плавающей точкой.
    Скорее всего в tsetTemp оказывается что-то вроде 76.99999999 (точнее 0.7699999999E2, ибо точка плавающая).
    При выводе с помощью println оно корректно округляется.
    При присваивании переменной типа byte просто отбрасывается дробная часть.
    Чтобы было однозначно правильно, в таком случае лучше преобразовывать не простым присваиванием, а функцией round().

    byte ttsetTemp = round(tsetTemp);
     
    ИгорьК нравится это.
  3. Megakoteyka

    Megakoteyka Оракул Модератор

    Пишите в память байты исходного double, тогда точно ничего теряться не будет.
    Код (C++):
    void EEPROM_writeDouble(int ee, double value)
    {
       byte* p = (byte*)(void*)&value;
       for (int i = 0; i < sizeof(value); i++)
           EEPROM.write(ee++, *p++);
    }

    double EEPROM_readDouble(int ee)
    {
       double value = 0.0;
       byte* p = (byte*)(void*)&value;
       for (int i = 0; i < sizeof(value); i++)
           *p++ = EEPROM.read(ee++);
       return value;
    }
    Будет занимать 4 или 8 байт в зависимости от платы, см. sizeof(value).
     
    ИгорьК нравится это.
  4. ИгорьК

    ИгорьК Гуру

    Ясно. Лучше всего обойтись без плавающей точки. Она нужна только при отправке температуры в PID, поскольку библиотека так написана.
    А до этого буду работать с int: не 37.7, а 377.