0
\$\begingroup\$

I'm using the 1-Wire protocol to communicate with around 24 DS18B20 devices distributed on 3 stubs, each stub being 2m long, all connected to a bus 1m in length, with 100 ohm resistors on each stub connection to the bus. In the maxim 1-wire application note AN148 it mentions using a second pin on the microcontroller to control slew rate (and even a third to control an active pullup) in appendix B and C, I'm wondering how exactly this may be done in firmware? I'm using the OneWire.h library for Arduino, modified so that it works in the ESP-IDF for an ESP32 since the pin stuff works differently on each platform. Additionally, since my ESP32 microcontroller is 3v3 I am using a logic level shifter to go from 3v3 (microcontroller side) to 5v on the bus.

I would appreciate a little explanation of how I could do slew rate control and/or active pullup control in firmware, is it as simple as setting the slew pin low when I want to write the bus low, and setting the active pullup high whenever I set the bus high? How might this work for reading bits? Are there any timings or other considerations I need to make, etc.?

Below are the write_bit and read_bit functions which I imagine are the most important things.

Write bit:

void OneWire::write_bit(uint8_t v)
{
    if (v & 1)      //if bit v is high
    {
        noInterrupts();
        DIRECT_WRITE_LOW;
        DIRECT_MODE_OUTPUT; // drive output low
        ets_delay_us(10);
        DIRECT_WRITE_HIGH;  // drive output high
        interrupts();
        ets_delay_us(55);
    }
    else            //if bit v is low
    {
        noInterrupts();
        DIRECT_WRITE_LOW;
        DIRECT_MODE_OUTPUT; // drive output low
        ets_delay_us(65);
        DIRECT_WRITE_HIGH;  // drive output high
        interrupts();
        ets_delay_us(5);
    }
}

Read bit:

uint8_t OneWire::read_bit(void)
{
    uint8_t r;

    noInterrupts();
    DIRECT_MODE_OUTPUT;
    DIRECT_WRITE_LOW;
    ets_delay_us(3);
    DIRECT_MODE_INPUT;  // let pin float, pull up will raise
    ets_delay_us(10);
    r = DIRECT_READ;
    interrupts();
    ets_delay_us(53);
    return r;
}
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

The default OneWire library does not work on ESP32. This is due to delayMicroseconds() featuring a 64-bit division, which adds a random delay of a few µs to the requested delay, which is enough to fail the timings.

I think ets_delay_us() does not have this problem. You can check with this:

for(;;)
{
    noInterrupts();
    DIRECT_MODE_OUTPUT;
    DIRECT_WRITE_LOW;
    ets_delay_us(3);
    DIRECT_MODE_INPUT;  // let pin float, pull up will raise
    interrupts();
    ets_delay_us(10);
}

Then probe the 1-wire pin with a scope. You should get a pulse of reasonably accurate 3µs width, and the width should be constant, it should not look like the pulse is "pulsating" by randomly changing its width. If this test passes, then you can use this code, it'll work fine. Otherwise you can check this library.

24 DS18B20 devices distributed on 3 stubs, each stub being 2m long, all connected to a bus 1m in length

1 wire is slow enough that you should not have trouble.

If you have lots of read errors, the most likely culprit is wrong timings on the ESP32 side. Most likely the sampling time for the read is wrong. This one needs to be quite accurate.

You can check with a scope. It can be useful to trigger the scope on another GPIO that signals the beginning of the transaction. If you can see the devices always respond, then it means the ESP32 is transmitting correctly, the devices are receiving and responding. Then the OneWire code signals a CRC error because it sampled a bit at the wrong timing, so it got the wrong value.

If you see the devices not responding then you should look for signal integrity problems on the transmit side, also probe on the device side, loop for deformed edges. With just a few meters, it is unlikely.

Maybe also lack of decoupling or bad power supply stability on the DS18B20 side. I have a lot of them, my bus is much longer than yours, and I did add some 100nF caps on each plus a TVS diode as protection. All devices are read every second and I get a few read errors per year, so it's pretty reliable.

However the 1wire bus is not really meant to make a star topology. The solution to this is pretty simple : just use one bus per stub. So in this case since you have 3 strings of sensors, just make each string a bus, and connect each to its own ESP32 pin with its own pullup and voltage translation MOSFET. Then you just change the pin number in the code when you want to talk to the corresponding sensors. This removes all signal integrity issues related to the star topology, because it's no longer a star.

The slew rate control paper would be relevant if you had hundreds of meters of cable, but at the lengths you're using, it is not necessary.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.