Управление для аквариума

Ajlex71
Offline
Зарегистрирован: 10.12.2016

Помогите новичку. Пишу простой скетч первый раз вчера и сегодня. Было много проблем на программном уровне, разобрался. Скетч работает, но есть одна непонятка.

Вот скетч

[code]
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 rtc;
const int rele_sv = 3;
const int rele_bul = 2;
const int button_bul = 9;
//int btn_bul = 0;
unsigned long nowBtn = 0;


//char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
  while (!Serial); // for Leonardo/Micro/Zero

  Serial.begin(57600);

  pinMode(rele_sv, OUTPUT);
  digitalWrite(rele_sv, LOW);   
  pinMode(rele_bul, OUTPUT);
  digitalWrite(rele_bul, LOW); 
  pinMode (button_bul, INPUT);  

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
   // rtc.adjust(DateTime(2017, 2, 24, 17, 32, 0));
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    //rtc.adjust(DateTime(2016, 2, 24, 17, 17, 0));
  }
  

}

void loop () {

  DateTime now = rtc.now(); //Получили текущее время
  int btn_bul =  digitalRead(button_bul); //Считали состояние кнопки
    
    Serial.print(now.hour(), DEC); // Время в монитор
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    

  if ((now.hour() == 6) && (now.minute() == 50)) { //Если 6:50 включили реле света и компресора
    digitalWrite(rele_sv, LOW);    
    digitalWrite(rele_bul, LOW);    
  };

    
  if ((now.hour() == 21) && (now.minute() == 0)) { // Если 21:00 выключили реле света и компресора
    digitalWrite(rele_sv, HIGH);
    digitalWrite(rele_bul, HIGH); 
  };
  

//  if (((now.hour() >= 6) && (now.minute() >= 50)) || ((now.hour() <= 21) && (now.minute() <= 0))) { // Здесь не сработала  проверка нажатия кнопки только между 6:50 и 21:00 
    if (btn_bul == 1) {  //Если нажата кнопка должны получить время в секундах с 0:0:0                                                                               
       nowBtn = (int(now.hour())*3600) + (now.minute()*60) + now.second();    //но получается какая то хрень в 19:54:48 получаем 6149
    };
//  };

  if ((((int(now.hour()) * 3600) + (now.minute() * 60) + now.second())-nowBtn)<1200) {  // если разница между полученным значением и текущим меньше 20 минут отключаем компрессор (кормим рыбок)
        digitalWrite(rele_bul, HIGH); 
  }
  else {
        digitalWrite(rele_bul, LOW);      
  };
  
  Serial.print(btn_bul); // выводим статус нажатия кнопки
  Serial.println();
  Serial.print(nowBtn);  //выводим  значение переменной nowBtn
  Serial.println();
  delay(3000);
  
}

[/code]

Непонятно как считается переменная nowBtn, в коментах описано.

вот пример

какая то хрень в 19:54:48 получаем 6149
   

по идее должно быть 19*3600+54*60+48=71697, а в монитор выводится 6149. Не могу понять почему.

Помогите.

 

Волшебник
Offline
Зарегистрирован: 22.12.2016

Переполнение интегер , макс. 64536. Попробуйте

     nowBtn = (now.hour()*3600UL) + (now.minute()*60) + now.second();    //но 

Ajlex71
Offline
Зарегистрирован: 10.12.2016

Спасибо помогло.

Теперь бы еще понять как такое произошло на будущее.

Я так понимаю UL преобразует к unsigned long

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

2Волшебник: 32767. А unsigned int 65535.

Волшебник
Offline
Зарегистрирован: 22.12.2016

Ajlex71 пишет:

Спасибо помогло.

Теперь бы еще понять как такое произошло на будущее.

Я так понимаю UL преобразует к unsigned long

На будущее запомните что размер переменных ограничен, у байта 255 а у 16-битной 65535 ( как мне правильно заметили)

И ещё у авр-гсс есть старый хитрый баг, если сделать uint32_t proizvedenie = (uint16_t A) x (uint16_t B) то результат с больший долей вероятности будет неверный, потому что сначала произведение посчитается как 16-бит а уже потом присвоится на 32-бита. Поэтому, если есть сомнения всегда лучше указать компилёру 32-битную операцию, путём подстановки UL в названии константы, или принудителным кастингом (uint32_t) для переменной.   uint32_t proizvedenie = ((uint32_t)(uint16_t A)) x (uint16_t B)

Волшебник
Offline
Зарегистрирован: 22.12.2016

andriano пишет:

2Волшебник: 32767. А unsigned int 65535.

Верно, но у него там <по идее должно быть 19*3600+54*60+48=71697, а в монитор выводится 6149. > Видно что 65000 где-то нехватает, и если мы говорим о недостаче, то она и будет равна 65536 при переполнении 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

Общее же правило - результат зависит от типа операндов. Правда, есть исключение: промежуточные результаты всегда не менее 16 разрядов. Поэтому, например, можно сдвинуть байт на 8 разрядов и они не пропадут.

Волшебник
Offline
Зарегистрирован: 22.12.2016

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

16-bit x 16-bit Routines
Signed/unsigned multiply 16 x 16 = 16 bits 6 (9)
Unsigned multiply 16 x 16 = 32 bits 13 (17)
Signed multiply 16 x 16 = 32 bits 15 (19)
Signed multiply-accumulate 16 x 16 += 32 bits 19 (23)
Fractional signed multiply 16 x 16 = 32 bits 16 (20)
Fractional signed multiply-accumulate 16 x 16 += 32 bits 21 (25)
Unsigned multiply 16 x 16 = 24 bits 10 (14)
Signed multiply 16 x 16 = 24 bits 10 (14)
Signed multiply-accumulate 16 x 16 += 24 bits 12 (16)
 
У них там 9 вариантов, и по мне 16х16 = 16 надо вообще запретить, какому идиоту нужно умножение которое почти всегда врёт? Если только это не хорошо придуманая диверсия
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

1. Ну, как бы приведенные 9 вариантов - это как раз запротоколированное творчество программистов. А по стандарту 16х16 допускается быть 16. Т.е. если программисту нужно, чтобы было 32, он (и именно он) должен об этом позаботиться.

2. Тогда 32х32 тоже запретить.

3. Весь Си - одна большая диверсия.

Волшебник
Offline
Зарегистрирован: 22.12.2016

andriano пишет:

 А по стандарту 16х16 допускается быть 16. Т.е. если программисту нужно, чтобы было 32, он (и именно он) должен об этом позаботиться.

На фик такой стандарт, он идиотский. Язык С тут ни чего не решает, авр-гсс выбирает сам какую процедуру использовать.  И какому правилу азыка С-99/ 2011 следовать а какому нет.

Кстати, там есть вторая диверсия, если принудить выполнить операцию 32 х 16 кастингом одного из 16-битных операндов, то будет выполнена 32х32 - а она как минимум в 2.5 раза более трудоёмка. На ардуино 5 микросекунд, я себе макрос на ассемблере писал 32х16 по времени 2 мксек.  А то БПФ медленно очень выполнялось.

Logik
Offline
Зарегистрирован: 05.08.2014

/// Я честно скажу, стндарты не читаю

))) Наш чел, понимаю, сам такой, стандарт читать - последнее дело если уже ниче не помогает, а такого пока ещё не было.. (Даже знаю какие форумчане нас заплевывать будут, но то их проблемы)

Почему такая лажа с этой разрядностью?! Так Си он кросплатформенный, должен давать одинаковый результати на 8-и и на 64-х разрядной машине. А всем не угодиш. Что скомпилится коротко на 8-и будет громоздко на 64-х и наоборот. Потому такое компромисное решение. Увы, нет идеала.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Logik пишет:

Почему такая лажа с этой разрядностью?! Так Си он кросплатформенный, должен давать одинаковый результати на 8-и и на 64-х разрядной машине.

Отнюдь.

Разработчики явно указывают, что не должен.

И в то же время указывают, что существуют средства языка, обеспечивающие кроссплатформенность, но использование именно этих средств - выбор программиста. Авторы языка явно указывают, что язык рассчитан на квалифицированного программиста, который знает, что делает. Следовательно, язык не отслеживает и не прощает глупые ошибки. В том числе и с неправильным применением типов.