How Many Devices?

Unfortunately there are other ways that a bus scan can go wrong. It can fail to detect one or more devices. It only takes a single bit error and the scan will abort getting the serial number for that device. If it is the first device in the scan and there are no backtrack points as yet the entire scan is canceled. However more usually just one device goes missing during a scan. This will not be detected as an error because the serial numbers returns will not be in error and will not trigger a CRC error. 

There are a number of solution to this problem. You could add error detection to the scanning function but even if you did this correctly there are times when a device simply doesn't respond at all, presumably because it misses the initialization pulse.  and hence goes missing without triggering an error. 

In practice there are two easy things you can do to deal with missing devices. 

You can scan the bus more than once and take the maximum number of correct serial numbers you find as the number of devices on the bus. 

You can set the number of devices as a constant and only accept scans that return that number.

In practice it is normal to know how many devices there are on the bus and as such enforcing the scan to find that number is the best solution. 

For example here is an error proof bus scan for exactly four devices:

  for (j = 0; j < 100; j++) {
            d = oneWireScan(RPI_GPIO_P1_11, serial);
            if (d != 4)continue;
            crc = 0;
            for (i = 0; i < d; i++) {
                code = (uint8_t*) & serial[i ];
                crc += crc8(code, 8);
            }        
            if (crc == 0)break;       
            bcm2835_delayMicroseconds(30000);      
        }

You can adjust the retries and time delays to suit your application. The delay is included to give time for any noise or instability in the system to subside. If you don't use twisted pair cable then 1-wire is prone to RF interference. The scan is retried 100 times i.e. over a max of 3 seconds. If you don't get a good scan in that time something is seriously wrong - most likely one of the devices has failed. 

Reading Specific Devices

Now that we have an array of serial numbers we can use them to read the devices they correspond to . Before you do this you should check the family codes, i.e. the first byte of the serial number to discover what kind of device you are about to work with. In practice you usually already know.

In this example it is assumed that all of the devices are DS18B20 temperature sensors. 

To read a specific device all you have to do is send a Match ROM commands 0x55 in place of a Skip ROM 0xCC command and then send the 64 bit serial number to select a single device. After this the transaction is the same and you can, in the case of the DS18B20 initiate a conversion and then read the scratchpad.  

The new function to read a specific device is very similar to the original that read a single device on the bus. 

First we have to issue a convert command after selecting the device:

float getDeviceTemperature(uint8_t pin, uint64_t device) {
    if (presence(pin) == 1) return -1000;
    writeByte(pin, 0x55);
    int i, bit;
    uint64_t d = device;
    for (i = 0; i < 64; i++) {
        bit = d & 0x01;
        d = d >> 1;
        writeBit(pin, bit);
    }
    convert(pin);

After sending the Match ROM command 0x55 we send the bits of the device serial number, all 64 bits, low order bits first. This causes all of the devices that do not match the serial number to go into an idle state. 

The convert function waits for the active device to complete its conversion. 

Now we have to read the scratchpad. This works in the same way but we have to select the device a second time because the initialization pulse actives all of the devices. This may seem a nuisance but you can use it to your advantage if you want to read a set of devices in that you could first set each one to convert and then read each one after waiting long enough for them to settle:

    presence(pin);
    writeByte(pin, 0x55);
    d = device;
    for (i = 0; i < 64; i++) {
        bit = d & 0x01;
        d = d >> 1;
        writeBit(pin, bit);
    }
    writeByte(pin, 0xBE);

    uint8_t data[9];
    for (i = 0; i < 9; i++) {
        data[i] = readByte(pin);
    }

The final part of the function is identical to the original read temperature function. 

The entire function but using a new matchROM function to select the device is:

float getDeviceTemperature(uint8_t pin, uint64_t device) {
    if (presence(pin) == 1) return -1000;

    matchROM(pin, device);
    convert(pin);
    presence(pin);
    matchROM(pin, device);
    writeByte(pin, 0xBE);

    uint8_t data[9];
    int i;
    for (i = 0; i < 9; i++) {
        data[i] = readByte(pin);
    }
    uint8_t crc = crc8(data, 9);
    if (crc != 0) return -2000;
    int t1 = data[0];
    int t2 = data[1];
    int16_t temp1 = (t2 << 8 | t1);
    float temp = (float) temp1 / 16;
    return temp;
}

void matchROM(uint8_t pin, uint64_t device) {
    writeByte(pin, 0x55);
    int i, bit;
    for (i = 0; i < 64; i++) {
        bit = device & 0x01;
        device >>= 1;
        writeBit(pin, bit);
    }

An example main program that scans the bus and reads the devices found is:

int main(int argc, char **argv) {
    const struct sched_param priority = {5};
    sched_setscheduler(0, SCHED_FIFO, &priority);
    mlockall(MCL_CURRENT | MCL_FUTURE);
    if (!bcm2835_init())
        return 1;
    bcm2835_gpio_fsel(RPI_GPIO_P1_07, BCM2835_GPIO_FSEL_INPT);
    uint64_t serial[15];
    int i, j;
    uint8_t *code;
    int crc,d;   
    for (;;) {
        for (j = 0; j < 1000; j++) {
            d = oneWireScan(RPI_GPIO_P1_07, serial);
            if (d != 4)continue;
            crc = 0;
            for (i = 0; i < d; i++) {
                code = (uint8_t*) & serial[i ];
                crc += crc8(code, 8);
            }
            printf("%hho %d %d\n\r", crc, d, j);
            fflush(stdout);
            if (crc == 0)break;
            bcm2835_delayMicroseconds(30000);
        }
        for (i = 0; i < d; i++) {
            printf("%llX \n", serial[i]);
            float temp = getDeviceTemperature(RPI_GPIO_P1_07, serial[i]);
            printf("temperature = %f C \n", temp);
        }
    }

Alarm Scan

You can use the same technique to pick out devices with an alarm set. The DS18B20 for example can have a high and low limit temperature set by writing data to the scratch pad. If the temperature goes outside of these limits then an alarm is set. You can obtain the serial numbers of all devices with an alarm set by scanning the bus but using Alarm Search 0xEC rather than Match ROM 0x55 - just change the code send in the function. In this case you will get a list of serial numbers corresponding to devices with alarms set and you can continue on to process just these. 

This seems like an attractive option but given the propensity for errors it isn't particularly robust - you could easily miss a device with an alarm set. If possible it is better to perform a manual scan of each device on the bus and read its alarm status.

Final Thoughts

The 1-wire bus is simple and immensely useful - as long as you don't try to push it too far. Problems start to arise when you use long, over 2 or 4m connections or try to put lots of devices on the same bus. With the price of a Pi Zero and the number of GPIO lines available this seems like an unnecessary complication. 

 

 

Now On Sale!

You can now buy a print edition of Raspberry Pi IoT in C

You can buy it from:

USA and World   Amazon.com
Canada              Amazon.ca
UK                     Amazon.co.uk
France               Amazon.fr
Germany            Amazon.de
Spain                 Amazon.es
Brazil                  Amazon.br
Italy                    Amazon.it
Japan                 Amazon.co.jp
Mexico               Amazon.com.mx 

 

For Errata and Listings Visit: IO Press

 

 

This our ebook on using the Raspberry Pi to implement IoT devices using the C programming language. The full contents can be seen below. Notice this is a first draft and a work in progress. 

Chapter List

  1. Introducing Pi (paper book only)

  2. Getting Started With NetBeans In this chapter we look at why C is a good language to work in when you are creating programs for the IoT and how to get started using NetBeans. Of course this is where Hello C World makes an appearance.

  3. First Steps With The GPIO
    The bcm2835C library is the easiest way to get in touch with the Pi's GPIO lines. In this chapter we take a look at the basic operations involved in using the GPIO lines with an emphasis on output. How fast can you change a GPIO line, how do you generate pulses of a given duration and how can you change multiple lines in sync with each other? 

  4. GPIO The SYSFS Way
    There is a Linux-based approach to working with GPIO lines and serial buses that is worth knowing about because it provides an alternative to using the bcm2835 library. Sometimes you need this because you are working in a language for which direct access to memory isn't available. It is also the only way to make interrupts available in a C program.

  5. Input and Interrupts
    There is no doubt that input is more difficult than output. When you need to drive a line high or low you are in command of when it happens but input is in the hands of the outside world. If your program isn't ready to read the input or if it reads it at the wrong time then things just don't work. What is worse is that you have no idea what your program was doing relative to the event you are trying to capture - welcome to the world of input.

  6. Memory Mapped I/O
    The bcm2835 library uses direct memory access to the GPIO and other peripherals. In this chapter we look at how this works. You don't need to know this but if you need to modify the library or access features that the library doesn't expose this is the way to go. 

  7. Near Realtime Linux
    You can write real time programs using standard Linux as long as you know how to control scheduling. In fact it turns out to be relatively easy and it enables the Raspberry Pi to do things you might not think it capable of. There are also some surprising differences between the one and quad core Pis that make you think again about real time Linux programming.

  8. PWM
    One way around the problem of getting a fast response from a microcontroller is to move the problem away from the processor. In the case of the Pi's processor there are some builtin devices that can use GPIO lines to implement protocols without the CPU being involved. In this chapter we take a close look at pulse width modulation PWM including, sound, driving LEDs and servos.

  9. I2C Temperature Measurement
    The I2C bus is one of the most useful ways of connecting moderately sophisticated sensors and peripherals to the any processor. The only problem is that it can seem like a nightmare confusion of hardware, low level interaction and high level software. There are few general introductions to the subject because at first sight every I2C device is different, but here we present one.

  10. A Custom Protocol - The DHT11/22
    In this chapter we make use of all of the ideas introduced in earlier chapters to create a raw interface with the low cost DHT11/22 temperature and humidity sensor. It is an exercise in implementing a custom protocol directly in C. 

  11. One Wire Bus Basics
    The Raspberry Pi is fast enough to be used to directly interface to 1-Wire bus without the need for drivers. The advantages of programming our own 1-wire bus protocol is that it doesn't depend on the uncertainties of a Linux driver.

  12. iButtons
    If you haven't discovered iButtons then you are going to find of lots of uses for them. At its simples an iButton is an electronic key providing a unique coce stored in its ROM which can be used to unlock or simply record the presence of a particular button. What is good news is that they are easy to interface to a Pi. 

  13. The DS18B20
    Using the software developed in previous chapters we show how to connect and use the very popular DS18B20 temperature sensor without the need for external drivers. 

  14. The Multidrop 1-wire bus
    Some times it it just easier from the point of view of hardware to connect a set of 1-wire devices to the same GPIO line but this makes the software more complex. Find out how to discover what devices are present on a multi-drop bus and how to select the one you want to work with.

  15. SPI Bus
    The SPI bus can be something of a problem because it doesn't have a well defined standard that every device conforms to. Even so if you only want to work with one specific device it is usually easy to find a configuration that works - as long as you understand what the possibilities are. 

  16. SPI MCP3008/4 AtoD  (paper book only)

  17. Serial (paper book only)

  18. Getting On The Web - After All It Is The IoT (paper book only)

  19. WiFi (paper book only)

 

 

Related Articles

1-Wire Search Algorithm

Advanced 1-Wire Network Driver

Guidelines for Reliable Long Line 1-Wire Networks

Real Raspberry Pi - Getting Started And Custom NOOBS

    

 

comments powered by Disqus