// ----------------------------------------------------------------------------- // Dallas OneWire Sensor // Uses OneWire library // Copyright (C) 2017-2019 by Xose PĂ©rez // ----------------------------------------------------------------------------- #if SENSOR_SUPPORT && DALLAS_SUPPORT #pragma once #include #include #include #include "BaseSensor.h" #define DS_CHIP_DS18S20 0x10 #define DS_CHIP_DS2406 0x12 #define DS_CHIP_DS1822 0x22 #define DS_CHIP_DS18B20 0x28 #define DS_CHIP_DS1825 0x3B #define DS_DATA_SIZE 9 #define DS_PARASITE 1 #define DS_DISCONNECTED -127 #define DS_CMD_START_CONVERSION 0x44 #define DS_CMD_READ_SCRATCHPAD 0xBE // ====== DS2406 specific constants ======= #define DS2406_CHANNEL_ACCESS 0xF5; // CHANNEL CONTROL BYTE // 7 6 5 4 3 2 1 0 // ALR IM TOG IC CHS1 CHS0 CRC1 CRC0 // 0 1 0 0 0 1 0 1 0x45 // CHS1 CHS0 Description // 0 0 (not allowed) // 0 1 channel A only // 1 0 channel B only // 1 1 both channels interleaved // TOG IM CHANNELS EFFECT // 0 0 one channel Write all bits to the selected channel // 0 1 one channel Read all bits from the selected channel // 1 0 one channel Write 8 bits, read 8 bits, write, read, etc. to/from the selected channel // 1 1 one channel Read 8 bits, write 8 bits, read, write, etc. from/to the selected channel // 0 0 two channels Repeat: four times (write A, write B) // 0 1 two channels Repeat: four times (read A, read B) // 1 0 two channels Four times: (write A, write B), four times: (readA, read B), write, read, etc. // 1 1 two channels Four times: (read A, read B), four times: (write A, write B), read, write, etc. // CRC1 CRC0 Description // 0 0 CRC disabled (no CRC at all) // 0 1 CRC after every byte // 1 0 CRC after 8 bytes // 1 1 CRC after 32 bytes #define DS2406_CHANNEL_CONTROL_BYTE 0x45; #define DS2406_STATE_BUF_LEN 7 class DallasSensor : public BaseSensor { public: // --------------------------------------------------------------------- // Public // --------------------------------------------------------------------- DallasSensor() { _sensor_id = SENSOR_DALLAS_ID; } ~DallasSensor() { if (_wire) delete _wire; gpioUnlock(_gpio); } // --------------------------------------------------------------------- void setGPIO(unsigned char gpio) { if (_gpio == gpio) return; _gpio = gpio; _dirty = true; } // --------------------------------------------------------------------- unsigned char getGPIO() { return _gpio; } // --------------------------------------------------------------------- // Sensor API // --------------------------------------------------------------------- // Initialization method, must be idempotent void begin() { if (!_dirty) return; // Manage GPIO lock if (_previous != GPIO_NONE) { gpioUnlock(_previous); } _previous = GPIO_NONE; if (!gpioLock(_gpio)) { _error = SENSOR_ERROR_GPIO_USED; return; } // OneWire if (_wire) delete _wire; _wire = new OneWire(_gpio); // Search devices loadDevices(); // If no devices found check again pulling up the line if (_count == 0) { pinMode(_gpio, INPUT_PULLUP); loadDevices(); } // Check connection if (_count == 0) { gpioUnlock(_gpio); } else { _previous = _gpio; } _ready = true; _dirty = false; } // Loop-like method, call it in your main loop void tick() { static unsigned long last = 0; if (millis() - last < DALLAS_READ_INTERVAL) return; last = millis(); // Every second we either start a conversion or read the scratchpad static bool conversion = true; if (conversion) { // Start conversion _wire->reset(); _wire->skip(); _wire->write(DS_CMD_START_CONVERSION, DS_PARASITE); } else { // Read scratchpads for (unsigned char index=0; index<_devices.size(); index++) { if (_devices[index].address[0] == DS_CHIP_DS2406) { uint8_t data[DS2406_STATE_BUF_LEN]; // Read scratchpad if (_wire->reset() == 0) { // Force a CRC check error _devices[index].data[0] = _devices[index].data[0] + 1; return; } _wire->select(_devices[index].address); data[0] = DS2406_CHANNEL_ACCESS; data[1] = DS2406_CHANNEL_CONTROL_BYTE; data[2] = 0xFF; _wire->write_bytes(data,3); // 3 cmd bytes, 1 channel info byte, 1 0x00, 2 CRC16 for(int i = 3; iread(); } // Read scratchpad if (_wire->reset() == 0) { // Force a CRC check error _devices[index].data[0] = _devices[index].data[0] + 1; return; } memcpy(_devices[index].data, data, DS2406_STATE_BUF_LEN); } else { // Read scratchpad if (_wire->reset() == 0) { // Force a CRC check error _devices[index].data[0] = _devices[index].data[0] + 1; return; } _wire->select(_devices[index].address); _wire->write(DS_CMD_READ_SCRATCHPAD); uint8_t data[DS_DATA_SIZE]; for (unsigned char i = 0; i < DS_DATA_SIZE; i++) { data[i] = _wire->read(); } if (_wire->reset() != 1) { // Force a CRC check error _devices[index].data[0] = _devices[index].data[0] + 1; return; } memcpy(_devices[index].data, data, DS_DATA_SIZE); } } } conversion = !conversion; } // Descriptive name of the sensor String description() { char buffer[20]; snprintf(buffer, sizeof(buffer), "Dallas @ GPIO%d", _gpio); return String(buffer); } // Address of the device String address(unsigned char index) { char buffer[20] = {0}; if (index < _count) { uint8_t * address = _devices[index].address; snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X%02X%02X", address[0], address[1], address[2], address[3], address[4], address[5], address[6], address[7] ); } return String(buffer); } // Descriptive name of the slot # index String description(unsigned char index) { if (index < _count) { char buffer[40]; uint8_t * address = _devices[index].address; snprintf(buffer, sizeof(buffer), "%s (%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%d", chipAsString(index).c_str(), address[0], address[1], address[2], address[3], address[4], address[5], address[6], address[7], _gpio ); return String(buffer); } return String(); } // Type for slot # index unsigned char type(unsigned char index) { if (index < _count) { if (chip(index) == DS_CHIP_DS2406) { return MAGNITUDE_DIGITAL; } else { return MAGNITUDE_TEMPERATURE; } } return MAGNITUDE_NONE; } // Number of decimals for a magnitude (or -1 for default) signed char decimals(sensor::Unit unit) { switch (unit) { // Smallest increment is 0.0625 C, so 2 decimals case sensor::Unit::Celcius: return 2; // In case we have DS2406, there is no decimal places default: return 0; } } // Pre-read hook (usually to populate registers with up-to-date data) void pre() { _error = SENSOR_ERROR_OK; } // Current value for slot # index double value(unsigned char index) { if (index >= _count) return 0; uint8_t * data = _devices[index].data; if (chip(index) == DS_CHIP_DS2406) { if (!OneWire::check_crc16(data, 5, &data[5])) { _error = SENSOR_ERROR_CRC; return 0; } // 3 cmd bytes, 1 channel info byte, 1 0x00, 2 CRC16 // CHANNEL INFO BYTE // Bit 7 : Supply Indication 0 = no supply // Bit 6 : Number of Channels 0 = channel A only // Bit 5 : PIO-B Activity Latch // Bit 4 : PIO-A Activity Latch // Bit 3 : PIO B Sensed Level // Bit 2 : PIO A Sensed Level // Bit 1 : PIO-B Channel Flip-Flop Q // Bit 0 : PIO-A Channel Flip-Flop Q return (data[3] & 0x04) != 0; } if (OneWire::crc8(data, DS_DATA_SIZE-1) != data[DS_DATA_SIZE-1]) { _error = SENSOR_ERROR_CRC; return 0; } // Registers // byte 0: temperature LSB // byte 1: temperature MSB // byte 2: high alarm temp // byte 3: low alarm temp // byte 4: DS18S20: store for crc // DS18B20 & DS1822: configuration register // byte 5: internal use & crc // byte 6: DS18S20: COUNT_REMAIN // DS18B20 & DS1822: store for crc // byte 7: DS18S20: COUNT_PER_C // DS18B20 & DS1822: store for crc // byte 8: SCRATCHPAD_CRC int16_t raw = (data[1] << 8) | data[0]; if (chip(index) == DS_CHIP_DS18S20) { raw = raw << 3; // 9 bit resolution default if (data[7] == 0x10) { raw = (raw & 0xFFF0) + 12 - data[6]; // "count remain" gives full 12 bit resolution } } else { byte cfg = (data[4] & 0x60); if (cfg == 0x00) raw = raw & ~7; // 9 bit res, 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 // 12 bit res, 750 ms } double value = (float) raw / 16.0; if (value == DS_DISCONNECTED) { _error = SENSOR_ERROR_CRC; return 0; } return value; } protected: // --------------------------------------------------------------------- // Protected // --------------------------------------------------------------------- bool validateID(unsigned char id) { return (id == DS_CHIP_DS18S20) || (id == DS_CHIP_DS18B20) || (id == DS_CHIP_DS1822) || (id == DS_CHIP_DS1825) || (id == DS_CHIP_DS2406) ; } unsigned char chip(unsigned char index) { if (index < _count) return _devices[index].address[0]; return 0; } String chipAsString(unsigned char index) { unsigned char chip_id = chip(index); if (chip_id == DS_CHIP_DS18S20) return String("DS18S20"); if (chip_id == DS_CHIP_DS18B20) return String("DS18B20"); if (chip_id == DS_CHIP_DS1822) return String("DS1822"); if (chip_id == DS_CHIP_DS1825) return String("DS1825"); if (chip_id == DS_CHIP_DS2406) return String("DS2406"); return String("Unknown"); } void loadDevices() { uint8_t address[8]; _wire->reset(); _wire->reset_search(); while (_wire->search(address)) { // Check CRC if (_wire->crc8(address, 7) == address[7]) { // Check ID if (validateID(address[0])) { ds_device_t device; memcpy(device.address, address, 8); _devices.push_back(device); } } } _count = _devices.size(); } typedef struct { uint8_t address[8]; uint8_t data[DS_DATA_SIZE]; } ds_device_t; std::vector _devices; unsigned char _gpio = GPIO_NONE; unsigned char _previous = GPIO_NONE; OneWire * _wire = NULL; }; #endif // SENSOR_SUPPORT && DALLAS_SUPPORT