Using new fantastic RMT based library for DS18B20, banished issues with dodgy readings from sensor.

Persistent variables now used for temperature, pump and cyclic mode enabled settings (not NV)
NV save is now staged, so it can be performed at an appropriate time, after reading DS18B20!
JSONpack, instead of lame JSONloose to allow single line JSON output
This commit is contained in:
Ray Jones 2019-06-29 18:08:37 +10:00
parent eef4365a83
commit 924a079fb2
29 changed files with 7610 additions and 115 deletions

View file

@ -86,8 +86,6 @@
*/
//#include "src/WiFi/ABMqtt.h"
#include <OneWire.h>
#include <Wire.h>
#include "src/cfg/BTCConfig.h"
#include "src/cfg/pins.h"
#include "src/RTC/Timers.h"
@ -106,7 +104,9 @@
#include "src/Utility/BoardDetect.h"
#include "src/OLED/ScreenManager.h"
#include "src/OLED/keypad.h"
#include <DallasTemperature.h>
#include "src/Utility/TempSense.h"
#include <rom/rtc.h>
#if USE_SPIFFS == 1
#include <esp_spiffs.h>
#include <SPIFFS.h>
@ -119,8 +119,8 @@
#define RX_DATA_TIMOUT 50
const int FirmwareRevision = 23;
const int FirmwareSubRevision = 3;
const char* FirmwareDate = "25 Jun 2019";
const int FirmwareSubRevision = 4;
const char* FirmwareDate = "29 Jun 2019";
#ifdef ESP32
@ -149,14 +149,13 @@ void heaterOn();
void heaterOff();
// DS18B20 temperature sensor support
OneWire ds(15); // on pin 5 (a 4.7K resistor is necessary)
DallasTemperature TempSensor(&ds);
DeviceAddress tempSensorAddress;
// Uses the RMT timeslot driver to operate as a one-wire bus
CTempSense TempSensor;
long lastTemperatureTime; // used to moderate DS18B20 access
float fFilteredTemperature = -100; // -100: force direct update uopn first pass
const float fAlpha = 0.95; // exponential mean alpha
int DS18B20holdoff = 2;
int DS18B20holdon = 0;
int BoardRevision = 0;
unsigned long lastAnimationTime; // used to sequence updates to LCD for animation
@ -180,7 +179,12 @@ long lastRxTime; // used to observe inter character delays
bool bHasOEMController = false;
bool bHasOEMLCDController = false;
bool bHasHtrData = false;
bool bUserON = false;
// these variables will persist over a soft reboot.
__NOINIT_ATTR bool bForceInit; // = false;
__NOINIT_ATTR bool bUserON; // = false;
__NOINIT_ATTR uint8_t demandDegC;
__NOINIT_ATTR uint8_t demandPump;
bool bReportBlueWireData = REPORT_RAW_DATA;
bool bReportJSONData = REPORT_JSON_TRANSMIT;
@ -260,19 +264,6 @@ void parentKeyHandler(uint8_t event)
}
const char* print18B20Address(DeviceAddress deviceAddress)
{
static char addrStr[32];
addrStr[0] = 0;
for (uint8_t i = 0; i < 8; i++)
{
char subset[8];
sprintf(subset, "%02X%c", deviceAddress[i], i<7 ? ':' : ' ');
strcat(addrStr, subset);
}
return addrStr;
}
#if USE_SPIFFS == 1
void listDir(fs::FS &fs, const char * dirname, uint8_t levels)
{
@ -312,6 +303,13 @@ void interruptReboot()
void setup() {
// ensure proper initialisation of persistent vars after power on
if(rtc_get_reset_reason(0) == 1 || bForceInit) {
bForceInit = false;
bUserON = false;
demandPump = demandDegC = 22;
}
// initially, ensure the GPIO outputs are not activated during startup
// (GPIO2 tends to be one with default chip startup)
pinMode(GPIOout1_pin, OUTPUT);
@ -319,8 +317,6 @@ void setup() {
digitalWrite(GPIOout1_pin, LOW);
digitalWrite(GPIOout2_pin, LOW);
TempSensor.begin();
// initialise TelnetSpy (port 23) as well as Serial to 115200
// Serial is the usual USB connection to a PC
// DO THIS BEFORE WE TRY AND SEND DEBUG INFO!
@ -334,9 +330,17 @@ void setup() {
DebugPort.begin(115200);
DebugPort.println("_______________________________________________________________");
DebugPort.println("DS18B20 status dump");
DebugPort.printf(" Temperature for device#1 (idx 0) is: %.1f\r\n", TempSensor.getTempCByIndex(0));
DebugPort.printf("Reset reason: core0:%d, core1:%d\r\n", rtc_get_reset_reason(0), rtc_get_reset_reason(0));
DebugPort.printf("Previous user ON = %d\r\n", bUserON); // state flag required for cyclic mode to persist properly after a WD reboot :-)
// initialise DS18B20 sensor interface
TempSensor.begin(DS18B20_Pin);
TempSensor.startConvert(); // kick off initial temperature sample
lastTemperatureTime = millis();
lastAnimationTime = millis();
BoardRevision = BoardDetect();
DebugPort.printf("Board revision: V%.1f\r\n", float(BoardRevision) * 0.1);
@ -354,41 +358,6 @@ void setup() {
}
#endif
// locate devices on the bus
DebugPort.print("Locating DS18B20 devices...");
// initialise DS18B20 temperature sensor(s)
// Grab a count of devices on the wire
int numberOfDevices = TempSensor.getDeviceCount();
DebugPort.printf(" Found %d devices\r\n", numberOfDevices);
// report parasite power requirements
DebugPort.printf(" Parasite power is: %s\r\n", TempSensor.isParasitePowerMode() ? "ON" : "OFF");
// Loop through each device, print out address
for(int i=0;i<numberOfDevices; i++)
{
// Search the wire for address
DeviceAddress tempDeviceAddress;
if(TempSensor.getAddress(tempDeviceAddress, i)) {
DebugPort.printf(" Found DS18B20 device#%d with address: %s\r\n", i+1, print18B20Address(tempDeviceAddress));
DebugPort.printf(" Resolution: %d bits\r\n", TempSensor.getResolution(tempDeviceAddress));
} else {
DebugPort.printf(" Found ghost @ device#%d, but could not detect address. Check power and cabling\r\n", i+1);
}
}
memset(tempSensorAddress, 0, 8);
if(numberOfDevices)
TempSensor.getAddress(tempSensorAddress, 0);
TempSensor.setWaitForConversion(false);
TempSensor.requestTemperatures();
lastTemperatureTime = millis();
lastAnimationTime = millis();
NVstore.init();
NVstore.load();
@ -782,14 +751,14 @@ void loop()
tDelta = timenow - lastTemperatureTime;
if(tDelta > MIN_TEMPERATURE_INTERVAL) { // maintain a minimum holdoff period
lastTemperatureTime = millis(); // reset time to observe temeprature
fTemperature = TempSensor.getTempCByIndex(0); // read sensor
// DebugPort.printf("DS18B20 = %f\r\n", fTemperature);
// initialise filtered temperature upon very first pass
if(fTemperature > -80) { // avoid disconnected sensor readings being integrated
DS18B20holdon = 0;
if(DS18B20holdoff)
DS18B20holdoff--; // first value upon sensor connect is bad
if(TempSensor.readTemperature(fTemperature)) {
if(DS18B20holdoff) {
DS18B20holdoff--;
DebugPort.printf("Skipped initial DS18B20 reading: %f\r\n", fTemperature);
} // first value upon sensor connect is bad
else {
// initialise filtered temperature upon very first pass
if(fFilteredTemperature < -90) { // avoid FP exactness issues - starts as -100 on boot
fFilteredTemperature = fTemperature; // prime with first *valid* reading
}
@ -800,21 +769,17 @@ void loop()
}
}
else {
DS18B20holdon++;
if(DS18B20holdon > 2) {
DS18B20holdon = 2;
if(!DS18B20holdoff)
DebugPort.println("\007DS18B20 sensor removed?");
DS18B20holdoff = 2;
DS18B20holdoff = 3;
fFilteredTemperature = -100;
}
}
TempSensor.requestTemperatures(); // prep sensor for future reading
TempSensor.startConvert(); // request a new conversion, will be ready by the time we loop back around
ScreenManager.reqUpdate();
}
updateJSONclients(bReportJSONData);
CommState.set(CommStates::Idle);
NVstore.doSave(); // now is a good time to store to the NV storage, well away from any blue wire activity
break;
} // switch(CommState)
@ -936,14 +901,10 @@ bool reqTemp(unsigned char newTemp, bool save)
// set and save the demand to NV storage
// note that we now maintain fixed Hz and Thermostat set points seperately
sUserSettings settings = NVstore.getUserSettings();
if(getThermostatModeActive())
settings.demandDegC = newTemp;
demandDegC = newTemp;
else
settings.demandPump = newTemp;
NVstore.setUserSettings(settings);
if(save)
NVstore.save();
demandPump = newTemp;
ScreenManager.reqUpdate();
return true;
@ -953,9 +914,9 @@ bool reqTempDelta(int delta)
{
unsigned char newTemp;
if(getThermostatModeActive())
newTemp = NVstore.getUserSettings().demandDegC + delta;
newTemp = demandDegC + delta;
else
newTemp = NVstore.getUserSettings().demandPump + delta;
newTemp = demandPump + delta;
return reqTemp(newTemp);
}
@ -1017,13 +978,29 @@ void reqPumpPrime(bool on)
DefaultBTCParams.setPump_Prime(on);
}
void forceBootInit()
{
bForceInit = true;
}
uint8_t getDemandDegC()
{
return demandDegC;
}
uint8_t getDemandPump()
{
return demandPump;
}
float getTemperatureDesired()
{
if(bHasOEMController) {
return getHeaterInfo().getHeaterDemand();
}
else {
return NVstore.getUserSettings().demandDegC;
return demandDegC;
}
}

View file

@ -0,0 +1,39 @@
# Build and deploy doxygen documention to GitHub Pages
sudo: false
dist: trusty
# Blacklist
branches:
only:
- master
# Environment variables
env:
global:
- GH_REPO_REF: github.com/DavidAntliff/esp32-ds18b20.git
# Install dependencies
addons:
apt:
packages:
- doxygen
- doxygen-doc
- doxygen-latex
- doxygen-gui
- graphviz
# Build the docs
script:
- cd doc
- doxygen
# Deploy using Travis-CI/GitHub Pages integration support
deploy:
provider: pages
skip-cleanup: true
local-dir: doc/html
github-token: $GITHUB_TOKEN
on:
branch: master
target-branch: gh-pages

View file

@ -0,0 +1,21 @@
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.

View file

@ -0,0 +1,65 @@
# esp32-ds18b20
## Introduction
This is a ESP32-compatible C component for the Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer device.
It supports multiple devices on the same 1-Wire bus.
It is written and tested for v2.1 and v3.0 of the [ESP-IDF](https://github.com/espressif/esp-idf) environment, using the xtensa-esp32-elf toolchain (gcc version 5.2.0).
## Dependencies
Requires [esp32-owb](https://github.com/DavidAntliff/esp32-owb).
## Example
See [esp32-ds18b20-example](https://github.com/DavidAntliff/esp32-ds18b20-example) for an example that supports single and multiple devices on a single bus.
## Features
In cooperation with the underlying esp32-owb component, this component includes:
* External power supply mode (parasitic mode not yet supported).
* Static (stack-based) or dynamic (malloc-based) memory model.
* No globals - support any number of DS18B20 devices on any number of 1-Wire buses simultaneously.
* 1-Wire device detection and validation, including search for multiple devices on a single bus.
* Addressing optimisation for a single (solo) device on a bus.
* CRC checks on temperature data.
* Programmable temperature measurement resolution (9, 10, 11 or 12-bit resolution).
* Temperature conversion and retrieval.
* Separation of conversion and temperature retrieval to allow for simultaneous conversion across multiple devices.
## Documentation
Automatically generated API documentation (doxygen) is available [here](https://davidantliff.github.io/esp32-ds18b20/index.html).
## Source Code
The source is available from [GitHub](https://www.github.com/DavidAntliff/esp32-ds18b20).
## License
The code in this project is licensed under the MIT license - see LICENSE for details.
## Links
* [DS18B20 Datasheet](http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf)
* [1-Wire Communication Through Software](https://www.maximintegrated.com/en/app-notes/index.mvp/id/126)
* [1-Wire Search Algorithm](https://www.maximintegrated.com/en/app-notes/index.mvp/id/187)
* [Espressif IoT Development Framework for ESP32](https://github.com/espressif/esp-idf)
## Acknowledgements
Parts of this code are based on references provided to the public domain by Maxim Integrated.
"1-Wire" is a registered trademark of Maxim Integrated.
## Roadmap
The following features are anticipated but not yet implemented:
* Concurrency support (multiple tasks accessing devices on the same bus).
* Alarm support.
* EEPROM support.
* Parasitic power support.

View file

@ -0,0 +1 @@
# Use defaults

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,497 @@
/*
* 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 "..\esp32-owb\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
#define DS18B20_FUNCTION_SCRATCHPAD_WRITE 0x4E
#define DS18B20_FUNCTION_SCRATCHPAD_READ 0xBE
#define DS18B20_FUNCTION_SCRATCHPAD_COPY 0x48
#define DS18B20_FUNCTION_EEPROM_RECALL 0xB8
#define DS18B20_FUNCTION_POWER_SUPPLY_READ 0xB4
/// @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;
// ROM code not required
// 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;
}

View file

@ -0,0 +1,196 @@
/*
* 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.h
* @brief Interface definitions for the Maxim Integrated DS18B20 Programmable
* Resolution 1-Wire Digital Thermometer device.
*
* This component provides structures and functions that are useful for communicating
* with DS18B20 devices connected via a Maxim Integrated 1-Wire® bus.
*/
#ifndef DS18B20_H
#define DS18B20_H
#include "../esp32-owb/owb.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Success and error codes.
*/
typedef enum
{
DS18B20_ERROR_UNKNOWN = -1,
DS18B20_OK = 0,
DS18B20_ERROR_DEVICE, ///< A device error occurred
DS18B20_ERROR_CRC, ///< A CRC error occurred
DS18B20_ERROR_OWB, ///< A One Wire Bus error occurred
DS18B20_ERROR_NULL, ///< A parameter or value is NULL
} DS18B20_ERROR;
/**
* @brief Symbols for the supported temperature resolution of the device.
*/
typedef enum
{
DS18B20_RESOLUTION_INVALID = -1, ///< Invalid resolution
DS18B20_RESOLUTION_9_BIT = 9, ///< 9-bit resolution, LSB bits 2,1,0 undefined
DS18B20_RESOLUTION_10_BIT = 10, ///< 10-bit resolution, LSB bits 1,0 undefined
DS18B20_RESOLUTION_11_BIT = 11, ///< 11-bit resolution, LSB bit 0 undefined
DS18B20_RESOLUTION_12_BIT = 12, ///< 12-bit resolution (default)
} DS18B20_RESOLUTION;
/**
* @brief Structure containing information related to a single DS18B20 device connected
* via a 1-Wire bus.
*/
typedef struct
{
bool init; ///< True if struct has been initialised, otherwise false
bool solo; ///< True if device is intended to be the only one connected to the bus, otherwise false
bool use_crc; ///< True if CRC checks are to be used when retrieving information from a device on the bus
const OneWireBus * bus; ///< Pointer to 1-Wire bus information relevant to this device
OneWireBus_ROMCode rom_code; ///< The ROM code used to address this device on the bus
DS18B20_RESOLUTION resolution; ///< Temperature measurement resolution per reading
} DS18B20_Info;
/**
* @brief Construct a new device info instance.
* New instance should be initialised before calling other functions.
* @return Pointer to new device info instance, or NULL if it cannot be created.
*/
DS18B20_Info * ds18b20_malloc(void);
/**
* @brief Delete an existing device info instance.
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in,out] ds18b20_info Pointer to device info instance that will be freed and set to NULL.
*/
void ds18b20_free(DS18B20_Info ** ds18b20_info);
/**
* @brief Initialise a device info instance with the specified GPIO.
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] bus Pointer to initialised 1-Wire bus instance.
* @param[in] rom_code Device-specific ROM code to identify a device on the bus.
*/
void ds18b20_init(DS18B20_Info * ds18b20_info, const OneWireBus * bus, OneWireBus_ROMCode rom_code);
/**
* @brief Initialise a device info instance with the specified GPIO as a solo device on the bus.
*
* This is subject to the requirement that this device is the ONLY device on the bus.
* This allows for faster commands to be used without ROM code addressing.
*
* NOTE: if additional devices are added to the bus, operation will cease to work correctly.
*
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] bus Pointer to initialised 1-Wire bus instance.
* @param[in] rom_code Device-specific ROM code to identify a device on the bus.
*/
void ds18b20_init_solo(DS18B20_Info * ds18b20_info, const OneWireBus * bus);
/**
* @brief Enable or disable use of CRC checks on device communications.
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] use_crc True to enable CRC checks, false to disable.
*/
void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc);
/**
* @brief Set temperature measurement resolution.
*
* This programs the hardware to the specified resolution and sets the cached value to be the same.
* If the program fails, the value currently in hardware is used to refresh the cache.
*
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] resolution Selected resolution.
* @return True if successful, otherwise false.
*/
bool ds18b20_set_resolution(DS18B20_Info * ds18b20_info, DS18B20_RESOLUTION resolution);
/**
* @brief Update and return the current temperature measurement resolution from the device.
* @param[in] ds18b20_info Pointer to device info instance.
* @return The currently configured temperature measurement resolution.
*/
DS18B20_RESOLUTION ds18b20_read_resolution(DS18B20_Info * ds18b20_info);
/**
* @brief Read 64-bit ROM code from device - only works when there is a single device on the bus.
* @param[in] ds18b20_info Pointer to device info instance.
* @return The 64-bit value read from the device's ROM.
*/
OneWireBus_ROMCode ds18b20_read_rom(DS18B20_Info * ds18b20_info);
/**
* @brief Start a temperature measurement conversion on a single device.
* @param[in] ds18b20_info Pointer to device info instance.
*/
bool ds18b20_convert(const DS18B20_Info * ds18b20_info);
/**
* @brief Start temperature conversion on all connected devices.
*
* This should be followed by a sufficient delay to ensure all devices complete
* their conversion before the measurements are read.
* @param[in] bus Pointer to initialised bus instance.
*/
void ds18b20_convert_all(const OneWireBus * bus);
/**
* @brief Wait for the maximum conversion time according to the current resolution of the device.
* @param[in] bus Pointer to initialised bus instance.
* @return An estimate of the time elapsed, in milliseconds. Actual elapsed time may be greater.
*/
float ds18b20_wait_for_conversion(const DS18B20_Info * ds18b20_info);
/**
* @brief Read last temperature measurement from device.
*
* This is typically called after ds18b20_start_mass_conversion(), provided enough time
* has elapsed to ensure that all devices have completed their conversions.
* @param[in] ds18b20_info Pointer to device info instance. Must be initialised first.
* @param[out] value Pointer to the measurement value returned by the device, in degrees Celsius.
* @return DS18B20_OK if read is successful, otherwise error.
*/
DS18B20_ERROR ds18b20_read_temp(const DS18B20_Info * ds18b20_info, float * value);
/**
* @brief Convert, wait and read current temperature from device.
* @param[in] ds18b20_info Pointer to device info instance. Must be initialised first.
* @param[out] value Pointer to the measurement value returned by the device, in degrees Celsius.
* @return DS18B20_OK if read is successful, otherwise error.
*/
DS18B20_ERROR ds18b20_convert_and_read_temp(const DS18B20_Info * ds18b20_info, float * value);
#ifdef __cplusplus
}
#endif
#endif // DS18B20_H

View file

@ -0,0 +1,42 @@
# Build and deploy doxygen documention to GitHub Pages
sudo: false
dist: trusty
# Blacklist
branches:
only:
- master
# Environment variables
env:
global:
- GH_REPO_REF: github.com/DavidAntliff/esp32-owb.git
# Install dependencies
addons:
apt:
packages:
- doxygen
- doxygen-doc
- doxygen-latex
- doxygen-gui
- graphviz
# Build the docs
script:
- cd doc
- doxygen
# Deploy using Travis-CI/GitHub Pages integration support
deploy:
# https://github.com/travis-ci/travis-ci/issues/9312#issuecomment-371590249
edge:
branch: v1.8.47
provider: pages
skip-cleanup: true
local-dir: doc/html
github-token: $GITHUB_TOKEN
on:
branch: master
target-branch: gh-pages

View file

@ -0,0 +1,21 @@
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.

View file

@ -0,0 +1,54 @@
# esp32-owb
This is a ESP32-compatible C component for the Maxim Integrated "1-Wire" protocol.
It is written and tested for version 3.0 of the [ESP-IDF](https://github.com/espressif/esp-idf) environment, using the xtensa-esp32-elf toolchain (gcc version 5.2.0, crosstool-ng-1.22.0-80-g6c4433a).
Support for v2.1 is available on the [ESP-IDF_v2.1](https://github.com/DavidAntliff/esp32-owb/tree/ESP-IDF_v2.1) branch.
## Features
This library includes:
* External power supply mode (parasitic mode not yet supported).
* Static (stack-based) or dynamic (malloc-based) memory model.
* No globals - support any number of 1-Wire buses simultaneously.
* 1-Wire device detection and validation, including search for multiple devices on a single bus.
* Addressing optimisation for a single (solo) device on a bus.
* 1-Wire bus operations including multi-byte read and write operations.
* CRC checks on ROM code.
This component includes two methods of bus access - delay-driven GPIO and RMT-driven slots.
The original implementation used CPU delays to construct the 1-Wire read/write timeslots
however this proved to be too unreliable. A second method, using the ESP32's RMT peripheral,
results in very accurate read/write timeslots and more reliable operation.
Therefore I highly recommend that you use the RMT driver. The GPIO driver should be considered deprecated.
## Documentation
Automatically generated API documentation (doxygen) is available [here](https://davidantliff.github.io/esp32-owb/index.html).
## Source Code
The source is available from [GitHub](https://www.github.com/DavidAntliff/esp32-owb).
## License
The code in this project is licensed under the MIT license - see LICENSE for details.
## Links
* [esp32-ds18b20](https://github.com/DavidAntliff/esp32-ds18b20) - ESP32-compatible DS18B20 Digital Thermometer component for ESP32
* [1-Wire Communication Through Software](https://www.maximintegrated.com/en/app-notes/index.mvp/id/126)
* [1-Wire Search Algorithm](https://www.maximintegrated.com/en/app-notes/index.mvp/id/187)
* [Espressif IoT Development Framework for ESP32](https://github.com/espressif/esp-idf)
## Acknowledgements
Thank you to [Chris Morgan](https://github.com/chmorgan) for his contribution of adding RMT peripheral support for more reliable operation.
Parts of this code are based on references provided to the public domain by Maxim Integrated.
"1-Wire" is a registered trademark of Maxim Integrated.

View file

@ -0,0 +1 @@
# Use defaults.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,570 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
*/
#include <stddef.h>
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "owb.h"
#include "owb_gpio.h"
static const char * TAG = "owb";
static bool _is_init(const OneWireBus * bus)
{
bool ok = false;
if (bus != NULL)
{
if (bus->driver)
{
// OK
ok = true;
}
else
{
ESP_LOGE(TAG, "bus is not initialised");
}
}
else
{
ESP_LOGE(TAG, "bus is NULL");
}
return ok;
}
/**
* @brief 1-Wire 8-bit CRC lookup.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Byte to feed into CRC.
* @return Resultant CRC value.
*/
static uint8_t _calc_crc(uint8_t crc, uint8_t data)
{
// https://www.maximintegrated.com/en/app-notes/index.mvp/id/27
static const uint8_t table[256] = {
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};
return table[crc ^ data];
}
static uint8_t _calc_crc_block(uint8_t crc, const uint8_t * buffer, size_t len)
{
do
{
crc = _calc_crc(crc, *buffer++);
ESP_LOGD(TAG, "crc 0x%02x, len %d", (int)crc, (int)len);
}
while (--len > 0);
return crc;
}
/**
* @param[out] is_found true if a device was found, false if not
* @return status
*/
static owb_status _search(const OneWireBus * bus, OneWireBus_SearchState * state, bool *is_found)
{
// Based on https://www.maximintegrated.com/en/app-notes/index.mvp/id/187
// initialize for search
int id_bit_number = 1;
int last_zero = 0;
int rom_byte_number = 0;
uint8_t id_bit = 0;
uint8_t cmp_id_bit = 0;
uint8_t rom_byte_mask = 1;
uint8_t search_direction = 0;
bool search_result = false;
uint8_t crc8 = 0;
owb_status status;
// if the last call was not the last one
if (!state->last_device_flag)
{
// 1-Wire reset
bool is_present;
bus->driver->reset(bus, &is_present);
if (!is_present)
{
// reset the search
state->last_discrepancy = 0;
state->last_device_flag = false;
state->last_family_discrepancy = 0;
*is_found = false;
return OWB_STATUS_OK;
}
// issue the search command
bus->driver->write_bits(bus, OWB_ROM_SEARCH, 8);
// loop to do the search
do
{
id_bit = cmp_id_bit = 0;
// read a bit and its complement
bus->driver->read_bits(bus, &id_bit, 1);
bus->driver->read_bits(bus, &cmp_id_bit, 1);
// check for no devices on 1-wire (signal level is high in both bit reads)
if (id_bit && cmp_id_bit)
{
break;
} else
{
// all devices coupled have 0 or 1
if (id_bit != cmp_id_bit)
{
search_direction = (id_bit) ? 1 : 0; // 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 < state->last_discrepancy)
search_direction = ((state->rom_code.bytes[rom_byte_number] & rom_byte_mask) > 0);
else
// if equal to last pick 1, if not then pick 0
search_direction = (id_bit_number == state->last_discrepancy);
// 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)
state->last_family_discrepancy = last_zero;
}
}
// set or clear the bit in the ROM byte rom_byte_number
// with mask rom_byte_mask
if (search_direction == 1)
state->rom_code.bytes[rom_byte_number] |= rom_byte_mask;
else
state->rom_code.bytes[rom_byte_number] &= ~rom_byte_mask;
// serial number search direction write bit
bus->driver->write_bits(bus, search_direction, 1);
// 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)
{
crc8 = owb_crc8_byte(crc8, state->rom_code.bytes[rom_byte_number]); // accumulate the CRC
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) || (crc8 != 0)))
{
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
state->last_discrepancy = last_zero;
// check for last device
if (state->last_discrepancy == 0)
state->last_device_flag = true;
search_result = true;
}
}
// if no device found then reset counters so next 'search' will be like a first
if (!search_result || !state->rom_code.bytes[0])
{
state->last_discrepancy = 0;
state->last_device_flag = false;
state->last_family_discrepancy = 0;
search_result = false;
}
status = OWB_STATUS_OK;
*is_found = search_result;
return status;
}
// Public API
owb_status owb_uninitialize(OneWireBus * bus)
{
owb_status status;
if(!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
bus->driver->uninitialize(bus);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_use_crc(OneWireBus * bus, bool use_crc)
{
owb_status status;
if(!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
bus->use_crc = use_crc;
ESP_LOGD(TAG, "use_crc %d", bus->use_crc);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_rom(const OneWireBus * bus, OneWireBus_ROMCode *rom_code)
{
owb_status status;
memset(rom_code, 0, sizeof(OneWireBus_ROMCode));
if(!bus || !rom_code)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
bool is_present;
bus->driver->reset(bus, &is_present);
if (is_present)
{
uint8_t value = OWB_ROM_READ;
bus->driver->write_bits(bus, value, 8);
owb_read_bytes(bus, rom_code->bytes, sizeof(OneWireBus_ROMCode));
if (bus->use_crc)
{
if (owb_crc8_bytes(0, rom_code->bytes, sizeof(OneWireBus_ROMCode)) != 0)
{
ESP_LOGE(TAG, "CRC failed");
memset(rom_code->bytes, 0, sizeof(OneWireBus_ROMCode));
status = OWB_STATUS_CRC_FAILED;
} else
{
status = OWB_STATUS_OK;
}
} else
{
status = OWB_STATUS_OK;
}
char rom_code_s[OWB_ROM_CODE_STRING_LENGTH];
owb_string_from_rom_code(*rom_code, rom_code_s, sizeof(rom_code_s));
ESP_LOGD(TAG, "rom_code %s", rom_code_s);
}
else
{
status = OWB_STATUS_DEVICE_NOT_RESPONDING;
ESP_LOGE(TAG, "ds18b20 device not responding");
}
}
return status;
}
owb_status owb_verify_rom(const OneWireBus * bus, OneWireBus_ROMCode rom_code, bool* is_present)
{
owb_status status;
bool result = false;
if (!bus || !is_present)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
OneWireBus_SearchState state = {
.last_discrepancy = 64,
.last_device_flag = false,
};
bool is_found = false;
while (!state.last_device_flag && !is_found)
{
_search(bus, &state, &is_found);
if (is_found)
{
result = true;
for (int i = 0; i < sizeof(state.rom_code.bytes) && result; ++i)
{
result = rom_code.bytes[i] == state.rom_code.bytes[i];
ESP_LOGD(TAG, "%02x %02x", rom_code.bytes[i], state.rom_code.bytes[i]);
}
is_found = result;
}
}
ESP_LOGD(TAG, "rom code %sfound", result ? "" : "not ");
*is_present = result;
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_reset(const OneWireBus * bus, bool* a_device_present)
{
owb_status status;
if(!bus || !a_device_present)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if(!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
bus->driver->reset(bus, a_device_present);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_byte(const OneWireBus * bus, uint8_t data)
{
owb_status status;
if(!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
bus->driver->write_bits(bus, data, 8);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_byte(const OneWireBus * bus, uint8_t *out)
{
owb_status status;
if(!bus || !out)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
bus->driver->read_bits(bus, out, 8);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, unsigned int len)
{
owb_status status;
if(!bus || !buffer)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
for (int i = 0; i < len; ++i)
{
uint8_t out;
bus->driver->read_bits(bus, &out, 8);
buffer[i] = out;
}
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_bytes(const OneWireBus * bus, const uint8_t * buffer, unsigned int len)
{
owb_status status;
if(!bus || !buffer)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
for (int i = 0; i < len; i++)
{
bus->driver->write_bits(bus, buffer[i], 8);
}
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_rom_code(const OneWireBus * bus, OneWireBus_ROMCode rom_code)
{
owb_status status;
if(!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
owb_write_bytes(bus, (uint8_t*)&rom_code, sizeof(rom_code));
status = OWB_STATUS_OK;
}
return status;
}
uint8_t owb_crc8_byte(uint8_t crc, uint8_t data)
{
return _calc_crc(crc, data);
}
uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len)
{
return _calc_crc_block(crc, data, len);
}
owb_status owb_search_first(const OneWireBus * bus, OneWireBus_SearchState * state, bool* found_device)
{
bool result;
owb_status status;
if(!bus || !state || !found_device)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
memset(&state->rom_code, 0, sizeof(state->rom_code));
state->last_discrepancy = 0;
state->last_family_discrepancy = 0;
state->last_device_flag = false;
_search(bus, state, &result);
status = OWB_STATUS_OK;
*found_device = result;
}
return status;
}
owb_status owb_search_next(const OneWireBus * bus, OneWireBus_SearchState * state, bool* found_device)
{
owb_status status;
bool result = false;
if(!bus || !state || !found_device)
{
status = OWB_STATUS_PARAMETER_NULL;
} else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
} else
{
_search(bus, state, &result);
status = OWB_STATUS_OK;
*found_device = result;
}
return status;
}
char * owb_string_from_rom_code(OneWireBus_ROMCode rom_code, char * buffer, size_t len)
{
for (int i = sizeof(rom_code.bytes) - 1; i >= 0; i--)
{
sprintf(buffer, "%02x", rom_code.bytes[i]);
buffer += 2;
}
return buffer;
}

View file

@ -0,0 +1,272 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
* @brief Interface definitions for the 1-Wire bus component.
*
* This component provides structures and functions that are useful for communicating
* with devices connected to a Maxim Integrated 1-Wire® bus via a single GPIO.
*
* Currently only externally powered devices are supported. Parasitic power is not supported.
*/
#ifndef ONE_WIRE_BUS_H
#define ONE_WIRE_BUS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// ROM commands
#define OWB_ROM_SEARCH 0xF0
#define OWB_ROM_READ 0x33
#define OWB_ROM_MATCH 0x55
#define OWB_ROM_SKIP 0xCC
#define OWB_ROM_SEARCH_ALARM 0xEC
#define OWB_ROM_CODE_STRING_LENGTH (17) ///< Typical length of OneWire bus ROM ID as ASCII hex string, including null terminator
struct owb_driver;
/**
* @brief Structure containing 1-Wire bus information relevant to a single instance.
*/
typedef struct
{
const struct _OneWireBus_Timing * timing; ///< Pointer to timing information
bool use_crc; ///< True if CRC checks are to be used when retrieving information from a device on the bus
const struct owb_driver *driver;
} OneWireBus;
/**
* @brief Represents a 1-Wire ROM Code. This is a sequence of eight bytes, where
* the first byte is the family number, then the following 6 bytes form the
* serial number. The final byte is the CRC8 check byte.
*/
typedef union
{
/// Provides access via field names
struct fields
{
uint8_t family[1]; ///< family identifier (1 byte, LSB - read/write first)
uint8_t serial_number[6]; ///< serial number (6 bytes)
uint8_t crc[1]; ///< CRC check byte (1 byte, MSB - read/write last)
} fields; ///< Provides access via field names
uint8_t bytes[8]; ///< Provides raw byte access
} OneWireBus_ROMCode;
/**
* @brief Represents the state of a device search on the 1-Wire bus.
*
* Pass a pointer to this structure to owb_search_first() and
* owb_search_next() to iterate through detected devices on the bus.
*/
typedef struct
{
OneWireBus_ROMCode rom_code;
int last_discrepancy;
int last_family_discrepancy;
int last_device_flag;
} OneWireBus_SearchState;
typedef enum
{
OWB_STATUS_OK,
OWB_STATUS_NOT_INITIALIZED,
OWB_STATUS_PARAMETER_NULL,
OWB_STATUS_DEVICE_NOT_RESPONDING,
OWB_STATUS_CRC_FAILED,
OWB_STATUS_TOO_MANY_BITS,
OWB_STATUS_HW_ERROR
} owb_status;
/** NOTE: Driver assumes that (*init) was called prior to any other methods */
struct owb_driver
{
const char* name;
owb_status (*uninitialize)(const OneWireBus * bus);
owb_status (*reset)(const OneWireBus * bus, bool *is_present);
/** NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb */
owb_status (*write_bits)(const OneWireBus *bus, uint8_t out, int number_of_bits_to_write);
/** NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read */
owb_status (*read_bits)(const OneWireBus *bus, uint8_t *in, int number_of_bits_to_read);
};
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/**
* @brief call to release resources after completing use of the OneWireBus
*/
owb_status owb_uninitialize(OneWireBus * bus);
/**
* @brief Enable or disable use of CRC checks on device communications.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] use_crc True to enable CRC checks, false to disable.
* @return status
*/
owb_status owb_use_crc(OneWireBus * bus, bool use_crc);
/**
* @brief Read ROM code from device - only works when there is a single device on the bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] rom_code the value read from the device's rom
* @return status
*/
owb_status owb_read_rom(const OneWireBus * bus, OneWireBus_ROMCode *rom_code);
/**
* @brief Verify the device specified by ROM code is present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] rom_code ROM code to verify.
* @param[out] is_present set to true if a device is present, false if not
* @return status
*/
owb_status owb_verify_rom(const OneWireBus * bus, OneWireBus_ROMCode rom_code, bool* is_present);
/**
* @brief Reset the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] is_present set to true if at least one device is present on the bus
* @return status
*/
owb_status owb_reset(const OneWireBus * bus, bool* a_device_present);
/**
* @brief Write a single byte to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] data Byte value to write to bus.
* @return status
*/
owb_status owb_write_byte(const OneWireBus * bus, uint8_t data);
/**
* @brief Read a single byte from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] out The byte value read from the bus.
* @return status
*/
owb_status owb_read_byte(const OneWireBus * bus, uint8_t *out);
/**
* @brief Read a number of bytes from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in, out] buffer Pointer to buffer to receive read data.
* @param[in] len Number of bytes to read, must not exceed length of receive buffer.
* @return status.
*/
owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, size_t len);
/**
* @brief Write a number of bytes to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] buffer Pointer to buffer to write data from.
* @param[in] len Number of bytes to write.
* @return status
*/
owb_status owb_write_bytes(const OneWireBus * bus, const uint8_t * buffer, size_t len);
/**
* @brief Write a ROM code to the 1-Wire bus ensuring LSB is sent first.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] rom_code ROM code to write to bus.
* @return status
*/
owb_status owb_write_rom_code(const OneWireBus * bus, OneWireBus_ROMCode rom_code);
/**
* @brief 1-Wire 8-bit CRC lookup.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Byte to feed into CRC.
* @return Resultant CRC value.
* Should be zero if last byte was the CRC byte and the CRC matches.
*/
uint8_t owb_crc8_byte(uint8_t crc, uint8_t data);
/**
* @brief 1-Wire 8-bit CRC lookup with accumulation over a block of bytes.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Array of bytes to feed into CRC.
* @param[in] len Length of data array in bytes.
* @return Resultant CRC value.
* Should be zero if last byte was the CRC byte and the CRC matches.
*/
uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len);
/**
* @brief Locates the first device on the 1-Wire bus, if present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in,out] state Pointer to an existing search state structure.
* @param[out] found_device True if a device is found, false if no devices are found.
* If a device is found, the ROM Code can be obtained from the state.
* @return status
*/
owb_status owb_search_first(const OneWireBus * bus, OneWireBus_SearchState * state, bool *found_device);
/**
* @brief Locates the next device on the 1-Wire bus, if present, starting from
* the provided state. Further calls will yield additional devices, if present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in,out] state Pointer to an existing search state structure.
* @param[out] found_device True if a device is found, false if no devices are found.
* If a device is found, the ROM Code can be obtained from the state.
* @return status
*/
owb_status owb_search_next(const OneWireBus * bus, OneWireBus_SearchState * state, bool *found_device);
/**
* @brief Create a string representation of a ROM code, most significant byte (CRC8) first.
* @param[in] rom_code The ROM code to convert to string representation.
* @param[out] buffer The destination for the string representation. It will be null terminated.
* @param[in] len The length of the buffer in bytes. 64-bit ROM codes require 16 characters
* to represent as a string, plus a null terminator, for 17 bytes.
* See OWB_ROM_CODE_STRING_LENGTH.
* @return pointer to the byte beyond the last byte written
*/
char * owb_string_from_rom_code(OneWireBus_ROMCode rom_code, char * buffer, size_t len);
#include "owb_gpio.h"
#include "owb_rmt.h"
#ifdef __cplusplus
}
#endif
#endif // ONE_WIRE_BUS_H

View file

@ -0,0 +1,57 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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.
*/
#pragma once
#ifndef OWB_GPIO_H
#define OWB_GPIO_H
#include "owb.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int gpio; ///< Value of the GPIO connected to the 1-Wire bus
OneWireBus bus; ///< OneWireBus instance
} owb_gpio_driver_info;
/**
* @brief Initialise the GPIO driver.
* @return OneWireBus*, pass this into the other OneWireBus public API functions
*/
OneWireBus * owb_gpio_initialize(owb_gpio_driver_info *driver_info, int gpio);
/**
* @brief Clean up after a call to owb_gpio_initialize()
*/
void owb_gpio_uninitialize(owb_gpio_driver_info *driver_info);
#ifdef __cplusplus
}
#endif
#endif // OWB_GPIO_H

View file

@ -0,0 +1,463 @@
/*
Created by Chris Morgan based on the nodemcu project driver.
Copyright 2017 Chris Morgan <chmorgan@gmail.com>
Ported to ESP32 RMT peripheral for low-level signal generation by Arnim Laeuger.
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.
Much of the code was inspired by Derek Yerger's code, though I don't
think much of that remains. In any event that was..
(copyleft) 2006 by Derek Yerger - Free to distribute freely.
The CRC code was excerpted and inspired by the Dallas Semiconductor
sample code bearing this copyright.
//---------------------------------------------------------------------------
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
//
// 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 DALLAS SEMICONDUCTOR 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.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//--------------------------------------------------------------------------
*/
#include "owb.h"
#include "driver/rmt.h"
#include "driver/gpio.h"
#include "esp_log.h"
#undef OW_DEBUG
// bus reset: duration of low phase [us]
#define OW_DURATION_RESET 480
// overall slot duration
#define OW_DURATION_SLOT 75
// write 1 slot and read slot durations [us]
#define OW_DURATION_1_LOW 2
#define OW_DURATION_1_HIGH (OW_DURATION_SLOT - OW_DURATION_1_LOW)
// write 0 slot durations [us]
#define OW_DURATION_0_LOW 65
#define OW_DURATION_0_HIGH (OW_DURATION_SLOT - OW_DURATION_0_LOW)
// sample time for read slot
#define OW_DURATION_SAMPLE (15-2)
// RX idle threshold
// needs to be larger than any duration occurring during write slots
#define OW_DURATION_RX_IDLE (OW_DURATION_SLOT + 2)
static const char * TAG = "owb_rmt";
#define info_of_driver(owb) container_of(owb, owb_rmt_driver_info, bus)
// flush any pending/spurious traces from the RX channel
static void onewire_flush_rmt_rx_buf(const OneWireBus * bus)
{
void *p;
size_t s;
owb_rmt_driver_info *i = info_of_driver(bus);
while ((p = xRingbufferReceive(i->rb, &s, 0)))
{
ESP_LOGD(TAG, "flushing entry");
vRingbufferReturnItem(i->rb, p);
}
}
static owb_status _reset(const OneWireBus *bus, bool *is_present)
{
rmt_item32_t tx_items[1];
bool _is_present = false;
int res = OWB_STATUS_OK;
owb_rmt_driver_info *i = info_of_driver(bus);
tx_items[0].duration0 = OW_DURATION_RESET;
tx_items[0].level0 = 0;
tx_items[0].duration1 = 0;
tx_items[0].level1 = 1;
uint16_t old_rx_thresh;
rmt_get_rx_idle_thresh(i->rx_channel, &old_rx_thresh);
rmt_set_rx_idle_thresh(i->rx_channel, OW_DURATION_RESET+60);
onewire_flush_rmt_rx_buf(bus);
rmt_rx_start(i->rx_channel, true);
if (rmt_write_items(i->tx_channel, tx_items, 1, true) == ESP_OK)
{
size_t rx_size;
rmt_item32_t* rx_items = (rmt_item32_t *)xRingbufferReceive(i->rb, &rx_size, 100 / portTICK_PERIOD_MS);
if (rx_items)
{
if (rx_size >= (1 * sizeof(rmt_item32_t)))
{
#ifdef OW_DEBUG
ESP_LOGI(TAG, "rx_size: %d", rx_size);
for (int i = 0; i < (rx_size / sizeof(rmt_item32_t)); i++)
{
ESP_LOGI(TAG, "i: %d, level0: %d, duration %d", i, rx_items[i].level0, rx_items[i].duration0);
ESP_LOGI(TAG, "i: %d, level1: %d, duration %d", i, rx_items[i].level1, rx_items[i].duration1);
}
#endif
// parse signal and search for presence pulse
if ((rx_items[0].level0 == 0) && (rx_items[0].duration0 >= OW_DURATION_RESET - 2))
{
if ((rx_items[0].level1 == 1) && (rx_items[0].duration1 > 0))
{
if (rx_items[1].level0 == 0)
{
_is_present = true;
}
}
}
}
vRingbufferReturnItem(i->rb, (void *)rx_items);
}
else
{
// time out occurred, this indicates an unconnected / misconfigured bus
ESP_LOGE(TAG, "rx_items == 0");
res = OWB_STATUS_HW_ERROR;
}
}
else
{
// error in tx channel
ESP_LOGE(TAG, "Error tx");
res = OWB_STATUS_HW_ERROR;
}
rmt_rx_stop(i->rx_channel);
rmt_set_rx_idle_thresh(i->rx_channel, old_rx_thresh);
*is_present = _is_present;
ESP_LOGD(TAG, "_is_present %d", _is_present);
return res;
}
static rmt_item32_t _encode_write_slot(uint8_t val)
{
rmt_item32_t item;
item.level0 = 0;
item.level1 = 1;
if (val)
{
// write "1" slot
item.duration0 = OW_DURATION_1_LOW;
item.duration1 = OW_DURATION_1_HIGH;
}
else
{
// write "0" slot
item.duration0 = OW_DURATION_0_LOW;
item.duration1 = OW_DURATION_0_HIGH;
}
return item;
}
/** NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb */
static owb_status _write_bits(const OneWireBus * bus, uint8_t out, int number_of_bits_to_write)
{
rmt_item32_t tx_items[number_of_bits_to_write + 1];
owb_rmt_driver_info *info = info_of_driver(bus);
if (number_of_bits_to_write > 8)
{
return OWB_STATUS_TOO_MANY_BITS;
}
// write requested bits as pattern to TX buffer
for (int i = 0; i < number_of_bits_to_write; i++)
{
tx_items[i] = _encode_write_slot(out & 0x01);
out >>= 1;
}
// end marker
tx_items[number_of_bits_to_write].level0 = 1;
tx_items[number_of_bits_to_write].duration0 = 0;
owb_status status;
if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_write+1, true) == ESP_OK)
{
status = OWB_STATUS_OK;
}
else
{
status = OWB_STATUS_HW_ERROR;
ESP_LOGE(TAG, "rmt_write_items() failed");
}
return status;
}
static rmt_item32_t _encode_read_slot(void)
{
rmt_item32_t item;
// construct pattern for a single read time slot
item.level0 = 0;
item.duration0 = OW_DURATION_1_LOW; // shortly force 0
item.level1 = 1;
item.duration1 = OW_DURATION_1_HIGH; // release high and finish slot
return item;
}
/** NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read */
static owb_status _read_bits(const OneWireBus * bus, uint8_t *in, int number_of_bits_to_read)
{
rmt_item32_t tx_items[number_of_bits_to_read + 1];
uint8_t read_data = 0;
int res = OWB_STATUS_OK;
owb_rmt_driver_info *info = info_of_driver(bus);
if (number_of_bits_to_read > 8)
{
ESP_LOGE(TAG, "_read_bits() OWB_STATUS_TOO_MANY_BITS");
return OWB_STATUS_TOO_MANY_BITS;
}
// generate requested read slots
for (int i = 0; i < number_of_bits_to_read; i++)
{
tx_items[i] = _encode_read_slot();
}
// end marker
tx_items[number_of_bits_to_read].level0 = 1;
tx_items[number_of_bits_to_read].duration0 = 0;
onewire_flush_rmt_rx_buf(bus);
rmt_rx_start(info->rx_channel, true);
if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_read+1, true) == ESP_OK)
{
size_t rx_size;
rmt_item32_t* rx_items = (rmt_item32_t *)xRingbufferReceive(info->rb, &rx_size, portMAX_DELAY);
if (rx_items)
{
#ifdef OW_DEBUG
for (int i = 0; i < rx_size / 4; i++)
{
ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level1, rx_items[i].duration1);
}
#endif
if (rx_size >= number_of_bits_to_read * sizeof(rmt_item32_t))
{
for (int i = 0; i < number_of_bits_to_read; i++)
{
read_data >>= 1;
// parse signal and identify logical bit
if (rx_items[i].level1 == 1)
{
if ((rx_items[i].level0 == 0) && (rx_items[i].duration0 < OW_DURATION_SAMPLE))
{
// rising edge occured before 15us -> bit 1
read_data |= 0x80;
}
}
}
read_data >>= 8 - number_of_bits_to_read;
}
vRingbufferReturnItem(info->rb, (void *)rx_items);
}
else
{
// time out occurred, this indicates an unconnected / misconfigured bus
ESP_LOGE(TAG, "rx_items == 0");
res = OWB_STATUS_HW_ERROR;
}
}
else
{
// error in tx channel
ESP_LOGE(TAG, "Error tx");
res = OWB_STATUS_HW_ERROR;
}
rmt_rx_stop(info->rx_channel);
*in = read_data;
return res;
}
static owb_status _uninitialize(const OneWireBus *bus)
{
owb_rmt_driver_info * info = info_of_driver(bus);
rmt_driver_uninstall(info->tx_channel);
rmt_driver_uninstall(info->rx_channel);
return OWB_STATUS_OK;
}
static struct owb_driver rmt_function_table =
{
.name = "owb_rmt",
.uninitialize = _uninitialize,
.reset = _reset,
.write_bits = _write_bits,
.read_bits = _read_bits
};
static owb_status _init(owb_rmt_driver_info *info, uint8_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel)
{
owb_status status = OWB_STATUS_HW_ERROR;
// Ensure the RMT peripheral is not already running
// Note: if using RMT elsewhere, don't call this here, call it at the start of your prgoram instead.
//periph_module_disable(PERIPH_RMT_MODULE);
//periph_module_enable(PERIPH_RMT_MODULE);
info->bus.driver = &rmt_function_table;
info->tx_channel = tx_channel;
info->rx_channel = rx_channel;
info->gpio = gpio_num;
#ifdef OW_DEBUG
ESP_LOGI(TAG, "RMT TX channel: %d", info->tx_channel);
ESP_LOGI(TAG, "RMT RX channel: %d", info->rx_channel);
#endif
rmt_config_t rmt_tx;
rmt_tx.channel = info->tx_channel;
rmt_tx.gpio_num = gpio_num;
rmt_tx.mem_block_num = 1;
rmt_tx.clk_div = 80;
rmt_tx.tx_config.loop_en = false;
rmt_tx.tx_config.carrier_en = false;
rmt_tx.tx_config.idle_level = 1;
rmt_tx.tx_config.idle_output_en = true;
rmt_tx.rmt_mode = RMT_MODE_TX;
if (rmt_config(&rmt_tx) == ESP_OK)
{
if (rmt_driver_install(rmt_tx.channel, 0, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
{
rmt_config_t rmt_rx;
rmt_rx.channel = info->rx_channel;
rmt_rx.gpio_num = gpio_num;
rmt_rx.clk_div = 80;
rmt_rx.mem_block_num = 1;
rmt_rx.rmt_mode = RMT_MODE_RX;
rmt_rx.rx_config.filter_en = true;
rmt_rx.rx_config.filter_ticks_thresh = 30;
rmt_rx.rx_config.idle_threshold = OW_DURATION_RX_IDLE;
if (rmt_config(&rmt_rx) == ESP_OK)
{
if (rmt_driver_install(rmt_rx.channel, 512, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
{
rmt_get_ringbuf_handle(info->rx_channel, &info->rb);
status = OWB_STATUS_OK;
}
else
{
ESP_LOGE(TAG, "failed to install rx driver");
}
}
else
{
status = OWB_STATUS_HW_ERROR;
ESP_LOGE(TAG, "failed to configure rx, uninstalling rmt driver on tx channel");
rmt_driver_uninstall(rmt_tx.channel);
}
}
else
{
ESP_LOGE(TAG, "failed to install tx driver");
}
}
else
{
ESP_LOGE(TAG, "failed to configure tx");
}
// attach GPIO to previous pin
if (gpio_num < 32)
{
GPIO.enable_w1ts = (0x1 << gpio_num);
}
else
{
GPIO.enable1_w1ts.data = (0x1 << (gpio_num - 32));
}
// attach RMT channels to new gpio pin
// ATTENTION: set pin for rx first since gpio_output_disable() will
// remove rmt output signal in matrix!
rmt_set_pin(info->rx_channel, RMT_MODE_RX, gpio_num);
rmt_set_pin(info->tx_channel, RMT_MODE_TX, gpio_num);
// force pin direction to input to enable path to RX channel
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[gpio_num]);
// enable open drain
GPIO.pin[gpio_num].pad_driver = 1;
return status;
}
OneWireBus * owb_rmt_initialize(owb_rmt_driver_info *info, uint8_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel)
{
ESP_LOGD(TAG, "%s: gpio_num: %d, tx_channel: %d, rx_channel: %d",
__func__, gpio_num, tx_channel, rx_channel);
owb_status status = _init(info, gpio_num, tx_channel, rx_channel);
if(status != OWB_STATUS_OK)
{
ESP_LOGE(TAG, "_init() failed with status %d", status);
}
return &(info->bus);
}

View file

@ -0,0 +1,59 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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.
*/
#pragma once
#ifndef OWB_RMT_H
#define OWB_RMT_H
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include "driver/rmt.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int tx_channel; ///< RMT channel to use for TX
int rx_channel; ///< RMT channel to use for RX
RingbufHandle_t rb; ///< Ring buffer handle
int gpio; ///< OneWireBus GPIO
OneWireBus bus; ///< OneWireBus instance
} owb_rmt_driver_info;
/**
* @brief Initialise the RMT driver.
* @return OneWireBus*, pass this into the other OneWireBus public API functions
*/
OneWireBus* owb_rmt_initialize(owb_rmt_driver_info *info, uint8_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel);
#ifdef __cplusplus
}
#endif
#endif // OWB_RMT_H

View file

@ -12,6 +12,8 @@
#include <Update.h>
#include "ArduinoJson.h"
extern void forceBootInit();
esp32FOTA::esp32FOTA(String firwmareType, int firwmareVersion)
{
_firwmareType = firwmareType;
@ -146,6 +148,7 @@ void esp32FOTA::execOTA()
if (Update.isFinished())
{
Serial.println("Update successfully completed. Rebooting.");
forceBootInit();
ESP.restart();
}
else

View file

@ -172,6 +172,10 @@ CSettingsScreen::keyHandler(uint8_t event)
// _ScreenManager.selectMenu(CScreenManager::BranchMenu, CScreenManager::ExperimentalUI);
_ScreenManager.selectMenu(CScreenManager::UserSettingsLoop, CScreenManager::ExThermostatUI);
}
// THREE FINGER SALUTE!
if((event & (key_Left|key_Right|key_Centre)) == (key_Left|key_Right|key_Centre)) {
for(;;); // force watchdog reboot!
}
}
}
_ScreenManager.reqUpdate();

View file

@ -127,19 +127,19 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
float tActual = getTemperatureSensor();
uint8_t u8Temp = (uint8_t)(tActual + 0.5);
m_TxFrame.setTemperature_Actual(u8Temp); // use current temp, for now
m_TxFrame.setHeaterDemand(NVstore.getUserSettings().demandDegC);
m_TxFrame.setHeaterDemand(getDemandDegC());
m_TxFrame.setThermostatModeProtocol(1); // assume using thermostat control for now
if(!getThermostatModeActive()) {
m_TxFrame.setThermostatModeProtocol(0); // not using any form of thermostat control
m_TxFrame.setHeaterDemand(NVstore.getUserSettings().demandPump); // set fixed Hz demand instead
m_TxFrame.setHeaterDemand(getDemandPump()); // set fixed Hz demand instead
m_TxFrame.setTemperature_Actual(0); // must force actual to 0 for Hz mode
}
else if(NVstore.getUserSettings().ThermostatMethod) {
uint8_t ThermoMode = NVstore.getUserSettings().ThermostatMethod; // get the METHOD of thermostat control
float Window = NVstore.getUserSettings().ThermostatWindow;
float tCurrent = getTemperatureSensor();
float tDesired = float(NVstore.getUserSettings().demandDegC);
float tDesired = float(getDemandDegC());
float tDelta = tCurrent - tDesired;
float fTemp;
#ifdef DEBUG_THERMOSTAT
@ -163,11 +163,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
u8Temp = (uint8_t)(tActual + 0.5); // use rounded actual unless within window
if(fabs(tDelta) < Window) {
// hold at desired if inside window
u8Temp = NVstore.getUserSettings().demandDegC;
u8Temp = getDemandDegC();
}
else if(fabs(tDelta) <= 1.0) {
// force outside if delta is <= 1 but greater than window
u8Temp = NVstore.getUserSettings().demandDegC + ((tDelta > 0) ? 1 : -1);
u8Temp = getDemandDegC() + ((tDelta > 0) ? 1 : -1);
}
m_TxFrame.setTemperature_Actual(u8Temp);
#ifdef DEBUG_THERMOSTAT

View file

@ -43,6 +43,7 @@ BTCDateTime::operator=(const DateTime& rhs)
hh = rhs.hour();
mm = rhs.minute();
ss = rhs.second();
return *this;
}
BTCDateTime&
@ -54,6 +55,7 @@ BTCDateTime::operator=(const BTCDateTime& rhs)
hh = rhs.hh;
mm = rhs.mm;
ss = rhs.ss;
return *this;
}
void

View file

@ -216,12 +216,12 @@ void interpretJsonCommand(char* pLine)
else if(strcmp("GPin2", it->key) == 0) {
simulateGPIOin(it->value.as<unsigned char>() ? 0x02 : 0x00); // simulate key 2 press
}
else if(strcmp("JSONloose", it->key) == 0) {
else if(strcmp("JSONpack", it->key) == 0) {
sUserSettings us = NVstore.getUserSettings();
uint8_t loose = it->value.as<unsigned char>() ? 0x01 : 0x00;
us.JSON.LF = loose;
us.JSON.padding = loose;
us.JSON.singleElement = loose;
uint8_t packed = it->value.as<unsigned char>() ? 0x00 : 0x01;
us.JSON.LF = packed;
us.JSON.padding = packed;
us.JSON.singleElement = packed;
NVstore.setUserSettings(us);
NVstore.save();
resetJSONmoderator();

View file

@ -218,6 +218,7 @@ CESP32HeaterStorage::~CESP32HeaterStorage()
void
CESP32HeaterStorage::init()
{
_bShouldSave = false;
_calValues.init();
}
@ -235,8 +236,10 @@ CESP32HeaterStorage::load()
}
void
CESP32HeaterStorage::save()
CESP32HeaterStorage::doSave()
{
if(_bShouldSave) {
_bShouldSave = false;
DebugPort.println("Saving to NV storage");
_calValues.heaterTuning.save();
for(int i=0; i<14; i++) {
@ -246,6 +249,13 @@ CESP32HeaterStorage::save()
_calValues.MQTT.save();
_calValues.Credentials.save();
}
}
void
CESP32HeaterStorage::save()
{
_bShouldSave = true; // queue request to save to NV
}
void
sHeaterTuning::load()
@ -322,8 +332,6 @@ sUserSettings::load()
validatedLoad("menuTimeout", menuTimeout, 60000, 0, 300000);
validatedLoad("degF", degF, 0, u8inBounds, 0, 1);
validatedLoad("thermostat", useThermostat, 1, u8inBounds, 0, 1);
validatedLoad("demandDegC", demandDegC, 22, u8inBounds, 8, 35);
validatedLoad("demandPump", demandPump, 22, u8inBounds, 8, 35);
validatedLoad("thermoMethod", ThermostatMethod, 0, u8inBounds, 0, 255);
// catch and migrate old combined method & window
if(ThermostatMethod & 0xFC) {
@ -359,8 +367,6 @@ sUserSettings::save()
preferences.putLong("dimTime", dimTime);
preferences.putLong("menuTimeout", menuTimeout);
preferences.putUChar("thermostat", useThermostat);
preferences.putUChar("demandDegC", demandDegC);
preferences.putUChar("demandPump", demandPump);
preferences.putUChar("degF", degF);
preferences.putUChar("thermoMethod", ThermostatMethod);
preferences.putFloat("thermoWindow", ThermostatWindow);

View file

@ -129,9 +129,9 @@ struct sCyclicThermostat {
struct sJSONoptions {
int8_t singleElement;
int8_t LF;
int8_t padding;
uint8_t singleElement;
uint8_t LF;
uint8_t padding;
bool valid() {
bool retval = true;
retval &= singleElement <= 1;
@ -208,8 +208,6 @@ struct sMQTTparams : public CESP32_NVStorage {
struct sUserSettings : public CESP32_NVStorage {
long dimTime;
long menuTimeout;
uint8_t demandDegC;
uint8_t demandPump;
uint8_t degF;
uint8_t ThermostatMethod; // 0: standard heater, 1: Narrow Hysterisis, 2:Managed Hz mode
float ThermostatWindow;
@ -226,8 +224,6 @@ struct sUserSettings : public CESP32_NVStorage {
bool retval = true;
retval &= INBOUNDS(dimTime, -600000, 600000); // +/- 10 mins
retval &= INBOUNDS(menuTimeout, 0, 300000); // 5 mins
retval &= INBOUNDS(demandDegC, 8, 35);
retval &= INBOUNDS(demandPump, 8, 35);
retval &= (degF == 0) || (degF == 1);
retval &= (ThermostatMethod & 0x03) < 3; // only modes 0, 1 or 2
retval &= INBOUNDS(ThermostatWindow, 0.2f, 10.f);
@ -245,8 +241,6 @@ struct sUserSettings : public CESP32_NVStorage {
void init() {
dimTime = 60000;
menuTimeout = 60000;
demandDegC = 22;
demandPump = 22;
degF = 0;
ThermostatMethod = 0;
ThermostatWindow = 1.0;
@ -266,8 +260,6 @@ struct sUserSettings : public CESP32_NVStorage {
sUserSettings& operator=(const sUserSettings& rhs) {
dimTime = rhs.dimTime;
menuTimeout = rhs.menuTimeout;
demandDegC = rhs.demandDegC;
demandPump = rhs.demandPump;
degF = rhs.degF;
ThermostatMethod = rhs.ThermostatMethod;
ThermostatWindow = rhs.ThermostatWindow;
@ -319,6 +311,7 @@ public:
virtual void init() {};
virtual void load() {};
virtual void save() {};
virtual void doSave() {}
const sCyclicThermostat& getCyclicMode() const;
@ -348,12 +341,14 @@ public:
class CESP32HeaterStorage : public CHeaterStorage {
bool _bShouldSave;
public:
CESP32HeaterStorage();
virtual ~CESP32HeaterStorage();
void init();
void load();
void save();
void doSave();
};
extern CHeaterStorage& NVstore;

View file

@ -0,0 +1,148 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include <Arduino.h>
#include "TempSense.h"
#include "DebugPort.h"
CTempSense::CTempSense()
{
_TempSensor = NULL;
_owb = NULL;
}
void CTempSense::begin(int pin)
{
// initialise DS18B20 sensor interface
// create one wire bus interface, using RMT peripheral
_owb = owb_rmt_initialize(&_rmt_driver_info, pin, RMT_CHANNEL_1, RMT_CHANNEL_0);
owb_use_crc(_owb, true); // enable CRC check for ROM code
bool found = find();
readROMcode();
// Create DS18B20 device on the 1-Wire bus
if(found) {
attach();
}
}
bool
CTempSense::readTemperature(float& tempReading)
{
if(_TempSensor == NULL) {
bool found = find();
if(found) {
DebugPort.println("Found DS18B20 device");
readROMcode();
attach();
startConvert(); // request a new conversion,
waitConvertDone();
}
}
if(_TempSensor != NULL) {
DS18B20_ERROR error = ds18b20_read_temp(_TempSensor, &tempReading);
// DebugPort.printf(">>>> DS18B20 = %f, error=%d\r\n", fTemperature, error);
if(error == DS18B20_OK) {
return true;
}
else {
DebugPort.println("\007DS18B20 sensor removed?");
ds18b20_free(&_TempSensor);
}
}
return false;
}
bool
CTempSense::find()
{
// Find all connected devices
// DebugPort.printf("Finding one wire bus devices...");
OneWireBus_SearchState search_state = {0};
bool found = false;
owb_search_first(_owb, &search_state, &found);
if(found)
DebugPort.println("Found a one wire device");
else
DebugPort.println("No one wire devices found!!");
return found;
}
bool
CTempSense::readROMcode()
{
// For a single device only:
OneWireBus_ROMCode rom_code;
owb_status status = owb_read_rom(_owb, &rom_code);
if (status == OWB_STATUS_OK)
{
char rom_code_s[OWB_ROM_CODE_STRING_LENGTH];
owb_string_from_rom_code(rom_code, rom_code_s, sizeof(rom_code_s));
DebugPort.printf("Device %s present\r\n", rom_code_s);
return true;
}
else
{
DebugPort.printf("Error #%d occurred attempting to read ROM code\r\n", status);
return false;
}
}
bool
CTempSense::attach()
{
if(_TempSensor == NULL) {
_TempSensor = ds18b20_malloc(); // heap allocation
DebugPort.printf("Single device optimisations enabled\r\n");
ds18b20_init_solo(_TempSensor, _owb); // only one device on bus
ds18b20_use_crc(_TempSensor, true); // enable CRC check for temperature readings
ds18b20_set_resolution(_TempSensor, DS18B20_RESOLUTION_12_BIT);
}
return true;
}
void
CTempSense::startConvert()
{
// kick off the initial temperature conversion
if(_TempSensor)
ds18b20_convert(_TempSensor);
}
void
CTempSense::waitConvertDone()
{
if(_TempSensor)
ds18b20_wait_for_conversion(_TempSensor);
}

View file

@ -0,0 +1,47 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2018 Ray Jones <ray@mrjones.id.au>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#ifndef __BTC_TEMPSENSE_H__
#define __BTC_TEMPSENSE_H__
#include "../Libraries/esp32-owb/owb.h"
#include "../Libraries/esp32-owb/owb_rmt.h"
#include "../Libraries/esp32-ds18b20/ds18b20.h"
class CTempSense {
OneWireBus * _owb;
owb_rmt_driver_info _rmt_driver_info;
DS18B20_Info * _TempSensor = NULL;
bool _discover();
public:
CTempSense();
void begin(int pin);
bool find();
bool readROMcode();
bool attach();
bool readTemperature(float& tempReading);
void startConvert();
void waitConvertDone();
};
#endif

View file

@ -27,6 +27,7 @@
struct sGPIO;
extern void forceBootInit();
extern void requestOn();
extern void requestOff();
@ -36,7 +37,10 @@ extern bool reqThermoToggle();
extern bool setThermostatMode(unsigned char);
extern bool getThermostatModeActive(); // OEM: actual mode from blue wire, BTC: or our NV
extern void reqPumpPrime(bool on);
float getTemperatureDesired(); // OEM: the advertised value, BTC our setpoint
extern float getTemperatureDesired(); // OEM: the advertised value, BTC our setpoint
extern uint8_t getDemandDegC();
extern uint8_t getDemandPump();
extern float getTemperatureSensor();
extern void setPumpMin(float);
extern void setPumpMax(float);

View file

@ -309,15 +309,16 @@ void initWebServer(void) {
}
else {
if(Update.hasError()) {
DebugPort.println("WEB: UDPATE FAIL");
DebugPort.println("WEB: UPDATE FAIL");
server.send(200, "text/plain", "FAIL - Afterburner will reboot shortly");
}
else {
DebugPort.println("WEB: UDPATE OK");
DebugPort.println("WEB: UPDATE OK");
server.send(200, "text/plain", "OK - Afterburner will reboot shortly");
}
delay(1000);
// javascript redirects to root page so we go there after reboot!
forceBootInit();
ESP.restart(); // reboot
}
}, []() {