From 494c2d9cec132dbd71402b3b8affdb9e4a9c2e50 Mon Sep 17 00:00:00 2001 From: Alex Stewart Date: Thu, 17 Mar 2016 16:03:46 -0700 Subject: [PATCH] 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;