Подключение нескольких DS18b20 на одну шину и на разные выводы

123ksn
Offline
Зарегистрирован: 24.11.2014
Всем доброго времени суток.
Выручайте, мужики. Весь мозг себе сломал.
Есть вроде бы простая задача подключить несколько датчиков DS18b20 на одну шину и на разные выводы, а конкретно 3 датчика подключить на пин 8 и по одному датчику на пины 5, 6, 7. Примеров в интернете море, НО
 
1)Многие примеры создатели "гавно-сайтов" просто копипастят не понимая смысла и потому код нерабочий.
Например, описывают подключение нескольких DS18b20 на шину, а в коде объявляют переменные только для одного датчика : 
byte data[12];
byte addr[8];
2)Есть примеры, в которых ID (ROM) каждого DS18b20 надо определять отдельно и прописывать в коде. По-моему это как мерседес заводить ручкой ("кривым стартером")
3)Рабочие примеры для нескольких датчиков DS18b20 даны с использованием библиотеки DallasTemperature.h. Но в ней прописана задержка 750мсек, что для моего проекта не подходит.
 
Вот список страниц, на которых в какой-то степени есть решение моего вопроса:
http://playground.arduino.cc/Learning/OneWire  Dallas Semiconductor's 1-Wire Protocol
http://research.andbas.com/2012/02/1-wire-ds18s20.html   Измерение температуры или освоение 1-wire протокола на примере DS18S20
http://robocraft.ru/blog/187.html   вопрос по DS18B20
http://habrahabr.ru/post/242377/  Читаем все датчики в автоматическом режиме
http://geektimes.ru/post/257256/  Zyxel Keenetic 4G, arduino и датчики температуры ds18b20
http://arduino.ru/forum/programmirovanie/pomogite-optimizirovat-kod-i-pa...  Помогите оптимизировать код и пару вопросов по 1-Ware
 
Решения на приведенных страницах я не нашел.
Что хочу я?
1)Решить вопрос с использованием библиотеки OneWire.h средствами ардуино
2)Понимать используемый код, т.е. какие переменные используются и как они изменяются
3)Подключенные датчики DS18B20 автоматически определяются в SETUP, что бы в LOOP использовать без потери времени.
Вот часть моего кода в части DS18B20
#include <OneWire.h>//для работы с DS18b20(датчик температуры)
//****************** Создаем объекты библиотечных классов **********************************
//Если на 1-шине сидит один датчик, то его ID знать не требуется!
OneWire ds5(5); // создаем объект ds класса OneWire с единственным параметром номер пина шины OneWire
OneWire ds6(6);
OneWire ds7(7);

OneWire ds(8);//!!!! на этой шине будет несколько датчиков

const byte kol_DS = 3; //количество датчиков на 1-wire шине
byte addr[kol_DS][8];//Создал массив: строки-кол-во датчиков по 8 байт в каждой для хранения адреса(ROM, ID) датчика
byte present = 0;
byte data[kol_DS][12];//Создал массив: строки-ном датчика по 12 байт с температурой
byte type_s;


void setup(void)
  {
  Serial.begin(9600);
Serial.println("Find"); //Find DS18хх
  Find_DS18b20();//вызов подпрограммы
}

void loop(void)
 {
//Когда данные датчиков будут в массиве, тогда понятно как получать температуру
}

void Find_DS18b20()
{
  //Пример кода, когда датчиков 2шт: http://playground.arduino.cc/Learning/OneWire

  // 1) Сначала проверим, а есть ли хоть какой датчик на шине
  for (byte i = 0; i < kol_DS; i++) //начало цикла (0,1,2) определения адресов датчиков
  {
    Serial.println(i);
    if ( !ds.search(addr[i])) //тогда нет. addr - у меня двумерный массив байтов, в котором будет храниться номер п.п. и адрес
    {
      Serial.println("NoDS");//No DS18"
      //delay(50);
      //break;
            ds.reset_search(); // если не нашли, сбрасываем поиск в начало
      return; // и возвращаемся в самое начало главного цикла void loop(void)

    }//КонецIF
    else // что-то на шине есть
    {
      //*********для отладки******
      for (int g = 0; g < 8; g++)
      {
        Serial.write(' ');
        Serial.print(addr[i][g], HEX);
      }
      Serial.println();
     //******конец для отладки***************
      
 //     ds.reset_search();//addr[k] Если разремировать, то определяется только один датчик)!!!!!!!!!!!!!!! 
//      {
//        switch (addr[i][0]) //n_ds18xx - номер п/п датчика, 0- 1-й байт содержит тип датчика
//        {
//          case 0x10://Find_Chip: DS18S20
//            type_s = 1;
//            break;
//          case 0x28:
//            //Serial.println("Find_Chip: DS18B20");
//            type_s = 0;
//            break;
//          case 0x22://Find_Chip: DS1822
//            type_s = 0;
//            break;
//            //default:
//            //Serial.println("Device is not a DS18x20 family device.");
//            //     return;
//        }//КонецSwitch
//      }
    }
  }
}

 

Вот что программа выдает при этом коде
Find
0
 28 51 2 45 6 0 0 34
1
 28 69 F6 44 6 0 0 4E
2
 28 D5 AC 2 5 0 0 11
 
Но мне нужен весь код. Если я разремирую, 
 
#include <OneWire.h>//для работы с DS18b20(датчик температуры)
//****************** Создаем объекты библиотечных классов **********************************
//Если на 1-шине сидит один датчик, то его ID знать не требуется!
OneWire ds5(5); // создаем объект ds класса OneWire с единственным параметром номер пина шины OneWire
OneWire ds6(6);
OneWire ds7(7);

OneWire ds(8);//на этой шине будет несколько датчиков
const byte kol_DS = 3; //количество датчиков на 1-wire шине
byte addr[kol_DS][8];//Создал массив: строки-кол-во датчиков по 8 байт в каждой для хранения адреса(ROM, ID) датчика
byte present = 0;
byte data[kol_DS][12];//Создал массив: строки-ном датчика по 12 байт с температурой
byte type_s;
byte Gl_buffer_save[16];//Буфер (0-16) сбора информации

void setup(void)
  {
  Serial.begin(9600);
Serial.println("Find"); //Find DS18хх
  Find_DS18b20();//вызов подпрограммы
}

void loop(void)
 {
//Когда данные датчиков будут в массиве, тогда понятно как получать температуру
//start_termo();//начать измерение температуры
//delay(1000);
//get_termo();
//Send_UART_SD();//отправка данных в UART
}

void Find_DS18b20()
{
  //Пример кода, когда датчиков 2шт: http://playground.arduino.cc/Learning/OneWire

  // 1) Сначала проверим, а есть ли хоть какой датчик на шине
  for (byte i = 0; i < kol_DS; i++) //начало цикла (0,1,2) определения адресов датчиков
  {
    Serial.println(i);
    if ( !ds.search(addr[i])) //тогда нет. addr - у меня двумерный массив байтов, в котором будет храниться номер п.п. и адрес
    {
      Serial.println("NoDS");//No DS18"
      //delay(50);
      //break;
            ds.reset_search(); // если не нашли, сбрасываем поиск в начало
      return; // и возвращаемся в самое начало главного цикла void loop(void)

    }//КонецIF
    else // что-то на шине есть
    {
      //*********для отладки******
      for (int g = 0; g < 8; g++)
      {
        Serial.write(' ');
        Serial.print(addr[i][g], HEX);
      }
      Serial.println();
     //******конец для отладки***************
      
     ds.reset_search();//addr[k] Если разремировать, то определяется только один датчик)!!!!!!!!!!!!!!! 
      {
        switch (addr[i][0]) //n_ds18xx - номер п/п датчика, 0- 1-й байт содержит тип датчика
        {
          case 0x10://Find_Chip: DS18S20
            type_s = 1;
            break;
          case 0x28:
            //Serial.println("Find_Chip: DS18B20");
            type_s = 0;
            break;
          case 0x22://Find_Chip: DS1822
            type_s = 0;
            break;
            //default:
            //Serial.println("Device is not a DS18x20 family device.");
            //     return;
        }//КонецSwitch
      }
    }
  }
}

то получу :

Find
0
 28 51 2 45 6 0 0 34
1
 28 51 2 45 6 0 0 34
2
 28 51 2 45 6 0 0 34
Т.е. программа видит только один датчик.
Также хотелось бы от умных людей услышать описание "на пальцах" приведенных ниже команд.
Вот как понимаю их я:
ds.search(addr[i])
Арудино пытается определить есть ли хоть какое-нибуть устройство на 1-wire. Если есть, то записывает
"засветившийся" ROM (8байт) в строку i массива addr. Это понятно для одного датчика. Но если датчиков много, то при каждом обращении к шине подается сигнал сброса (здесь не видно) и первым откликнувшимся опять является "шустрый" DS18x20, что собственно и видно из второго примера.
Допустим, библиоткека сама определяет, что такой ROM она уже сохранила и продолжает "пинговать" шину, пока не словит нововый ROM. Т.е. я подсовываю команде ds.search()только место для сохранения ROM, а она сама все делает.
Но тогда непонятен смысл команды ds.reset_search(). Что я ей должен скормить? Она что-то куда-то запишет?
P.S. Кода решится вопрос с несколькми датчиками, я думаю, что сам разберусь с датчиками на отдельных пинах.
P.S.S Толковый учитель по вопросам ученика видит его больше, чем по ответам. И только даун при любом вопросе видит дауна.
Очень надеюсь на помощь.
 
bwn
Offline
Зарегистрирован: 25.08.2014

Почитайте  , там весьма подробно описаны все команды и алгоритмы.

Radjah
Offline
Зарегистрирован: 06.08.2014

VCC на 3,3 В

GND на GND

Все DATA на нужный пин

Между пином DATA и VCC подключаешь резистор на 4,7 кОм.

У каждого датчика есть свой уникальный адрес типа MAC на сетевой карте. В примере из DallasTemperature для двух датчиков есть кусок для получения адремсов всех датчков на пине.

Если у тебя датчики привязаны к конкретному месту уставноки, то храни адреса в EEPROM (один адрес - 8 байт), считывай их при запуске контроллера и используй в программе.

Команду на начала измерений можно пулять без задания адреса, тогда её получат все датчики на шине и начнут замер.

123ksn
Offline
Зарегистрирован: 24.11.2014

bwn пишет:

Почитайте  , там весьма подробно описаны все команды и алгоритмы.

Спасибо, но Яндекс говорит, что такой страницы нет (404)

 

Radjah пишет:

VCC на 3,3 В

GND на GND

Все DATA на нужный пин

Между пином DATA и VCC подключаешь резистор на 4,7 кОм.

У каждого датчика есть свой уникальный адрес типа MAC на сетевой карте. В примере из DallasTemperature для двух датчиков есть кусок для получения адремсов всех датчков на пине.

Если у тебя датчики привязаны к конкретному месту уставноки, то храни адреса в EEPROM (один адрес - 8 байт), считывай их при запуске контроллера и используй в программе.

Команду на начала измерений можно пулять без задания адреса, тогда её получат все датчики на шине и начнут замер.

Похоже у Вас между мозгом и глазами стоит стена. Я не спрашиваю как мне ОБОЙТИ проблему, я спрашиваю как её РЕШИТЬ в конкретных условиях. Чувствуете разницу?

Утро вечера мудреннее. Вот рабочий скетч для поиска и использования НЕСКОЛЬКИХ (2, 3, 4, 5, 6 ...) датчиков (указывается в настройках #define kol_DS 3 ) DS18b20 на одной шине с использованием библиотеки OneWire.h

компилировал под IDE 1.6.5. Библиотеку OneWire скачивал, так как в "коробке 1.6.5" не было.   http://www.pjrc.com/teensy/td_libs_OneWire.html    Version 2.1:


#include <OneWire.h>//для работы с DS18b20(датчик температуры)
//****************** Создаем объекты библиотечных классов **********************************
//Если на 1-шине сидит один датчик, то его ID знать не требуется!

OneWire ds(8);//на этой шине будет несколько датчиков
#define kol_DS 3
//const byte kol_DS = 3; //количество датчиков на 1-wire шине
byte addr[kol_DS][8];//Создал массив: строки-кол-во датчиков по 8 байт в каждой для хранения адреса(ROM, ID) датчика
byte present = 0;
byte data[kol_DS][12];//Создал массив: строки-ном датчика по 12 байт с температурой
byte type_s;
byte Gl_buffer_save[16];//Буфер (0-16) сбора информации


void setup(void)
{
  Serial.begin(9600);
  Serial.println("Find"); //Find DS18хх
  Find_DS18b20();//вызов подпрограммы
}

void loop(void)
{
  //Когда данные датчиков будут в массиве, тогда понятно как получать температуру
  start_termo();//начать измерение температуры
  delay(1000);//подождать пока DS18b20 закончит пересчитывать температуру в цифровой код
  get_termo();//прочитать температуру из всех датчиков DS18b20
  Send_UART_SD();//отправить данные в UART
}

void Find_DS18b20()
{
  for (int i = 0; i < kol_DS; i++) 
  {
 //   Serial.println("Initialising DS18B20...");
    if (!ds.search(addr[i]))
    {
      Serial.println("ErrInit1w");//инициализация не выполнена:DS18B20 не найдены
      return;
    }
    else //датчики найдены
    {
//**********вывести номер датчика и его ROM ******     
//      Serial.print("Temp. Sensor ");
//      Serial.print(i);
//      Serial.print(": ");
//      for (int j = 0; j < 8; j++)
//      {
//        Serial.print(addr[i][j], HEX);
//        Serial.print(addr[i][j], HEX);
//      }
//**************вывел ROM**********
    }//конец else
//    Serial.println("...done!");
    if (OneWire::crc8(addr[i], 7) != addr[i][7]) 
    {
      Serial.println("ErrCRC");//CRC Failed!
      return;
    }
    if (addr[i][0] != 0x28)
    {
      Serial.println("notDS18B20");//"OW Device is not DS18B20!"
    }
  }//КонецЦикла
}

//начало измер температуры***********************************************************************************************
void start_termo()
{

  //для отладки
  //Serial.println("1000_start_termo()");
  // Serial.println(n_ds18xx);
  //Запускаем преобразователи во всех датчиках, найденных на этапе SETUP
  for (byte i  = 0; i < 3; i++)
  {
    ds.reset();
    ds.select(addr[i]);
    ds.write(0x44, 1); // start conversion, with parasite power on at the end
    /*После окончания преобразования данные сохраняются в 2-байтовом температурном регистре в
       оперативной памяти, а DS18B20 возвращается в неактивное состояние с низким
      энергопотреблением*/
  }

  //установить разрешение DS18b20
  //00011111 -9bit 0x1F
  //00111111 -10bit
  //01011111 -11bit
  //01111111 -12bit 0x7F
  //byte conf=0x7F;//
  //НЕ РАБОТАЕТ
  //  ds.reset();
  //  ds.skip(); // skip ROM, что бы обратиться ко всем устройствам на шине одновременно
  //  ds.write(0x4E); // write scratchpad Для записи данных в байты 2, 3, и 4 ОЗУ
  //  ds.write(0); // Th
  //  ds.write(0); // Tl
  //  ds.write(conf); // configuration

}

//получение температуры*************************************************
void get_termo()
{
  //для отладки http://usbsergdev.narod.ru/DOC/DS18B20_RU.pdf
  // Serial.println("1016_get_termo()");
  //Serial.println(n_ds18xx);

  for (byte n_ds18xx = 0; n_ds18xx < kol_DS; n_ds18xx++) //начало цикла (0,1,2) определения адресов датчиков
  {
    present = ds.reset();//работа с датчиком всегда должна начинаться с сигнала ресета
    ds.select(addr[n_ds18xx]);//выбирается адрес (ROM ) датчика
    //Чтобы проверить корректность записи данных, необходимо выполнить чтение (используя команду чтения
    //Read Scratchpad [BEh]) после того, как данные (какие) будут записаны(куда).
    ds.write(0xBE);
    /*(0xBE = 190)/читать содержание ПАМЯТИ. Передача данных начинается с наименьшего значащего бита байта 0 и продолжается до 9-ого байта (байт 8
       - циклический контроль избыточности). */
    for ( byte i = 0; i < 9; i++)
    {
      data[n_ds18xx][i] = ds.read();//сохраняем температуру(9 байт) в массиве соответствующего датчика
    }
    //проверка контрольной суммы:Старишие 8бит из 64 ROM, а не температуры!! содержат crc
    if ( OneWire::crc8( addr[n_ds18xx], 7) != addr[n_ds18xx][7])
    {
      Serial.println("ErrCRC");
      //если контрольная сумма не совпала с тем, что мы приняли – работать с такими данными нельзя.
      //Поэтому запишем в массив с результатом такие значения, которых в нормальных условиях мы получить не сможем
      //return;
      Gl_buffer_save[n_ds18xx + 5] = 131;//придумать получше вариант
    }
    else
    {
      int16_t raw = (data[n_ds18xx][1] << 8) | data[n_ds18xx][0];//Тип 16-разрядных целых
      byte cfg = (data[n_ds18xx][4] & 0x60);
      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

      byte celsius = (float)raw / 16.0; //16.0 будет 2 знака после запятой. Мне надо целое
      //byte s = 0;
      Gl_buffer_save[n_ds18xx + 5] = celsius;
      //для отладки
//      Serial.print("1051  t=");
//      Serial.print(Gl_buffer_save[n_ds18xx + 5]);
//      Serial.println("  ");
    }
  }
}
//******Конец Функция void get_termo() измерения температуры



void Send_UART_SD()
{
  //Serial.println("793_Send_UART_SD()");

  String dataString = "";
  byte tt = 0;
  for ( byte s = 0; s < 17; s++) // перебираем(0,1,2...) индексы массива Gl_buffer_save[ ]
  {
    tt = Gl_buffer_save[s];

    if (((s == 5) or (s == 6) or (s == 7)) and (tt > 127)) //больше 127- значит температура отрицательная
    {
      tt = 256 - tt;
      Serial.print('-');
      dataString += '-';//наполняем строку (выводимой в UART) информацией для записи на SD карты
    }
    Serial.print(tt);//name[7] = i%10 + '0';
    dataString += String(tt);//наполняем строку (выводимой в UART) информацией для записи на SD карты
    Serial.print(" ; ");
    dataString += ";";//наполняем строку (выводимой в UART) информацией для записи на SD карты
  }
  Serial.println();

}//Конец Send_UART_SD()

Пользуйтесь на здоровье! И забудьте про этот дебилизм загонять ID датчиков в прошивку

Обратите внимание на одну тонкость: для хранения темпрературы я выделил байт. Т.е. можно записать число от 0 до 255, но тепература может быть отрицательной . Положительная температура будет записана числом от 0 до 127, а отрицательная от 128 (старший бит =1) до 255. Поэтому ставим знак минус перед числом. а модуль температуры получаем как (256-число)

Radjah
Offline
Зарегистрирован: 06.08.2014

> указывается в настройках #define kol_DS 3

Взоржал с тебя.

Не путай flash и eeprom, засмеют.

И вопрос со звездочкой. Как датчик успеет измерить температуру, если у тебя не предполагаются задерки?

А код бибилиотеки всё же изучи. Много полезного про нее узнаешь.

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

Тогда наберите "DS18B20 описание на русском" Чернов Геннадий.

На тему загоняния адресов в прошивку или EEPROM, готов сильно поспорить. После сбоя питания, откуда программа узнает местонахождение всех датчиков? Ваша попытка повесить их на отдельные пины, подозреваю связана именно с этим.

Andrey-S
Offline
Зарегистрирован: 02.01.2015

123ksn пишет:

Похоже у Вас между мозгом и глазами стоит стена. Я не спрашиваю как мне ОБОЙТИ проблему, я спрашиваю как её РЕШИТЬ в конкретных условиях. Чувствуете разницу?

Вот бы Пухлявому так ответил)))))))

123ksn
Offline
Зарегистрирован: 24.11.2014

Radjah пишет:

> указывается в настройках #define kol_DS 3

Взоржал с тебя.

1)Смех продлевает жизнь, а ржачь делает из человека животное.

Radjah пишет:

Не путай flash и eeprom, засмеют.

2) Меня это не смущает. Есть пословица: Умный промолчит, дурак не заметит. Засмеивающие, мне кажется, ближе ко второй категории. Не стоит обращать на них внимания. За информацию спасибо. Гляну.  Но код от этого не перестанет работать

Radjah пишет:

И вопрос со звездочкой. Как датчик успеет измерить температуру, если у тебя не предполагаются задерки?

Открываем канал между глазами и мозгами и смотрим функцию LOOP

Я сделаю и забуду про этот проект. Некогда вникать во все библиотеки. 

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

И все таки, как прога определяет местонахождение датчика? Могу предположить: датчики отсортированы по адресам и расположены последовательно. Что делать если датчик сдох? Перемещать все датчики, у которых адрес выше? Объясните, в чем преимущество вашего метода? Крику много, но не понятно.

123ksn
Offline
Зарегистрирован: 24.11.2014

bwn пишет:

1)И все таки, как прога определяет местонахождение датчика? 2)Могу предположить: датчики отсортированы по адресам и расположены последовательно. 3)Что делать если датчик сдох? 4)Перемещать все датчики, у которых адрес выше? 5)Объясните, в чем преимущество вашего метода? 6)Крику много, но не понятно.

Извините, но внес в цитаты цифры, что бы проще было отвечать.

1)датчики, подключенные к отдельным пинам будут установлены на определенные физические объекты и под это будет заточена программа. "Гирлянда"  будет крепиться по обстоятельствам. Как узнать местонашождения датчика с гирлянды? Погреть и записать на бумажку, если угодно.

2)Ничего не сортировал. Ответ в п1

3)Если датчик сдох на отдельном пине, то об этом сообщит программа. Заменю, перезагружу программу и забуду. Если на "гирлянде" - тоже самое.

4)Наши абстракции не пересекаются. Не понимаю, о чем Вы.

5)Я не знаю, что Вы подразумеваете под "моим методом".

6)Странно, я вообще-то не кричал. 

Видимо Ваши вопросы от потери контекста моего вопроса. Кратко повтою. Интесовал код для работы несколькими датчиками DS18b20 в определенных программных условиях. Другие моменты неактуальны, например, точность температуры.

123ksn
Offline
Зарегистрирован: 24.11.2014

Radjah пишет:

1)Извини чувак, я хрустальный шар вчера разбил, хотя будущее он всё равно хреново показывал.

2)А хамить не надо, хамить я и сам умею. А таких шибкоумных велосипедостроителей тут в неделю по 5 штук бывает.

3)И вот тут я ловлю тебя на слове.

4)Ты сначала говришь про неприемлимость делаев и вот уже правишь пост с delay(1000). Либо крестик сними, либо трусы надень.

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

> И все таки, как прога определяет местонахождение датчика?

5)Да никак. Я уже имел дело с таким говнокодером. Сренькнул и в кусты.

1)Думаю моё прощение Вам не поможет.

2)При обвинениях приводят цитаты. Но "чуваки" об этом не знают.

3)Опять бы цитата не помешала.

4)Т.е. моя фраза "Вот часть моего кода в части DS18B20" Вам ничего не говорит. Ладно скажу по другому. Так как проект очень большой и нет смысла его весь выкладывать, я выложил только часть кода, которая показывает суть проблемы. А что бы этот демо-код работал я добавил задержку в 1 сек там где надо.

5)Вообще-то я сам решил свой вопрос и выложил решение в общий доступ. Но у Вас, очевидно, к таким поспупкам свое специфическое отношение. Поэтому более с Вами объясняться не желаю.

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

Мальчики не сортесь по пустякам! 123ksn Ты вобще чего хотел то?! смотри стр.57 //     ds.reset_search(); заремлен. В цикле нашли 1 датчик, потом 2 и третий. Разремил  ds.reset_search(); и нашли 1 датчик сбросили контекст поиска снованашли первый датчик..  Убер      ds.reset_search(); на...

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

Да, еще как с тремя датчиками на одном выводе буш работать, то конвертацию запускай по очереди, иначе питания может нехватать, если сильно надо одновременно, то надо подпитку приличную. Но токо вот зачем оно может быть надо одновременно,  не термоядерный реактор мониторить же, температура меняется не быстро. Можна и все датчики на один вывод поцепить.

А с сканированием датчиков кажется класно, типа новый датчик подключил, он автоматом обнаружился, но в реале неудобняк - ну нашли допустим ваши 3 датчика (а что если 2, или 4 обнаружилось?), а какой из них какую температуру меряет?  Нужна табличка соответствия, с сохранением адресов датчиков в энергонезависимой памяти данных ;)  Это загрузка,  и/или редактирование. Невесело получается. Подключил новый датчик - пропиши его, заменил старый - правь табличку. Если же есть табличка - нафига сканировать каждый раз?!

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

Logik пишет:

А с сканированием датчиков кажется класно, типа новый датчик подключил, он автоматом обнаружился, но в реале неудобняк - ну нашли допустим ваши 3 датчика (а что если 2, или 4 обнаружилось?), а какой из них какую температуру меряет?  Нужна табличка соответствия, с сохранением адресов датчиков в энергонезависимой памяти данных ;)  Это загрузка,  и/или редактирование. Невесело получается. Подключил новый датчик - пропиши его, заменил старый - правь табличку. Если же есть табличка - нафига сканировать каждый раз?!

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

123ksn
Offline
Зарегистрирован: 24.11.2014

Logik пишет:

1) Да, еще как с тремя датчиками на одном выводе буш работать, то конвертацию запускай по очереди, иначе питания может нехватать, если сильно надо одновременно, то надо подпитку приличную. Но токо вот зачем оно может быть надо одновременно,  не термоядерный реактор мониторить же, температура меняется не быстро. Можна и все датчики на один вывод поцепить.

2)А с сканированием датчиков кажется класно, типа новый датчик подключил, он автоматом обнаружился, но в реале неудобняк - ну нашли допустим ваши 3 датчика (а что если 2, или 4 обнаружилось?), а какой из них какую температуру меряет?  Нужна табличка соответствия, с сохранением адресов датчиков в энергонезависимой памяти данных ;)  Это загрузка,  и/или редактирование. Невесело получается. Подключил новый датчик - пропиши его, заменил старый - правь табличку. Если же есть табличка - нафига сканировать каждый раз?!

1)Я датчики DS18b20 давно использую и с тонкостями знаком. Вот на ардуине впервый раз. Но спасибо за добрый совет. 

2)Logik, не придумывайте сложности. Пусть каждый  делает как ему лучше, а я буду делать как мне лучше. Как говорится на вкус и цвет... DS18b20 можно подключить квазизвездой. Пользователь может даже не знать, что перед ним не звезда. Переключил вилку в нужную розетку и готово (Я использую провода и коннекторы от телефона)

Да, извините, строку 57 нет смысла обсуждать, так как я опубликовал пример рабочего кода

inspiritus
Онлайн
Зарегистрирован: 17.12.2012

Кароч вывод простой : каждый точит как хочет :)

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

Спасибо.

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

123ksn
Offline
Зарегистрирован: 24.11.2014

inspiritus пишет:

1)Кароч вывод простой : каждый точит как хочет :)

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

2)Спасибо.

3) PS. Когда помещаете код, не забывайте пожалуйста ставить крыжык в закладке дополнительно, пункт сворачивать.

1) Конечно, сколько людей столько и мнений. 

2)Не знаю за что спасибо, но скажу пожалуйста.

3)Спасибо. Не знал о такой возможности.

alex1608
Offline
Зарегистрирован: 01.09.2015
Здраствуйте как серийный номер DS18b20 ( Data = 1 0 0 FF FF 7F FF C 10 D4  CRC=AF)с экрана монитора 
 привести к такому виду 
DeviceAddress sensor1 = {0x28, 0xA8, 0x3E, 0xF9, 0x05, 0x0, 0x0, 0x12} для вставки  В код?
bwn
Offline
Зарегистрирован: 25.08.2014

Добавьте впереди незначащие нули и вписывайте после х, только то, что вам написал экран, точно не DS18B20. Его ID начинается с 28.

alex1608
Offline
Зарегистрирован: 01.09.2015

спасибо разобрался

 

alex1608
Offline
Зарегистрирован: 01.09.2015

В чем причина при использовании кода периодически теряется связь с датчиками выводит на дисплей как о("sensor error") испытываю в протеусе 

#include <EEPROM.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h>
 #define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature ds(&oneWire);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
const byte OUT[5] = {13, 12, 11, 3, 2}; // номера выходов 
byte pos;
byte tempOUTon[5];  // массив с температурой включения выхода
float tempSensor[5]; // массив куда читается температура
 
 byte qty; // количество градусников на шине.
 
// адреса градусников.
DeviceAddress sensor1 = {0x28, 0x31, 0xC5, 0xB8, 0x0, 0x0, 0x0, 0xB9};
DeviceAddress sensor2 = {0x28, 0x30, 0xC5, 0xB8, 0x0, 0x0, 0x0, 0x8E};
DeviceAddress sensor3 = {0x28, 0x34, 0xC5, 0xB8, 0x0, 0x0, 0x0, 0x52};
DeviceAddress sensor4 = {0x28, 0x33, 0xC5, 0xB8, 0x0, 0x0, 0x0, 0xD7};
DeviceAddress sensor5 = {0x28, 0x29, 0xC5, 0xB8, 0x0, 0x0, 0x0, 0x43};
 
void readSet(){}
void outOff(){ // выключает выходы
  for (byte i = 0; i < qty; i++) digitalWrite(OUT[i], LOW);}
 
void erorr(){ // останавливает работу программы и сигнализирует ошибку
  outOff(); // выключаем выходы    
  lcd.clear();
  lcd.print("sensor error");
   while(1){ // крутим бесконечный цикл
      analogWrite(10, 100);
      delay(500);
      analogWrite(10, 255);
      delay(500);
  }}
void getTemp(){ // читаем температуру и заполняем массив
   ds.requestTemperatures();    
   
   tempSensor[0] = ds.getTempC(sensor1); // немного китайского кода
   tempSensor[1] = ds.getTempC(sensor2); 
   tempSensor[2] = ds.getTempC(sensor3);
   tempSensor[3] = ds.getTempC(sensor4);
   tempSensor[4] = ds.getTempC(sensor5);
   }
 
void sensorTest(){  // ищим датчики на шине, если количество изменилось, останавливаем работу
   ds.begin();
   if(ds.getDeviceCount() != qty) erorr();
}
void setup() {
 //  Serial.begin(9600); 
  ds.begin();   
  qty = ds.getDeviceCount(); // при включении, сохраняем количество градусников, 
                             // можно и лучше количество указать руками. 
  
  for (int i = 0; i < qty; i++) pinMode(OUT[i], OUTPUT);  
  for (int i = 0; i < qty; i++) digitalWrite(OUT[i], LOW);
  
 
 
  lcd.begin(16, 2);
  lcd.clear();
  
  }
void loop() {   
  
 
  sensorTest(); // тест наличия градусников на шине
  getTemp(); // читаем температуру с датчиков  
 
 
  
  /////// вывод инфы на экран
  lcd.setCursor(0, 0); 
  lcd.print(tempSensor[0]); 
  lcd.print("C ");
  lcd.setCursor(0, 1);
  lcd.print(tempSensor[1]); 
  lcd.print("C ") ;
   lcd.setCursor(7, 0);
  lcd.print(tempSensor[3]); 
  lcd.print("C ") ;
    lcd.setCursor(7, 1);
  lcd.print(tempSensor[4]); 
  lcd.print("C ") ;
delay(300); 
  
  }
 
 
123ksn
Offline
Зарегистрирован: 24.11.2014

alex1608 пишет:

В чем причина при использовании кода периодически теряется связь с датчиками выводит на дисплей как о("sensor error") испытываю в протеусе 

Не задумывались почему примеры в IDE только для одной функции (термометр/индикатор/часы)?
Разработчики не гарантируют работу двух устройств (библиотек) в одном проекте. Как пример, DallasTemperature использует внутри себя задержки. Нет гарантии, что библиотека LiquidCrystal или другая не перенастраивает под себя конфигурацию МК. В ардуино нельзя весь функционал сразу лепить в кучу. Только мелкими шагами. Тогда будете знать где возникает конфликт "интересов". Или используйте чужой проверенный код, благо такого здесь хватает.
Radjah
Offline
Зарегистрирован: 06.08.2014

Ошибка - error.

Потыкай датчики по индексу вместо адреса.

И еще http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukomment...

alex1608
Offline
Зарегистрирован: 01.09.2015

Radjah пишет:

Ошибка - error.

Потыкай датчики по индексу вместо адреса.

И еще http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukomment...

по индексу изначально не известно где какой датчик

 

Radjah
Offline
Зарегистрирован: 06.08.2014

Если по индексу все датчики отвечают, то проверяй адреса. Если не отвечают, то смотри соединение. Логика работает или где?

alex1608
Offline
Зарегистрирован: 01.09.2015

Radjah пишет:

Если по индексу все датчики отвечают, то проверяй адреса. Если не отвечают, то смотри соединение. Логика работает или где?

 Я согласен в реальной схеме соединения контакт не надежен но не в протеусе ( хотя Хрен его  Знает

может протеус глючит)?  

alex1608
Offline
Зарегистрирован: 01.09.2015

При проверке в метале  код работает  получается протеус не всегда есть хорошо

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

alex1608 пишет:

При проверке в метале  код работает  получается протеус не всегда есть хорошо

Что то вроде резиновой женщины. ИМХО.

Allex 78
Offline
Зарегистрирован: 02.03.2016

Помогите пожалуйста -начальство уже мозг проело, не могу прописать 6 датчиков ds18b20 на sd накопитель. Пишет только один датчик на флешку,остальные не удается. Микроконтлллер Atmega 328p. Ардуино писал Си плюс иде.Помогите как прописать поавильно чтобы писались все датчики на sd карту. Выручите мужики!

Radjah
Offline
Зарегистрирован: 06.08.2014

Код и схему в студию. Без кода в "Ищу исполнителя".

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

Allex 78 пишет:
Помогите пожалуйста -начальство уже мозг проело, не могу прописать 6 датчиков ds18b20 на sd накопитель. Пишет только один датчик на флешку,остальные не удается. Микроконтлллер Atmega 328p. Ардуино писал Си плюс иде.Помогите как прописать поавильно чтобы писались все датчики на sd карту. Выручите мужики!

А за каким ..... их писать на SD? Вы базу адресов наличных датчиков создаете? Родного EEPROMа 328 штук на 100 датчиков хватит. Или у вас их больше?

valiktom
Offline
Зарегистрирован: 19.12.2016
двумя руками поддерживаю "123ksn"!!!
вот моя мини-переписка с другого сайта:

>>>>>>>>>>>>>>>>>>>>>>
valiktom
кто-нибудь может разобраться со скетчем ?
1.что делает "12" в byte data[12]; ,если собираем значения до девяти
"for ( i = 0; i < 9; i++) { // нам необходимо 9 байт" ?
2.что делает параметр "present" в "present = ds.reset();"и почему без него нельзя как выше "ds.reset();"?
3.как берутся и меняются значения в "addr[8];" для "if ( !ds.search(addr)) {" ?
4.какие значения сбрасывает "ds.reset_search();" ?
5.для чего нужен "delay(250);"?
заранее спасибо...

Е. > valiktom 
1. мне тоже не понятно. У меня с data[9] всё как надо работает.
2. без present тоже всё работает прекрасно. А present - видимо, можно использовать, например, для вывода сообщения об ошибке: if (!present) {....}
3. Что касается ds.reset_search(), вот кусок из OneWire.cpp:
// You need to use this function to start a search again from the beginning.
// You do not need to do it for the first search, though you could.
//
void OneWire::reset_search()
{
// reset the search state
LastDiscrepancy = 0;
LastDeviceFlag = FALSE;
LastFamilyDiscrepancy = 0;
for(int i = 7; ; i--) {
ROM_NO[i] = 0;
if ( i == 0) break;
}

valiktom > Е. 
по поводу "addr[8];" для "if ( !ds.search(addr)) {" поставлю вопрос по-другому:

...если ds.search(addr) прочёсывает все адреса от минимального до максимального,
натыкается на существующий, выдаёт единицу и дальше по коду, 
то почему он до этого не ушёл в "No more addresses." и "loop"...
...а если код библиотеки (может я не разобрался) не даёт выйти из прочёсывания адресов,
то как мы видим два и больше сенсора за раз цикла...
..а если это разные циклы,
то откуда известно с какого адреса продолжать, ведь он объявляется по-новому каждый цикл 
и нигде не запоминается...
где собака зарыта?
<<<<<<<<<<<<<<<<<<<<<<<<

в конечном счёте всё сводится к тому,что происходит в цикле?
while (ds.search(addr) == 1) {
Serial.println(addr);
}

виден аналогичный взгляд на вещи...

 

 

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

 вот тоже понадобилось на три датчика на три входа повесить, без привязки к внутренним номерам датчиков.

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

для развития темы, притащил код из интернета:

 

/ подключение нескольких датчиков на разніе пині ардуино.
/*
Стандартная библиотека DallasTemperature несколько модернизирована и позволяет подключить несколько датчиков к одному пину Ардуино 
и обращаться к ним по уникальным адресам, что удобно, если надо провести датчики температуры  на большие расстояния от Ардуино к 
местам измерения температур, так как количество проводов уменьшается до трёх или  даже двух с паразитным подключением, и толщина 
проводов может передать сигнал напряжением в 5 вольт на большие расстояния.  Но что делать, если датчик испортился, например из-за 
перегрева, при замене цифрового датчика на шине с другими датчиками нужно заново прописывать в программе новый уникальный номер 
датчика DS18b20  и заново прошивать Ардуино.
Одно из решений подключить цифровые датчики, как аналоговые каждый на свой вывод и считывать температуру с привязкой к 
выводу, а не к уникальному адресу или к номеру в последовательности определения на шине.
Если требуется подключить несколько цифровых датчиков DS18b20 на разные выводы по шине onewire  воспользуётесь примером скетча:
* два цифровых датчика DS18B20 на отдельные пины  для быстрой замены с разрешениями для быстроты срабатывания (менее 2 секунд) и проверкой на отсутствие
*/
#include <OneWire.h>
#include <DallasTemperature.h>
// пин 10 датчик температуры 1
OneWire oW_podacha(10);
// пин 11 датчик температуры 2
OneWire oW_Obratka(11);
DallasTemperature podacha(&oW_podacha);
DallasTemperature Obratka(&oW_Obratka);
DeviceAddress podachaAddress, ObratkaAddress;
int Obrat;
int podach;
int raznost;
// float floatObrat;
// float floatpodach;
void setup(void)
{
Serial.begin(9600);
podacha.begin();
Obratka.begin();
podacha.setResolution(11);
Obratka.setResolution(11);
<p>// при максимальной точности время срабатывания 2 секунды если не указана точность
// 12  точность 0.06
// 11 точность 0.12
// 10 точность 0.25
// 9 точность 0.5
// 6 точность 0.5
}
void loop(void)
{
Serial.println("Datchiki temperaturi:");
Obratka.requestTemperatures();
delay(40);
podacha.requestTemperatures();
delay(40);
Serial.print("Podacha:");
Serial.println(podacha.getTempCByIndex(0));
Serial.print("Obratka:");
Serial.println(Obratka.getTempCByIndex(0));
int Obrat = Obratka.getTempCByIndex(0);
int podach = podacha.getTempCByIndex(0);
int raznost = podach - Obrat;
Serial.print("RaZnost:");
Serial.println(raznost);
delay(100);
oW_podacha.reset_search();
oW_Obratka.reset_search();
if (  !podacha.getAddress(podachaAddress, 0) ) { Serial.print("datchika Podachi net");}
if (  !Obratka.getAddress(ObratkaAddress, 0) ) { Serial.print("datchika Obratki net");}
}
//Также часто требуется проверка на наличие  датчика в случае плохого контакта.
//Для ускорения работы цифровых датчиков следует установить разрешение  (точность ) снятия измерений, если не указывать разрешение , 
//по умолчанию оно будет максимальным, а значит процесс замера температуры займёт 2 секунды.

 

gena
Offline
Зарегистрирован: 04.11.2012
AlexeySh
Offline
Зарегистрирован: 16.01.2017

bwn пишет:

Тогда наберите "DS18B20 описание на русском" Чернов Геннадий.

Для развития темы скажу, что пробовал его библиотеку под контроллер PIC. После танцев с бубном все заработало. Обработка датчиков через прерывания сделана грамотно. Но код не просто перенести на другой микроконтроллер. Мне пришлось два вечера потратить на сверку регистров таймера моего микроконтроллера с тем, который использовал Геннадий, переделку кода и вылавливание ошибок. И там используется устаревший метод работы с EEPROM из-за чего библиотека занимает много места в памяти микроконтроллера. Т. е. все операции чтения/записи в EEPROM там расписаны вручную. В современных компиляторах для PIC этого не нужно, компилятор все делает сам, достаточно указать префикс eeprom при описании переменной.

А в целом именно так должа выглядеть нормальная библиотека для работы с датчиками температуры: до 16 датчиков на один провод, автоматический опрос датчиков, сохранение кодов датчиков в EEPROM, автоматическая замена кодов датчиков в EEPROM при смене датчика, работа через прерывания, что исключает задержки на ожидание ответа от датчика. В идеале ещё переделать её под ООП, чтобы можно было одновременно запускать несколько датчиков по нескольким пинам.

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

AlexeySh, даже и не знал про его библиотеки, именно перевод-описание работы с DS18B20 понравилось.

AlexeySh
Offline
Зарегистрирован: 16.01.2017

bwn пишет:

AlexeySh, даже и не знал про его библиотеки, именно перевод-описание работы с DS18B20 понравилось.

Добавлю ложку дегтя. Его же библиотеку для работы с LCD дисплеями на HD44780 запустить так и не удалось. Хотя она вообще не использует прерывания, только временные задержки. После пары дней развлечений с её отладкой нашел в Инете другую библиотеку, на которой сейчас и работает моё устройство на PIC16F648A.

valiktom
Offline
Зарегистрирован: 19.12.2016
#include <EEPROM.h>
#include <OneWire.h>
// OneWire DS18B20 only
//Скетч использует 10 574 байт (32%) памяти устройства. Всего доступно 32 256 байт.
//Глобальные переменные используют 455 байт (22%) динамической памяти,
//оставляя 1 593 байт для локальных переменных. Максимум: 2 048 байт.
/* UNO:
   32 кБ флэш памяти
   2 кБ ОЗУ (SRAM)
   1 Кб EEPROM
   14 цифровых выводов
   Последовательная шина: 0 (RX) и 1 (TX)
   Внешнее прерывание: 2 и 3
   ШИМ: 3, 5, 6, 9, 10, 11
   SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK).
   LED: 13
   I2C: 4 (SDA) и 5 (SCL).
   6 аналоговых входов (обозначенных как A0 .. A5)/14..19 в качестве цифровых выводов
   7, 8 - только для цифровых выводов
*/

#define NumGroup 18 //количество задействованных пинов для датчиков
#define MaxNumberSensor 113 //максимальное число датчиков, ограниченное объёмом EEPROM
//113x9=1017<1024 byte max EEPROM
unsigned long timeSens;//время события
int diftime = 0;//разница времени между двумя событиями
//список номеров задействованных пинов:
byte numPin[NumGroup] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};//for UNO: A0..A5=>14..19
int Sensor[MaxNumberSensor];//значения измерянной температуры
boolean ReadSens = false;//признак чтения\записи датчика
boolean boot = false;//признак завершения полного первого цикла записи\чтения всех датчиков
byte present;//признак готовности датчика к ответу
//без паразитного питания - если датчик готов к ответу =1,иначе =0. для паразитного питания - читайте документацию

void setup() {
  Serial.begin(9600);
  timeSens = millis();// 4,294,967,295 max
  Serial.println(F("If you Need to Run the Automatic Scanning T* Sensors DS18B20 - Send Any Value\r\nWaiting for Answer 10sec..."));
  //ждём ответа пользователя 10 секунд
  while (Serial.read() == -1 && diftime < 100) {
    diftime = int(millis() - timeSens) / 100;
  }
  if (diftime < 100) {//если согласились на автоматическое сканирование датчиков
    Serial.println(F("Runing..."));
    //проверяем присутствие уже записаных в EEPROM датчиков по их номерам:
    //каждый 9-й адрес(включая нулевой) в EEPROM соответствует порядковому номеру датчика,
    //по нему и может обращаться программа контроля температуры;
    //его значение совпадает с номером пина, к которому подключён датчик.
    //MaxNumberSensor * 9 - число задействованных адресов в EEPROM(макс. 113x9 для UNO)
    for (int eAddrSens = 0; eAddrSens < MaxNumberSensor * 9; eAddrSens = eAddrSens + 9) {
      byte Pin = EEPROM.read(eAddrSens);//читаем номер пина в EEPROM
      if (Pin != 255) {//значит - записан в EEPROM
        byte n = 0;
        byte ID[8];//собираем его ID
        for (byte i = eAddrSens + 1; i < eAddrSens + 9; i++) {
          ID[n] = EEPROM.read(i);
          n++;
        }
        OneWire  ds(Pin);//активизируем общение по записанному пину
        n = 0;//количество возможных повторов опроса датчика
        while (n < 3) {
          present = ds.reset();//если датчик готов к ответу present=1.
          //полезно только для общего случая:
          //если на пине больше одного датчика, то пользы мало - ответят все,
          //но "ds.reset();" обязан быть до начала общения с датчиком.
          ds.select(ID);//выбираем датчик по записанному ID
          //без преобразования - значение не важно, важно наличие верного ответа
          ds.write(0xBE);//Чтение памяти(+85*С после сброса питания или предыдущее значение измерения без сброса)
          byte data[9];//( - если датчик отсутсвует)
          Serial.println();
          //смотрим ответ датчика - для индикации
          for (byte d = 0; d < 9; d++) {//собираем его ответ
            data[d] = ds.read();//читаем по-байтово
            Serial.print(data[d], HEX);
            Serial.print(' ');
          }
          Serial.print("CRC=");
          byte CRC = OneWire::crc8(data, 8);
          Serial.println(CRC, HEX);
          int T = (data[1] << 8) | data[0];//может быть и отрицательным
          T = (T + 8) >> 4;//целые *C с округлением
          Serial.print(F("Temp = "));
          Serial.print(T);//0*C - если датчик отсутсвует
          Serial.println(F("*C"));
          //проверяем ответ датчика. если датчик отсутсвует:
          //1.если датчик на дигитальном пине:
          //-если резистор подключён к пину, то data = {FF FF FF FF FF FF FF FF FF},а CRC=C9 и != FF(data[8])
          //-если пин висит в воздухе, то data = {0 0 0 0 0 0 0 0 0},а CRC=0 и == 0(data[8])
          //2.если датчик на аналоговом пине:
          //-если резистор подключён к пину, то data = {FF FF FF FF FF FF FF FF FF},а CRC=C9 и != FF(data[8])
          //-если пин висит в воздухе, то в зависимости от наводок сработает один из двух вариантов
          // дигитального пина или 'CRC' общего случая, если наводки прыгают на грани срабатывания
          //по-этому:
          if (CRC != data[8] || present == 0 || CRC == 0) {//датчик отсутсвует или мусор
            n++;
            delay(100);//повторяем опрос датчика
          }
          else {//опрос успешный
            break;
          }
        }
        //обобщаем данные
        Serial.print(F("Sensor #"));
        Serial.print(eAddrSens / 9 + 1);
        Serial.print(F(" ID from EEPROM # "));
        for (byte d = 0; d < 8; d++) {
          Serial.print(ID[d], HEX);
          Serial.write(' ');
        }
        Serial.print(F("on Pin #"));
        Serial.print(Pin);
        if (n == 3) {//если датчик отсутсвует
          Serial.println(F(" not Responds.\r\nWaiting for Answer: Delete = 'd', Skip = 's'..."));
          char answer;
          do {//ждём ответа пользователя
            answer = Serial.read();
            if (answer == 'd') {//удаляем датчик из EEPROM
              for (int i = eAddrSens; i < eAddrSens + 9; i++) {
                EEPROM.write(i, 255);
              }
              Serial.println(F("Deleted."));
            }
            else if (answer == 's') {//пропускаем до выяснения потом
              Serial.println(F("Skipped."));
            }
            else if (answer >= 0) {//что-то ответили, но не то
              Serial.println(F("Waiting for the Correct Answer..."));
              answer = '\0';//ждём в цикле верный ответ
            }
            else {//ничего не ответили//answer == -1(int)
              answer = '\0';//ждём в цикле ответ
            }
          }
          while (answer == '\0');
        }
        else {//всё впорядке
          Serial.println(F(" is Exists and Responds."));
        }
      }
    }
    SearchSensor();
  }
  else {
    Serial.println(F("Skipping..."));
  }
  Serial.println(F("\r\nEnd Setup.\r\nIf you Need to Run the Automatic Scanning during program execution - Send Value 's'.\r\n"));
}
//======================================================================
void loop() {
  //надо помнить, что исполнение основной программы управления и выходы
  //зависают в том положении, в котором мы остановились в момент начала поиска датчиков
  if (Serial.read() == 's') {//если хотим проверить датчики без перезагрузки
    SearchSensor();
    ReadSens = false;
    boot = false;
  }
  else {//в програмном режиме
    if (timeSens > millis()) {
      diftime = int(4294967295 - timeSens + millis());
    }
    else {
      diftime = int(millis() - timeSens);
    }
    if (ReadSens == false) {
      for (byte i = 0; i < NumGroup; i++) {
        OneWire  ds(numPin[i]);
        // обращение ко всем датчикам шины сразу:
        ds.reset();//сброс шины
        ds.write(0xCC);//Игнорировать адрес
        if (boot == false) {//имеет смысл при первом запуске или замене\добавлении датчиков
          ds.write(0x4E);//разрешение записать конфиг //write scratchpad (starts at byte 2)
          //задаём аварийные температуры(верхняя\нижняя) - изменить, если пользуетесь
          ds.write(0x4B);//default value of TH reg (user byte 1) - верхняя
          ds.write(0x46);//default value of TL reg (user byte 2) - нижняя
          //задаём точность измерения датчика(от неё зависит время преобразованя)= 0,125гр
          ds.write(0x5F);//default value of Config = 0x7F (user byte 3)
          //точность 0,5гр = 1F; 0,25гр = 3F; 0,125гр = 5F; 0,0625гр = 7F - подставить нужное\учесть время преобразования
          ds.write(0x48);// копируем три байта в EEPROM DS18B20, чтобы сохранить постоянно
        }
        ds.write(0x44);//начать преобразование температуры(без паразитного питания)
      }
      ReadSens = true;
      timeSens = millis();
    }
    else if (ReadSens == true && diftime >= 500) {//500ms(с запасом)-т.к. время преобразования 375ms для (0x5F)
      for (byte NumSens = 0; NumSens < MaxNumberSensor; NumSens++) {
        int AddrSensor = NumSens * 9;
        byte Pin = EEPROM.read(AddrSensor);
        if (Pin != 255) {
          OneWire  ds(Pin);
          byte m = 0;
          byte ID[8];
          Serial.print(F("Sensor ID #"));
          for (int id = AddrSensor + 1; id < AddrSensor + 9; id++) {
            ID[m] = EEPROM.read(id);
            Serial.print(ID[m], HEX);
            Serial.write(' ');
            m++;
          }
          Serial.println();
          byte crc = 0;//индекс числа повторов опроса датчика
          while (crc < 3) {
            present = ds.reset();//коментарии в "void setup()"
            ds.select(ID);
            ds.write(0xBE);//Чтение памяти
            byte data[9];
            for (byte i = 0; i < 9; i++) {
              data[i] = ds.read();
            }
            //проверяем ответ датчика
            byte CRC = OneWire::crc8(data, 8);//коментарии в "void setup()"
            if (CRC == data[8] && present == 1 && CRC != 0) {//ответ корректный
              Sensor[NumSens] = (data[1] << 8) | data[0];
              Sensor[NumSens] = (Sensor[NumSens] * 10) >> 4;//целое в десятых *C (214=>21,4*C)//(t*10)>>4 == (t*10)/16
              break;
            }
            else {//датчик отсутсвует или мусор
              crc++;
              Serial.print(F("Error "));
              Serial.println(crc);
              delay(100);
            }
          }
          Serial.print(F("Sensor #"));
          Serial.print(NumSens + 1);
          if (crc == 3) {
            Serial.println(F(" Error.\r\n"));
          }
          else {
            Serial.print(F(" Temp: "));
            Serial.print(Sensor[NumSens] / 10);
            Serial.write(',');
            Serial.print(Sensor[NumSens] % 10); //если в десятых *C
            Serial.println(F("*C.\r\n"));
          }
        }
        else if (boot == false) {
          Serial.print(F("Sensor #"));
          Serial.print(NumSens + 1);
          Serial.println(F(" is Blank.\r\n"));
        }
      }
      boot = true;
      ReadSens = false;
      //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      //место основной программы для Sensor[Sensor Number],
      //т.к. в другом месте значения не меняются
      //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    }
  }
  //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  //место другой основной программы
  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
//=========================================================================================
void SearchSensor()
{
  //задавая номер обнаруженного нового датчика мы можем/хотим перезаписать уже существующий в EEPROM.
  //по-этому после записи нового датчика в EEPROM мы должны заново проверить датчики на пине заменяемого старого датчика,
  //а затем вернуться на предыдущий пин нового и продолжить поиск с того-же места,где прервали поиск.
  //это позволяет добавлять/заменять/тасовать датчики по номерам,не меняя основную программу.
  //в общем случае пины могут и совпадать, а поиск может и дублироваться повтором.
  //эти значения заведомо больше, чем NumGroup, например:
  byte ReplaceNumPin = NumGroup + 1;//номер группы(привязаной к пину) датчика на замену по номеру(остановка в непрерывном поиске)
  byte OldNumPin = NumGroup + 1;//номер группы(привязаной к пину) старого датчика с тем-же номером
  //делаем поиск датчиков перебирая пины
  for (byte Group = 0; Group < NumGroup; Group++) {
    //т.к. ds.search(addr) не останавливается сам в поиске датчиков и идёт по-кругу, нам надо создать
    //список их ID, и если ID повторяется - значит мы пошли на следующий круг...тут и выходим из него.
    //в общем случае всё максимальное число датчиков может быть и на одном пине...поэтому:
    byte ID[MaxNumberSensor][9];//список всех найденых на пине ID датчиков:ID[s][1_8] с их програмными номерами датчиков в EEPROM:ID[s][0].
    //если ID[s][0]=0,то датчик не записан в EEPROM и по-этому он без номера.
    for (byte n = 0; n < MaxNumberSensor; n++) {//обнуляем значения
      for (byte x = 0; x < 9; x++) {
        ID[n][x] = 0;
      }
    }
    //если хотим заменить датчик:
    if (OldNumPin != NumGroup + 1) {
      Group = OldNumPin;//проверяем группу заменяемого датчика
      OldNumPin = NumGroup + 1;//отменяем повтор на следующий цикл
    }
    else if (ReplaceNumPin != NumGroup + 1) {
      Group = ReplaceNumPin;//проверяем группу нового датчика на замену
      ReplaceNumPin = NumGroup + 1;//отменяем повтор на следующий цикл
    }
    OneWire  ds(numPin[Group]);//активизируем общение по очередному пину
    Serial.print(F("\r\nStart Search Pin #"));
    Serial.println(numPin[Group]);
    byte addr[8];//прочитанный ID датчика
    byte end_search = 0;//число совершённых поисков датчиков на пине
    byte s = 0;//порядковый номер в списке ID
    byte crc = 0;//индекс числа повторов поиска датчиков при ошибке чтения "crc"
    while (end_search < 2) {//ищем существующий ID датчика на пине
      timeSens = millis();// 4,294,967,295 max
      //запускаем поиск датчиков на ограниченное время,
      //в нашем случае = 2сек(с запасом)- т.к. возможно идентифицировать до 75 ROM в секунду
      while (ds.search(addr) != 1 && diftime < 200) {//2sec
        diftime = int(millis() - timeSens) / 10;
      }
      if (diftime < 200) {//если нашли датчик за это время, то проверяем CRC для его ID
        if (OneWire::crc8(addr, 7) != addr[7]) {//если мусор
          Serial.println(F("Detected New Sensor but CRC is not valid!"));
          if (crc == 0) {//сброс и повтор поиска сначала
            Serial.println(F("Repeat Search..."));
            crc++;
            ds.reset_search();
            delay(250);
          }
          else {//уже повторяли поиск
            Serial.println(F("Skipping..."));
          }
        }
        else {//если корректный ID
          //проверяем есть ли такой ID в списке
          for (byte n = 0; n < MaxNumberSensor; n++) {
            if (ID[n][8] == 0) {//если попали на пустой ID ,то прекращаем поиск
              break;
            }
            byte m = 0;//иначе сравниваем побайтово значения записи с обнаруженым ID
            for (byte x = 0; x < 8; x++) {
              if (ID[n][x + 1] == addr[x]) {
                m++;
              }
              else {//не совпадает
                break;//идём к следующей записи
              }
              //"break" не даёт обойти "goto", по-этому - условие:
              if (m == 8) {//если есть в списке, значит мы прошли полный цикл поиска с момента его сброса
                Serial.println(F("Checked All Possible IDs. Found Sensor IDs:"));
                goto EndSearchPin;//уходим на следующий пин
              }
            }
          }
          //если дошли сюда, то нашли новый ID и заносим его в список
          for (byte i = 0; i < 8; i++) {
            ID[s][i + 1] = addr[i];
          }
          //проверяем, записан ли найденый датчик в EEPROM
          byte same = 0;//индекс соответствия в поиске
          for (int eAddrSens = 0; eAddrSens < MaxNumberSensor * 9; eAddrSens = eAddrSens + 9) {
            byte Pin = EEPROM.read(eAddrSens);
            if (Pin == numPin[Group]) {
              for (byte n = eAddrSens + 1; n < eAddrSens + 9; n++) {
                if (addr[same] == EEPROM.read(n)) {
                  same++;
                }
                else {
                  break;
                }
              }
              if (same == 8) {//если найден в EEPROM
                Serial.print(F("Sensor #"));
                Serial.print(eAddrSens / 9 + 1);
                Serial.print(F(" ID # "));
                for (byte d = 0; d < 8; d++) {
                  Serial.print(addr[d], HEX);
                  Serial.write(' ');
                }
                Serial.print(F("on Pin #"));
                Serial.print(numPin[Group]);
                Serial.println(F(" Already Recorded on EEPROM."));
                ID[s][0] = eAddrSens / 9 + 1;//записываем номер датчика
                break;
              }
              else {
                same = 0;
              }
            }
          }
          if (same == 0) {//если нет в EEPROM, то датчик новый
            Serial.print(F("Detected New Sensor ID # "));
            for (byte d = 0; d < 8; d++) {
              Serial.print(addr[d], HEX);
              Serial.write(' ');
            }
            Serial.print(F("on Pin #"));
            Serial.println(numPin[Group]);
            Serial.print(F("You Want to Add to EEPROM?\r\nWaiting for Answer:Sensor Number=(0<'?'<"));
            Serial.print(MaxNumberSensor + 1);
            Serial.println(F(")/NO='n'..."));
            String answer;
            do {//ждём ответа пользователя
              answer = Serial.readString();
              byte numSens = answer.toInt();//теоретически тут можно начудить с ответом,
              //по-этому дальше попросим подтверждения ответа
              if (answer == "n") {//не хотим вносить в EEPROM
                Serial.println(F("Continue without Changes..."));
                //ID[s][0] = 0;//обнуляем номер датчика-он и был нулём
                goto ContSearchPin;
              }
              else if (numSens > 0) {//если задали номер датчика,то смотрим его
                Serial.print(F("Sensor #"));
                Serial.print(numSens);
                numSens--;//приводим к програмному значению
                if (numSens < MaxNumberSensor) {//если в рамках допустимого
                  int eAddrSens = numSens * 9;//вычисляем начальный адрес ID датчика в EEPROM
                  if (EEPROM.read(eAddrSens) == 255) {//если он свободен(пустой)
                    Serial.println(F("\r\nAre You Sure?\r\nWaiting for Answer:YES='y'/NO='n'..."));
                    char ans;//просим подтверждения ответа
                    do {//ждём ответа пользователя
                      ans = Serial.read();
                      if (ans == 'n') {//если ошиблись или передумали, то возвращаемся к предыдущему вопросу
                        Serial.print(F("Waiting for Answer:Add to EEPROM Sensor Number=(0<'?'<"));
                        Serial.print(MaxNumberSensor + 1);
                        Serial.println(F(")/NO='n'..."));
                        answer = "";
                      }
                      else if (ans == 'y') {//если согласны, то:(same==0)
                        EEPROM.write(eAddrSens, numPin[Group]);//записываем номер пина в EEPROM
                        for (int y = eAddrSens + 1; y < eAddrSens + 9; y++) {// записываем ID датчика в EEPROM
                          EEPROM.write(y, addr[same]);
                          same++;
                        }
                        ID[s][0] = numSens + 1;//записываем номер датчика в список
                        Serial.println(F("Added."));
                      }
                      else if (ans >= 0) {//что-то ответили, но не то
                        Serial.println(F("Waiting for the Correct Answer..."));
                        ans = '\0';//ждём в цикле верный ответ
                      }
                      else {//ничего не ответили//ans == -1(int)
                        ans = '\0';//ждём в цикле ответ
                      }
                    }
                    while (ans == '\0');
                  }
                  else {//если он не занят старой записью
                    Serial.println(F(" is Exists on EEPROM.You Want to Replace?\r\nWaiting for Answer:YES='y'/NO='n'/Change Number='c'..."));
                    char ans;
                    do {//ждём ответа пользователя
                      ans = Serial.read();
                      if (ans == 'n') {//не хотим заменяь датчик в EEPROM
                        Serial.println(F("Continue without Changes..."));
                        //ID[s][0] = 0;//обнуляем номер датчика-он и был нулём
                        goto ContSearchPin;
                      }
                      else if (ans == 'c') {//хотим поменяь номер датчика
                        Serial.print(F("Waiting for Answer:Add Sensor Number=(0<'?'<"));
                        Serial.print(MaxNumberSensor + 1);
                        Serial.println(F(")/NO='n'..."));
                        answer = "";//ждём в цикле ответ
                      }
                      else if (ans == 'y') {//если согласны заменить датчик
                        ReplaceNumPin = Group;//запоминаем номер группы найденого нового датчика
                        OldNumPin = EEPROM.read(eAddrSens);//читаем\запоминаем номер пина старого датчика
                        for (byte i = 0; i < NumGroup; i++) {//переводим номер пина старого датчика в номер его группы
                          if (numPin[i] == OldNumPin) {
                            OldNumPin = i;//запоминаем номер группы старого датчика
                            break;
                          }
                        }
                        //проверяем, есть ли старый ID в списке
                        for (byte n = 0; n < MaxNumberSensor; n++) {
                          if (ID[n][8] == 0) {//если попали на пустой ID ,то прекращаем поиск
                            break;
                          }
                          byte m = 0;//иначе
                          for (byte x = 1; x < 9; x++) {//сравниваем побайтово записи
                            if (ID[n][x] == EEPROM.read(eAddrSens + x)) {
                              m++;
                            }
                            else {//если не соответствует, то переходим к следующей записи
                              break;
                            }
                            //если дошли сюда, значит ID есть в списке с порядковым номером "n".
                            if (n == MaxNumberSensor - 1) {//если запись последняя, то просто обнуляем её
                              for (byte i = 0; i < 9; i++) {
                                ID[n][i] = 0;
                              }
                            }
                            else {//иначе, затираем её,сдвигая все записи
                              for (byte z = n; z < MaxNumberSensor; z++) {
                                if (ID[z][8] == 0) {//если попали на пустой ID,
                                  break;//то прекращаем перезапись
                                }
                                for (byte i = 0; i < 9; i++) {
                                  ID[z][i] = ID[z + 1][i];
                                }
                              }
                            }
                            s--;//уменьшаем количество записей на одну
                          }
                          if (m == 8) {//если уже нашли старый ID в списке, то прекращаем поиск
                            break;
                          }
                        }

                        EEPROM.write(eAddrSens, numPin[Group]);//записываем номер пина в EEPROM(same==0)
                        for (int y = eAddrSens + 1; y < eAddrSens + 9; y++) {// записываем ID датчика в EEPROM
                          EEPROM.write(y, addr[same]);
                          same++;
                        }
                        ID[s][0] = numSens + 1;//записываем номер датчика в список
                        Serial.println(F("Replaced."));
                        ds.reset_search();
                        delay(250);
                      }
                      else if (ans >= 0) {
                        Serial.println(F("Waiting for the Correct Answer..."));
                        ans = '\0';
                      }
                      else {
                        ans = '\0';
                      }
                    }
                    while (ans == '\0');
                  }
                }
                else {
                  Serial.print(numSens + 1);
                  Serial.println(F(" is greater than the maximum number of sensors = "));
                  Serial.print(MaxNumberSensor);
                  answer = "";
                }
              }
              else if (answer == "") {
                //answer = "";
              }
              else {
                Serial.println(F("Waiting for the Correct Answer..."));
                answer = "";
              }
            }
            while (answer == "");
          }
        }
      }
      else {
        Serial.print(F("Sensors not Found."));
        if (end_search == 0) {
          Serial.println(F("Repeat Search..."));
          ds.reset_search();
          delay(250);
        }
        else {
          Serial.println(F("Skipping..."));
        }
        end_search++;
      }
ContSearchPin:
      s++;
      if (s == MaxNumberSensor) {
        Serial.print(F("Found maximum number of sensors = "));
        Serial.println(MaxNumberSensor);
        break;//выходим из поиска на пине
      }
      diftime = 0;
    }
EndSearchPin:
    for (byte n = 0; n < s; n++) {// значения по порядку нахождения ID
      if (ID[n][8] != 0) {
        Serial.print(F("Sensor #"));
        for (byte x = 0; x < 9; x++) {
          Serial.print(ID[n][x], HEX);
          Serial.write(' ');
        }
        if (ID[n][0] == 0) {
          Serial.print(F("Not writed to EEPROM."));
        }
        Serial.println();
      }
    }
    Serial.print(F("End Search Pin #"));
    Serial.println(numPin[Group]);
  }
}

написал тут программу поиска\замены датчиков...

думаю будет полезно для новичков, каким я и являюсь,

для понимания их работы.

Radjah
Offline
Зарегистрирован: 06.08.2014

> goto EndSearchPin

По рукам линейкой за такое.

valiktom
Offline
Зарегистрирован: 19.12.2016

профееееессор... и грубиян.

Radjah
Offline
Зарегистрирован: 06.08.2014

Прочитай про функции и отучайся от подобного говнокодерства.

valiktom
Offline
Зарегистрирован: 19.12.2016

ну я же говорю - грубиян.

gena
Offline
Зарегистрирован: 04.11.2012

   Да, Си-шники не любят оператор goto. А мне вот, после ассемблера, тоже очень трудно без него обходиться. Хотя стараюсь.

valiktom
Offline
Зарегистрирован: 19.12.2016
#include <EEPROM.h>
#include <OneWire.h>
// OneWire DS18B20 only
//Скетч использует 11 550 байт (35%) памяти устройства. Всего доступно 32 256 байт.
//Глобальные переменные используют 455 байт (22%) динамической памяти,
//оставляя 1 593 байт для локальных переменных. Максимум: 2 048 байт.
/* UNO:
   32 кБ флэш памяти
   2 кБ ОЗУ (SRAM)
   1 Кб EEPROM
   14 цифровых выводов
   Последовательная шина: 0 (RX) и 1 (TX)
   Внешнее прерывание: 2 и 3
   ШИМ: 3, 5, 6, 9, 10, 11
   SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK).
   LED: 13
   I2C: 4 (SDA) и 5 (SCL).
   6 аналоговых входов (обозначенных как A0 .. A5)/14..19 в качестве цифровых выводов
   7, 8 - только для цифровых выводов
*/

#define NumGroup 18 //количество задействованных пинов для датчиков
#define MaxNumberSensor 113 //максимальное число датчиков, ограниченное объёмом EEPROM
//113x9=1017<1024 byte max EEPROM
unsigned long timeSens;//время события
int diftime = 0;//разница времени между двумя событиями
//список номеров задействованных пинов:
byte numPin[NumGroup] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};//for UNO: A0..A5=>14..19
int Sensor[MaxNumberSensor];//значения измерянной температуры
boolean ReadSens = false;//признак чтения\записи датчика
boolean boot = false;//признак завершения полного первого цикла записи\чтения всех датчиков
byte present;//признак готовности датчика к ответу
//без паразитного питания - если датчик готов к ответу =1,иначе =0. для паразитного питания - читайте документацию

void setup() {
  Serial.begin(9600);
  timeSens = millis();// 4,294,967,295 max
  Serial.println(F("If you Need to Run the Automatic Scanning T* Sensors DS18B20 - Send Any Value\r\nWaiting for Answer 10sec..."));
  //ждём ответа пользователя 10 секунд
  while (Serial.read() == -1 && diftime < 100) {
    diftime = int(millis() - timeSens) / 100;
  }
  if (diftime < 100) {//если согласились на автоматическое сканирование датчиков
    Serial.println(F("Runing..."));
    //проверяем присутствие уже записаных в EEPROM датчиков по их номерам:
    //каждый 9-й адрес(включая нулевой) в EEPROM соответствует порядковому номеру датчика,
    //по нему и может обращаться программа контроля температуры;
    //его значение совпадает с номером пина, к которому подключён датчик.
    //MaxNumberSensor * 9 - число задействованных адресов в EEPROM(макс. 113x9 для UNO)
    for (int eAddrSens = 0; eAddrSens < MaxNumberSensor * 9; eAddrSens = eAddrSens + 9) {
      byte Pin = EEPROM.read(eAddrSens);//читаем номер пина в EEPROM
      if (Pin != 255) {//значит - записан в EEPROM
        byte n = 0;
        byte ID[8];//собираем его ID
        for (int i = eAddrSens + 1; i < eAddrSens + 9; i++) {
          ID[n] = EEPROM.read(i);
          n++;
        }
        OneWire  ds(Pin);//активизируем общение по записанному пину
        n = 0;//количество возможных повторов опроса датчика
        while (n < 3) {
          present = ds.reset();//если датчик готов к ответу present=1.
          //полезно только для общего случая:
          //если на пине больше одного датчика, то пользы мало - ответят все,
          //но "ds.reset();" обязан быть до начала общения с датчиком.
          ds.select(ID);//выбираем датчик по записанному ID
          //без преобразования - значение не важно, важно наличие верного ответа
          ds.write(0xBE);//Чтение памяти(+85*С после сброса питания или предыдущее значение измерения без сброса)
          byte data[9];
          Serial.println();
          //смотрим ответ датчика - для индикации
          for (byte d = 0; d < 9; d++) {//собираем его ответ
            data[d] = ds.read();//читаем по-байтово
            Serial.print(data[d], HEX);
            Serial.print(' ');
          }
          Serial.print("CRC=");
          byte CRC = OneWire::crc8(data, 8);
          Serial.println(CRC, HEX);
          int T = (data[1] << 8) | data[0];//может быть и отрицательным
          T = (T + 8) >> 4;//целые *C с округлением
          Serial.print(F("Temp = "));
          Serial.print(T);//0*C - если датчик отсутсвует
          Serial.println(F("*C"));
          //проверяем ответ датчика. если датчик отсутсвует:
          //1.если датчик на дигитальном пине:
          //-если резистор подключён к пину, то data = {FF FF FF FF FF FF FF FF FF},а CRC=C9 и != FF(data[8])
          //-если пин висит в воздухе, то data = {0 0 0 0 0 0 0 0 0},а CRC=0 и == 0(data[8])
          //2.если датчик на аналоговом пине:
          //-если резистор подключён к пину, то data = {FF FF FF FF FF FF FF FF FF},а CRC=C9 и != FF(data[8])
          //-если пин висит в воздухе, то в зависимости от наводок сработает один из двух вариантов
          // дигитального пина или 'CRC' общего случая, если наводки прыгают на грани срабатывания
          //по-этому:
          if (CRC != data[8] || present == 0 || CRC == 0) {//датчик отсутсвует или мусор
            n++;
            delay(100);//повторяем опрос датчика
          }
          else {//опрос успешный
            break;
          }
        }
        //обобщаем данные
        Serial.print(F("Sensor #"));
        Serial.print(eAddrSens / 9 + 1);
        Serial.print(F(" ID from EEPROM # "));
        for (byte d = 0; d < 8; d++) {
          Serial.print(ID[d], HEX);
          Serial.write(' ');
        }
        Serial.print(F("on Pin #"));
        Serial.print(Pin);
        if (n == 3) {//если датчик отсутсвует
          Serial.println(F(" not Responds.\r\nWaiting for Answer: Delete = 'd', Skip = 's'..."));
          char answer;
          do {//ждём ответа пользователя
            answer = Serial.read();
            if (answer == 'd') {//удаляем датчик из EEPROM
              for (int i = eAddrSens; i < eAddrSens + 9; i++) {
                EEPROM.write(i, 255);
              }
              Serial.println(F("Deleted."));
            }
            else if (answer == 's') {//пропускаем до выяснения потом
              Serial.println(F("Skipped."));
            }
            else if (answer >= 0) {//что-то ответили, но не то
              Serial.println(F("Waiting for the Correct Answer..."));
              answer = '\0';//ждём в цикле верный ответ
            }
            else {//ничего не ответили//answer == -1(int)
              answer = '\0';//ждём в цикле ответ
            }
          }
          while (answer == '\0');
        }
        else {//всё впорядке
          Serial.println(F(" is Exists and Responds."));
        }
      }
    }
    SearchSensor();
  }
  else {
    Serial.println(F("Skipping..."));
  }
  Serial.println(F("\r\nEnd Setup.\r\nIf you Need to Run the Automatic Scanning during program execution - Send Value 's'.\r\n"));
}
//======================================================================
void loop() {
  //надо помнить, что исполнение основной программы управления и выходы
  //зависают в том положении, в котором мы остановились в момент начала поиска\удаления датчиков
  if (Serial.read() == 's') {//если хотим проверить датчики без перезагрузки
    SearchSensor();
    ReadSens = false;
    boot = false;
  }
  else if (Serial.read() == 'd') {//если хотим удалить датчик без перезагрузки
    Serial.print(F("\r\nWaiting for Answer: Delete Sensor Number=(0<'?'<"));
    Serial.print(MaxNumberSensor + 1);
    Serial.println(F(")/NO='n'..."));
    String answer;
    do {//ждём ответа пользователя
      answer = Serial.readString();
      byte numSens = answer.toInt();//теоретически тут можно начудить с ответом,
      //по-этому дальше попросим подтверждения ответа
      if (answer == "n") {//не хотим удалять из EEPROM
        Serial.println(F("Continue without Changes..."));
        //продожаем исполнение основной программы
      }
      else if (numSens > 0) {//если задали номер датчика,то смотрим его
        Serial.print(F("Sensor Number = "));
        Serial.print(numSens);
        Serial.println(F("\r\nAre You Sure?\r\nWaiting for Answer:YES='y'/NO='n'..."));
        char ans;//просим подтверждения ответа
        do {//ждём ответа пользователя
          ans = Serial.read();
          if (ans == 'n') {//если ошиблись или передумали, то возвращаемся к предыдущему вопросу
            Serial.print(F("Waiting for Answer:Delete Sensor Number=(0<'?'<"));
            Serial.print(MaxNumberSensor + 1);
            Serial.println(F(")/NO='n'..."));
            answer = "";
          }
          else if (ans == 'y') {//если согласны, то:
            int eAddrSens = (numSens - 1) * 9;//вычисляем начальный адрес ID датчика в EEPROM
            for (int y = eAddrSens; y < eAddrSens + 9; y++) {//стираем датчик из EEPROM
              EEPROM.write(y, 255);
            }
            Serial.println(F("Deleted."));
            //продожаем исполнение основной программы
          }
          else if (ans >= 0) {//что-то ответили, но не то
            Serial.println(F("Waiting for the Correct Answer..."));
            ans = '\0';//ждём в цикле верный ответ
          }
          else {//ничего не ответили//ans == -1(int)
            ans = '\0';//ждём в цикле ответ
          }
        }
        while (ans == '\0');
      }
    }
    while (answer == "");
    ReadSens = false;
    boot = false;
  }
  else {//в програмном режиме
    if (timeSens > millis()) {
      diftime = int(4294967295 - timeSens + millis());
    }
    else {
      diftime = int(millis() - timeSens);
    }
    if (ReadSens == false) {
      for (byte i = 0; i < NumGroup; i++) {
        OneWire  ds(numPin[i]);
        // обращение ко всем датчикам шины сразу:
        ds.reset();//сброс шины
        ds.write(0xCC);//Игнорировать адрес
        if (boot == false) {//имеет смысл при первом запуске или замене\добавлении датчиков
          ds.write(0x4E);//разрешение записать конфиг //write scratchpad (starts at byte 2)
          //задаём аварийные температуры(верхняя\нижняя) - изменить, если пользуетесь
          ds.write(0x4B);//default value of TH reg (user byte 1) - верхняя
          ds.write(0x46);//default value of TL reg (user byte 2) - нижняя
          //задаём точность измерения датчика(от неё зависит время преобразованя)= 0,125гр
          ds.write(0x5F);//default value of Config = 0x7F (user byte 3)
          //точность 0,5гр = 1F; 0,25гр = 3F; 0,125гр = 5F; 0,0625гр = 7F - подставить нужное\учесть время преобразования
          ds.write(0x48);// копируем три байта в EEPROM DS18B20, чтобы сохранить постоянно
        }
        ds.write(0x44);//начать преобразование температуры(без паразитного питания)
      }
      ReadSens = true;
      timeSens = millis();
    }
    else if (ReadSens == true && diftime >= 500) {//500ms(с запасом)-т.к. время преобразования 375ms для (0x5F)
      for (byte NumSens = 0; NumSens < MaxNumberSensor; NumSens++) {
        int AddrSensor = NumSens * 9;
        byte Pin = EEPROM.read(AddrSensor);
        if (Pin != 255) {
          OneWire  ds(Pin);
          byte m = 0;
          byte ID[8];
          Serial.print(F("Sensor ID #"));
          for (int id = AddrSensor + 1; id < AddrSensor + 9; id++) {
            ID[m] = EEPROM.read(id);
            Serial.print(ID[m], HEX);
            Serial.write(' ');
            m++;
          }
          Serial.println();
          byte crc = 0;//индекс числа повторов опроса датчика
          while (crc < 3) {
            present = ds.reset();//коментарии в "void setup()"
            ds.select(ID);
            ds.write(0xBE);//Чтение памяти
            byte data[9];
            for (byte i = 0; i < 9; i++) {
              data[i] = ds.read();
            }
            //проверяем ответ датчика
            byte CRC = OneWire::crc8(data, 8);//коментарии в "void setup()"
            if (CRC == data[8] && present == 1 && CRC != 0) {//ответ корректный
              Sensor[NumSens] = (data[1] << 8) | data[0];
              Sensor[NumSens] = (Sensor[NumSens] * 10) >> 4;//целое в десятых *C (214=>21,4*C)//(t*10)>>4 == (t*10)/16
              break;
            }
            else {//датчик отсутсвует или мусор
              crc++;
              Serial.print(F("Sensor Error "));
              Serial.println(crc);
              delay(100);
            }
          }
          Serial.print(F("Sensor #"));
          Serial.print(NumSens + 1);
          if (crc == 3) {
            Serial.println(F(" Error.\r\n"));
          }
          else {
            Serial.print(F(" Temp: "));
            Serial.print(Sensor[NumSens] / 10);
            Serial.write(',');
            Serial.print(Sensor[NumSens] % 10); //если в десятых *C
            Serial.println(F("*C.\r\n"));
          }
        }
        else if (boot == false) {
          Serial.print(F("Sensor #"));
          Serial.print(NumSens + 1);
          Serial.println(F(" is Blank.\r\n"));
        }
      }
      boot = true;
      ReadSens = false;
      //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      //место основной программы для Sensor[Sensor Number],
      //т.к. в другом месте значения не меняются
      //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    }
  }
  //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  //место другой основной программы
  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
//=========================================================================================
void SearchSensor()
{
  //задавая номер обнаруженного нового датчика мы можем/хотим перезаписать уже существующий в EEPROM.
  //по-этому после записи нового датчика в EEPROM мы должны заново проверить датчики на пине заменяемого старого датчика,
  //а затем вернуться на предыдущий пин нового и продолжить поиск с того-же места,где прервали поиск.
  //это позволяет добавлять/заменять/тасовать датчики по номерам,не меняя основную программу.
  //в общем случае пины могут и совпадать, а поиск может и дублироваться повтором.
  //эти значения заведомо больше, чем NumGroup, например:
  byte ReplaceNumPin = NumGroup + 1;//номер группы(привязаной к пину) датчика на замену по номеру(остановка в непрерывном поиске)
  byte OldNumPin = NumGroup + 1;//номер группы(привязаной к пину) старого датчика с тем-же номером
  //делаем поиск датчиков перебирая пины
  for (byte Group = 0; Group < NumGroup; Group++) {
    //т.к. ds.search(addr) не останавливается сам в поиске датчиков и идёт по-кругу, нам надо создать
    //список их ID, и если ID повторяется - значит мы пошли на следующий круг...тут и выходим из него.
    //в общем случае всё максимальное число датчиков может быть и на одном пине...поэтому:
    byte ID[MaxNumberSensor][9];//список всех найденых на пине ID датчиков:ID[s][1_8] с их програмными номерами датчиков в EEPROM:ID[s][0].
    //если ID[s][0]=0,то датчик не записан в EEPROM и по-этому он без номера.
    for (byte n = 0; n < MaxNumberSensor; n++) {//обнуляем значения
      for (byte x = 0; x < 9; x++) {
        ID[n][x] = 0;
      }
    }
    Serial.println();
    //если хотим заменить датчик:
    if (OldNumPin != NumGroup + 1) {
      Group = OldNumPin;//проверяем группу заменяемого датчика
      OldNumPin = NumGroup + 1;//отменяем повтор на следующий цикл
      Serial.print(F("Re"));
    }
    else if (ReplaceNumPin != NumGroup + 1) {
      Group = ReplaceNumPin;//проверяем группу нового датчика на замену
      ReplaceNumPin = NumGroup + 1;//отменяем повтор на следующий цикл
      Serial.print(F("Re"));
    }
    OneWire  ds(numPin[Group]);//активизируем общение по очередному пину
    Serial.print(F("Start Search Pin #"));
    Serial.println(numPin[Group]);
    byte addr[8];//прочитанный ID датчика
    byte end_search = 0;//число совершённых поисков датчиков на пине
    byte s = 0;//порядковый номер в списке ID
    byte crc = 0;//индекс числа повторов поиска датчиков при ошибке чтения "crc"
    while (end_search < 2) {//ищем существующий ID датчика на пине
      timeSens = millis();// 4,294,967,295 max
      //запускаем поиск датчиков на ограниченное время,
      //в нашем случае = 2сек(с запасом)- т.к. возможно идентифицировать до 75 ROM в секунду
      while (ds.search(addr) != 1 && diftime < 200) {//2sec
        diftime = int(millis() - timeSens) / 10;
      }
      byte m = 0;//индекс нахождения записи в списке ID
      if (diftime < 200) {//если нашли датчик за это время, то проверяем CRC для его ID
        if (OneWire::crc8(addr, 7) != addr[7]) {//если мусор
          Serial.println(F("Detected New Sensor but CRC is not valid!"));
          if (crc == 0) {//сброс и повтор поиска сначала
            Serial.println(F("Repeat Search..."));
            crc++;
            ds.reset_search();
            delay(250);
          }
          else {//уже повторяли поиск
            Serial.println(F("Skipping..."));
          }
        }
        else {//если корректный ID
          //проверяем есть ли такой ID в списке
          for (byte n = 0; n < MaxNumberSensor; n++) {
            if (ID[n][8] == 0) {//если попали на пустой ID ,то прекращаем поиск
              break;
            }
            //иначе сравниваем побайтово значения записи с обнаруженым ID
            for (byte x = 0; x < 8; x++) {
              if (ID[n][x + 1] == addr[x]) {
                m++;
              }
              else {//не совпадает
                break;//идём к следующей записи
              }
              if (m == 8) {//запись обнаружена
                break;
              }
            }
            if (m == 8) {//запись уже обнаружена
              break;
            }
            m = 0;
          }
          if (m == 8) {//если есть в списке, значит мы прошли полный цикл поиска с момента его сброса
            Serial.println(F("Checked All Possible IDs. Found Sensor IDs:"));
            break;//выходим из поиска на пине
          }
          else {//если нет, то нашли новый ID и заносим его в список
            for (byte i = 0; i < 8; i++) {
              ID[s][i + 1] = addr[i];
            }
            //проверяем, записан ли найденый датчик в EEPROM
            byte same = 0;//индекс соответствия в поиске
            for (int eAddrSens = 0; eAddrSens < MaxNumberSensor * 9; eAddrSens = eAddrSens + 9) {
              byte Pin = EEPROM.read(eAddrSens);
              if (Pin == numPin[Group]) {
                for (int n = eAddrSens + 1; n < eAddrSens + 9; n++) {
                  if (addr[same] == EEPROM.read(n)) {
                    same++;
                  }
                  else {
                    break;
                  }
                }
                if (same == 8) {//если найден в EEPROM
                  Serial.print(F("Sensor #"));
                  Serial.print(eAddrSens / 9 + 1);
                  Serial.print(F(" ID # "));
                  for (byte d = 0; d < 8; d++) {
                    Serial.print(addr[d], HEX);
                    Serial.write(' ');
                  }
                  Serial.print(F("on Pin #"));
                  Serial.print(numPin[Group]);
                  Serial.println(F(" Already Recorded on EEPROM."));
                  ID[s][0] = eAddrSens / 9 + 1;//записываем номер датчика
                  break;
                }
                else {
                  same = 0;
                }
              }
            }
            if (same == 0) {//если нет в EEPROM, то датчик новый
              Serial.print(F("Detected New Sensor ID # "));
              for (byte d = 0; d < 8; d++) {
                Serial.print(addr[d], HEX);
                Serial.write(' ');
              }
              Serial.print(F("on Pin #"));
              Serial.println(numPin[Group]);
              Serial.print(F("You Want to Add to EEPROM?\r\nWaiting for Answer:Sensor Number=(0<'?'<"));
              Serial.print(MaxNumberSensor + 1);
              Serial.println(F(")/NO='n'..."));
              String answer;
              do {//ждём ответа пользователя
                answer = Serial.readString();
                byte numSens = answer.toInt();//теоретически тут можно начудить с ответом,
                //по-этому дальше попросим подтверждения ответа
                if (answer == "n") {//не хотим вносить в EEPROM
                  Serial.println(F("Continue without Changes..."));
                  //ID[s][0] = 0;//обнуляем номер датчика-он и был нулём
                  break;//продожаем поиск датчиков на пине
                }
                else if (numSens > 0) {//если задали номер датчика,то смотрим его
                  Serial.print(F("Sensor Number = "));
                  Serial.print(numSens);
                  numSens--;//приводим к програмному значению
                  if (numSens < MaxNumberSensor) {//если в рамках допустимого
                    int eAddrSens = numSens * 9;//вычисляем начальный адрес ID датчика в EEPROM
                    if (EEPROM.read(eAddrSens) == 255) {//если он свободен(пустой)
                      Serial.println(F("\r\nAre You Sure?\r\nWaiting for Answer:YES='y'/NO='n'..."));
                      char ans;//просим подтверждения ответа
                      do {//ждём ответа пользователя
                        ans = Serial.read();
                        if (ans == 'n') {//если ошиблись или передумали, то возвращаемся к предыдущему вопросу
                          Serial.print(F("Waiting for Answer:Add to EEPROM Sensor Number=(0<'?'<"));
                          Serial.print(MaxNumberSensor + 1);
                          Serial.println(F(")/NO='n'..."));
                          answer = "";
                        }
                        else if (ans == 'y') {//если согласны, то:(same==0)
                          EEPROM.write(eAddrSens, numPin[Group]);//записываем номер пина в EEPROM
                          for (int y = eAddrSens + 1; y < eAddrSens + 9; y++) {// записываем ID датчика в EEPROM
                            EEPROM.write(y, addr[same]);
                            same++;
                          }
                          ID[s][0] = numSens + 1;//записываем номер датчика в список
                          Serial.println(F("Added."));
                        }
                        else if (ans >= 0) {//что-то ответили, но не то
                          Serial.println(F("Waiting for the Correct Answer..."));
                          ans = '\0';//ждём в цикле верный ответ
                        }
                        else {//ничего не ответили//ans == -1(int)
                          ans = '\0';//ждём в цикле ответ
                        }
                      }
                      while (ans == '\0');
                    }
                    else {//если он занят старой записью
                      Serial.println(F(" is Exists on EEPROM.You Want to Replace?\r\nWaiting for Answer:YES='y'/NO='n'/Change Number='c'..."));
                      char ans;
                      do {//ждём ответа пользователя
                        ans = Serial.read();
                        if (ans == 'n') {//не хотим заменяь датчик в EEPROM
                          Serial.println(F("Continue without Changes..."));
                          //ID[s][0] = 0;//обнуляем номер датчика-он и был нулём
                          break;//продожаем поиск датчиков на пине
                        }
                        else if (ans == 'c') {//хотим поменяь номер датчика
                          Serial.print(F("Waiting for Answer:Add Sensor Number=(0<'?'<"));
                          Serial.print(MaxNumberSensor + 1);
                          Serial.println(F(")/NO='n'..."));
                          answer = "";//ждём в цикле ответ
                        }
                        else if (ans == 'y') {//если согласны заменить датчик
                          ReplaceNumPin = Group;//запоминаем номер группы найденого нового датчика
                          OldNumPin = EEPROM.read(eAddrSens);//читаем\запоминаем номер пина старого датчика
                          for (byte i = 0; i < NumGroup; i++) {//переводим номер пина старого датчика в номер его группы
                            if (numPin[i] == OldNumPin) {
                              OldNumPin = i;//запоминаем номер группы старого датчика
                              break;
                            }
                          }
                          //проверяем, есть ли старый ID в списке
                          for (byte n = 0; n < MaxNumberSensor; n++) {
                            if (ID[n][8] == 0) {//если попали на пустой ID ,то прекращаем поиск
                              break;
                            }
                            //иначе
                            byte y = 0;//индекс нахождения  ID в списке
                            for (byte x = 1; x < 9; x++) {//сравниваем побайтово записи
                              if (ID[n][x] == EEPROM.read(eAddrSens + x)) {
                                y++;
                              }
                              else {//если не соответствует, то переходим к следующей записи
                                break;
                              }
                              if (y == 8) {//если дошли сюда, значит ID есть в списке с порядковым номером "n".
                                if (n == MaxNumberSensor - 1) {//если запись последняя, то просто обнуляем её
                                  for (byte i = 0; i < 9; i++) {
                                    ID[n][i] = 0;
                                  }
                                }
                                else {//иначе, затираем её,сдвигая все записи
                                  for (byte z = n; z < MaxNumberSensor; z++) {
                                    if (ID[z][8] == 0) {//если попали на пустой ID,
                                      break;//то прекращаем перезапись
                                    }
                                    for (byte i = 0; i < 9; i++) {
                                      ID[z][i] = ID[z + 1][i];
                                    }
                                  }
                                }
                                s--;//уменьшаем количество записей на одну
                                break;//если нашли старый ID в списке, то прекращаем сравнение
                              }
                            }
                            if (y == 8) {//если уже нашли старый ID в списке, то прекращаем поиск
                              break;
                            }
                          }
                          EEPROM.write(eAddrSens, numPin[Group]);//записываем номер пина в EEPROM(same==0)
                          for (int y = eAddrSens + 1; y < eAddrSens + 9; y++) {// записываем ID датчика в EEPROM
                            EEPROM.write(y, addr[same]);
                            same++;
                          }
                          ID[s][0] = numSens + 1;//записываем номер датчика в список
                          Serial.println(F("Replaced."));
                          ds.reset_search();
                          delay(250);
                        }
                        else if (ans >= 0) {
                          Serial.println(F("Waiting for the Correct Answer..."));
                          ans = '\0';
                        }
                        else {
                          ans = '\0';
                        }
                      }
                      while (ans == '\0');
                    }
                  }
                  else {
                    Serial.print(F(" is greater than the maximum number of sensors = "));
                    Serial.println(MaxNumberSensor);
                    Serial.println(F("Waiting for the Correct Number..."));
                    answer = "";
                  }
                }
                else if (answer == "") {
                  //answer = "";
                }
                else {
                  Serial.println(F("Waiting for the Correct Answer..."));
                  answer = "";
                }
              }
              while (answer == "");
            }
          }
        }
        s++;
      }
      else {
        Serial.print(F("Sensors not Found."));
        if (end_search == 0) {
          Serial.println(F("Repeat Search..."));
          ds.reset_search();
          delay(250);
        }
        else {
          Serial.println(F("Skipping..."));
        }
        end_search++;
      }
      if (s == MaxNumberSensor) {
        Serial.print(F("Found maximum number of sensors = "));
        Serial.println(MaxNumberSensor);
        break;//выходим из поиска на пине
      }
      diftime = 0;
    }
    for (byte n = 0; n < s; n++) {// значения по порядку нахождения ID
      Serial.print(F("Sensor #"));
      Serial.print(ID[n][0]);
      Serial.print(F(" ID # "));
      for (byte x = 1; x < 9; x++) {
        Serial.print(ID[n][x], HEX);
        Serial.write(' ');
      }
      if (ID[n][0] == 0) {
        Serial.print(F("Not writed to EEPROM."));
      }
      Serial.println();
    }
    Serial.print(F("End Search Pin #"));
    Serial.println(numPin[Group]);
  }
}

по просьбе отдельных професоров ушёл от "goto",

заодно дополнив кое-что и исправив неколько описок\багов и коментариев...

вроде работает...

123ksn
Offline
Зарегистрирован: 24.11.2014

valiktom пишет:

по просьбе отдельных професоров ушёл от "goto",

вроде работает...

1) Не обращайте внимание на "профессоров". Главное, что бы работало.

2)А вот "вроде работает" опускает Вас значительно ниже чем чем "goto". 

Успехов!

valiktom
Offline
Зарегистрирован: 19.12.2016

123ksn пишет:

valiktom пишет:

по просьбе отдельных професоров ушёл от "goto",

вроде работает...

1) Не обращайте внимание на "профессоров". Главное, что бы работало.

2)А вот "вроде работает" опускает Вас значительно ниже чем чем "goto". 

Успехов!

какую не прогнозируюмую реакцию может вызвать попытка выражения скромности...

но это недостаток всех форумов: больше воды, чем обсуждения по сути...

а суть в данном случае, по-моему, в следуещем:

я попытался показать способ исправления недостатка библиотеки в поиске датчиков

(на мой взгляд, если я её правильно понял; - тоже не факт)

применительно к желанию пользователей(и моему тоже) автоматически зафиксировать их

(удалять\добавлять\тасовать\заменять) путём создания временного списка их ID,

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

адресов в программу(и почему это "просто" не сделано в библеотеке?).

плюс общеизвестный способ хранения ID в EEPROM,

плюс упорядочение и так доступной информации по датчикам для новичков...

а код...  он зависит от времени пребывания в нём... никто не идеален,

иначе нафиг нужны форумы? для взаимного выяснения IQ?

 

123ksn
Offline
Зарегистрирован: 24.11.2014

valiktom пишет:

какую не прогнозируюмую реакцию может вызвать попытка выражения скромности...

.....

иначе нафиг нужны форумы? для взаимного выяснения IQ?

Извините. Просто отделяю "мух от котлет". Думал, что и Вы такой-же. Ошибся. Еще раз извините.

valiktom
Offline
Зарегистрирован: 19.12.2016

не извиняйтесь... такой-же... про IQ - это не к вам...а вообще к форумам...

sva_khv
Offline
Зарегистрирован: 19.12.2016

valiktom пишет:

по просьбе отдельных професоров ушёл от "goto",

заодно дополнив кое-что и исправив неколько описок\багов и коментариев...

вроде работает...

Респект и уважуха за проделанную работу. 

Теперь можно разбираться и ставить эксперементы как НАЙТИ, ПРОНУМЕРОВАТЬ и использовать несколько датчиков на одной шине.

Код я буду писать заново под свои нужды. :-)  Главное с принципом работы разобрались!

В качестве замечания по форме - старайтесь объявлять переменные в начале функции, а не в процессе их применения. Так потом легче разобраться если косяк с типом переменной и вообще откуда она взялась.

И для перфекционистов (да и простым смертным помогает) - первые 1-2 символа от типа переменной, а потом имя. (int iX; char chText[10]; byte bAdress)

А в целом текст программы очень хорошо читается - спасибо! Особенно за развернутые коменты.

 

Гриша
Offline
Зарегистрирован: 27.04.2014

Всем привет, "чукча не читатель, чукча писатель" прочитал по диагонали, нужного не нашел.

ЗАДАЧА:  сверить показания нескольких датчиков  или вывести показания группы датчиков по одной шине с известными адресами датчиков.

СХЕМА:  все датчики в параллель, подтяжка шины данных 2,2к  , подключение с питанием - без паразитки.

БИБЛИАТЕКА ЭТА:

OneWire.h

#ifndef OneWire_h
#define OneWire_h

#include <inttypes.h>

// you can exclude onewire_search by defining that to 0
#ifndef ONEWIRE_SEARCH
#define ONEWIRE_SEARCH 1
#endif

// You can exclude CRC checks altogether by defining this to 0
#ifndef ONEWIRE_CRC
#define ONEWIRE_CRC 1
#endif

// Select the table-lookup method of computing the 8-bit CRC by setting this to 1
#ifndef ONEWIRE_CRC8_TABLE
#define ONEWIRE_CRC8_TABLE 0
#endif

// You can allow 16-bit CRC checks by defining this to 1
// (Note that ONEWIRE_CRC must also be 1.)
#ifndef ONEWIRE_CRC16
#define ONEWIRE_CRC16 0
#endif

class OneWire
{
  private:
#if ONEWIRE_SEARCH
    uint8_t address[8];
    char searchJunction;
    uint8_t searchExhausted;
#endif
    uint8_t pin;
    uint8_t port;
    uint8_t bitmask;
    volatile uint8_t *outputReg;
    volatile uint8_t *inputReg;
    volatile uint8_t *modeReg;

  public:
    OneWire( uint8_t pin);
    
    // Perform a 1-Wire reset cycle. Returns 1 if a device responds
    // with a presence pulse.  Returns 0 if there is no device or the
    // bus is shorted or otherwise held low for more than 250uS
    uint8_t reset();

    // Issue a 1-Wire rom select command, you do the reset first.
    void select( uint8_t rom[8]);

    // Issue a 1-Wire rom skip command, to address all on bus.
    void skip();

    // Write a byte. If 'power' is one then the wire is held high at
    // the end for parasitically powered devices. You are responsible
    // for eventually depowering it by calling depower() or doing
    // another read or write.
    void write( uint8_t v, uint8_t power = 0);

    // Read a byte.
    uint8_t read();

    // Write a bit. The bus is always left powered at the end, see
    // note in write() about that.
    void write_bit( uint8_t v);

    // Read a bit.
    uint8_t read_bit();

    // Stop forcing power onto the bus. You only need to do this if
    // you used the 'power' flag to write() or used a write_bit() call
    // and aren't about to do another read or write. You would rather
    // not leave this powered if you don't have to, just in case
    // someone shorts your bus.
    void depower();

#if ONEWIRE_SEARCH
    // Clear the search state so that if will start from the beginning again.
    void reset_search();

    // Look for the next device. Returns 1 if a new address has been
    // returned. A zero might mean that the bus is shorted, there are
    // no devices, or you have already retrieved all of them.  It
    // might be a good idea to check the CRC to make sure you didn't
    // get garbage.  The order is deterministic. You will always get
    // the same devices in the same order.
    uint8_t search(uint8_t *newAddr);
#endif

#if ONEWIRE_CRC
    // Compute a Dallas Semiconductor 8 bit CRC, these are used in the
    // ROM and scratchpad registers.
    static uint8_t crc8( uint8_t *addr, uint8_t len);

#if ONEWIRE_CRC16
    // Compute a Dallas Semiconductor 16 bit CRC. Maybe. I don't have
    // any devices that use this so this might be wrong. I just copied
    // it from their sample code.
    static unsigned short crc16(unsigned short *data, unsigned short len);
#endif
#endif
};

#endif 

OneWire.cpp

/*
Copyright (c) 2007, Jim Studt

Updated to work with arduino-0008 and to include skip() as of
2007/07/06. --RJL20

Modified to calculate the 8-bit CRC directly, avoiding the need for
the 256-byte lookup table to be loaded in RAM.  Tested in arduino-0010
-- Tom Pollard, Jan 23, 2008

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Much of the code was inspired by Derek Yerger's code, though I don't
think much of that remains.  In any event that was..
    (copyleft) 2006 by Derek Yerger - Free to distribute freely.

The CRC code was excerpted and inspired by the Dallas Semiconductor 
sample code bearing this copyright.
//---------------------------------------------------------------------------
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//--------------------------------------------------------------------------
*/

#include "OneWire.h"

#if defined(ARDUINO) && ARDUINO >= 100
  #include "Arduino.h"
#else
  #include "WProgram.h"
  #include "pins_arduino.h"
#endif 

extern "C" {
#include <avr/io.h>
}

OneWire::OneWire( uint8_t pinArg)
{
    pin = pinArg;
    port = digitalPinToPort(pin);
    bitmask =  digitalPinToBitMask(pin);
    outputReg = portOutputRegister(port);
    inputReg = portInputRegister(port);
    modeReg = portModeRegister(port);
#if ONEWIRE_SEARCH
    reset_search();
#endif
}

//
// 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;
//
// Returns 1 if a device asserted a presence pulse, 0 otherwise.
//
uint8_t OneWire::reset() {
    uint8_t r;
    uint8_t retries = 125;

    // wait until the wire is high... just in case
    pinMode(pin,INPUT);
    do {
	if ( retries-- == 0) return 0;
	delayMicroseconds(2); 
    } while( !digitalRead( pin));
    
    digitalWrite(pin,0);   // pull low for 500uS
    pinMode(pin,OUTPUT);
    delayMicroseconds(500);
    pinMode(pin,INPUT);
    delayMicroseconds(65);
    r = !digitalRead(pin);
    delayMicroseconds(490);
    return r;
}

//
// Write a bit. Port and bit is used to cut lookup time and provide
// more certain timing.
//
void OneWire::write_bit(uint8_t v) {
    static uint8_t lowTime[] = { 55, 5 };
    static uint8_t highTime[] = { 5, 55};
    
    v = (v&1);
    *modeReg |= bitmask;  // make pin an output, do first since we
                          // expect to be at 1
    *outputReg &= ~bitmask; // zero
    delayMicroseconds(lowTime[v]);
    *outputReg |= bitmask; // one, push pin up - important for
                           // parasites, they might start in here
    delayMicroseconds(highTime[v]);
}

//
// Read a bit. Port and bit is used to cut lookup time and provide
// more certain timing.
//
uint8_t OneWire::read_bit() {
    uint8_t r;
    
    *modeReg |= bitmask;    // make pin an output, do first since we expect to be at 1
    *outputReg &= ~bitmask; // zero
    delayMicroseconds(1);
    *modeReg &= ~bitmask;     // let pin float, pull up will raise
    delayMicroseconds(5);          // A "read slot" is when 1mcs > t > 2mcs
    r = ( *inputReg & bitmask) ? 1 : 0; // check the bit
    delayMicroseconds(50);        // whole bit slot is 60-120uS, need to give some time
    
    return r;
}

//
// Write a byte. The writing code uses the active drivers to raise the
// pin high, if you need power after the write (e.g. DS18S20 in
// parasite power mode) then set 'power' to 1, otherwise the pin will
// go tri-state at the end of the write to avoid heating in a short or
// other mishap.
//
void OneWire::write(uint8_t v, uint8_t power) {
    uint8_t bitMask;
    
    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
	OneWire::write_bit( (bitMask & v)?1:0);
    }
    if ( !power) {
	pinMode(pin,INPUT);
	digitalWrite(pin,0);
    }
}

//
// 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;
}

//
// Do a ROM select
//
void OneWire::select( uint8_t rom[8])
{
    int i;

    write(0x55,0);         // Choose ROM

    for( i = 0; i < 8; i++) write(rom[i],0);
}

//
// Do a ROM skip
//
void OneWire::skip()
{
    write(0xCC,0);         // Skip ROM
}

void OneWire::depower()
{
    pinMode(pin,INPUT);
}

#if ONEWIRE_SEARCH

//
// You need to use this function to start a search again from the beginning.
// You do not need to do it for the first search, though you could.
//
void OneWire::reset_search()
{
    uint8_t i;
    
    searchJunction = -1;
    searchExhausted = 0;
    for( i = 7; ; i--) {
	address[i] = 0;
	if ( i == 0) break;
    }
}

//
// Perform a search. If this function returns a '1' then it has
// enumerated the next device and you may retrieve the ROM from the
// OneWire::address variable. If there are no devices, no further
// devices, or something horrible happens in the middle of the
// enumeration then a 0 is returned.  If a new device is found then
// its address is copied to newAddr.  Use OneWire::reset_search() to
// start over.
// 
uint8_t OneWire::search(uint8_t *newAddr)
{
    uint8_t i;
    char lastJunction = -1;
    uint8_t done = 1;
    
    if ( searchExhausted) return 0;
    
    if ( !reset()) return 0;
    write( 0xf0, 0);
    
    for( i = 0; i < 64; i++) {
	uint8_t a = read_bit( );
	uint8_t nota = read_bit( );
	uint8_t ibyte = i/8;
	uint8_t ibit = 1<<(i&7);
	
	if ( a && nota) return 0;  // I don't think this should happen, this means nothing responded, but maybe if
	// something vanishes during the search it will come up.
	if ( !a && !nota) {
	    if ( i == searchJunction) {   // this is our time to decide differently, we went zero last time, go one.
		a = 1;
		searchJunction = lastJunction;
	    } else if ( i < searchJunction) {   // take whatever we took last time, look in address
		if ( address[ ibyte]&ibit) a = 1;
		else {                            // Only 0s count as pending junctions, we've already exhasuted the 0 side of 1s
		    a = 0;
		    done = 0;
		    lastJunction = i;
		}
	    } else {                            // we are blazing new tree, take the 0
		a = 0;
		searchJunction = i;
		done = 0;
	    }
	    lastJunction = i;
	}
	if ( a) address[ ibyte] |= ibit;
	else address[ ibyte] &= ~ibit;
	
	write_bit( a);
    }
    if ( done) searchExhausted = 1;
    for ( i = 0; i < 8; i++) newAddr[i] = address[i];
    return 1;  
}
#endif

#if ONEWIRE_CRC
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
//

#if ONEWIRE_CRC8_TABLE
// This table comes from Dallas sample code where it is freely reusable, 
// though Copyright (C) 2000 Dallas Semiconductor Corporation
static uint8_t dscrc_table[] = {
      0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
    157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
     35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
    190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
     70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
    219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
    101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
    248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
    140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
     17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
    175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
     50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
    202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
     87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
    233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
    116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};

//
// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
// and the registers.  (note: this might better be done without to
// table, it would probably be smaller and certainly fast enough
// compared to all those delayMicrosecond() calls.  But I got
// confused, so I use this table from the examples.)  
//
uint8_t OneWire::crc8( uint8_t *addr, uint8_t len)
{
    uint8_t i;
    uint8_t crc = 0;
    
    for ( i = 0; i < len; i++) {
	crc  = dscrc_table[ crc ^ addr[i] ];
    }
    return crc;
}
#else
//
// Compute a Dallas Semiconductor 8 bit CRC directly. 
//
uint8_t OneWire::crc8( uint8_t *addr, uint8_t len)
{
    uint8_t i, j;
    uint8_t crc = 0;
    
    for (i = 0; i < len; i++) {
        uint8_t inbyte = addr[i];
        for (j = 0; j < 8; j++) {
            uint8_t mix = (crc ^ inbyte) & 0x01;
            crc >>= 1;
            if (mix) crc ^= 0x8C;
            inbyte >>= 1;
        }
    }
    return crc;
}
#endif

#if ONEWIRE_CRC16
static short oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };

//
// Compute a Dallas Semiconductor 16 bit CRC. I have never seen one of
// these, but here it is.
//
unsigned short OneWire::crc16(unsigned short *data, unsigned short len)
{
    unsigned short i;
    unsigned short crc = 0;
    
    for ( i = 0; i < len; i++) {
	unsigned short cdata = data[len];
	
	cdata = (cdata ^ (crc & 0xff)) & 0xff;
	crc >>= 8;
	
	if (oddparity[cdata & 0xf] ^ oddparity[cdata >> 4]) crc ^= 0xc001;
	
	cdata <<= 6;
	crc ^= cdata;
	cdata <<= 1;
	crc ^= cdata;
    }
    return crc;
}
#endif

#endif 

КОД  РАБОЧИЙ ЭТОТ:

//100% рабочий алгоритм

#include <OneWire.h>
OneWire  ds(A2);  // линия 1-Wire будет на этом пине

byte ndT = 9;     // сколько датчиков подключили

// список адресов моих датчиков для теста
byte DeviceAddres[9][8]={                        // ...а датчиков у мня 7 штук в наличии
  {0x28, 0xB2, 0x29, 0xD7, 0x8, 0x0, 0x0, 0xDE}, // датчик с нулевым адресом, 1й
  {0x28, 0x96, 0x66, 0xFA, 0x8, 0x0, 0x0, 0xDF}, // адрес 1,  2й  второй по порядку
  {0x28, 0x3F, 0x43, 0xD6, 0x8, 0x0, 0x0, 0x5D}, // 3й
  {0x28,  0xD, 0xFD, 0xD7, 0x8, 0x0, 0x0, 0xBC}, // 4й
  {0x28, 0x28, 0x88, 0xD6, 0x8, 0x0, 0x0, 0xD1}, // 5й
  {0x28, 0xF1, 0xD0, 0xFA, 0x8, 0x0, 0x0, 0xD7}, // 6й - нет такого у меня, просто для теста кода
  {0x28, 0x2B, 0xB7, 0xF7, 0x8, 0x0, 0x0, 0x9C}, // 7й
  {0x28, 0xF1, 0xD0, 0xF7, 0x8, 0x0, 0x0, 0xD7}, // 8й
  {0x28, 0xF1, 0xF1, 0xF1, 0x8, 0x0, 0x0, 0xD7}, // 9й - нет такого у меня, просто для теста кода
};

byte CheckDevicePresent[9]= {0, 0, 0, 0, 0, 0, 0, 0, 0}; // заняли место в памяти под проверку датчиков и переменные
byte data[12];                                           // переменная для данных из датчика
int SignBit;           // знак температуры
int Tc_100;            // умножено на 100 целочисленное значение температуры, т.е. запятая "подвинута >>" на 2 знака

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


void loop() 
{

  for (byte i = 0; i < ndT; i++) // чИпЯтаем шапку таблЫцЫ
  {
                 Serial.print( "Device №" );
                 Serial.print( i );
                 Serial.print( "   " );
  }
  
  for (byte i = 0; i < ndT; i++) // проверяем все датчикии, запускаем АЦП
  {
     start_conversion_T (i);
  }

          Serial.println( " " ); 
          Serial.print( "   " ); 
               for (byte i = 0; i < ndT; i++) // пичатаем для самоконтроля
                {
                 Serial.print( CheckDevicePresent[i] , DEC );
                 Serial.print( "            " );
                }
Serial.println( " " );
   delay (1000); // задержка на конвертацию

   

  for (byte i = 0; i < ndT; i++) // получаем и печатаем результаты
  {
       GETTEMP (i); // получаем температуру


  if (CheckDevicePresent[i] != 0)   //если датчик не нашли
  {Serial.print( "   NC        " );}
  else
  {
  if (SignBit) {Serial.print( "  -" );}
  else         {Serial.print( "   " );}
  Serial.print( Tc_100 );
  Serial.print( "      " ); 
  }
  }


                 Serial.println( " " );
                 Serial.println( " " );
                 Serial.println( " " );
   delay (30000); // задержка на конвертацию !!!!!! :))))))))))))))

} // конй loop




void start_conversion_T (byte No) 
{

  ds.reset();
  ds.select(DeviceAddres[No]);
  ds.write(0xBE);    // Считываем ОЗУ датчика  

  for (byte i = 0; i < 9; i++)    // читаем в байты, 9 байт ОЗУ датчика
  {     data[i] = ds.read();   }

  if (data[4] == 255) // проверяем CRC 8 или CONFIG 4 если его нет, нет датчика = 255 (0xFF)
                      // в конфиге бит 1 всегда 0, а CRC может быть 255
  {
      CheckDevicePresent[No]= 1; // пишем, что датчика нет
     return;
  } 

  CheckDevicePresent[No]= 0;  // датчик есть
  ds.reset();
  ds.select(DeviceAddres[No]);
  ds.write(0x44, 1);   // Запускаем конвертацию (получение температуры)
}


void GETTEMP (byte No)
{
  
  if (CheckDevicePresent[No] != 0) // если датчика нет, пропускаем
  {   return;    }
  
  ds.reset();
  ds.select(DeviceAddres[No]);
  ds.write(0xBE);    // Считываем ОЗУ датчика  

  for (byte i = 0; i < 9; i++)    // Обрабатываем 9 байт
  {     data[i] = ds.read();   }
  
  // Высчитываем температуру :)

  int TReading;
  TReading = (data[1] << 8) + data[0]; // собираем в кучку оба байта
  
   SignBit = TReading & 0x8000;      // Проверяем дубак там или нет
   if (SignBit)                      // Если на улице дубак :)
   { TReading = (TReading ^ 0xffff) + 1;  }

// Tc_100 - это температура из датчика формата int со сдвинутой запятой на 2 знака 
   Tc_100 = (6 * TReading) + TReading / 4;     // Умножаем на (100 * 0.0625) или 6.25
                                               // т.е. убираем 4 лишних бита
}

ПРОБЛЕМА:  не могу избавиться от чтения   9 байт ОЗУ при поиске датчиков. Строки  89 - 96.

перечитал даташит - идеи не появились,  может я чего не понял :( .

UP