ESP8266 and the Dallas DS18B20 and DS18B20P

Please note – this information has now been superseded by the info on the new blog at http://tech.scargill.net – please visit the new blog.

Having wasted half the night following up dodgy temperature readings for the DS18B20P only to realise I’d left existing code running for the DHT22 !!!  I managed in the process to reduce the necessary code down to a minimum.

If you look at a previous blog in here I referred to some simplified Arduino code I’d written back in the dark ages and wondering if I could get similar here.  I’m only interested in temperatures to the nearest degree C for thermostatic control and was very concerned about the typical 1 second delay to read these chips.

I reasoned that if you were to read the chip before conversion then start a conversion off, your first reading would be rubbish – but subsequent readings would be fine without the delays associated with the more normal code.

So, here is what I ended up needing for a library – very little as you’ll see… most of it is comments…

 

/*
* Adaptation of Paul Stoffregen’s One wire library to the ESP8266 and
* Necromant’s Frankenstein firmware by Erland Lewin <erland@lewin.nu>
*
* Paul’s original library site:
*   http://www.pjrc.com/teensy/td_libs_OneWire.html
*
* See also http://playground.arduino.cc/Learning/OneWire
*
* Stripped down to bare minimum by Peter Scargill for single DS18B20 or DS18B20P integer read
*/

static int gpioPin;

void ICACHE_FLASH_ATTR ds_init( int gpio )
{
//set gpio2 as gpio pin
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
//disable pulldown
PIN_PULLDWN_DIS(PERIPHS_IO_MUX_GPIO2_U);
//enable pull up R
PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO2_U);
// Configure the GPIO with internal pull-up
// PIN_PULLUP_EN( gpio );
GPIO_DIS_OUTPUT( gpio );
gpioPin = gpio;
}

// Perform the onewire reset function.  We will wait up to 250uS for
// the bus to come high, if it doesn’t then it is broken or shorted
// and we return;

void ICACHE_FLASH_ATTR ds_reset(void)
{
//    IO_REG_TYPE mask = bitmask;
//    volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
uint8_t retries = 125;
// noInterrupts();
// DIRECT_MODE_INPUT(reg, mask);
GPIO_DIS_OUTPUT( gpioPin );
// interrupts();
// wait until the wire is high… just in case
do {
if (–retries == 0) return;
os_delay_us(2);
} while ( !GPIO_INPUT_GET( gpioPin ));
// noInterrupts();
GPIO_OUTPUT_SET( gpioPin, 0 );
// DIRECT_WRITE_LOW(reg, mask);
// DIRECT_MODE_OUTPUT(reg, mask);    // drive output low
// interrupts();
os_delay_us(480);
// noInterrupts();
GPIO_DIS_OUTPUT( gpioPin );
// DIRECT_MODE_INPUT(reg, mask);    // allow it to float
os_delay_us(70);
// r = !DIRECT_READ(reg, mask);
//r = !GPIO_INPUT_GET( gpioPin );
// interrupts();
os_delay_us(410);
}

//
// Write a bit. Port and bit is used to cut lookup time and provide
// more certain timing.
//
static inline void write_bit( int v )
{
// IO_REG_TYPE mask=bitmask;
//    volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
GPIO_OUTPUT_SET( gpioPin, 0 );
if( v ) {
// noInterrupts();
//    DIRECT_WRITE_LOW(reg, mask);
//    DIRECT_MODE_OUTPUT(reg, mask);    // drive output low
os_delay_us(10);
GPIO_OUTPUT_SET( gpioPin, 1 );
// DIRECT_WRITE_HIGH(reg, mask);    // drive output high
// interrupts();
os_delay_us(55);
} else {
// noInterrupts();
//    DIRECT_WRITE_LOW(reg, mask);
//    DIRECT_MODE_OUTPUT(reg, mask);    // drive output low
os_delay_us(65);
GPIO_OUTPUT_SET( gpioPin, 1 );
//    DIRECT_WRITE_HIGH(reg, mask);    // drive output high
//        interrupts();
os_delay_us(5);
}
}

//
// Read a bit. Port and bit is used to cut lookup time and provide
// more certain timing.
//
static inline int read_bit(void)
{
//IO_REG_TYPE mask=bitmask;
//volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg;
int r;
// noInterrupts();
GPIO_OUTPUT_SET( gpioPin, 0 );
// DIRECT_MODE_OUTPUT(reg, mask);
// DIRECT_WRITE_LOW(reg, mask);
os_delay_us(3);
GPIO_DIS_OUTPUT( gpioPin );
// DIRECT_MODE_INPUT(reg, mask);    // let pin float, pull up will raise
os_delay_us(10);
// r = DIRECT_READ(reg, mask);
r = GPIO_INPUT_GET( gpioPin );
// interrupts();
os_delay_us(53);
return r;
}
//
// Write a byte. The writing code uses the active drivers to raise the
// pin high, if you need power after the write (e.g. DS18S20 in
// parasite power mode) then set ‘power’ to 1, otherwise the pin will
// go tri-state at the end of the write to avoid heating in a short or
// other mishap.
//
void ICACHE_FLASH_ATTR  ds_write( uint8_t v, int power ) {
uint8_t bitMask;
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
write_bit( (bitMask & v)?1:0);
}
if ( !power) {
// noInterrupts();
GPIO_DIS_OUTPUT( gpioPin );
GPIO_OUTPUT_SET( gpioPin, 0 );
// DIRECT_MODE_INPUT(baseReg, bitmask);
// DIRECT_WRITE_LOW(baseReg, bitmask);
// interrupts();
}
}
//
// Read a byte
//
uint8_t ICACHE_FLASH_ATTR ds_read() {
uint8_t bitMask;
uint8_t r = 0;
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
if ( read_bit()) r |= bitMask;
}
return r;
}

and here is the code that ended up in my timer callback routine, sending off an MQTT message for the temperature every 5 seconds – this works with both chips and works reliably.

    ds_init(2); // one off for the DS18B20

The above goes in my INIT routine – note that I’ve referred to GPIO2  – this will not work for other port bits as the Port initialisations seems to be partly hard coded – if anyone cares to re-write that so that anyone can easily access any of the usable pins.. would be nice..

I’ve also renamed the  routines, adding ds_ at the start to avoid confusion with other routines.

And now  for the actual code.. I call this every 5 seconds for testing and as you’ll see there’s a little extra code to send this out via MQTT. The extra code is in bold. “Temperature” is an integer defined elsewhere.  As you can see, no search, no checksum, no delay for processing – this works with both the DS18B20 and the DS18B20P chips.

char *tBuf = (char*)os_zalloc(6);
ds_reset();
ds_write(0xcc,1);
ds_write(0xbe,1);

temperature=(int)ds_read();
temperature=temperature+(int)ds_read()*256;
temperature/=16;
if (temperature>100) temperature-=4096;
ds_reset();
ds_write(0xcc,1);
ds_write(0x44,1);
os_sprintf(tBuf, “%d”, temperature);
MQTT_Publish(&mqttClient, “temperature”,tBuf,strlen(tBuf), 0, 0);
os_free(tBuf);

And so how to test all of this. For the life of me I can’t find a simple, no-nonsense program to read MQTT and graph it – you would think there would be loads around – I have asked the writer of MQTT-SPY if he’d like to add something into the program – for what’s out there – you seem to have to sign up and go through a whole learning curve – a little excessive for what I wanted (i.e. MQTT in – graph out), so I hooked up Node-RED to simply store the incoming data in a file.

node-red
And now having that information in a simple text file, it was easy to graph in EXCEL but again a whole FAFF to simply update dynamically until I discovered Live-Graph – not the prettiest but set to read the file every 5 seconds and update the graph –  I have to say it works a treat.

Here is my little sensor, as it happens the P variety I was originally having problems with, happily monitoring the ambient air temperature with a little excitement added on two occasions from my Dust-off air blower which is able to freeze the life out of the sensor on demand!

plot[3]

As you can see, the code is working well – doesn’t even need averaging.

Advertisements