diff --git a/code/espurna/sensors/DallasSensor.h b/code/espurna/sensors/DallasSensor.h index 6a4415a4..2b417a22 100644 --- a/code/espurna/sensors/DallasSensor.h +++ b/code/espurna/sensors/DallasSensor.h @@ -27,6 +27,9 @@ #define DS_CMD_START_CONVERSION 0x44 #define DS_CMD_READ_SCRATCHPAD 0xBE +#define DS18x20_ADDR_LEN 8 +#define DS18x20_SCRATCHPAD_LEN 9 + // ====== DS2406 specific constants ======= #define DS2406_CHANNEL_ACCESS 0xF5; @@ -58,19 +61,20 @@ // 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 { private: - using Address = std::array; - using Data = std::array; + using Address = std::array; + using Data = std::array; struct Device { Address address; Data data; + uint8_t error{ SENSOR_ERROR_OK }; + double value{ 0.0 }; }; public: @@ -78,9 +82,8 @@ class DallasSensor : public BaseSensor { // --------------------------------------------------------------------- void setGPIO(unsigned char gpio) { - if (_gpio == gpio) return; + _dirty = _gpio != gpio; _gpio = gpio; - _dirty = true; } // --------------------------------------------------------------------- @@ -158,77 +161,11 @@ class DallasSensor : public BaseSensor { // Every second we either start a conversion or read the scratchpad if (_conversion) { - - // Start conversion - _wire->reset(); - _wire->skip(); - _wire->write(DS_CMD_START_CONVERSION, DS_PARASITE); - + _startConversion(); } else { - - // Read scratchpads - for (size_t 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()); - - 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(size_t i = 3; i < DS2406_STATE_BUF_LEN; ++i) { - data[i] = _wire->read(); - } - - // 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(), 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.data()); - _wire->write(DS_CMD_READ_SCRATCHPAD); - - Data data{}; - for (size_t i = 0; i < 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; - } - - _devices[index].data = data; - } - } + _readScratchpad(); } + _conversion = !_conversion; } @@ -242,31 +179,28 @@ class DallasSensor : public BaseSensor { // Address of the device String address(unsigned char index) const override { - char buffer[20] = {0}; + String out; if (index < _devices.size()) { - const auto& address = _devices[index].address; - snprintf_P(buffer, sizeof(buffer), - PSTR("%02X%02X%02X%02X%02X%02X%02X%02X"), - address[0], address[1], address[2], address[3], - address[4], address[5], address[6], address[7]); + out = hexEncode(_devices[index].address); } - return String(buffer); + + return out; } // Descriptive name of the slot # index String description(unsigned char index) const override { - char buffer[40]; + String out; if (index < _devices.size()) { - const auto& address = _devices[index].address; + char buffer[64]{}; snprintf_P(buffer, sizeof(buffer), - PSTR("%s (%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%hhu"), + PSTR("%s (%s) @ GPIO%hhu"), chipAsString(index).c_str(), - address[0], address[1], address[2], address[3], - address[4], address[5], address[6], address[7], - _gpio - ); + hexEncode(_devices[index].address).c_str(), _gpio); + + out = buffer; } - return String(buffer); + + return out; } // Type for slot # index @@ -278,6 +212,7 @@ class DallasSensor : public BaseSensor { return MAGNITUDE_TEMPERATURE; } } + return MAGNITUDE_NONE; } @@ -288,91 +223,219 @@ class DallasSensor : public BaseSensor { return 2; } - // In case we have DS2406, there is no decimal places + // In case we have DS2406, it is a digital sensor and there are no decimal places return 0; } // Pre-read hook (usually to populate registers with up-to-date data) void pre() override { _error = SENSOR_ERROR_OK; + + for (auto& device : _devices) { + const auto chip_id = chip(device); + + switch (chip_id) { + case DS_CHIP_DS2406: + device.value = _valueDs2406(device.data); + break; + + case DS_CHIP_DS18S20: + device.value = _valueDs18s20(device.data); + break; + + default: + device.value = _valueGeneric(device.data); + break; + } + + if ((chip_id != DS_CHIP_DS2406) && (device.value == DS_DISCONNECTED)) { + device.error = SENSOR_ERROR_OUT_OF_RANGE; + } + + if (device.error != SENSOR_ERROR_OK) { + DEBUG_MSG_P(PSTR("[DALLAS] %s @ GPIO%hhu (#%zu) reading failed\n"), + _chipIdToString(chip(device)).c_str(), _gpio, index); + _error = device.error; + return; + } + } } // Current value for slot # index double value(unsigned char index) override { + if (index <= _devices.size()) { + return _devices[index].value; + } + + return 0.0; + } + + protected: + + // --------------------------------------------------------------------- + // Protected + // --------------------------------------------------------------------- + + // 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 + static double _valueGeneric(const Data& data) { + int16_t raw = (data[1] << 8) | data[0]; + + const uint8_t res = ((data[4] & 0x60) >> 5) & 0b11; + switch (res) { + // 9 bit res, 93.75 ms + case 0x00: + raw = raw & ~7; + break; + + // 10 bit res, 187.5 ms + case 0x20: + raw = raw & ~3; + break; + + // 11 bit res, 375 ms + case 0x40: + raw = raw & ~1; + break; + + // 12 bit res, 750 ms + default: + break; - if (index >= _devices.size()) { - return 0; } - const auto& data = _devices[index].data; + double out = raw; + raw /= 16.0; - if (chip(index) == DS_CHIP_DS2406) { + return out; + } - if (!OneWire::check_crc16(data.data(), 5, &data[5])) { - _error = SENSOR_ERROR_CRC; - return 0; - } + // See _valueGeneric(const Data&) for register info + static double _valueDs18s20(const Data& data) { + int16_t raw = (data[1] << 8) | data[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; + // 9 bit resolution default + raw = raw << 3; + + // "count remain" gives full 12 bit resolution + if (data[7] == 0x10) { + raw = (raw & 0xFFF0) + 12 - data[6]; } - if (OneWire::crc8(data.data(), data.size() - 1) != data.back()) { - _error = SENSOR_ERROR_CRC; - return 0; + double out = raw; + out /= 16.0; + + return out; + } + + // 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 + static double _valueDs2406(const Data& data) { + return ((data[3] & 0x04) != 0) ? 1.0 : 0.0; + } + + bool _readDs2406(Device& device) { + device.error = SENSOR_ERROR_OK; + if (_wire->reset() == 0) { + device.error = SENSOR_ERROR_TIMEOUT; + return false; } - // 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 + _wire->select(device.address.data()); - 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 { - uint8_t 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 + std::array data; + data[0] = DS2406_CHANNEL_ACCESS; + data[1] = DS2406_CHANNEL_CONTROL_BYTE; + data[2] = 0xFF; + + _wire->write_bytes(data.data(), 3); + + // 3 cmd bytes, 1 channel info byte, 1 0x00, 2 CRC16 + _wire->read_bytes(data.data(), data.size()); + + // Read scratchpad + if (_wire->reset() == 0) { + device.error = SENSOR_ERROR_TIMEOUT; + return false; } - if ((raw & (int16_t) 0xfff0) == DS_DISCONNECTED) { - _error = SENSOR_ERROR_CRC; - return 0; + if (!OneWire::check_crc16(data.data(), 5, &data[5])) { + device.error = SENSOR_ERROR_CRC; } - return raw / 16.0; + static_assert(data.size() <= decltype(Device::data){}.size(), ""); + std::copy(data.begin(), data.end(), device.data.begin()); + return device.error == SENSOR_ERROR_OK; } - protected: + bool _readGeneric(Device& device) { + device.error = SENSOR_ERROR_OK; + if (_wire->reset() == 0) { + device.error = SENSOR_ERROR_TIMEOUT; + return false; + } - // --------------------------------------------------------------------- - // Protected - // --------------------------------------------------------------------- + _wire->select(device.address.data()); + _wire->write(DS_CMD_READ_SCRATCHPAD); + + Data data{}; + _wire->read_bytes(data.data(), data.size()); + + if (_wire->reset() == 0) { + device.error = SENSOR_ERROR_TIMEOUT; + return false; + } + + if (OneWire::crc8(data.data(), data.size() - 1) != data.back()) { + device.error = SENSOR_ERROR_CRC; + } + + device.data = data; + + return device.error == SENSOR_ERROR_OK; + } + + bool _readScratchpad() { + for (size_t index = 0; index < _devices.size(); ++index) { + auto& device = _devices[index]; + + auto status = + (device.address[0] == DS_CHIP_DS2406) + ? _readDs2406(device) + : _readGeneric(device); + + if (!status) { + return false; + } + } + + return true; + } + + void _startConversion() { + _wire->reset(); + _wire->skip(); + _wire->write(DS_CMD_START_CONVERSION, DS_PARASITE); + } static bool validateID(unsigned char id) { return (id == DS_CHIP_DS18S20) @@ -382,37 +445,48 @@ class DallasSensor : public BaseSensor { || (id == DS_CHIP_DS2406); } - String chipAsString(unsigned char index) const { - const char* ptr { nullptr }; + static espurna::StringView _chipIdToStringView(unsigned char id) { + espurna::StringView out; - switch (chip(index)) { + switch (id) { case DS_CHIP_DS18S20: - ptr = PSTR("DS18S20"); + out = STRING_VIEW("DS18S20"); break; case DS_CHIP_DS18B20: - ptr = PSTR("DS18B20"); + out = STRING_VIEW("DS18B20"); break; case DS_CHIP_DS1822: - ptr = PSTR("DS1822"); + out = STRING_VIEW("DS1822"); break; case DS_CHIP_DS1825: - ptr = PSTR("DS1825"); + out = STRING_VIEW("DS1825"); break; case DS_CHIP_DS2406: - ptr = PSTR("DS2406"); + out = STRING_VIEW("DS2406"); + break; + default: + out = STRING_VIEW("Unknown"); break; } - if (!ptr) { - ptr = PSTR("Unknown"); - } + return out; + } + + static String _chipIdToString(unsigned char id) { + return _chipIdToStringView(id).toString(); + } + + String chipAsString(unsigned char index) const { + return _chipIdToString(chip(index)); + } - return String(FPSTR(ptr)); + static unsigned char chip(const Device& device) { + return device.address[0]; } unsigned char chip(unsigned char index) const { if (index < _devices.size()) { - return _devices[index].address[0]; + return chip(_devices[index]); } return 0;