Tue, 03 Oct 2023 17:24:06 +0200
Version 0.3.0. Backported network code from experimental roaming project. Will now connect after reset to the strongest AP. Id the signal level drops below -67, extra scans are done to see for a better AP. Nothing is done yet. Removed config.conf file, all info is taken from the project menu and live tests. Better log the board type and send it via json mqtt. Send bssid and current channel too.
/* * MIT License * * Copyright (c) 2017 David Antliff * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * @file ds18b20.c * * Resolution is cached in the DS18B20_Info object to avoid querying the hardware * every time a temperature conversion is required. However this can result in the * cached value becoming inconsistent with the hardware value, so care must be taken. * */ #include <stddef.h> #include <string.h> #include <stdlib.h> #include <stdbool.h> #include <stdint.h> #include <math.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/gpio.h" #include "esp_system.h" #include "esp_log.h" #include "ds18b20.h" #include "owb.h" static const char * TAG = "ds18b20"; static const int T_CONV = 750; // maximum conversion time at 12-bit resolution in milliseconds // Function commands #define DS18B20_FUNCTION_TEMP_CONVERT 0x44 ///< Start temperature conversion #define DS18B20_FUNCTION_SCRATCHPAD_WRITE 0x4E ///< Write scratchpad #define DS18B20_FUNCTION_SCRATCHPAD_READ 0xBE ///< Read scratchpad #define DS18B20_FUNCTION_SCRATCHPAD_COPY 0x48 ///< Copy scratchpad #define DS18B20_FUNCTION_EEPROM_RECALL 0xB8 ///< EEPROM recall #define DS18B20_FUNCTION_POWER_SUPPLY_READ 0xB4 ///< Read powersupply mode /// @cond ignore typedef struct { uint8_t temperature[2]; // [0] is LSB, [1] is MSB uint8_t trigger_high; uint8_t trigger_low; uint8_t configuration; uint8_t reserved[3]; uint8_t crc; } Scratchpad; /// @endcond ignore static void _init(DS18B20_Info * ds18b20_info, const OneWireBus * bus) { if (ds18b20_info != NULL) { ds18b20_info->bus = bus; memset(&ds18b20_info->rom_code, 0, sizeof(ds18b20_info->rom_code)); ds18b20_info->use_crc = false; ds18b20_info->resolution = DS18B20_RESOLUTION_INVALID; ds18b20_info->solo = false; // assume multiple devices unless told otherwise ds18b20_info->init = true; } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } } static bool _is_init(const DS18B20_Info * ds18b20_info) { bool ok = false; if (ds18b20_info != NULL) { if (ds18b20_info->init) { // OK ok = true; } else { ESP_LOGE(TAG, "ds18b20_info is not initialised"); } } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } return ok; } static bool _address_device(const DS18B20_Info * ds18b20_info) { bool present = false; if (_is_init(ds18b20_info)) { owb_reset(ds18b20_info->bus, &present); if (present) { if (ds18b20_info->solo) { // if there's only one device on the bus, we can skip // sending the ROM code and instruct it directly owb_write_byte(ds18b20_info->bus, OWB_ROM_SKIP); } else { // if there are multiple devices on the bus, a Match ROM command // must be issued to address a specific slave owb_write_byte(ds18b20_info->bus, OWB_ROM_MATCH); owb_write_rom_code(ds18b20_info->bus, ds18b20_info->rom_code); } } else { ESP_LOGE(TAG, "ds18b20 device not responding"); } } return present; } static bool _check_resolution(DS18B20_RESOLUTION resolution) { return (resolution >= DS18B20_RESOLUTION_9_BIT) && (resolution <= DS18B20_RESOLUTION_12_BIT); } static float _wait_for_conversion(DS18B20_RESOLUTION resolution) { float elapsed_time = 0.0f; if (_check_resolution(resolution)) { int divisor = 1 << (DS18B20_RESOLUTION_12_BIT - resolution); ESP_LOGD(TAG, "divisor %d", divisor); float max_conversion_time = (float)T_CONV / (float)divisor; int ticks = ceil(max_conversion_time / portTICK_PERIOD_MS); ESP_LOGD(TAG, "wait for conversion: %.3f ms, %d ticks", max_conversion_time, ticks); // wait at least this maximum conversion time vTaskDelay(ticks); // TODO: measure elapsed time more accurately elapsed_time = ticks * portTICK_PERIOD_MS; } return elapsed_time; } static float _decode_temp(uint8_t lsb, uint8_t msb, DS18B20_RESOLUTION resolution) { float result = 0.0f; if (_check_resolution(resolution)) { // masks to remove undefined bits from result static const uint8_t lsb_mask[4] = { ~0x03, ~0x02, ~0x01, ~0x00 }; uint8_t lsb_masked = lsb_mask[resolution - DS18B20_RESOLUTION_9_BIT] & lsb; int16_t raw = (msb << 8) | lsb_masked; result = raw / 16.0f; } else { ESP_LOGE(TAG, "Unsupported resolution %d", resolution); } return result; } static size_t _min(size_t x, size_t y) { return x > y ? y : x; } static Scratchpad _read_scratchpad(const DS18B20_Info * ds18b20_info, size_t count) { count = _min(sizeof(Scratchpad), count); // avoid overflow Scratchpad scratchpad = {0}; ESP_LOGD(TAG, "scratchpad read %d bytes: ", count); if (_address_device(ds18b20_info)) { owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_READ); owb_read_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad, count); //esp_log_buffer_hex(TAG, &scratchpad, count); } return scratchpad; } static bool _write_scratchpad(const DS18B20_Info * ds18b20_info, const Scratchpad * scratchpad, bool verify) { bool result = false; // Only bytes 2, 3 and 4 (trigger and configuration) can be written. // All three bytes MUST be written before the next reset to avoid corruption. if (_is_init(ds18b20_info)) { if (_address_device(ds18b20_info)) { owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_WRITE); owb_write_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad->trigger_high, 3); ESP_LOGD(TAG, "scratchpad write 3 bytes:"); //esp_log_buffer_hex(TAG, &scratchpad->trigger_high, 3); result = true; if (verify) { Scratchpad read = _read_scratchpad(ds18b20_info, offsetof(Scratchpad, configuration) + 1); if (memcmp(&scratchpad->trigger_high, &read.trigger_high, 3) != 0) { ESP_LOGE(TAG, "scratchpad verify failed: " "wrote {0x%02x, 0x%02x, 0x%02x}, " "read {0x%02x, 0x%02x, 0x%02x}", scratchpad->trigger_high, scratchpad->trigger_low, scratchpad->configuration, read.trigger_high, read.trigger_low, read.configuration); result = false; } } } } return result; } // Public API DS18B20_Info * ds18b20_malloc(void) { DS18B20_Info * ds18b20_info = malloc(sizeof(*ds18b20_info)); if (ds18b20_info != NULL) { memset(ds18b20_info, 0, sizeof(*ds18b20_info)); ESP_LOGD(TAG, "malloc %p", ds18b20_info); } else { ESP_LOGE(TAG, "malloc failed"); } return ds18b20_info; } void ds18b20_free(DS18B20_Info ** ds18b20_info) { if (ds18b20_info != NULL && (*ds18b20_info != NULL)) { ESP_LOGD(TAG, "free %p", *ds18b20_info); free(*ds18b20_info); *ds18b20_info = NULL; } } void ds18b20_init(DS18B20_Info * ds18b20_info, const OneWireBus * bus, OneWireBus_ROMCode rom_code) { if (ds18b20_info != NULL) { _init(ds18b20_info, bus); ds18b20_info->rom_code = rom_code; // read current resolution from device as it may not be power-on or factory default ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } } void ds18b20_init_solo(DS18B20_Info * ds18b20_info, const OneWireBus * bus) { if (ds18b20_info != NULL) { _init(ds18b20_info, bus); ds18b20_info->solo = true; // read current resolution from device as it may not be power-on or factory default ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } } void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc) { if (_is_init(ds18b20_info)) { ds18b20_info->use_crc = use_crc; ESP_LOGD(TAG, "use_crc %d", ds18b20_info->use_crc); } } bool ds18b20_set_resolution(DS18B20_Info * ds18b20_info, DS18B20_RESOLUTION resolution) { bool result = false; if (_is_init(ds18b20_info)) { if (_check_resolution(ds18b20_info->resolution)) { // read scratchpad up to and including configuration register Scratchpad scratchpad = _read_scratchpad(ds18b20_info, offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1); // modify configuration register to set resolution uint8_t value = (((resolution - 1) & 0x03) << 5) | 0x1f; scratchpad.configuration = value; ESP_LOGD(TAG, "configuration value 0x%02x", value); // write bytes 2, 3 and 4 of scratchpad result = _write_scratchpad(ds18b20_info, &scratchpad, /* verify */ true); if (result) { ds18b20_info->resolution = resolution; ESP_LOGD(TAG, "Resolution set to %d bits", (int)resolution); } else { // Resolution change failed - update the info resolution with the value read from configuration ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); ESP_LOGW(TAG, "Resolution consistency lost - refreshed from device: %d", ds18b20_info->resolution); } } else { ESP_LOGE(TAG, "Unsupported resolution %d", resolution); } } return result; } DS18B20_RESOLUTION ds18b20_read_resolution(DS18B20_Info * ds18b20_info) { DS18B20_RESOLUTION resolution = DS18B20_RESOLUTION_INVALID; if (_is_init(ds18b20_info)) { // read scratchpad up to and including configuration register Scratchpad scratchpad = _read_scratchpad(ds18b20_info, offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1); resolution = ((scratchpad.configuration >> 5) & 0x03) + DS18B20_RESOLUTION_9_BIT; if (!_check_resolution(resolution)) { ESP_LOGE(TAG, "invalid resolution read from device: 0x%02x", scratchpad.configuration); resolution = DS18B20_RESOLUTION_INVALID; } else { ESP_LOGD(TAG, "Resolution read as %d", resolution); } } return resolution; } bool ds18b20_convert(const DS18B20_Info * ds18b20_info) { bool result = false; if (_is_init(ds18b20_info)) { const OneWireBus * bus = ds18b20_info->bus; if (_address_device(ds18b20_info)) { // initiate a temperature measurement owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT); result = true; } else { ESP_LOGE(TAG, "ds18b20 device not responding"); } } return result; } void ds18b20_convert_all(const OneWireBus * bus) { bool is_present = false; owb_reset(bus, &is_present); owb_write_byte(bus, OWB_ROM_SKIP); owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT); } float ds18b20_wait_for_conversion(const DS18B20_Info * ds18b20_info) { float elapsed_time = 0.0f; if (_is_init(ds18b20_info)) { elapsed_time = _wait_for_conversion(ds18b20_info->resolution); } return elapsed_time; } DS18B20_ERROR ds18b20_read_temp(const DS18B20_Info * ds18b20_info, float * value) { DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN; if (_is_init(ds18b20_info)) { const OneWireBus * bus = ds18b20_info->bus; if (_address_device(ds18b20_info)) { // read measurement if (owb_write_byte(bus, DS18B20_FUNCTION_SCRATCHPAD_READ) == OWB_STATUS_OK) { err = DS18B20_OK; uint8_t temp_LSB = 0; uint8_t temp_MSB = 0; if (!ds18b20_info->use_crc) { // Without CRC: owb_read_byte(bus, &temp_LSB); owb_read_byte(bus, &temp_MSB); bool is_present = false; owb_reset(bus, &is_present); // terminate early } else { // with CRC: uint8_t buffer[9] = {0}; owb_read_bytes(bus, buffer, 9); temp_LSB = buffer[0]; temp_MSB = buffer[1]; if (owb_crc8_bytes(0, buffer, 9) != 0) { ESP_LOGE(TAG, "CRC failed"); temp_LSB = 0x00; temp_MSB = 0x80; err = DS18B20_ERROR_CRC; } } if (err == DS18B20_OK) { float temp = _decode_temp(temp_LSB, temp_MSB, ds18b20_info->resolution); ESP_LOGD(TAG, "temp_LSB 0x%02x, temp_MSB 0x%02x, temp %f", temp_LSB, temp_MSB, temp); if (value) { *value = temp; } } } else { ESP_LOGE(TAG, "owb_write_byte failed"); err = DS18B20_ERROR_OWB; } } else { ESP_LOGE(TAG, "ds18b20 device not responding"); err = DS18B20_ERROR_DEVICE; } } return err; } DS18B20_ERROR ds18b20_convert_and_read_temp(const DS18B20_Info * ds18b20_info, float * value) { DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN; if (_is_init(ds18b20_info)) { if (ds18b20_convert(ds18b20_info)) { // wait at least maximum conversion time _wait_for_conversion(ds18b20_info->resolution); if (value) { *value = 0.0f; err = ds18b20_read_temp(ds18b20_info, value); } else { err = DS18B20_ERROR_NULL; } } } return err; }