From a2b9d688ea1cd66d46618a26062ccf8ada225328 Mon Sep 17 00:00:00 2001 From: Alex Stewart Date: Mon, 14 Mar 2016 21:59:39 -0700 Subject: [PATCH 1/5] Multiple cleanups/tweaks for onewire driver Use onewire_addr_t for onewire addresses Move internal defines out of onewire.h Remove global variables for search state use taskENTER_CRITICAL instead of portDISABLE_INTERRUPTS remove unnecessary onewire_init function Remove unnecessary critical sections Use GPIO_OUT_OPEN_DRAIN reformat/style cleanup --- examples/ds18b20_onewire/ds18b20_onewire.c | 4 - extras/ds18b20/ds18b20.c | 28 +- extras/onewire/onewire.c | 542 ++++++++++----------- extras/onewire/onewire.h | 89 ++-- 4 files changed, 304 insertions(+), 359 deletions(-) diff --git a/examples/ds18b20_onewire/ds18b20_onewire.c b/examples/ds18b20_onewire/ds18b20_onewire.c index 78d13bf..e152b10 100644 --- a/examples/ds18b20_onewire/ds18b20_onewire.c +++ b/examples/ds18b20_onewire/ds18b20_onewire.c @@ -12,8 +12,6 @@ // DS18B20 driver #include "ds18b20/ds18b20.h" -// Onewire init -#include "onewire/onewire.h" void print_temperature(void *pvParameters) { @@ -26,8 +24,6 @@ void print_temperature(void *pvParameters) // Use GPIO 13 as one wire pin. uint8_t GPIO_FOR_ONE_WIRE = 13; - onewire_init(GPIO_FOR_ONE_WIRE); - while(1) { // Search all DS18B20, return its amount and feed 't' structure with result data. amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t); diff --git a/extras/ds18b20/ds18b20.c b/extras/ds18b20/ds18b20.c index dc028fc..9814018 100644 --- a/extras/ds18b20/ds18b20.c +++ b/extras/ds18b20/ds18b20.c @@ -17,27 +17,28 @@ #define DS1820_CONVERT_T 0x44 uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { - - uint8_t addr[8]; + onewire_addr_t addr; + onewire_search_t search; uint8_t sensor_id = 0; - onewire_reset_search(pin); + + onewire_search_start(&search); - while(onewire_search(pin, addr)){ - uint8_t crc = onewire_crc8(addr, 7); - if (crc != addr[7]){ - printf("CRC check failed: %02X %02X\n", addr[7], crc); + while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) { + uint8_t crc = onewire_crc8((uint8_t *)&addr, 7); + if (crc != (addr >> 56)){ + printf("CRC check failed: %02X %02X\n", (unsigned)(addr >> 56), crc); return 0; } onewire_reset(pin); onewire_select(pin, addr); - onewire_write(pin, DS1820_CONVERT_T, ONEWIRE_DEFAULT_POWER); + onewire_write(pin, DS1820_CONVERT_T); vTaskDelay(750 / portTICK_RATE_MS); onewire_reset(pin); onewire_select(pin, addr); - onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER); + onewire_write(pin, DS1820_READ_SCRATCHPAD); uint8_t get[10]; @@ -71,15 +72,14 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { float ds18b20_read_single(uint8_t pin) { onewire_reset(pin); - - onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER); - onewire_write(pin, DS1820_CONVERT_T, ONEWIRE_DEFAULT_POWER); + onewire_skip_rom(pin); + onewire_write(pin, DS1820_CONVERT_T); vTaskDelay(750 / portTICK_RATE_MS); onewire_reset(pin); - onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER); - onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER); + onewire_skip_rom(pin); + onewire_write(pin, DS1820_READ_SCRATCHPAD); uint8_t get[10]; diff --git a/extras/onewire/onewire.c b/extras/onewire/onewire.c index 3a946ba..8962f26 100644 --- a/extras/onewire/onewire.c +++ b/extras/onewire/onewire.c @@ -1,206 +1,176 @@ #include "onewire.h" - -// global search state -static unsigned char ROM_NO[ONEWIRE_NUM][8]; -static uint8_t LastDiscrepancy[ONEWIRE_NUM]; -static uint8_t LastFamilyDiscrepancy[ONEWIRE_NUM]; -static uint8_t LastDeviceFlag[ONEWIRE_NUM]; - -void onewire_init(uint8_t pin) -{ - gpio_enable(pin, GPIO_INPUT); - onewire_reset_search(pin); -} +#include "string.h" +#include "task.h" +#include "esp/gpio.h" // 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; +// and we return false; // -// Returns 1 if a device asserted a presence pulse, 0 otherwise. +// Returns true if a device asserted a presence pulse, false otherwise. // -uint8_t onewire_reset(uint8_t pin) -{ - uint8_t r; - uint8_t retries = 125; +bool onewire_reset(int pin) { + bool r; + const int retries = 50; - noInterrupts(); - DIRECT_MODE_INPUT(pin); - interrupts(); - // wait until the wire is high... just in case - do { - if (--retries == 0) return 0; - delayMicroseconds(2); - } while ( !DIRECT_READ(pin)); + gpio_enable(pin, GPIO_OUT_OPEN_DRAIN); + gpio_write(pin, 1); + // wait until the wire is high... just in case + for (int i = 0; i < retries; i++) { + if (gpio_read(pin)) break; + sdk_os_delay_us(5); + } + if (!gpio_read(pin)) { + // Bus shorted? + return false; + } - noInterrupts(); - DIRECT_WRITE_LOW(pin); - DIRECT_MODE_OUTPUT(pin); // drive output low - interrupts(); - delayMicroseconds(480); - noInterrupts(); - DIRECT_MODE_INPUT(pin); // allow it to float - delayMicroseconds(70); - r = !DIRECT_READ(pin); - interrupts(); - delayMicroseconds(410); - return r; + gpio_write(pin, 0); + sdk_os_delay_us(480); + + taskENTER_CRITICAL(); + gpio_write(pin, 1); // allow it to float + sdk_os_delay_us(70); + r = !gpio_read(pin); + taskEXIT_CRITICAL(); + + // Wait for all devices to finish pulling the bus low before returning + for (int i = 0; i < retries; i++) { + if (gpio_read(pin)) break; + sdk_os_delay_us(5); + } + sdk_os_delay_us(2); + + return r; } -// Write a bit. Port and bit is used to cut lookup time and provide -// more certain timing. -// -static void onewire_write_bit(uint8_t pin, uint8_t v) -{ - if (v & 1) { - noInterrupts(); - DIRECT_WRITE_LOW(pin); - DIRECT_MODE_OUTPUT(pin); // drive output low - delayMicroseconds(10); - DIRECT_WRITE_HIGH(pin); // drive output high - interrupts(); - delayMicroseconds(55); - } else { - noInterrupts(); - DIRECT_WRITE_LOW(pin); - DIRECT_MODE_OUTPUT(pin); // drive output low - delayMicroseconds(65); - DIRECT_WRITE_HIGH(pin); // drive output high - interrupts(); - delayMicroseconds(5); - } +static void onewire_write_bit(int pin, uint8_t v) { + //TODO: should verify that the bus is high before starting + if (v & 1) { + taskENTER_CRITICAL(); + gpio_write(pin, 0); // drive output low + sdk_os_delay_us(10); + gpio_write(pin, 1); // allow output high + taskEXIT_CRITICAL(); + sdk_os_delay_us(55); + } else { + taskENTER_CRITICAL(); + gpio_write(pin, 0); // drive output low + sdk_os_delay_us(65); + gpio_write(pin, 1); // allow output high + taskEXIT_CRITICAL(); + } + sdk_os_delay_us(1); } -// Read a bit. Port and bit is used to cut lookup time and provide -// more certain timing. -// -static uint8_t onewire_read_bit(uint8_t pin) -{ - uint8_t r; +static int onewire_read_bit(int pin) { + int r; - noInterrupts(); - DIRECT_MODE_OUTPUT(pin); - DIRECT_WRITE_LOW(pin); - delayMicroseconds(3); - DIRECT_MODE_INPUT(pin); // let pin float, pull up will raise - delayMicroseconds(10); - r = DIRECT_READ(pin); - interrupts(); - delayMicroseconds(53); - return r; + //TODO: should verify that the bus is high before starting + taskENTER_CRITICAL(); + gpio_write(pin, 0); + sdk_os_delay_us(2); + gpio_write(pin, 1); // let pin float, pull up will raise + sdk_os_delay_us(11); + r = gpio_read(pin); // Must sample within 15us of start + taskEXIT_CRITICAL(); + sdk_os_delay_us(48); + + 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. +// Write a byte. The writing code uses open-drain mode and expects the pullup +// resistor to pull the line high when not driven low. If you need strong +// power after the write (e.g. DS18B20 in parasite power mode) then call +// onewire_power() after this is complete to actively drive the line high. // -void onewire_write(uint8_t pin, uint8_t v, uint8_t power /* = 0 */) { - uint8_t bitMask; +void onewire_write(int pin, uint8_t v) { + uint8_t bitMask; - for (bitMask = 0x01; bitMask; bitMask <<= 1) { - onewire_write_bit(pin, (bitMask & v)?1:0); - } - if ( !power) { - noInterrupts(); - DIRECT_MODE_INPUT(pin); - DIRECT_WRITE_LOW(pin); - interrupts(); - } + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + onewire_write_bit(pin, (bitMask & v)?1:0); + } } -void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power /* = 0 */) { - uint16_t i; - for (i = 0 ; i < count ; i++) - onewire_write(pin, buf[i], ONEWIRE_DEFAULT_POWER); - if (!power) { - noInterrupts(); - DIRECT_MODE_INPUT(pin); - DIRECT_WRITE_LOW(pin); - interrupts(); - } +void onewire_write_bytes(int pin, const uint8_t *buf, size_t count) { + size_t i; + + for (i = 0 ; i < count ; i++) { + onewire_write(pin, buf[i]); + } } // Read a byte // -uint8_t onewire_read(uint8_t pin) { - uint8_t bitMask; - uint8_t r = 0; +uint8_t onewire_read(int pin) { + uint8_t bitMask; + uint8_t r = 0; - for (bitMask = 0x01; bitMask; bitMask <<= 1) { - if (onewire_read_bit(pin)) r |= bitMask; - } - return r; + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + if (onewire_read_bit(pin)) r |= bitMask; + } + return r; } -void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count) { - uint16_t i; - for (i = 0 ; i < count ; i++) - buf[i] = onewire_read(pin); +void onewire_read_bytes(int pin, uint8_t *buf, size_t count) { + size_t i; + + for (i = 0 ; i < count ; i++) { + buf[i] = onewire_read(pin); + } } // Do a ROM select // -void onewire_select(uint8_t pin, const uint8_t rom[8]) -{ +void onewire_select(int pin, onewire_addr_t rom) { uint8_t i; - onewire_write(pin, 0x55, ONEWIRE_DEFAULT_POWER); // Choose ROM + onewire_write(pin, 0x55); // Choose ROM - for (i = 0; i < 8; i++) onewire_write(pin, rom[i], ONEWIRE_DEFAULT_POWER); + for (i = 0; i < 8; i++) { + onewire_write(pin, rom & 0xff); + rom >>= 8; + } } // Do a ROM skip // -void onewire_skip(uint8_t pin) -{ - onewire_write(pin, 0xCC, ONEWIRE_DEFAULT_POWER); // Skip ROM +void onewire_skip_rom(int pin) { + onewire_write(pin, 0xCC); // Skip ROM } -void onewire_depower(uint8_t pin) -{ - noInterrupts(); - DIRECT_MODE_INPUT(pin); - interrupts(); +void onewire_power(int pin) { + gpio_enable(pin, GPIO_OUTPUT); + gpio_write(pin, 1); } -// 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 pin) -{ - // reset the search state - LastDiscrepancy[pin] = 0; - LastDeviceFlag[pin] = 0; - LastFamilyDiscrepancy[pin] = 0; - int i; - for(i = 7; ; i--) { - ROM_NO[pin][i] = 0; - if ( i == 0) break; - } +void onewire_depower(int pin) { + gpio_enable(pin, GPIO_OUT_OPEN_DRAIN); +} + +void onewire_search_start(onewire_search_t *search) { + // reset the search state + memset(search, 0, sizeof(*search)); } // Setup the search to find the device type 'family_code' on the next call // to search(*newAddr) if it is present. // -void onewire_target_search(uint8_t pin, uint8_t family_code) -{ - // set the search state to find SearchFamily type devices - ROM_NO[pin][0] = family_code; - uint8_t i; - for (i = 1; i < 8; i++) - ROM_NO[pin][i] = 0; - LastDiscrepancy[pin] = 64; - LastFamilyDiscrepancy[pin] = 0; - LastDeviceFlag[pin] = 0; +void onewire_search_prefix(onewire_search_t *search, uint8_t family_code) { + uint8_t i; + + search->rom_no[0] = family_code; + for (i = 1; i < 8; i++) { + search->rom_no[i] = 0; + } + search->last_discrepancy = 64; + search->last_device_found = false; } -// 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 +// Perform a search. If the next device has been successfully enumerated, its +// ROM address will be returned. 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 +// enumeration then ONEWIRE_NONE is returned. Use OneWire::reset_search() to // start over. // // --- Replaced by the one from the Dallas Semiconductor web site --- @@ -210,129 +180,115 @@ void onewire_target_search(uint8_t pin, uint8_t family_code) // Return 1 : device found, ROM number in ROM_NO buffer // 0 : device not found, end of search // -uint8_t onewire_search(uint8_t pin, uint8_t *newAddr) -{ - uint8_t id_bit_number; - uint8_t last_zero, rom_byte_number, search_result; - uint8_t id_bit, cmp_id_bit; +onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) { + uint8_t id_bit_number; + uint8_t last_zero, search_result; + int rom_byte_number; + uint8_t id_bit, cmp_id_bit; + onewire_addr_t addr; - unsigned char rom_byte_mask, search_direction; + unsigned char rom_byte_mask, search_direction; - // initialize for search - id_bit_number = 1; - last_zero = 0; - rom_byte_number = 0; - rom_byte_mask = 1; - search_result = 0; + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; - // if the last call was not the last one - if (!LastDeviceFlag[pin]) - { - // 1-Wire reset - if (!onewire_reset(pin)) - { - // reset the search - LastDiscrepancy[pin] = 0; - LastDeviceFlag[pin] = 0; - LastFamilyDiscrepancy[pin] = 0; - return 0; - } + // if the last call was not the last one + if (!search->last_device_found) { + // 1-Wire reset + if (!onewire_reset(pin)) { + // reset the search + search->last_discrepancy = 0; + search->last_device_found = false; + return ONEWIRE_NONE; + } - // issue the search command - onewire_write(pin, 0xF0, ONEWIRE_DEFAULT_POWER); + // issue the search command + onewire_write(pin, 0xF0); - // loop to do the search - do - { - // read a bit and its complement - id_bit = onewire_read_bit(pin); - cmp_id_bit = onewire_read_bit(pin); + // loop to do the search + do { + // read a bit and its complement + id_bit = onewire_read_bit(pin); + cmp_id_bit = onewire_read_bit(pin); - // check for no devices on 1-wire - if ((id_bit == 1) && (cmp_id_bit == 1)) - break; - else - { - // all devices coupled have 0 or 1 - if (id_bit != cmp_id_bit) - search_direction = id_bit; // bit write value for search - else - { - // if this discrepancy if before the Last Discrepancy - // on a previous next then pick the same as last time - if (id_bit_number < LastDiscrepancy[pin]) - search_direction = ((ROM_NO[pin][rom_byte_number] & rom_byte_mask) > 0); - else - // if equal to last pick 1, if not then pick 0 - search_direction = (id_bit_number == LastDiscrepancy[pin]); + // check for no devices on 1-wire + if ((id_bit == 1) && (cmp_id_bit == 1)) { + break; + } else { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) { + search_direction = id_bit; // bit write value for search + } else { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < search->last_discrepancy) { + search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0); + } else { + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == search->last_discrepancy); + } - // if 0 was picked then record its position in LastZero - if (search_direction == 0) - { - last_zero = id_bit_number; + // if 0 was picked then record its position in LastZero + if (search_direction == 0) { + last_zero = id_bit_number; + } + } - // check for Last discrepancy in family - if (last_zero < 9) - LastFamilyDiscrepancy[pin] = last_zero; - } + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction == 1) { + search->rom_no[rom_byte_number] |= rom_byte_mask; + } else { + search->rom_no[rom_byte_number] &= ~rom_byte_mask; + } + + // serial number search direction write bit + onewire_write_bit(pin, search_direction); + + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if (rom_byte_mask == 0) { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } while (rom_byte_number < 8); // loop until through all ROM bytes 0-7 + + // if the search was successful then + if (!(id_bit_number < 65)) { + // search successful so set last_discrepancy,last_device_found,search_result + search->last_discrepancy = last_zero; + + // check for last device + if (search->last_discrepancy == 0) { + search->last_device_found = true; } - // set or clear the bit in the ROM byte rom_byte_number - // with mask rom_byte_mask - if (search_direction == 1) - ROM_NO[pin][rom_byte_number] |= rom_byte_mask; - else - ROM_NO[pin][rom_byte_number] &= ~rom_byte_mask; + search_result = 1; + } + } - // serial number search direction write bit - onewire_write_bit(pin, search_direction); - - // increment the byte counter id_bit_number - // and shift the mask rom_byte_mask - id_bit_number++; - rom_byte_mask <<= 1; - - // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask - if (rom_byte_mask == 0) - { - rom_byte_number++; - rom_byte_mask = 1; - } - } - } - while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 - - // if the search was successful then - if (!(id_bit_number < 65)) - { - // search successful so set LastDiscrepancy,LastDeviceFlag,search_result - LastDiscrepancy[pin] = last_zero; - - // check for last device - if (LastDiscrepancy[pin] == 0) - LastDeviceFlag[pin] = 1; - - search_result = 1; - } - } - - // if no device found then reset counters so next 'search' will be like a first - if (!search_result || !ROM_NO[pin][0]) - { - LastDiscrepancy[pin] = 0; - LastDeviceFlag[pin] = 0; - LastFamilyDiscrepancy[pin] = 0; - search_result = 0; - } - else - { - for (rom_byte_number = 0; rom_byte_number < 8; rom_byte_number++) - { - newAddr[rom_byte_number] = ROM_NO[pin][rom_byte_number]; - //printf("Ok I found something at %d - %x...\n",rom_byte_number, newAddr[rom_byte_number]); - } - } - return search_result; + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !search->rom_no[0]) { + search->last_discrepancy = 0; + search->last_device_found = false; + return ONEWIRE_NONE; + } else { + addr = 0; + for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) { + addr = (addr << 8) | search->rom_no[rom_byte_number]; + } + //printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr); + } + return addr; } // The 1-Wire CRC scheme is described in Maxim Application Note 27: @@ -371,41 +327,38 @@ static const uint8_t dscrc_table[] = { // compared to all those delayMicrosecond() calls. But I got // confused, so I use this table from the examples.) // -uint8_t onewire_crc8(const uint8_t *addr, uint8_t len) -{ - uint8_t crc = 0; +uint8_t onewire_crc8(const uint8_t *data, uint8_t len) { + uint8_t crc = 0; - while (len--) { - crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); - } - return crc; + while (len--) { + crc = pgm_read_byte(dscrc_table + (crc ^ *data++)); + } + return crc; } #else // // Compute a Dallas Semiconductor 8 bit CRC directly. // this is much slower, but much smaller, than the lookup table. // -uint8_t onewire_crc8(const uint8_t *addr, uint8_t len) -{ - uint8_t crc = 0; - - while (len--) { - uint8_t inbyte = *addr++; - uint8_t i; - for (i = 8; i; i--) { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) crc ^= 0x8C; - inbyte >>= 1; - } - } - return crc; +uint8_t onewire_crc8(const uint8_t *data, uint8_t len) { + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *data++; + for (int i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; } #endif // Compute the 1-Wire CRC16 and compare it against the received CRC. // Example usage (reading a DS2408): - // // Put everything in a buffer so we can compute the CRC easily. +// // Put everything in a buffer so we can compute the CRC easily. // uint8_t buf[13]; // buf[0] = 0xF0; // Read PIO Registers // buf[1] = 0x88; // LSB address @@ -423,9 +376,8 @@ uint8_t onewire_crc8(const uint8_t *addr, uint8_t len) // *not* at a 16-bit integer. // @param crc - The crc starting value (optional) // @return 1, iff the CRC matches. -bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc) -{ - crc = ~onewire_crc16(input, len, crc); +bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv) { + uint16_t crc = ~onewire_crc16(input, len, crc_iv); return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; } @@ -441,8 +393,8 @@ bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inve // @param len - How many bytes to use. // @param crc - The crc starting value (optional) // @return The CRC16, as defined by Dallas Semiconductor. -uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc) -{ +uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) { + uint16_t crc = crc_iv; static const uint8_t oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; @@ -463,4 +415,4 @@ uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc) crc ^= cdata; } return crc; -} \ No newline at end of file +} diff --git a/extras/onewire/onewire.h b/extras/onewire/onewire.h index 1a0b15d..4ee314b 100644 --- a/extras/onewire/onewire.h +++ b/extras/onewire/onewire.h @@ -29,75 +29,72 @@ #define ONEWIRE_CRC8_TABLE 0 #endif -// Platform specific I/O definitions -#define noInterrupts portDISABLE_INTERRUPTS -#define interrupts portENABLE_INTERRUPTS -#define delayMicroseconds sdk_os_delay_us +typedef uint64_t onewire_addr_t; -#define DIRECT_READ(pin) gpio_read(pin) -#define DIRECT_MODE_INPUT(pin) gpio_enable(pin, GPIO_INPUT) -#define DIRECT_MODE_OUTPUT(pin) gpio_enable(pin, GPIO_OUTPUT) -#define DIRECT_WRITE_LOW(pin) gpio_write(pin, 0) -#define DIRECT_WRITE_HIGH(pin) gpio_write(pin, 1) +typedef struct { + uint8_t rom_no[8]; + uint8_t last_discrepancy; + bool last_device_found; +} onewire_search_t; -void onewire_init(uint8_t pin); +// The following is an invalid ROM address that will never occur in a device +// (CRC mismatch), and so can be useful as an indicator for "no-such-device", +// etc. +#define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL)) // 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 onewire_reset(uint8_t pin); +bool onewire_reset(int pin); // Issue a 1-Wire rom select command, you do the reset first. -void onewire_select(uint8_t pin, const uint8_t rom[8]); +void onewire_select(int pin, const onewire_addr_t rom); // Issue a 1-Wire rom skip command, to address all on bus. -void onewire_skip(uint8_t pin); +void onewire_skip_rom(int pin); -// 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 onewire_write(uint8_t pin, uint8_t v, uint8_t power); +// Write a byte. The writing code uses open-drain mode and expects the pullup +// resistor to pull the line high when not driven low. If you need strong +// power after the write (e.g. DS18B20 in parasite power mode) then call +// onewire_power() after this is complete to actively drive the line high. +void onewire_write(int pin, uint8_t v); -void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power); +void onewire_write_bytes(int pin, const uint8_t *buf, size_t count); // Read a byte. -uint8_t onewire_read(uint8_t pin); +uint8_t onewire_read(int pin); -void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count); +void onewire_read_bytes(int pin, uint8_t *buf, size_t count); -// Write a bit. The bus is always left powered at the end, see -// note in write() about that. -// void onewire_write_bit(uint8_t pin, uint8_t v); - -// Read a bit. -// uint8_t onewire_read_bit(uint8_t pin); +// Actively drive the bus high to provide extra power for certain operations of +// parasitically-powered devices. +void onewire_power(int pin); // 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 onewire_depower(uint8_t pin); +// you previously called onewire_power() to drive the bus high and now want to +// allow it to float instead. Note that onewire_reset() will also +// automatically depower the bus first, so you do not need to call this first +// if you just want to start a new operation. +void onewire_depower(int pin); // Clear the search state so that if will start from the beginning again. -void onewire_reset_search(uint8_t pin); +void onewire_search_start(onewire_search_t *search); // Setup the search to find the device type 'family_code' on the next call // to search(*newAddr) if it is present. -void onewire_target_search(uint8_t pin, uint8_t family_code); +void onewire_search_prefix(onewire_search_t *search, uint8_t family_code); -// 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 onewire_search(uint8_t pin, uint8_t *newAddr); +// Look for the next device. Returns the address of the next device on the bus, +// or ONEWIRE_NONE if there is no next address. ONEWIRE_NONE 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. +onewire_addr_t onewire_search_next(onewire_search_t *search, int pin); // Compute a Dallas Semiconductor 8 bit CRC, these are used in the // ROM and scratchpad registers. -uint8_t onewire_crc8(const uint8_t *addr, uint8_t len); +uint8_t onewire_crc8(const uint8_t *data, uint8_t len); // Compute the 1-Wire CRC16 and compare it against the received CRC. // Example usage (reading a DS2408): @@ -117,9 +114,9 @@ uint8_t onewire_crc8(const uint8_t *addr, uint8_t len); // @param inverted_crc - The two CRC16 bytes in the received data. // This should just point into the received data, // *not* at a 16-bit integer. -// @param crc - The crc starting value (optional) +// @param crc_iv - The crc starting value (optional) // @return True, iff the CRC matches. -bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc); +bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv); // Compute a Dallas Semiconductor 16 bit CRC. This is required to check // the integrity of data received from many 1-Wire devices. Note that the @@ -131,8 +128,8 @@ bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inve // byte order than the two bytes you get from 1-Wire. // @param input - Array of bytes to checksum. // @param len - How many bytes to use. -// @param crc - The crc starting value (optional) +// @param crc_iv - The crc starting value (optional) // @return The CRC16, as defined by Dallas Semiconductor. -uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc); +uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv); #endif From 9b49b426f6dad015a7eb3e0bd10c07b453d1fcbe Mon Sep 17 00:00:00 2001 From: Alex Stewart Date: Thu, 17 Mar 2016 13:36:31 -0700 Subject: [PATCH 2/5] Added error-checking in onewire routines --- extras/onewire/onewire.c | 88 ++++++++++++++++++++++++++-------------- extras/onewire/onewire.h | 10 +++-- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/extras/onewire/onewire.c b/extras/onewire/onewire.c index 8962f26..f21915f 100644 --- a/extras/onewire/onewire.c +++ b/extras/onewire/onewire.c @@ -3,6 +3,22 @@ #include "task.h" #include "esp/gpio.h" +// Waits up to `max_wait` microseconds for the specified pin to go high. +// Returns true if successful, false if the bus never comes high (likely +// shorted). +static inline bool _onewire_wait_for_bus(int pin, int max_wait) { + bool state; + for (int i = 0; i < ((max_wait + 4) / 5); i++) { + if (gpio_read(pin)) break; + sdk_os_delay_us(5); + } + state = gpio_read(pin); + // Wait an extra 1us to make sure the devices have an adequate recovery + // time before we drive things low again. + sdk_os_delay_us(1); + return state; +} + // 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 false; @@ -11,19 +27,11 @@ // bool onewire_reset(int pin) { bool r; - const int retries = 50; gpio_enable(pin, GPIO_OUT_OPEN_DRAIN); gpio_write(pin, 1); // wait until the wire is high... just in case - for (int i = 0; i < retries; i++) { - if (gpio_read(pin)) break; - sdk_os_delay_us(5); - } - if (!gpio_read(pin)) { - // Bus shorted? - return false; - } + if (!_onewire_wait_for_bus(pin, 250)) return false; gpio_write(pin, 0); sdk_os_delay_us(480); @@ -35,17 +43,13 @@ bool onewire_reset(int pin) { taskEXIT_CRITICAL(); // Wait for all devices to finish pulling the bus low before returning - for (int i = 0; i < retries; i++) { - if (gpio_read(pin)) break; - sdk_os_delay_us(5); - } - sdk_os_delay_us(2); + if (!_onewire_wait_for_bus(pin, 410)) return false; return r; } -static void onewire_write_bit(int pin, uint8_t v) { - //TODO: should verify that the bus is high before starting +static bool _onewire_write_bit(int pin, uint8_t v) { + if (!_onewire_wait_for_bus(pin, 10)) return false; if (v & 1) { taskENTER_CRITICAL(); gpio_write(pin, 0); // drive output low @@ -61,12 +65,14 @@ static void onewire_write_bit(int pin, uint8_t v) { taskEXIT_CRITICAL(); } sdk_os_delay_us(1); + + return true; } -static int onewire_read_bit(int pin) { +static int _onewire_read_bit(int pin) { int r; - //TODO: should verify that the bus is high before starting + if (!_onewire_wait_for_bus(pin, 10)) return -1; taskENTER_CRITICAL(); gpio_write(pin, 0); sdk_os_delay_us(2); @@ -84,40 +90,56 @@ static int onewire_read_bit(int pin) { // power after the write (e.g. DS18B20 in parasite power mode) then call // onewire_power() after this is complete to actively drive the line high. // -void onewire_write(int pin, uint8_t v) { +bool onewire_write(int pin, uint8_t v) { uint8_t bitMask; for (bitMask = 0x01; bitMask; bitMask <<= 1) { - onewire_write_bit(pin, (bitMask & v)?1:0); + if (!_onewire_write_bit(pin, (bitMask & v)?1:0)) { + return false; + } } + return true; } -void onewire_write_bytes(int pin, const uint8_t *buf, size_t count) { +bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count) { size_t i; for (i = 0 ; i < count ; i++) { - onewire_write(pin, buf[i]); + if (!onewire_write(pin, buf[i])) { + return false; + } } + return true; } // Read a byte // -uint8_t onewire_read(int pin) { +int onewire_read(int pin) { uint8_t bitMask; - uint8_t r = 0; + int r = 0; + int bit; for (bitMask = 0x01; bitMask; bitMask <<= 1) { - if (onewire_read_bit(pin)) r |= bitMask; + bit = _onewire_read_bit(pin); + if (bit < 0) { + return -1; + } else if (bit) { + r |= bitMask; + } } return r; } -void onewire_read_bytes(int pin, uint8_t *buf, size_t count) { +bool onewire_read_bytes(int pin, uint8_t *buf, size_t count) { size_t i; + int b; for (i = 0 ; i < count ; i++) { - buf[i] = onewire_read(pin); + b = onewire_read(pin); + if (b < 0) return false; + buf[i] = b; } + return true; } // Do a ROM select @@ -181,6 +203,7 @@ void onewire_search_prefix(onewire_search_t *search, uint8_t family_code) { // 0 : device not found, end of search // onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) { + //TODO: add more checking for read/write errors uint8_t id_bit_number; uint8_t last_zero, search_result; int rom_byte_number; @@ -212,11 +235,14 @@ onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) { // loop to do the search do { // read a bit and its complement - id_bit = onewire_read_bit(pin); - cmp_id_bit = onewire_read_bit(pin); + id_bit = _onewire_read_bit(pin); + cmp_id_bit = _onewire_read_bit(pin); // check for no devices on 1-wire - if ((id_bit == 1) && (cmp_id_bit == 1)) { + if ((id_bit < 0) || (cmp_id_bit < 0)) { + // Read error + break; + } else if ((id_bit == 1) && (cmp_id_bit == 1)) { break; } else { // all devices coupled have 0 or 1 @@ -247,7 +273,7 @@ onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) { } // serial number search direction write bit - onewire_write_bit(pin, search_direction); + _onewire_write_bit(pin, search_direction); // increment the byte counter id_bit_number // and shift the mask rom_byte_mask diff --git a/extras/onewire/onewire.h b/extras/onewire/onewire.h index 4ee314b..ccbccff 100644 --- a/extras/onewire/onewire.h +++ b/extras/onewire/onewire.h @@ -57,14 +57,16 @@ void onewire_skip_rom(int pin); // resistor to pull the line high when not driven low. If you need strong // power after the write (e.g. DS18B20 in parasite power mode) then call // onewire_power() after this is complete to actively drive the line high. -void onewire_write(int pin, uint8_t v); +// Returns true if successful, false on error. +bool onewire_write(int pin, uint8_t v); -void onewire_write_bytes(int pin, const uint8_t *buf, size_t count); +bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count); // Read a byte. -uint8_t onewire_read(int pin); +// Returns the read byte on success, negative value on error. +int onewire_read(int pin); -void onewire_read_bytes(int pin, uint8_t *buf, size_t count); +bool onewire_read_bytes(int pin, uint8_t *buf, size_t count); // Actively drive the bus high to provide extra power for certain operations of // parasitically-powered devices. From 9c37da68346fa9ca33b9c71dc06eb5ceebb29f57 Mon Sep 17 00:00:00 2001 From: Alex Stewart Date: Thu, 17 Mar 2016 19:24:29 -0700 Subject: [PATCH 3/5] Add more documentation for onewire.h --- extras/onewire/onewire.c | 53 ++++---- extras/onewire/onewire.h | 274 ++++++++++++++++++++++++++------------- 2 files changed, 217 insertions(+), 110 deletions(-) diff --git a/extras/onewire/onewire.c b/extras/onewire/onewire.c index f21915f..159360b 100644 --- a/extras/onewire/onewire.c +++ b/extras/onewire/onewire.c @@ -3,6 +3,10 @@ #include "task.h" #include "esp/gpio.h" +#define ONEWIRE_SELECT_ROM 0x55 +#define ONEWIRE_SKIP_ROM 0xcc +#define ONEWIRE_SEARCH 0xf0 + // Waits up to `max_wait` microseconds for the specified pin to go high. // Returns true if successful, false if the bus never comes high (likely // shorted). @@ -48,9 +52,9 @@ bool onewire_reset(int pin) { return r; } -static bool _onewire_write_bit(int pin, uint8_t v) { +static bool _onewire_write_bit(int pin, bool v) { if (!_onewire_wait_for_bus(pin, 10)) return false; - if (v & 1) { + if (v) { taskENTER_CRITICAL(); gpio_write(pin, 0); // drive output low sdk_os_delay_us(10); @@ -94,7 +98,7 @@ bool onewire_write(int pin, uint8_t v) { uint8_t bitMask; for (bitMask = 0x01; bitMask; bitMask <<= 1) { - if (!_onewire_write_bit(pin, (bitMask & v)?1:0)) { + if (!_onewire_write_bit(pin, (bitMask & v))) { return false; } } @@ -142,28 +146,36 @@ bool onewire_read_bytes(int pin, uint8_t *buf, size_t count) { return true; } -// Do a ROM select -// -void onewire_select(int pin, onewire_addr_t rom) { +bool onewire_select(int pin, onewire_addr_t addr) { uint8_t i; - onewire_write(pin, 0x55); // Choose ROM + if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) { + return false; + } for (i = 0; i < 8; i++) { - onewire_write(pin, rom & 0xff); - rom >>= 8; + if (!onewire_write(pin, addr & 0xff)) { + return false; + } + addr >>= 8; } + + return true; } -// Do a ROM skip -// -void onewire_skip_rom(int pin) { - onewire_write(pin, 0xCC); // Skip ROM +bool onewire_skip_rom(int pin) { + return onewire_write(pin, ONEWIRE_SKIP_ROM); } -void onewire_power(int pin) { +bool onewire_power(int pin) { + // Make sure the bus is not being held low before driving it high, or we + // may end up shorting ourselves out. + if (!_onewire_wait_for_bus(pin, 10)) return false; + gpio_enable(pin, GPIO_OUTPUT); gpio_write(pin, 1); + + return true; } void onewire_depower(int pin) { @@ -175,9 +187,6 @@ void onewire_search_start(onewire_search_t *search) { memset(search, 0, sizeof(*search)); } -// Setup the search to find the device type 'family_code' on the next call -// to search(*newAddr) if it is present. -// void onewire_search_prefix(onewire_search_t *search, uint8_t family_code) { uint8_t i; @@ -209,8 +218,8 @@ onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) { int rom_byte_number; uint8_t id_bit, cmp_id_bit; onewire_addr_t addr; - - unsigned char rom_byte_mask, search_direction; + unsigned char rom_byte_mask; + bool search_direction; // initialize for search id_bit_number = 1; @@ -230,7 +239,7 @@ onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) { } // issue the search command - onewire_write(pin, 0xF0); + onewire_write(pin, ONEWIRE_SEARCH); // loop to do the search do { @@ -259,14 +268,14 @@ onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) { } // if 0 was picked then record its position in LastZero - if (search_direction == 0) { + if (!search_direction) { last_zero = id_bit_number; } } // set or clear the bit in the ROM byte rom_byte_number // with mask rom_byte_mask - if (search_direction == 1) { + if (search_direction) { search->rom_no[rom_byte_number] |= rom_byte_mask; } else { search->rom_no[rom_byte_number] &= ~rom_byte_mask; diff --git a/extras/onewire/onewire.h b/extras/onewire/onewire.h index ccbccff..89823d7 100644 --- a/extras/onewire/onewire.h +++ b/extras/onewire/onewire.h @@ -4,134 +4,232 @@ #include // sdk_os_delay_us #include "FreeRTOS.h" -// 1 for keeping the parasitic power on H -#define ONEWIRE_DEFAULT_POWER 1 +/** @file onewire.h + * + * Routines to access devices using the Dallas Semiconductor 1-Wire(tm) + * protocol. + */ -// Maximum number of devices. -#define ONEWIRE_NUM 20 - -// You can exclude certain features from OneWire. In theory, this -// might save some space. In practice, the compiler automatically -// removes unused code (technically, the linker, using -fdata-sections -// and -ffunction-sections when compiling, and Wl,--gc-sections -// when linking), so most of these will not result in any code size -// reduction. Well, unless you try to use the missing features -// and redesign your program to not need them! ONEWIRE_CRC8_TABLE -// is the exception, because it selects a fast but large algorithm -// or a small but slow algorithm. - -// Select the table-lookup method of computing the 8-bit CRC -// by setting this to 1. The lookup table enlarges code size by -// about 250 bytes. It does NOT consume RAM (but did in very -// old versions of OneWire). If you disable this, a slower -// but very compact algorithm is used. +/** Select the table-lookup method of computing the 8-bit CRC + * by setting this to 1 during compilation. The lookup table enlarges code + * size by about 250 bytes. By default, a slower but very compact algorithm + * is used. + */ #ifndef ONEWIRE_CRC8_TABLE #define ONEWIRE_CRC8_TABLE 0 #endif +/** Type used to hold all 1-Wire device ROM addresses (64-bit) */ typedef uint64_t onewire_addr_t; +/** Structure to contain the current state for onewire_search_next(), etc */ typedef struct { uint8_t rom_no[8]; uint8_t last_discrepancy; bool last_device_found; } onewire_search_t; -// The following is an invalid ROM address that will never occur in a device -// (CRC mismatch), and so can be useful as an indicator for "no-such-device", -// etc. +/** ::ONEWIRE_NONE is an invalid ROM address that will never occur in a device + * (CRC mismatch), and so can be useful as an indicator for "no-such-device", + * etc. + */ #define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL)) -// 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 +/** Perform a 1-Wire reset cycle. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @returns `true` if at least one device responds with a presence pulse, + * `false` if no devices were detected (or the bus is shorted, etc) + */ bool onewire_reset(int pin); -// Issue a 1-Wire rom select command, you do the reset first. -void onewire_select(int pin, const onewire_addr_t rom); +/** Issue a 1-Wire rom select command to select a particular device. + * + * It is necessary to call onewire_reset() before calling this function. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param addr The ROM address of the device to select + * + * @returns `true` if the "ROM select" command could be succesfully issued, + * `false` if there was an error. + */ +bool onewire_select(int pin, const onewire_addr_t addr); -// Issue a 1-Wire rom skip command, to address all on bus. -void onewire_skip_rom(int pin); +/** Issue a 1-Wire "skip ROM" command to select *all* devices on the bus. + * + * It is necessary to call onewire_reset() before calling this function. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @returns `true` if the "skip ROM" command could be succesfully issued, + * `false` if there was an error. + */ +bool onewire_skip_rom(int pin); -// Write a byte. The writing code uses open-drain mode and expects the pullup -// resistor to pull the line high when not driven low. If you need strong -// power after the write (e.g. DS18B20 in parasite power mode) then call -// onewire_power() after this is complete to actively drive the line high. -// Returns true if successful, false on error. +/** Write a byte on the onewire bus. + * + * The writing code uses open-drain mode and expects the pullup resistor to + * pull the line high when not driven low. If you need strong power after the + * write (e.g. DS18B20 in parasite power mode) then call onewire_power() after + * this is complete to actively drive the line high. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param v The byte value to write + * + * @returns `true` if successful, `false` on error. + */ bool onewire_write(int pin, uint8_t v); +/** Write multiple bytes on the 1-Wire bus. + * + * See onewire_write() for more info. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param buf A pointer to the buffer of bytes to be written + * @param count Number of bytes to write + * + * @returns `true` if all bytes written successfully, `false` on error. + */ bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count); -// Read a byte. -// Returns the read byte on success, negative value on error. +/** Read a byte from a 1-Wire device. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @returns the read byte on success, negative value on error. + */ int onewire_read(int pin); +/** Read multiple bytes from a 1-Wire device. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param buf A pointer to the buffer to contain the read bytes + * @param count Number of bytes to read + * + * @returns `true` on success, `false` on error. + */ bool onewire_read_bytes(int pin, uint8_t *buf, size_t count); -// Actively drive the bus high to provide extra power for certain operations of -// parasitically-powered devices. -void onewire_power(int pin); +/** Actively drive the bus high to provide extra power for certain operations + * of parasitically-powered devices. + * + * For parasitically-powered devices which need more power than can be + * provided via the normal pull-up resistor, it may be necessary for some + * operations to drive the bus actively high. This function can be used to + * perform that operation. + * + * The bus can be depowered once it is no longer needed by calling + * onewire_depower(), or it will be depowered automatically the next time + * onewire_reset() is called to start another command. + * + * Note: Make sure the device(s) you are powering will not pull more current + * than the ESP8266 is able to supply via its GPIO pins (this is especially + * important when multiple devices are on the same bus and they are all + * performing a power-intensive operation at the same time (i.e. multiple + * DS18B20 sensors, which have all been given a "convert T" operation by using + * onewire_skip_rom())). + * + * Note: This routine will check to make sure that the bus is already high + * before driving it, to make sure it doesn't attempt to drive it high while + * something else is pulling it low (which could cause a reset or damage the + * ESP8266). + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @returns `true` on success, `false` on error. + */ +bool onewire_power(int pin); -// Stop forcing power onto the bus. You only need to do this if -// you previously called onewire_power() to drive the bus high and now want to -// allow it to float instead. Note that onewire_reset() will also -// automatically depower the bus first, so you do not need to call this first -// if you just want to start a new operation. +/** Stop forcing power onto the bus. + * + * You only need to do this if you previously called onewire_power() to drive + * the bus high and now want to allow it to float instead. Note that + * onewire_reset() will also automatically depower the bus first, so you do + * not need to call this first if you just want to start a new operation. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + */ void onewire_depower(int pin); -// Clear the search state so that if will start from the beginning again. +/** Clear the search state so that it will start from the beginning on the next + * call to onewire_search_next(). + * + * @param search The onewire_search_t structure to reset. + */ void onewire_search_start(onewire_search_t *search); -// Setup the search to find the device type 'family_code' on the next call -// to search(*newAddr) if it is present. +/** Setup the search to search for devices with the specified "family code". + * + * @param search The onewire_search_t structure to update. + * @param family_code The "family code" to search for. + */ void onewire_search_prefix(onewire_search_t *search, uint8_t family_code); -// Look for the next device. Returns the address of the next device on the bus, -// or ONEWIRE_NONE if there is no next address. ONEWIRE_NONE 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. +/** Search for the next device on the bus. + * + * The order of returned device addresses is deterministic. You will always + * get the same devices in the same order. + * + * @returns the address of the next device on the bus, or ::ONEWIRE_NONE if + * there is no next address. ::ONEWIRE_NONE might also 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. + */ onewire_addr_t onewire_search_next(onewire_search_t *search, int pin); -// Compute a Dallas Semiconductor 8 bit CRC, these are used in the -// ROM and scratchpad registers. +/** Compute a Dallas Semiconductor 8 bit CRC. + * + * These are used in the ROM address and scratchpad registers to verify the + * transmitted data is correct. + */ uint8_t onewire_crc8(const uint8_t *data, uint8_t len); -// Compute the 1-Wire CRC16 and compare it against the received CRC. -// Example usage (reading a DS2408): -// // Put everything in a buffer so we can compute the CRC easily. -// uint8_t buf[13]; -// buf[0] = 0xF0; // Read PIO Registers -// buf[1] = 0x88; // LSB address -// buf[2] = 0x00; // MSB address -// WriteBytes(net, buf, 3); // Write 3 cmd bytes -// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 -// if (!CheckCRC16(buf, 11, &buf[11])) { -// // Handle error. -// } -// -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param inverted_crc - The two CRC16 bytes in the received data. -// This should just point into the received data, -// *not* at a 16-bit integer. -// @param crc_iv - The crc starting value (optional) -// @return True, iff the CRC matches. +/** Compute the 1-Wire CRC16 and compare it against the received CRC. + * + * Example usage (reading a DS2408): + * @code + * // Put everything in a buffer so we can compute the CRC easily. + * uint8_t buf[13]; + * buf[0] = 0xF0; // Read PIO Registers + * buf[1] = 0x88; // LSB address + * buf[2] = 0x00; // MSB address + * onewire_write_bytes(pin, buf, 3); // Write 3 cmd bytes + * onewire_read_bytes(pin, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 + * if (!onewire_check_crc16(buf, 11, &buf[11])) { + * // TODO: Handle error. + * } + * @endcode + * + * @param input Array of bytes to checksum. + * @param len Number of bytes in `input` + * @param inverted_crc The two CRC16 bytes in the received data. + * This should just point into the received data, + * *not* at a 16-bit integer. + * @param crc_iv The crc starting value (optional) + * + * @returns `true` if the CRC matches, `false` otherwise. + */ bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv); -// Compute a Dallas Semiconductor 16 bit CRC. This is required to check -// the integrity of data received from many 1-Wire devices. Note that the -// CRC computed here is *not* what you'll get from the 1-Wire network, -// for two reasons: -// 1) The CRC is transmitted bitwise inverted. -// 2) Depending on the endian-ness of your processor, the binary -// representation of the two-byte return value may have a different -// byte order than the two bytes you get from 1-Wire. -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param crc_iv - The crc starting value (optional) -// @return The CRC16, as defined by Dallas Semiconductor. +/** Compute a Dallas Semiconductor 16 bit CRC. + * + * This is required to check the integrity of data received from many 1-Wire + * devices. Note that the CRC computed here is *not* what you'll get from the + * 1-Wire network, for two reasons: + * 1. The CRC is transmitted bitwise inverted. + * 2. Depending on the endian-ness of your processor, the binary + * representation of the two-byte return value may have a different + * byte order than the two bytes you get from 1-Wire. + * + * @param input Array of bytes to checksum. + * @param len How many bytes are in `input`. + * @param crc_iv The crc starting value (optional) + * + * @returns the CRC16, as defined by Dallas Semiconductor. + */ uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv); #endif From 60e468bdb2c599a2d1c4a057c3922c108d93c729 Mon Sep 17 00:00:00 2001 From: Alex Stewart Date: Tue, 15 Mar 2016 14:50:37 -0700 Subject: [PATCH 4/5] Misc ds18b20 fixups/enhancements add onewire_power() after CONVERT_T Fix naming of DS18B20_* constants --- extras/ds18b20/ds18b20.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/extras/ds18b20/ds18b20.c b/extras/ds18b20/ds18b20.c index 9814018..a06ccc3 100644 --- a/extras/ds18b20/ds18b20.c +++ b/extras/ds18b20/ds18b20.c @@ -4,17 +4,17 @@ #include "onewire/onewire.h" #include "ds18b20.h" -#define DS1820_WRITE_SCRATCHPAD 0x4E -#define DS1820_READ_SCRATCHPAD 0xBE -#define DS1820_COPY_SCRATCHPAD 0x48 -#define DS1820_READ_EEPROM 0xB8 -#define DS1820_READ_PWRSUPPLY 0xB4 -#define DS1820_SEARCHROM 0xF0 -#define DS1820_SKIP_ROM 0xCC -#define DS1820_READROM 0x33 -#define DS1820_MATCHROM 0x55 -#define DS1820_ALARMSEARCH 0xEC -#define DS1820_CONVERT_T 0x44 +#define DS18B20_WRITE_SCRATCHPAD 0x4E +#define DS18B20_READ_SCRATCHPAD 0xBE +#define DS18B20_COPY_SCRATCHPAD 0x48 +#define DS18B20_READ_EEPROM 0xB8 +#define DS18B20_READ_PWRSUPPLY 0xB4 +#define DS18B20_SEARCHROM 0xF0 +#define DS18B20_SKIP_ROM 0xCC +#define DS18B20_READROM 0x33 +#define DS18B20_MATCHROM 0x55 +#define DS18B20_ALARMSEARCH 0xEC +#define DS18B20_CONVERT_T 0x44 uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { onewire_addr_t addr; @@ -32,13 +32,14 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { onewire_reset(pin); onewire_select(pin, addr); - onewire_write(pin, DS1820_CONVERT_T); + onewire_write(pin, DS18B20_CONVERT_T); + onewire_power(pin); vTaskDelay(750 / portTICK_RATE_MS); onewire_reset(pin); onewire_select(pin, addr); - onewire_write(pin, DS1820_READ_SCRATCHPAD); + onewire_write(pin, DS18B20_READ_SCRATCHPAD); uint8_t get[10]; @@ -73,13 +74,14 @@ float ds18b20_read_single(uint8_t pin) { onewire_reset(pin); onewire_skip_rom(pin); - onewire_write(pin, DS1820_CONVERT_T); + onewire_write(pin, DS18B20_CONVERT_T); + onewire_power(pin); vTaskDelay(750 / portTICK_RATE_MS); onewire_reset(pin); onewire_skip_rom(pin); - onewire_write(pin, DS1820_READ_SCRATCHPAD); + onewire_write(pin, DS18B20_READ_SCRATCHPAD); uint8_t get[10]; From 494c2d9cec132dbd71402b3b8affdb9e4a9c2e50 Mon Sep 17 00:00:00 2001 From: Alex Stewart Date: Thu, 17 Mar 2016 16:03:46 -0700 Subject: [PATCH 5/5] Implement new ds18b20 APIs --- .../ds18b20_broadcaster/ds18b20_broadcaster.c | 20 ++- examples/ds18b20_onewire/ds18b20_onewire.c | 89 +++++++----- extras/ds18b20/ds18b20.c | 115 ++++++++++++++- extras/ds18b20/ds18b20.h | 133 ++++++++++++++++++ 4 files changed, 311 insertions(+), 46 deletions(-) diff --git a/examples/ds18b20_broadcaster/ds18b20_broadcaster.c b/examples/ds18b20_broadcaster/ds18b20_broadcaster.c index fe12055..def94fd 100644 --- a/examples/ds18b20_broadcaster/ds18b20_broadcaster.c +++ b/examples/ds18b20_broadcaster/ds18b20_broadcaster.c @@ -19,15 +19,14 @@ // DS18B20 driver #include "ds18b20/ds18b20.h" -// Onewire init -#include "onewire/onewire.h" void broadcast_temperature(void *pvParameters) { uint8_t amount = 0; - uint8_t sensors = 2; - ds_sensor_t t[sensors]; + uint8_t sensors = 1; + ds18b20_addr_t addrs[sensors]; + float results[sensors]; // Use GPIO 13 as one wire pin. uint8_t GPIO_FOR_ONE_WIRE = 13; @@ -36,8 +35,6 @@ void broadcast_temperature(void *pvParameters) // Broadcaster part err_t err; - // Initialize one wire bus. - onewire_init(GPIO_FOR_ONE_WIRE); while(1) { @@ -66,18 +63,17 @@ void broadcast_temperature(void *pvParameters) for(;;) { // Search all DS18B20, return its amount and feed 't' structure with result data. - amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t); + amount = ds18b20_scan_devices(GPIO_FOR_ONE_WIRE, addrs, sensors); if (amount < sensors){ printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount); } - for (int i = 0; i < amount; ++i) + ds18b20_measure_and_read_multi(GPIO_FOR_ONE_WIRE, addrs, sensors, results); + for (int i = 0; i < sensors; ++i) { - int intpart = (int)t[i].value; - int fraction = (int)((t[i].value - intpart) * 100); - // Multiple "" here is just to satisfy compiler and don`t raise 'hex escape sequence out of range' warning. - sprintf(msg, "Sensor %d report: %d.%02d ""\xC2""\xB0""C\n",t[i].id, intpart, fraction); + // ("\xC2\xB0" is the degree character (U+00B0) in UTF-8) + sprintf(msg, "Sensor %08x%08x reports: %f \xC2\xB0""C\n", (uint32_t)(addrs[i] >> 32), (uint32_t)addrs[i], results[i]); printf("%s", msg); struct netbuf* buf = netbuf_new(); diff --git a/examples/ds18b20_onewire/ds18b20_onewire.c b/examples/ds18b20_onewire/ds18b20_onewire.c index e152b10..b9b7655 100644 --- a/examples/ds18b20_onewire/ds18b20_onewire.c +++ b/examples/ds18b20_onewire/ds18b20_onewire.c @@ -1,55 +1,78 @@ -/* ds18b20 - Retrieves temperature from ds18b20 sensors and print it out. +/* ds18b20_onewire.c - Retrieves readings from one or more DS18B20 temperature + * sensors, and prints the results to stdout. * * This sample code is in the public domain., */ -#include "espressif/esp_common.h" -#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" -#include "timers.h" -#include "queue.h" +#include "esp/uart.h" -// DS18B20 driver #include "ds18b20/ds18b20.h" -void print_temperature(void *pvParameters) -{ - int delay = 500; - uint8_t amount = 0; - // Declare amount of sensors - uint8_t sensors = 2; - ds_sensor_t t[sensors]; - - // Use GPIO 13 as one wire pin. - uint8_t GPIO_FOR_ONE_WIRE = 13; +#define SENSOR_GPIO 13 +#define MAX_SENSORS 8 +#define RESCAN_INTERVAL 8 +#define LOOP_DELAY_MS 250 + +void print_temperature(void *pvParameters) { + ds18b20_addr_t addrs[MAX_SENSORS]; + float temps[MAX_SENSORS]; + int sensor_count; + // There is no special initialization required before using the ds18b20 + // routines. However, we make sure that the internal pull-up resistor is + // enabled on the GPIO pin so that one can connect up a sensor without + // needing an external pull-up (Note: The internal (~47k) pull-ups of the + // ESP8266 do appear to work, at least for simple setups (one or two sensors + // connected with short leads), but do not technically meet the pull-up + // requirements from the DS18B20 datasheet and may not always be reliable. + // For a real application, a proper 4.7k external pull-up resistor is + // recommended instead!) + + gpio_set_pullup(SENSOR_GPIO, true, true); + while(1) { - // Search all DS18B20, return its amount and feed 't' structure with result data. - amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t); + // Every RESCAN_INTERVAL samples, check to see if the sensors connected + // to our bus have changed. + sensor_count = ds18b20_scan_devices(SENSOR_GPIO, addrs, MAX_SENSORS); - if (amount < sensors){ - printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount); - } + if (sensor_count < 1) { + printf("\nNo sensors detected!\n"); + } else { + printf("\n%d sensors detected:\n", sensor_count); + // If there were more sensors found than we have space to handle, + // just report the first MAX_SENSORS.. + if (sensor_count > MAX_SENSORS) sensor_count = MAX_SENSORS; - for (int i = 0; i < amount; ++i) - { - int intpart = (int)t[i].value; - int fraction = (int)((t[i].value - intpart) * 100); - // Multiple "" here is just to satisfy compiler and don`t raise 'hex escape sequence out of range' warning. - printf("Sensor %d report: %d.%02d ""\xC2""\xB0""C\n",t[i].id, intpart, fraction); + // Do a number of temperature samples, and print the results. + for (int i = 0; i < RESCAN_INTERVAL; i++) { + ds18b20_measure_and_read_multi(SENSOR_GPIO, addrs, sensor_count, temps); + for (int j = 0; j < sensor_count; j++) { + // The DS18B20 address is a 64-bit integer, but newlib-nano + // printf does not support printing 64-bit values, so we + // split it up into two 32-bit integers and print them + // back-to-back to make it look like one big hex number. + uint32_t addr0 = addrs[j] >> 32; + uint32_t addr1 = addrs[j]; + float temp_c = temps[j]; + float temp_f = (temp_c * 1.8) + 32; + printf(" Sensor %08x%08x reports %f deg C (%f deg F)\n", addr0, addr1, temp_c, temp_f); + } + printf("\n"); + + // Wait for a little bit between each sample (note that the + // ds18b20_measure_and_read_multi operation already takes at + // least 750ms to run, so this is on top of that delay). + vTaskDelay(LOOP_DELAY_MS / portTICK_RATE_MS); + } } - printf("\n"); - vTaskDelay(delay / portTICK_RATE_MS); } } -void user_init(void) -{ +void user_init(void) { uart_set_baud(0, 115200); - printf("SDK version:%s\n", sdk_system_get_sdk_version()); - xTaskCreate(&print_temperature, (signed char *)"print_temperature", 256, NULL, 2, NULL); } diff --git a/extras/ds18b20/ds18b20.c b/extras/ds18b20/ds18b20.c index a06ccc3..c965b04 100644 --- a/extras/ds18b20/ds18b20.c +++ b/extras/ds18b20/ds18b20.c @@ -1,7 +1,7 @@ #include "FreeRTOS.h" #include "task.h" +#include "math.h" -#include "onewire/onewire.h" #include "ds18b20.h" #define DS18B20_WRITE_SCRATCHPAD 0x4E @@ -16,6 +16,8 @@ #define DS18B20_ALARMSEARCH 0xEC #define DS18B20_CONVERT_T 0x44 +#define os_sleep_ms(x) vTaskDelay(((x) + portTICK_RATE_MS - 1) / portTICK_RATE_MS) + uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { onewire_addr_t addr; onewire_search_t search; @@ -108,3 +110,114 @@ float ds18b20_read_single(uint8_t pin) { return temperature; //printf("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100); } + +bool ds18b20_measure(int pin, ds18b20_addr_t addr, bool wait) { + if (!onewire_reset(pin)) { + return false; + } + if (addr == DS18B20_ANY) { + onewire_skip_rom(pin); + } else { + onewire_select(pin, addr); + } + taskENTER_CRITICAL(); + onewire_write(pin, DS18B20_CONVERT_T); + // For parasitic devices, power must be applied within 10us after issuing + // the convert command. + onewire_power(pin); + taskEXIT_CRITICAL(); + + if (wait) { + os_sleep_ms(750); + onewire_depower(pin); + } + + return true; +} + +bool ds18b20_read_scratchpad(int pin, ds18b20_addr_t addr, uint8_t *buffer) { + uint8_t crc; + uint8_t expected_crc; + + if (!onewire_reset(pin)) { + return false; + } + if (addr == DS18B20_ANY) { + onewire_skip_rom(pin); + } else { + onewire_select(pin, addr); + } + onewire_write(pin, DS18B20_READ_SCRATCHPAD); + + for (int i = 0; i < 8; i++) { + buffer[i] = onewire_read(pin); + } + crc = onewire_read(pin); + + expected_crc = onewire_crc8(buffer, 8); + if (crc != expected_crc) { + printf("CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc); + return false; + } + + return true; +} + +float ds18b20_read_temperature(int pin, ds18b20_addr_t addr) { + uint8_t scratchpad[8]; + int temp; + + if (!ds18b20_read_scratchpad(pin, addr, scratchpad)) { + return NAN; + } + + temp = scratchpad[1] << 8 | scratchpad[0]; + + return ((float)temp * 625.0)/10000; +} + +float ds18b20_measure_and_read(int pin, ds18b20_addr_t addr) { + if (!ds18b20_measure(pin, addr, true)) { + return NAN; + } + return ds18b20_read_temperature(pin, addr); +} + +bool ds18b20_measure_and_read_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list) { + if (!ds18b20_measure(pin, DS18B20_ANY, true)) { + for (int i=0; i < addr_count; i++) { + result_list[i] = NAN; + } + return false; + } + return ds18b20_read_temp_multi(pin, addr_list, addr_count, result_list); +} + +int ds18b20_scan_devices(int pin, ds18b20_addr_t *addr_list, int addr_count) { + onewire_search_t search; + onewire_addr_t addr; + int found = 0; + + onewire_search_start(&search); + while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) { + if (found < addr_count) { + addr_list[found] = addr; + } + found++; + } + return found; +} + +bool ds18b20_read_temp_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list) { + bool result = true; + + for (int i = 0; i < addr_count; i++) { + result_list[i] = ds18b20_read_temperature(pin, addr_list[i]); + if (isnan(result_list[i])) { + result = false; + } + } + return result; +} + + diff --git a/extras/ds18b20/ds18b20.h b/extras/ds18b20/ds18b20.h index 594227b..04b4a53 100644 --- a/extras/ds18b20/ds18b20.h +++ b/extras/ds18b20/ds18b20.h @@ -1,6 +1,139 @@ #ifndef DRIVER_DS18B20_H_ #define DRIVER_DS18B20_H_ +#include "onewire/onewire.h" + +/** @file ds18b20.h + * + * Communicate with the DS18B20 family of one-wire temperature sensor ICs. + * + */ + +typedef onewire_addr_t ds18b20_addr_t; + +/** An address value which can be used to indicate "any device on the bus" */ +#define DS18B20_ANY ONEWIRE_NONE + +/** Find the addresses of all DS18B20 devices on the bus. + * + * Scans the bus for all devices and places their addresses in the supplied + * array. If there are more than `addr_count` devices on the bus, only the + * first `addr_count` are recorded. + * + * @param pin The GPIO pin connected to the DS18B20 bus + * @param addr_list A pointer to an array of ds18b20_addr_t values. This + * will be populated with the addresses of the found + * devices. + * @param addr_count Number of slots in the `addr_list` array. At most this + * many addresses will be returned. + * + * @returns The number of devices found. Note that this may be less than, + * equal to, or more than `addr_count`, depending on how many DS18B20 devices + * are attached to the bus. + */ +int ds18b20_scan_devices(int pin, ds18b20_addr_t *addr_list, int addr_count); + +/** Tell one or more sensors to perform a temperature measurement and + * conversion (CONVERT_T) operation. This operation can take up to 750ms to + * complete. + * + * If `wait=true`, this routine will automatically drive the pin high for the + * necessary 750ms after issuing the command to ensure parasitically-powered + * devices have enough power to perform the conversion operation (for + * non-parasitically-powered devices, this is not necessary but does not + * hurt). If `wait=false`, this routine will drive the pin high, but will + * then return immediately. It is up to the caller to wait the requisite time + * and then depower the bus using onewire_depower() or by issuing another + * command once conversion is done. + * + * @param pin The GPIO pin connected to the DS18B20 device + * @param addr The 64-bit address of the device on the bus. This can be set + * to ::DS18B20_ANY to send the command to all devices on the bus + * at the same time. + * @param wait Whether to wait for the necessary 750ms for the DS18B20 to + * finish performing the conversion before returning to the + * caller (You will normally want to do this). + * + * @returns `true` if the command was successfully issued, or `false` on error. + */ +bool ds18b20_measure(int pin, ds18b20_addr_t addr, bool wait); + +/** Read the value from the last CONVERT_T operation. + * + * This should be called after ds18b20_measure() to fetch the result of the + * temperature measurement. + * + * @param pin The GPIO pin connected to the DS18B20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18B20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * + * @returns The temperature in degrees Celsius, or NaN if there was an error. + */ +float ds18b20_read_temperature(int pin, ds18b20_addr_t addr); + +/** Read the value from the last CONVERT_T operation for multiple devices. + * + * This should be called after ds18b20_measure() to fetch the result of the + * temperature measurement. + * + * @param pin The GPIO pin connected to the DS18B20 bus + * @param addr_list A list of addresses for devices to read. + * @param addr_count The number of entries in `addr_list`. + * @param result_list An array of floats to hold the returned temperature + * values. It should have at least `addr_count` entries. + * + * @returns `true` if all temperatures were fetched successfully, or `false` + * if one or more had errors (the temperature for erroring devices will be + * returned as NaN). + */ +bool ds18b20_read_temp_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list); + +/** Perform a ds18b20_measure() followed by ds18b20_read_temperature() + * + * @param pin The GPIO pin connected to the DS18B20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18B20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * + * @returns The temperature in degrees Celsius, or NaN if there was an error. + */ +float ds18b20_measure_and_read(int pin, ds18b20_addr_t addr); + +/** Perform a ds18b20_measure() followed by ds18b20_read_temp_multi() + * + * @param pin The GPIO pin connected to the DS18B20 bus + * @param addr_list A list of addresses for devices to read. + * @param addr_count The number of entries in `addr_list`. + * @param result_list An array of floats to hold the returned temperature + * values. It should have at least `addr_count` entries. + * + * @returns `true` if all temperatures were fetched successfully, or `false` + * if one or more had errors (the temperature for erroring devices will be + * returned as NaN). + */ +bool ds18b20_measure_and_read_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list); + +/** Read the scratchpad data for a particular DS18B20 device. + * + * This is not generally necessary to do directly. It is done automatically + * as part of ds18b20_read_temperature(). + * + * @param pin The GPIO pin connected to the DS18B20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18B20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param buffer An 8-byte buffer to hold the read data. + * + * @returns `true` if the data was read successfully, or `false` on error. + */ +bool ds18b20_read_scratchpad(int pin, ds18b20_addr_t addr, uint8_t *buffer); + +// The following are obsolete/deprecated APIs + typedef struct { uint8_t id; float value;