diff --git a/README.md b/README.md index b95fb00..9fbead9 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ -[![Foo](https://img.shields.io/badge/Version-3.8-brightgreen.svg?style=flat-square)](#versions) +[![Foo](https://img.shields.io/badge/Version-3.9-brightgreen.svg?style=flat-square)](#versions) [![Foo](https://img.shields.io/badge/Website-AlexGyver.ru-blue.svg?style=flat-square)](https://alexgyver.ru/) [![Foo](https://img.shields.io/badge/%E2%82%BD$%E2%82%AC%20%D0%9D%D0%B0%20%D0%BF%D0%B8%D0%B2%D0%BE-%D1%81%20%D1%80%D1%8B%D0%B1%D0%BA%D0%BE%D0%B9-orange.svg?style=flat-square)](https://alexgyver.ru/support_alex/) # microDS18B20 -Легкая и простая библиотека для работы с 1-Wire (OneWire) термометрами Dallas DS18B20 +Легкая библиотека для работы с 1-Wire (OneWire) термометрами Dallas DS18B20 - Работа с несколькими датчиками на одном пине (режим адресации) +- Хранение массива адресов в PROGMEM памяти - Работа с одним датчиком на пине (без использования адресации) - Расчет температуры в целых числах и с плавающей точкой +- Чтение сырых данных для случаев сильной экономии памяти - Проверка корректности полученной температуры - Настраиваемое разрешение преобразования -- Чтение сырых данных для случаев сильной экономии памяти -- Проверка подлинности данных "на лету", с использованием CRC8 от Dallas -- Расчет CRC8 (~6 мкс) или чтение из таблицы (<1 мкс + 256 байт flash) +- Проверка подлинности данных +- Проверка корректности работы датчика ### Совместимость Совместима со всеми Arduino платформами (используются Arduino-функции) @@ -42,9 +43,22 @@ ## Инициализация ```cpp -MicroDS18B20 ds; // один датчик на одном пине без адресации -MicroDS18B20 ds; // несколько датчиков на одном пине с адресацией -MicroDS18B20; // говорим, что датчик с адресацией, но не указываем адрес +// один датчик на пине без адресации +MicroDS18B20 ds; + +// несколько датчиков на пине с адресацией, указываем адрес (адрес - массив uint8_t) +MicroDS18B20 ds; + +// указываем, что будем работать с адресацией. Сам адрес передадим позже (в setAddress()) +MicroDS18B20; + +// указываем, что будем работать с адресацией, и на линии будет несколько (amount) датчиков +// см. пример async_read_many_bus +MicroDS18B20; + +// указываем, что будем работать с адресацией, на линии будет несколько (amount) датчиков, а адреса будем хранить в PROGMEM +// см. пример async_read_many_bus_pgm +MicroDS18B20; ``` @@ -53,7 +67,7 @@ MicroDS18B20; // говорим, что дат // ============= МЕТОДЫ КЛАССА ============= bool readAddress(uint8_t *addressArray); // Прочитать уникальный адрес термометра в массив. [true, если успешно] void setAddress(uint8_t *addr); // установить (сменить) адрес -void setResolution(uint8_t resolution); // Установить разрешение термометра 9-12 бит +void setResolution(uint8_t resolution); // Установить разрешение 9-12 бит bool online(); // проверить связь с датчиком (true - датчик онлайн). Шина должна быть подтянута void requestTemp(); // Запросить новое преобразование температуры @@ -63,6 +77,20 @@ float getTemp(); // получить значение int16_t getTempInt(); // получить значение температуры в int int16_t getRaw(); // получить "сырое" значение температуры (в 16 раз больше, чем реальная температура) +// ======= МЕТОДЫ ДЛЯ ШИНЫ ДАТЧИКОВ ======= +// см. примеры async_read_many_bus и async_read_many_bus_pgm +void setResolutionAll(uint8_t res); // Установить разрешение 9-12 бит у всех датчиков на линии +void setResolution(uint8_t resolution, uint8_t idx); // Установить разрешение 9-12 бит (датчик под номером idx) +bool online(uint8_t idx); // проверить связь (датчик под номером idx) + +void requestTempAll(); // запрос температуры у всех датчиков на линии +void requestTemp(uint8_t idx); // Запросить новое преобразование температуры (датчик под номером idx) +bool readTemp(uint8_t idx); // прочитать температуру с датчика (датчик под номером idx) + +float getTemp(uint8_t idx); // получить значение температуры в float (датчик под номером idx) +int16_t getTempInt(uint8_t idx); // получить значение температуры в int (датчик под номером idx) +int16_t getRaw(uint8_t idx); // получить "сырое" значение температуры (датчик под номером idx) + // =========== ФУНКЦИИ ВНЕ КЛАССА =========== int DS_rawToInt(int data); // преобразовать raw данные в температуру int float DS_rawToFloat(int data); // преобразовать raw данные в температуру float @@ -124,6 +152,13 @@ if (sensor.readTemp()) value = sensor.getTemp(); где `readTemp()` запрашивает данные с датчика и возвращает `true`, если они прочитаны корректно. После этого можно забрать текущую температуру из `getTemp()`, которая уже не запрашивает температуру с датчика, а отдаёт прочитанный в `readTemp()` результат. +#### Подключаем много датчиков на один объект +В версии библиотеки 3.9 появилась возможность подключить сколько угодно датчиков на один объект MicroDS18B20, не создавая массива объектов (как в старых версиях). +Нужно создать двумерный массив адресов и передать его в библиотеку, также указав количество датчиков на линии (можно максимальное, если оно будет меняться в процессе работы программы). +Это позволяет сэкономить немного памяти, но можно пойти дальше - засунуть массив адресов датчиков в PROGMEM, чтобы они не висели в оперативной памяти. +Инициализация в этом случае выглядит так: `MicroDS18B20<пин, DS_ADDR_MODE, колич-во>;` или `MicroDS18B20<пин, DS_ADDR_MODE, колич-во, DS_PROGMEM>;` для PROGMEM режима. +Адреса передаются в `setAddress()`, а для опроса просто передаём индекс датчика в те же функции что и раньше. Смотри примеры *async_read_many_bus*, *async_read_many_bus_pgm* и раздел документации *МЕТОДЫ ДЛЯ ШИНЫ ДАТЧИКОВ*. + ## Подключение ![scheme](/doc/scheme.png) @@ -316,6 +351,7 @@ void loop() { - v3.6 - исправлена ошибка компиляции, добавлена поддержка GyverCore (спасибо ArtemiyKolobov) - v3.7 - исправлена ошибка readTemp() при 0 градусов - v3.8 - небольшая оптимизация. Совместимость с ESP32 +- v3.9 - добавил расширенный режим адресации и хранение адресов в PROGMEM ## Баги и обратная связь diff --git a/examples/async_read_many_bus/async_read_many_bus.ino b/examples/async_read_many_bus/async_read_many_bus.ino new file mode 100644 index 0000000..48fa3c7 --- /dev/null +++ b/examples/async_read_many_bus/async_read_many_bus.ino @@ -0,0 +1,42 @@ +// работа в режиме шины - несколько датчиков на линии, один объект +// количество датчиков для удобства +#define SENS_AMOUNT 5 + +// создаём двухмерный массив с адресами +uint8_t addr[][8] = { + {0x28, 0xFF, 0x78, 0x5B, 0x50, 0x17, 0x4, 0xCF}, + {0x28, 0xFF, 0x99, 0x80, 0x50, 0x17, 0x4, 0x4D}, + {0x28, 0xFF, 0x53, 0xE5, 0x50, 0x17, 0x4, 0xC3}, + {0x28, 0xFF, 0x42, 0x5A, 0x51, 0x17, 0x4, 0xD2}, + {0x28, 0xFF, 0xCD, 0x59, 0x51, 0x17, 0x4, 0xFE}, +}; + +#include +MicroDS18B20 sensors; + +void setup() { + Serial.begin(9600); + // устанавливаем адреса + sensors.setAddress((uint8_t*)addr); + + // Установить разрешение 9-12 бит у всех датчиков на линии + //sensors.setResolutionAll(10); +} + +void loop() { + // конструкция программного таймера на 1c + static uint32_t tmr; + if (millis() - tmr >= 1000) { + tmr = millis(); + + // выводим показания в порт + for (int i = 0; i < SENS_AMOUNT; i++) { + Serial.print(sensors.getTemp(i)); + Serial.print(','); + } + Serial.println(); + + // запрашиваем новые + sensors.requestTempAll(); + } +} diff --git a/examples/async_read_many_bus_pgm/async_read_many_bus_pgm.ino b/examples/async_read_many_bus_pgm/async_read_many_bus_pgm.ino new file mode 100644 index 0000000..52b641f --- /dev/null +++ b/examples/async_read_many_bus_pgm/async_read_many_bus_pgm.ino @@ -0,0 +1,42 @@ +// работа в режиме шины - несколько датчиков на линии, один объект +// количество датчиков для удобства +#define SENS_AMOUNT 5 + +// создаём двухмерный массив с адресами +const uint8_t addr[][8] PROGMEM = { + {0x28, 0xFF, 0x78, 0x5B, 0x50, 0x17, 0x4, 0xCF}, + {0x28, 0xFF, 0x99, 0x80, 0x50, 0x17, 0x4, 0x4D}, + {0x28, 0xFF, 0x53, 0xE5, 0x50, 0x17, 0x4, 0xC3}, + {0x28, 0xFF, 0x42, 0x5A, 0x51, 0x17, 0x4, 0xD2}, + {0x28, 0xFF, 0xCD, 0x59, 0x51, 0x17, 0x4, 0xFE}, +}; + +#include +MicroDS18B20 sensors; + +void setup() { + Serial.begin(9600); + // устанавливаем адреса + sensors.setAddress((uint8_t*)addr); + + // Установить разрешение 9-12 бит у всех датчиков на линии + //sensors.setResolutionAll(10); +} + +void loop() { + // конструкция программного таймера на 1c + static uint32_t tmr; + if (millis() - tmr >= 1000) { + tmr = millis(); + + // выводим показания в порт + for (int i = 0; i < SENS_AMOUNT; i++) { + Serial.print(sensors.getTemp(i)); + Serial.print(','); + } + Serial.println(); + + // запрашиваем новые + sensors.requestTempAll(); + } +} diff --git a/keywords.txt b/keywords.txt index 2ff55f9..7cf5d75 100644 --- a/keywords.txt +++ b/keywords.txt @@ -20,6 +20,8 @@ getTemp KEYWORD2 getTempInt KEYWORD2 getRaw KEYWORD2 online KEYWORD2 +requestTempAll KEYWORD2 +setResolutionAll KEYWORD2 DS_rawToInt KEYWORD2 DS_rawToFloat KEYWORD2 @@ -29,4 +31,5 @@ DS_rawToFloat KEYWORD2 ####################################### DS_CHECK_CRC LITERAL1 DS_CRC_USE_TABLE LITERAL1 -DS_ADDR_MODE LITERAL1 \ No newline at end of file +DS_ADDR_MODE LITERAL1 +DS_PROGMEM LITERAL1 \ No newline at end of file diff --git a/library.properties b/library.properties index f34e6c2..0cd5aa8 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=microDS18B20 -version=3.8 +version=3.9 author=AlexGyver maintainer=AlexGyver sentence=Light library for DS18b20 sensor diff --git a/src/microDS18B20.h b/src/microDS18B20.h index 5e7d1f0..d7e70df 100644 --- a/src/microDS18B20.h +++ b/src/microDS18B20.h @@ -1,16 +1,17 @@ /* - Легкая и простая в обращении библиотека для работы с 1-Wire термометрами DS18B20 + Легкая библиотека для работы с 1-Wire (OneWire) термометрами Dallas DS18B20 Документация: GitHub: https://github.com/GyverLibs/microDS18B20 Возможности: - Работа с несколькими датчиками на одном пине (режим адресации) + - Хранение массива адресов в PROGMEM памяти - Работа с одним датчиком на пине (без использования адресации) - Расчет температуры в целых числах и с плавающей точкой + - Чтение сырых данных для случаев сильной экономии памяти - Проверка корректности полученной температуры - Настраиваемое разрешение преобразования - - Чтение сырых данных для случаев сильной экономии памяти - - Проверка подлинности данных "на лету", с использованием CRC8 от Dallas - - Расчет CRC8 (~6 мкс) или чтение из таблицы (<1 мкс + 256 байт flash) + - Проверка подлинности данных + - Проверка корректности работы датчика Egor 'Nich1con' Zakharov & AlexGyver, alex@alexgyver.ru https://alexgyver.ru/ @@ -27,6 +28,7 @@ v3.6 - исправлена ошибка компиляции, добавлена поддержка GyverCore (спасибо ArtemiyKolobov) v3.7 - исправлена ошибка readTemp() при 0 градусов v3.8 - небольшая оптимизация. Совместимость с ESP32 + v3.9 - добавил расширенный режим адресации и хранение адресов в PROGMEM */ #ifndef _microDS18B20_h @@ -35,6 +37,8 @@ #include "microOneWire.h" #include "DS_raw.h" +#define DS_PROGMEM 1 + #ifndef DS_CHECK_CRC #define DS_CHECK_CRC true // true/false - проверка контрольной суммы принятых данных - надежнее, но тратит немного больше flash #endif @@ -89,12 +93,12 @@ static const uint8_t PROGMEM _ds_crc8_table[] = { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" -static uint8_t _empDsAddr[1] = {1}; +uint8_t _empDsAddr[1] = {1}; #pragma GCC diagnostic pop #define DS_ADDR_MODE _empDsAddr // ====================== CLASS ====================== -template +template class MicroDS18B20 { public: MicroDS18B20() { @@ -103,15 +107,20 @@ class MicroDS18B20 { } // Установить разрешение термометра 9-12 бит - void setResolution(uint8_t res) { + void setResolution(uint8_t res, uint8_t idx = 0) { if (!oneWire_reset(DS_PIN)) return; // Проверка присутствия - addressRoutine(); // Процедура адресации + addressRoutine(idx); // Процедура адресации oneWire_write(0x4E, DS_PIN); // Запись RAM oneWire_write(0xFF, DS_PIN); // Максимум в верхний регистр тревоги oneWire_write(0x00, DS_PIN); // Минимум в верхний регистр тревоги oneWire_write(((constrain(res, 9, 12) - 9) << 5) | 0x1F, DS_PIN); // Запись конфигурации разрешения } + // Установить разрешение термометра 9-12 бит у всех датчиков на линии + void setResolutionAll(uint8_t res) { + for (int i = 0; i < DS_AM; i++) setResolution(res, i); + } + // установить адрес void setAddress(uint8_t *addr) { _addr = addr; @@ -134,36 +143,41 @@ class MicroDS18B20 { } // запрос температуры - void requestTemp() { - state = 0; // запрошена новая температура + void requestTemp(uint8_t idx = 0) { + state[idx] = 0; // запрошена новая температура if (!oneWire_reset(DS_PIN)) return; // Проверка присутствия - addressRoutine(); // Процедура адресации + addressRoutine(idx); // Процедура адресации oneWire_write(0x44, DS_PIN); // Запросить преобразование } + // запрос температуры у всех датчиков на линии + void requestTempAll() { + for (int i = 0; i < DS_AM; i++) requestTemp(i); + } + // получить температуру float - float getTemp() { - if (!state) readTemp(); - return (_buf / 16.0); + float getTemp(uint8_t idx = 0) { + if (!state[idx]) readTemp(idx); + return (_buf[idx] / 16.0); } // получить температуру int - int16_t getTempInt() { - if (!state) readTemp(); - return (_buf >> 4); + int16_t getTempInt(uint8_t idx = 0) { + if (!state[idx]) readTemp(idx); + return (_buf[idx] >> 4); } // получить "сырое" значение температуры - int16_t getRaw() { - if (!state) readTemp(); - return _buf; + int16_t getRaw(uint8_t idx = 0) { + if (!state[idx]) readTemp(idx); + return _buf[idx]; } // прочитать температуру с датчика. true если успешно - bool readTemp() { - state = 1; + bool readTemp(uint8_t idx = 0) { + state[idx] = 1; if (!oneWire_reset(DS_PIN)) return 0; // датчик оффлайн - addressRoutine(); // Процедура адресации + addressRoutine(idx); // Процедура адресации oneWire_write(0xBE, DS_PIN); // Запросить температуру uint8_t crc = 0; // обнуляем crc int16_t temp; // переменная для расчёта температуры @@ -178,15 +192,15 @@ class MicroDS18B20 { else if (i == 1) temp |= (data << 8); } if (sum == 0x8F7 || !sum || crc) return 0; // датчик оффлайн или данные повреждены - if (temp != 0x0550) _buf = temp; // пропускаем первое чтение (85 градусов) + if (temp != 0x0550) _buf[idx] = temp; // пропускаем первое чтение (85 градусов) return 1; } // проверить связь с датчиком (true - датчик на линии). ЛИНИЯ ДОЛЖНА БЫТЬ ПОДТЯНУТА - bool online() { - if (DS_ADDR) { + bool online(uint8_t idx = 0) { + if (DS_ADDR != nullptr) { if (!oneWire_reset(DS_PIN)) return 0; - addressRoutine(); + addressRoutine(idx); oneWire_write(0xBE, DS_PIN); uint16_t sum = 0; for (uint8_t i = 0; i < 5; i++) sum += oneWire_read(DS_PIN); @@ -195,14 +209,17 @@ class MicroDS18B20 { } private: - bool state = 0; + bool state[DS_AM]; + int16_t _buf[DS_AM]; uint8_t *_addr = DS_ADDR; - int16_t _buf = 0; - void addressRoutine() { // Процедура адресации - if (DS_ADDR) { // Адрес определен? + void addressRoutine(uint8_t idx) { // Процедура адресации + if (DS_ADDR != nullptr) { // Адрес определен? oneWire_write(0x55, DS_PIN); // Говорим термометрам слушать адрес - for (uint8_t i = 0; i < 8; i++) oneWire_write(_addr[i], DS_PIN); // Отправляем адрес + for (uint8_t i = 0; i < 8; i++) { + if (DS_PGM) oneWire_write(pgm_read_byte(&_addr[i + idx * 8]), DS_PIN); + else oneWire_write(_addr[i + idx * 8], DS_PIN); + } } else oneWire_write(0xCC, DS_PIN); // Адреса нет - пропускаем адресацию на линии }