Принять значение температуры с контроллера на контроллер

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Здравствуйте.

Имеется контроллер Arduino UNO. К нему (на нём) - крепится шилд TFT LCD 2.4". Так как TFT шилд занимает практически все пины на UNO, пришлось думать, как заиметь свободные пины для выполнения команд (замыкания/размыкания реле, считывание данных с датчиков температуры). В итоге - было принято решение подключить дополнительно второй контроллер Arduino nano, через пины RX и TX. В итоге эксперимент удался - простым примером удалось управлять светодиодом (пин 13) как с UNO на nano, так и наоборот. Далее - подключил датчик температуры DS18B20 к контроллеру nano. Значения температуры фиксируются. В порт посылаются. Это видно в IDE при просмотре порта.

Вопрос: как лучше принять это значение температуры на UNO и вывести на дисплее TFT?

Вот такой код сейчас загружен на контроллер nano (из стандартного примера попытался поубирать лишнее, но так и не знаю, как работает. Но работает.):

#include <OneWire.h>
OneWire  ds(2);  // on pin 2 (a 4.7K resistor is necessary)

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  byte addr[8];
  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }
   if (OneWire::crc8(addr, 7) != addr[7]) {
      return;
  }
   if (addr[0]==0x28){
    
  } else {
    Serial.println("Device is not a DS18B20.");
    return;
  }
   
  ds.reset();
  ds.select(addr);
  ds.write(0x44);        // start conversion, with parasite power on at the end
  delay(1000);     // maybe 750ms is enough, maybe not
  
  ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  byte i;
  byte data[9];
  for ( i = 0; i < 9; i++) {
    data[i] = ds.read();
    }
   if (OneWire::crc8(data, 8) != data[8]) {
      return;
  }

int16_t raw = (data[1] << 8) | data[0];
  if (0) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }

float celsius;
  celsius = (float)raw / 16.0;
  Serial.println(celsius);
  }

На приёмнике (UNO с TFT шилдом) пишу код типа:

tft.print(Serial.read());

в надежде получить значение температуры. Но ничего не получается. Не удаётся на экран TFT вывести значение температуры, которое принимает nano от датчика DS18B20.

Araris
Araris аватар
Онлайн
Зарегистрирован: 09.11.2012

Почитайте для начала, как на самом деле работает Serial.read(), и как правильно принять данные из Serial.

b707
Онлайн
Зарегистрирован: 26.05.2017

PPeterr пишет:

Имеется контроллер Arduino UNO. К нему (на нём) - крепится шилд TFT LCD 2.4". Так как TFT шилд занимает практически все пины на UNO, пришлось думать, как заиметь свободные пины для выполнения команд (замыкания/размыкания реле, считывание данных с датчиков температуры). В итоге - было принято решение подключить дополнительно второй контроллер Arduino nano, через пины RX и TX.

Отличный пример того, что использование шилдов ведет к атрофии мозга.

Сначала поставить шилд, а потом городить второй контроллер, потому что пинов больше нет :)  - это удаление аппендицита через жопу.

Автор!  О том, чтобы (о. боже!) снять TFT-шилд с Уно и подключить его несколькими проводами - вы не подумали?

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

b707, это неспортивно.  Каждому даччику - по Ардуине!!!

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

b707, дело не в том, что сам шилд занимает место ("мамы" пинов на UNO), а в том, что электрически свободным остаётся всего один аналоговый вход/выход (некоторые его используют для регулировки яркости подсветки, но с вмешательством в плату TFT).

Я поискал информацию про "Serial.read()". Вроде данная функция читает байты (но с символами "H" и "L" работало, когда я управлял светодиодом) и вроде как - цифры считывать не может. Вот что прочитал: "Если обмены ведутся в виде текста, то все ОК. А вот если передаются именно байты, возникает проблема: -1 это 0xFF, т.е. 255. такой же байт, как и все остальные. поэтому нужно сперва вызывать available ()." - http://pashkevich.me/article/6.html

Там же на той страничке написано, что чтобы получить именно численное значение с плавающей точкой - можно воспользоваться стандартной функцией "Serial.parseFloat()". Буду пытаться по этому пути. Но нет никакого представления. Всё наугад.

 

Кстати, по поводу того, что TFT занимает UNO - мне уже пришлось подпаять дополнительные "папы" пинов RX, TX, GND, 5V, 5V - чтобы подключить одновременно датчик, UNO и nano от одного USB шнура.

b707
Онлайн
Зарегистрирован: 26.05.2017

про "Serial.read()", байты и цифры написан бред, простите. Цифры в компьютере хранятся в виде байтов.... если вы не знали.

b707
Онлайн
Зарегистрирован: 26.05.2017

замечание не по делу... заметки на полях.

я поражен. сколько пинов нужно для работы этого дурацкого экрана... судя по картинке, он действительно занимает почти все выводы Уно. Это какой-то бред, неужели нельзя данные по SPI  передавать? нафига еще 8 пинов занимать?

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

b707, можно хоть по Serial, вопрос в скорости: при SPI 4 MHz - максимум 3 FPS по самым оптимистичным оценкам. При 1 MHz - соответственно менее 1 FPS.

Но в любом случае можно использовать A4 и A5 либо как I2C, либо как CS для двух SPI устройств. Все лучше чем Serial.

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Добрый день.

Вчера вечером довольно долго возился с проектом. Получил некоторый результат. В контроллере nano (он у меня является получателем информации с датчика температуры и передатчиком этой информации на UNO) в итоге был прописан следующий код (скопировал у кого-то здесь же на форуме):

#include <OneWire.h>
OneWire ds(2);
#define START_CONVERT 0
#define READ_TEMP 1

void setup() {
  Serial.begin(9600);
  tempProcess(START_CONVERT);//конвентируем
  delay(1000);
}

void loop() {
  int temp=0;
  temp= tempProcess(READ_TEMP);//читаем Т
  tempProcess(START_CONVERT);
  // Serial.print(temp); //если в целых *С
  Serial.println(temp);
  delay(3000);//только для примера
}
//============================== 
int tempProcess(boolean ch){
  int t=0; 
  if(!ch){
    ds.reset(); 
    ds.write(0xCC);
    ds.write(0x44);
  }
  else{
    ds.reset();
    ds.write(0xCC);
    ds.write(0xBE);
    t= ds.read(); 
    t = t | (ds.read()<<8); 
    //return  t>>4;//целые *C, десятые отброшены
    //return  (t+8)>>4;//целые *С с округлением
    return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)
    }
}

Контроллер nano посылает в порт значение температуры, умноженное на 10.

В "приёмнике" (UNO) я добавил следующую часть кода, относящуюся к выводу значения информации на дисплей (весь код нет смысла выкладывать, это только то, что влияет у меня на вывод температуры на дисплее):


float incomingInt = 0;//для получения температуры с датчика

void setup(void)
{
    Serial.begin(9600);
    Serial.setTimeout(3000);//для получения температуры с датчика
}

void loop()
{
//для получения температуры с датчика
if (Serial.available() > 0) 
   {
      incomingInt = Serial.parseInt();
         }
...         
else if (a==0)
//вывод температуры с датчика на главном экране
  { tft.setCursor(15, 5); // Устанавливаем курсор (X = , Y = )
   tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
   tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3 
   tft.print(incomingInt/10,1); // Выводим текст на экран, текст будет выведен с указанными выше настройками
  }
       }     

В итоге - получен некоторый результат. Значение температуры выводится на дисплей и проверяется и обновляется каждые 3 секунды (синим в верхнем левом углу экрана):

Но выявилась большая проблема - переходы по меню очень сильно стали "тормозиться". Такое впечатление, будто UNO занята каким-то процессом. При этом - когда я обрываю связь TX и RX между контроллерами - переходы по менюшкам TFT дислея работают быстро.

Помогите мне понять, в чём причина такой сильной "заторможенности" дисплея при приёме данных по температуре. И как это исправить.

Я уже и так увеличил delay в передатчике и Serial.setTimeout в приёмнике до 3000 мс - но это не помогло.

b707
Онлайн
Зарегистрирован: 26.05.2017

PPeterr пишет:

Я уже и так увеличил delay в передатчике и Serial.setTimeout в приёмнике до 3000 мс - но это не помогло.

пипец логика. Поскольку все тормозит (из-за задержек и таймаутов) - поставим задержки побольше :)

Слабо посмотреть в мануале, как связаны функции Serial.setTimeout и Serial.ParseInt? При обработке входящих данных с сериала функция  Serial.ParseInt ждет данных в течении времени, заданное Serial.setTimeout. В это время программа "висит" и ничего другого делать не может. Увеличив таймаут, вы только усугубили проблему.

Для ускорения обмена ввм надо совсем отказаться от ParseInt (поскольку даже при маленьком таймауте эта функция медленная) и обрабатывать входящие данные самостоятельно.

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Спасибо за разъяснения.

b707 пишет:

Для ускорения обмена ввм надо совсем отказаться от ParseInt (поскольку даже при маленьком таймауте эта функция медленная) и обрабатывать входящие данные самостоятельно.

Правильно ли я понимаю, что речь идёт о том, чтобы передавать байты подобно этому коду:

// скетч для Arduino, который передает данные

char str[4];

void setup() {

Serial.begin(9600);

}

void loop() {

int value=1234; // будет гораздо веселее, если это будут данные с какого-то сенсора

itoa(value, str, 10); // преобразует данные в массив символов

Serial.write(str, 4);

}

?

Пробовал вчера... почему-то не получилось. Не видит у меня IDE "itoa" как некую команду.

b707
Онлайн
Зарегистрирован: 26.05.2017

andriano пишет:

b707, можно хоть по Serial, вопрос в скорости: при SPI 4 MHz - максимум 3 FPS по самым оптимистичным оценкам. При 1 MHz - соответственно менее 1 FPS.

Интересно, а 1-3 FPS - это мало?  Данные-то сначала генерить надо. А у нас в качестве мозга - Уно. Сделайте ей канал в 10 FPS - ей передавать по нему нечего будет...

b707
Онлайн
Зарегистрирован: 26.05.2017

PPeterr пишет:

Правильно ли я понимаю, что речь идёт о том, чтобы передавать байты подобно этому коду:

неправильно.

Прочитайте внимательнее - я вам написал, что вам надо переписать прием. Передачу можете оставить как есть.

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

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

b707
Онлайн
Зарегистрирован: 26.05.2017

Простейший случай - функция atoi()

Для более сложного почитайте соседнюю тему http://arduino.ru/forum/programmirovanie/poluchenie-dannykh-po-uart-0

 

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Полазил по просторам интернета, поискал различные применения atoi().

Из того, что нашёл собрал код (касаемо приёма значения температуры):

char incoming;
char str[3];
int i=0;

void setup() {
  Serial.begin(9600);
}

void loop() {  
while (Serial.available()) {
incoming = Serial.read();
if (incoming < '0' || incoming > '9')break;
str[i] = incoming;
i++;
}
if (i > 2) //на данные отводим 3 знака
{
str[i] = 0;
i=0;
}

...
//вывод температуры с датчика DS18B20 на главном экране
  { tft.setCursor(15, 5); // Устанавливаем курсор (X = , Y = )
   tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
   tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3 
   tft.print((atoi(str))); // Выводим значение температуры

}

Доберусь до контроллеров - попробую "залить" код, в надежде что заработает. Передатчик у меня посылает в порт значение температуры умноженное на 10, например температура "23.2" посылается в порт как 232. Но мне хоть бы так получить - потом, надеюсь, разберусь с плавающей точкой.

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

b707 пишет:

Интересно, а 1-3 FPS - это мало?  Данные-то сначала генерить надо. А у нас в качестве мозга - Уно. Сделайте ей канал в 10 FPS - ей передавать по нему нечего будет...

10 FPS означает, что Уно будет заниматься только экраном, ни на что больше не реагируя, 100 мс, а 1 FPS - уже 1000 мс. О том, как выглядит обновление небольшого экранчика 1000 мс, я уже не говорю.

IMHO, если уж мы используем экран с глубиной цвета 16-18 бит, то отдать на него целиком все мощности Atmel-328 - не грех. Такая вот дешевенькая альтернатива Nextion. А если Уно при этом еще что-то должна делать, следует ограничиться однобитным, а то и вовсе текстовым экраном.

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Ура!!! У меня всё заработало (с использованием кода двумя постами выше). Проблему с выводом значения температуры с плавающей точкой решил просто, по аналогии как в одном из примеров с этого форума:

...
tft.print((atoi(str))/10);
tft.print(".");
tft.print((atoi(str))%10);

Менюшки по экрану переключаются бодро, температура выводится каждую секунду. b707, большое спасибо за подсказки, разъяснения.

Единственное, что меня смущает - датчик имеет погрешность 0.5 градуса. Может, нет смысла выводить каждые 0.1 градуса. Хочу сделать округление до 0.5 градуса. Может, подскажите, как лучше реализовать? (но шаг в 1 градус - это многовато для меня)

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Следующая моя задача - принимать одновременно значения температур на TFT LCD от трёх датчиков температур. Подскажите, код для "передатчика"  в сообщении №8 можно ли как-то по простому модифицировать под передачу температуры с трёх датчиков?

Или же необходимо полностью изменить код, аналогично как в этом сообщении для "Вариант с 2-мя датчиками"?

b707
Онлайн
Зарегистрирован: 26.05.2017

PPeterr пишет:

Следующая моя задача - принимать одновременно значения температур на TFT LCD от трёх датчиков температур. Подскажите, код для "передатчика"  в сообщении №8 можно ли как-то по простому модифицировать под передачу температуры с трёх датчиков?

Да можно особо не модифицировать. Добавьте перед цифрами букву, обозначающую каждый датчик - да и все.  Например, пусть "А254" это будет 25.4 гр с первого датчика, а "В187" - 18.7 со второго...Ну и в приемник придется добавить код для извлечения букв

 

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Думал-думал, искал-искал. Вот какой получается у меня код:


#include <OneWire.h>
OneWire ds(2);
#define START_CONVERT 0
#define READ_TEMP 1
byte addr1[8]={0x28,0x04,0x13,0x80,0x06,0x00,0x00,0xF8};
byte addr2[8]={0x28,0x47,0x1F,0x5C,0x06,0x00,0x00,0xE6};
byte addr3[8]={0x28,0x47,0x1F,0x5C,0x06,0x00,0x00,0xE7};

void setup() {
  Serial.begin(9600);
  //конвентируем
  tempProcess1(START_CONVERT);
  tempProcess2(START_CONVERT);
  tempProcess3(START_CONVERT);
  delay(1000);
}

void loop() {
  int temp1=0;
  int temp2=0;
  int temp3=0;
  temp1=tempProcess1(READ_TEMP);//читаем Т1
  temp2=tempProcess2(READ_TEMP);//читаем Т2
  temp3=tempProcess3(READ_TEMP);//читаем Т3
  
  tempProcess1(START_CONVERT);
  tempProcess2(START_CONVERT);
  tempProcess3(START_CONVERT);
  
  Serial.print("A");
  Serial.println(temp1);
  Serial.print("B");
  Serial.println(temp2);
  Serial.print("C");
  Serial.println(temp3);
  
  delay(1000);//только для примера
}
//============================== 
int tempProcess1(boolean ch){
  int t=0;
  if(!ch){
    ds.reset(); 
    ds.write(0xCC);
    ds.write(0x44);
  }
  else{
    ds.reset();
    ds.select(addr1);
    ds.write(0xBE);
    t = ds.read() | (ds.read()<<8);
    return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)
    }
}

int tempProcess2(boolean ch){
  int t=0;
  if(!ch){
    ds.reset(); 
    ds.write(0xCC);
    ds.write(0x44);
  }
  else{
    ds.reset();
    ds.select(addr2);
    ds.write(0xBE);
    t = ds.read() | (ds.read()<<8);
    return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)
    }
}

int tempProcess3(boolean ch){
  int t=0;
  if(!ch){
    ds.reset(); 
    ds.write(0xCC);
    ds.write(0x44);
  }
  else{
    ds.reset();
    ds.select(addr3);
    ds.write(0xBE);
    t = ds.read() | (ds.read()<<8);
    return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)
    }
}

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

Также интересует, насколько рационально записан код, нет ли чего-то лишнего. Адрес каждого из трёх датчиков - планируется прописать вручную.

sadman41
Offline
Зарегистрирован: 19.10.2016

PPeterr пишет:
Также интересует, насколько рационально записан код, нет ли чего-то лишнего.

Посмотрел. 2/3 кода - лишние.

b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41 пишет:

PPeterr пишет:
Также интересует, насколько рационально записан код, нет ли чего-то лишнего.

Посмотрел. 2/3 кода - лишние.

поддерживаю.

PPeterr, нафига отдельная процедура для каждого датчика? А если их будет 10? У вас в коде процедуры меняется только адрес - так передавайте его в виде параметра

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Спасибо. Подскажите, что убрать. "1, 2, 3" относящиеся к номеру датчика, как-то через переменную задать, типа "i++"?

b707
Онлайн
Зарегистрирован: 26.05.2017

PPeterr пишет:

Спасибо. Подскажите, что убрать. "1, 2, 3" относящиеся к номеру датчика, как-то через переменную задать, типа "i++"?

Нет, вы меня не поняли. Датчики можете перебирать как хотите - по одному как сейчас или в цикле - это несущественно. Главное - вам надо избавится от трех дублирующих процедур TempProcess1 , 2 и 3. Процедура для каждого датчика должна вызываться одна и та же, а адрес конкретного датчика ей нужно передавать в виде параметра.

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Пытаюсь сделать то, о чём говорите.

#include <OneWire.h>
OneWire ds(2);
#define START_CONVERT 0
#define READ_TEMP 1
byte addr1[8]={0x28,0x04,0x13,0x80,0x06,0x00,0x00,0xF8};
byte addr2[8]={0x28,0x47,0x1F,0x5C,0x06,0x00,0x00,0xE6};
byte addr3[8]={0x28,0x47,0x1F,0x5C,0x06,0x00,0x00,0xE7};

void setup() {
  Serial.begin(9600);
  //конвентируем
  tempProcess(START_CONVERT,addr1);
  tempProcess(START_CONVERT,addr2);
  tempProcess(START_CONVERT,addr3);
  delay(1000);
}

void loop() {
  int temp1=0;
  int temp2=0;
  int temp3=0;
  
  temp1=tempProcess(READ_TEMP, addr1);//читаем Т1
  tempProcess(START_CONVERT,addr1);
  Serial.print("A");
  Serial.println(temp1);

  temp2=tempProcess(READ_TEMP, addr2);//читаем Т2
  tempProcess(START_CONVERT,addr2);
  Serial.print("B");
  Serial.println(temp2);

  temp3=tempProcess(READ_TEMP, addr3);//читаем Т3
  tempProcess(START_CONVERT,addr3);
  Serial.print("C");
  Serial.println(temp3);
  
  delay(1000);//только для примера
}
//============================== 
int tempProcess(boolean ch, byte sensor){
  int t=0;
  if(!ch){
    ds.reset(); 
    ds.write(0xCC);
    ds.write(0x44);
  }
  else{
    ds.reset();
    ds.select(sensor);
    ds.write(0xBE);
    t = ds.read() | (ds.read()<<8);
    return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)
    }
}

IDE выдаёт ошибки типа:

"warning: invalid conversion from 'byte* {aka unsigned char*}' to 'byte {aka unsigned char}' [-fpermissive]

note: initializing argument 2 of 'int tempProcess(boolean, byte)'"

Не знаю, что это означает. На ваш взгляд - код должен быть рабочим?

kalapanga
Offline
Зарегистрирован: 23.10.2016

41-я строка наверное такая должна быть:

int tempProcess(boolean ch, byte *sensor) {

 

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

kalapanga, спасибо. Помогло. Ошибки в IDE исчезли. Вечером надеюсь "залить" код на контроллер.

sadman41
Offline
Зарегистрирован: 19.10.2016

PPeterr пишет:

kalapanga, спасибо. Помогло. Ошибки в IDE исчезли. Вечером надеюсь "залить" код на контроллер.

Далее ознакомьтесь с разделом "Multiple-device commands" на https://playground.arduino.cc/Learning/OneWire и запускайте конверсию одновременно на всех термометрах. Это и время выполнения сэкономит и объем кода (немного).

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

PPeterr пишет:

Единственное, что меня смущает - датчик имеет погрешность 0.5 градуса. Может, нет смысла выводить каждые 0.1 градуса. Хочу сделать округление до 0.5 градуса. Может, подскажите, как лучше реализовать? (но шаг в 1 градус - это многовато для меня)

Умножать на 2 и округлять до целого.

При выводе на экран - делить на 2, а если число нечетное (до деления), дописывать к нему ".5".

b707
Онлайн
Зарегистрирован: 26.05.2017

я бы не стал округлять. Погрешность в 0.5 гр вовсе не означает, что цифры после точки бесполезны. Как пример - часы могут отставать или спешить на несколько минут, но это не значит, что секундная стрелка на них не нужна...

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Здравствуйте.

Вчера подключил ещё два датчика температуры (в итоге - получилось три штуки - именно столько мне достаточно для проекта).

С помощью стандартного примера из "OneWire" - просмотрел адрес каждого конкретного датчика. Переписал в код, составленный вчера здесь. Вот какой получился в итоге код для контроллера nano (он у меня принимает значения температур с датчиков и передаёт эти значения на UNO):

#include <OneWire.h>
OneWire ds(2);
#define START_CONVERT 0
#define READ_TEMP 1
byte addr1[8]={0x28,0xFF,0x94,0xAE,0xA0,0x16,0x03,0x0E};
byte addr2[8]={0x28,0xFF,0x48,0x83,0xA0,0x16,0x04,0x1F};
byte addr3[8]={0x28,0xFF,0xEC,0x98,0xA0,0x16,0x04,0x9F};

void setup() {
  Serial.begin(9600);
  //конвентируем
  tempProcess(START_CONVERT,addr1);
  tempProcess(START_CONVERT,addr2);
  tempProcess(START_CONVERT,addr3);
  delay(1000);
}

void loop() {
  int temp1=0;
  int temp2=0;
  int temp3=0;
 
  temp1=tempProcess(READ_TEMP, addr1);//читаем Т1
  tempProcess(START_CONVERT,addr1);
  Serial.print(temp1);
  Serial.println("A");

  temp2=tempProcess(READ_TEMP, addr2);//читаем Т2
  tempProcess(START_CONVERT,addr2);
  Serial.print(temp2);
  Serial.println("B");

  temp3=tempProcess(READ_TEMP, addr3);//читаем Т3
  tempProcess(START_CONVERT,addr3);
  Serial.print(temp3);
  Serial.println("C");
 
  delay(1000);//только для примера
}
//==============================
int tempProcess(boolean ch, byte *sensor){
  int t=0;
  if(!ch){
    ds.reset();
    ds.write(0xCC);
    ds.write(0x44);
  }
  else{
    ds.reset();
    ds.select(sensor);
    ds.write(0xBE);
    t = ds.read() | (ds.read()<<8);
    return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)
    }
}

Далее возился с кодом на контроллере UNO (довольно долго). У меня получилось распознавать от какого датчика значение пришло - только когда буква "A, B, C" были в конце передаваемых по Serial значений температур. Вот что залито на UNO (касаемо приёма и вывода на LCD значения температуры):

char incoming;
char str[3];
int i=0;
int temp1;
int temp2;
int temp3;

void setup(void)
{
    Serial.begin(9600);
   }

void loop()
{
while (Serial.available()) {
incoming = Serial.read();
if (incoming < '0' || incoming > '9')break;
str[i] = incoming;
i++;
}
if (i > 2) //на данные отводим 3 знака
{
  if (incoming == 'A'){
  temp1=atoi(str);}
  if (incoming == 'B'){
  temp2=atoi(str);}
  if (incoming == 'C'){
  temp3=atoi(str);}
str[i] = 0;
i=0;
}

...
  {tft.setCursor(15, 5); // Устанавливаем курсор (X = , Y = )
   tft.setTextColor(BLACK, WHITEGREY); // Указываем цвет текста
   tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3 
   tft.print(temp1/10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
   tft.setCursor(80, 5); // Устанавливаем курсор (X = , Y = )
   tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
   tft.print(temp2/10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
   tft.print(".");
   tft.print(temp2%10);
   tft.setCursor(180, 5); // Устанавливаем курсор (X = , Y = )
   tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
   tft.print(temp3/10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
   }
        } 

Одно значение температуры (датчика №2) вывел с десятыми. Остальные - мне для проекта достаточно показаный целых значений градуса. Не знаю, есть ли смысл экономить память за счёт этого. Буду думать по окончании проекта - в зависимости от того, сколько останется свободной памяти на контроллерах.

На фото в начале сообщения - три значения температур выводятся в верху экранчика и меняются (обновляются) раз в секунду.

Прошу вас просмотреть выложенный в этом сообщении программный код и указать на грубые ошибки, если они есть.

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

неоптимально два раза читать, чтение - процедура долгая.  ds.read() что отдает, не помню, char или int? 

 t = ds.read() | (ds.read()<<8);

надо бы 

t = ds.read(); t = (t<<8) | (t & 0xFF);

sadman41
Offline
Зарегистрирован: 19.10.2016

Это ему не особо поможет. ScratchPad он читает не полностью, CRC не проверяет,  разрешение датчика не устанавливает/проверяет, undefined low bits не сбрасывает в зависимости от разрешения и пр. и др.

Надеюсь, что это не какой-нибудь датчик газового котла будет... иначе абзац соседям.

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Один из датчиков - должен мерить температуру воздуха на улице;

второй - температуру теплоносителя (воды) на входе в систему отопления;

третий - температуру воздуха в помещении.

Конечно же, я не знаю и не понимаю. Но я поэтому и обратился к помощи форума. Мне нужен некий минимальный набор кода (наиболее простой), но способный обеспечить вполне надёжную работу системы автоматического поддержания температуры в помещении. Основные исполнительные органы - котёл на дизельном топливе, насосы перекачивающие воду. У котла - своя автоматика защиты и контроля процесса горения (штатная, заводская). Я лишь планирую включать/выключать котёл с помощью реле.

b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41 пишет:

Это ему не особо поможет. ScratchPad он читает не полностью, CRC не проверяет,  разрешение датчика не устанавливает/проверяет, undefined low bits не сбрасывает в зависимости от разрешения и пр. и др.

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

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

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

Даже не знаю, как прокомментировать.

Т.е. 0 градусов на улице или датчик оторвался - вообще пофиг? Предлагаю тогда вообще не подключать термометры и сэкономить пин и второй контроллер. А значения на экран через rand() выводить. 

bwn
Offline
Зарегистрирован: 25.08.2014

sadman41 пишет:

b707 пишет:

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

Даже не знаю, как прокомментировать.

Т.е. 0 градусов на улице или датчик оторвался - вообще пофиг? Предлагаю тогда вообще не подключать термометры и сэкономить пин и второй контроллер. А значения на экран через rand() выводить. 

Там типичные, либо -127, либо 85. Смысла в CRC особого нет, только если действительно линия засранная. В этот случае проверку легко добавить.
 

Для ТС: Посмотрите эту тему, есть на сторожевом таймере, есть функция на миллис.

sadman41
Offline
Зарегистрирован: 19.10.2016

bwn пишет:

Там типичные, либо -127, либо 85. Смысла в CRC особого нет, только если действительно линия засранная. В этот случае проверку легко добавить.

Там - это где? Давайте поищем в datasheet на 18x20 число -127. 

-127 - это #define DEVICE_DISCONNECTED_C из библиотеки DallasTemperature. Я посмотрел, когда оно возвращается - по false == isConnected(deviceAddress, scratchPad). А isConnected(...) возвращает true если _wire->crc8(scratchPad, 8) == scratchPad[SCRATCHPAD_CRC]. 

Но я не наблюдаю того, что ТС где-то использует DallasTemperature, поэтому весьма сомнительно, что он от чистого OneWire получит при обрыве то число, которое вы привели.

Впрочем, конечно, дело ваше - CRC же придумали трусы ;)

 

 

 

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

bwn пишет:
Для ТС: Посмотрите эту тему, есть на сторожевом таймере, есть функция на миллис.

Спасибо. Я именно из этой темы и брал примеры, чтобы составить код получения температуры с датчиков (сообщения от Pyotr и dimax).

Но, к сожалению, на данный момент я не знаю, что такое "сторожевой таймер" и "функция на миллис". И если для моего случая это не критично - то я бы и не хотел тратить силы на понимание (и так ясно - программистом мне не быть).

Просто, сейчас вообще не могу "переварить" полученную информацию о что такое некие проверки датчика и нужны ли они в моём случае. Какова вероятность, что датчик может прислать некорректное значение температуры.  И почему могут возникнуть трудности с получением температуры 0 на улице.

Я могу себе представить некие грубые ограничения, для исключения совсем уж нелепых ошибок: например, задаться что диапазон температур на улице: -20...+40; в помещении: 0...+30; в системе: 0...+90. Это уже позволит отсеять большинство критических ошибок (которые могут повлиять на выход из строя оборудования, хотя это по-моему нереально. В худших случаях будет перерасход топлива либо заморозка системы отопления). И если значения получаемые от датчиков будут вне этого диапазона - просто, игнорировать их.

bwn
Offline
Зарегистрирован: 25.08.2014

PPeterr пишет:

Просто, сейчас вообще не могу "переварить" полученную информацию о что такое некие проверки датчика и нужны ли они в моём случае. Какова вероятность, что датчик может прислать некорректное значение температуры.  И почему могут возникнуть трудности с получением температуры 0 на улице.

В принципе, наиболее правильно добавить проверку CRC (у вас критичная система), тогда распознаем и обрыв и неверные значения. Для этой проверки надо будет считать не два байта, как сейчас, а все восемь и рассчитать CRC (примеры есть в библе OneWire). 
Хотя мне несколько лет и без него хорошо (в овощехранилище и на ректификаторе), так что решать вам.

sadman41
Offline
Зарегистрирован: 19.10.2016

Я немного почитал этот OneWire.cpp. В битовых масках не силен, поэтому с ходу сообразить как включается подтяжка Input pin-а я не могу. Однако по комменту в OneWire::read_bit() можно заключить, что библиотека ожидает увидеть подтяжку на шине:  "let pin float, pull up will raise".

Т.е. если я правильно понимаю, для передачи бита датчик тянет DQ на землю. Таким образом, конечно, при отрыве датчика, МК (у которого есть pull-up) будет читать все время HIGH, что, как я полагаю, трансформируется в byte[0]=255 и byte[1]=255. В случае с int t = byte[0] | (byte[1] << 8) мы будем иметь -32767, если я не ошибаюсь. Да, это невероятное для температуры число. Можно по нему отсеять. Однако, если на шине произойдет замыкание на землю, то мы получим byte[0]=0 и byte[1]=0. Т.е. t==0, при алгоритме, который используется в данном скетче. Т.е. датчик закоротило, а девайс считает, что в помещении дубак и поддает газу бесконечно. 

Конечно, есть способ узнать про замыкание - DS18x20 не дураки проектировали. В OneWire даже есть подсказка: "uint8_t OneWire::reset(void) // Perform the onewire reset function.  We will wait up to 250uS for the bus to come high, if it doesn't then it is broken or shorted and we return a 0". Нужно, как минимум, просто смотреть, что возвращает ds.reset(), чего в коде ТС нет.

Однако, и это не поможет распознать неверный результат, полученный, как правильно отмечено выше, от наводок на шину. Хоть это и невероятно, но можно представить, что в какой-нибудь из циклов опроса из-за битых байтов T окажется равной +60, автоматика инициирует отключение насоса (или чем там она будет заведовать), в следующий цикл в переменную придет +3, насос нужно будет включать (через секунду). Потом еще что-нибудь... и такая дребедень целый день. Но температура будет в пределах нормы. Мне кажется, что эта ситуация близка к той, в котором спасжилет надевать не надо, потому что за последние 10 лет на этом озере никто не утонул. Может и повезет...

Поэтому я даже и не знаю - стоит ли экономить на спичках и наворачивать какие-то проверки на вхождение в допустимый диапазон, отслеживание замыкания на линии или просто принять ScratchPad целиком и сравнить CRC. И скидывать неопределенные биты в зависимости от разрешения термометра.

Это мои соображения исключительно по использованию CRC в данном прожекте. Так, как напоследок ТС озвучил считывание отрицательных температур, то не могу не заметить, что простым сложением байт минусовую температуру в переменной не получить.  

b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41 пишет:

Т.е. если я правильно понимаю, для передачи бита датчик тянет DQ на землю. Таким образом, конечно, при отрыве датчика, МК (у которого есть pull-up) будет читать все время HIGH, что, как я полагаю, трансформируется в byte[0]=255 и byte[1]=255. В случае с int t = byte[0] | (byte[1] << 8) мы будем иметь -32767, если я не ошибаюсь. Да, это невероятное для температуры число. Можно по нему отсеять. Однако, если на шине произойдет замыкание на землю, то мы получим byte[0]=0 и byte[1]=0. Т.е. t==0, при алгоритме, который используется в данном скетче. Т.е. датчик закоротило, а девайс считает, что в помещении дубак и поддает газу бесконечно.

имхо, все вышеизложенное - не более чем ваши домыслы. Любой протокол, в том числе OneWire - основан в первую очередь на импульсах и их длительности, то есть на ИЗМЕНЕНИЯХ уровня с лоу на хай и обратно. При постоянном уровне в линии никаких изменений нет, фронты отсутствуют и никакие постоянные нули или единицы в регистры читаться не будут.

PPeterr
PPeterr аватар
Offline
Зарегистрирован: 12.09.2017

Подскажите мне пожалуйста, при использовании программного кода, который я выложил здесь выше - я не увижу на экране LCD дисплея отрицательных температур? Просто, сейчас проблемно выяснить это экспериментально. Не засуну же я макетик в морозилку... И что мне следует сделать, чтобы иметь возможность получать корректные данные температур при их отрицательных значениях.

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

имхо, все вышеизложенное - не более чем ваши домыслы. Любой протокол, в том числе OneWire - основан в первую очередь на импульсах и их длительности, то есть на ИЗМЕНЕНИЯХ уровня с лоу на хай и обратно. При постоянном уровне в линии никаких изменений нет, фронты отсутствуют и никакие постоянные нули или единицы в регистры читаться не будут.

Ну, хорошо, исключим меня и домыслы. 

Вот код из OneWire.cpp:

//
// Read a bit. Port and bit is used to cut lookup time and provide
// more certain timing.
//
uint8_t OneWire::read_bit(void)
{
	IO_REG_TYPE mask=bitmask;
	volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
	uint8_t r;

	noInterrupts();
	DIRECT_MODE_OUTPUT(reg, mask);
	DIRECT_WRITE_LOW(reg, mask);
	delayMicroseconds(3);
	DIRECT_MODE_INPUT(reg, mask);	// let pin float, pull up will raise
	delayMicroseconds(10);
	r = DIRECT_READ(reg, mask);
	interrupts();
	delayMicroseconds(53);
	return r;
}


//
// Read a byte
//
uint8_t OneWire::read() {
    uint8_t bitMask;
    uint8_t r = 0;

    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
	if ( OneWire::read_bit()) r |= bitMask;
    }
    return r;
}

По вашему мнению - что будет на выходе read() при отсутствии датчика, пульсирующего на шине , а так же при ее закорачивании на землю?

 

b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41, мне сейчас некогда рыться в исходниках - но очевидно, что так, как вы это себе представляете - работать не может. Приемник не может просто тупо измерять уровень на линии, не сообразуясь с моментом начала и окончания передачи. иначе при незначительном расхождении тайминга между приемником и передатчиком принималась бы полная белиберда.

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

sadman41, мне сейчас некогда рыться в исходниках - но очевидно, что так, как вы это себе представляете - работать не может.

Зачем рыться? Ключевую функцию я вам привел - OneWire::read_bit(). Не думаю, что вам понадобится более минуты чтобы понять, как именно она возвращает значение бита. И еще 30 сек на мысленное формирование байта в случае с обрывом или замыканием.

Это исключит домыслы о работе библиотеки как с моей стороны, так и с вашей. 

b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41 пишет:

Зачем рыться? Ключевую функцию я вам привел - OneWire::read_bit(). Не думаю, что вам понадобится более минуты чтобы понять, как именно она возвращает значение бита. И еще 30 сек на мысленное формирование байта в случае с обрывом или замыканием.

Я посмотрел этот код - но это не более как самый нижний уровень протокола. Синхронизация должна происходить на более высоком уровне. Без синхронизации значения, полученные OneWire::read_bit(). - просто идут в помойку.

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

Я посмотрел этот код - но это не более как самый нижний уровень протокола. Синхронизация должна происходить на более высоком уровне. Без синхронизации значения, полученные OneWire::read_bit(). - просто идут в помойку.

Далее происходит в OneWire::read() их сбор в байт. И всё. ds.read() - это OneWire::read(). Никакого другого уровня нет. Более высокий - это библиотека DallasTemperature, которой тут не пахнет. А она уже проверяет CRC.
 
Итого - что без домыслов в двух первых байтах при обрыве и при замыкании?
b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41 пишет:

Итого - что без домыслов в двух первых байтах при обрыве и при замыкании?

Ну ОК, будем считать, что вы правы. Хотя мне все равно не понятно, как это может работать. Но лезть в исходники сейчас и правда совершенно некогда. может дома гляну.

sadman41
Offline
Зарегистрирован: 19.10.2016
PPeterr пишет:
 
Подскажите мне пожалуйста, при использовании программного кода, который я выложил здесь выше - я не увижу на экране LCD дисплея отрицательных температур? Просто, сейчас проблемно выяснить это экспериментально. Не засуну же я макетик в морозилку...
 
 
Т.е. вы хотите все натурные испытания провести потом, когда девайс уже будет включать и выключать опасное устройство? Странно, что в этом посте еще нет Клопоуция - он такие бесстрашные эксперименты стороной не обходит.
 
Отрицательные цифры вы можете увидеть на экране, конечно. Но будут ли они корректной температурой - уже вопрос.
 
Вот вам про отрицательные температуры. Вам нужно искать код, начинающийся с signBit = raw & 0x8000;