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.
Now On Sale!
You can now buy a print or ebook edition of Raspberry Pi IoT in C from Amazon.
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
-
Introducing Pi (paper book only)
-
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.
-
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? -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
SPI MCP3008/4 AtoD (paper book only)
-
Serial (paper book only)
-
Getting On The Web - After All It Is The IoT (paper book only)
-
WiFi (paper book only)
The Maxim 1-Wire bus is a proprietary bus that is very easy to use and has a lot of useful devices you can connect to it including the iButton security devices which were described in the previous chapter. However, probably the most popular of all 1-wire devices is the DS18B20 temperature sensor - it is small, very cheap and very easy to use.
While there are drivers for 1-wire under Linux they have a few problems and before your program can run you have to make sure they are installed. However the protocol is easy enough to program in C and the Raspberry Pi is fast enough to work with it without needing anything extra.
The Hardware
The DS18B20 is available in an number of formats but the most common makes it look just like a standard BJT - which can sometimes be a problem when you are trying to find one.
You can also get them made up into waterproof sensors complete with cable.
No matter how packaged, they will work at 3.3V or 5V, Its basic specification is:
- Measures Temperatures from -55°C to +125°C (-67°F to +257°F)
- ±0.5°C Accuracy from -10°C to +85°C
- Thermometer Resolution is User Selectable from 9 to 12 Bits
- Converts Temperature to 12-Bit Digital Word in 750ms (Max)
It can also be powered from the data line making the bus physically need only two wires - data and ground - however this "parasitic power" mode is difficult to make work reliably and best avoided in an initial design. To supply it with enough power during a conversion the host has to essentially connect it directly to the data line by providing a "strong pullup" - essentially a transistor.
In normal powered mode there are just three connections:
Ground needs to be connected to the system ground, VDD to 3.3V and DQ to the pull-up resistor of an open collector bus.
There can be multiple devices on the bus and each one has a unique 64-bit lasered ROM code, which can be used as an address to select the active devices. For simplicity, it is better to start off with a single device and avoid the problem of enumerating the devices on the bus - although once you know how everything works this isn't difficult to implement.
The circuit is:
You can build it in a variety of ways. You can solder the resistor to the temperature sensor and then use some longer wires with clips to connect to the Pi. You could also solder directly to the Pi, a good plan for the Pi Zero or use a prototyping board.
Initialization
Every transaction with the a 1-wire device starts with an initialization handshake. The master holds the bus low for at least 480 microseconds, a 15 to 60 microsecond pause follows and then any and all of the devices on the bus pull the line low for 60 to 240 microseconds.
We have implemented this in a previous chapter and can simply use the presence function:
int main(int argc, char **argv) { const struct sched_param priority = {1}; 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); if(presence(RPI_GPIO_P1_07)==0){ read temperature };
I you try this and find it doesn't work then make sure that you are running the program as root as described at the start of chapter 2.
If you try this partial program and have a logic analyzer with a 1-wire protocol analyzer you will see something like:
Seeing a presence pulse is the simplest and quickest way to be sure that your hardware is working. From this point it is just a matter of using the function developed in the previous chapter to work with the commands defined int he data sheet.
Initiating A Temperature Conversion
After discovering that there is at least one device connected to the bus the master has to issue a ROM command. In many cases the ROM command used first will be the Search ROM command which enumerates the 64-bit codes of all of the devices on the bus. After collecting all of these codes the master can used the Match ROM commands with a specific 64-bit code to select the device the master wants to talk to.
While it is perfectly possible to implement the Search ROM procedure it is simpler to work with the single device by using commands which ignore the 64-bit code and address all of the devices on the bus at the same time. Of course this only works as long as there is only one device on the bus.
If there is only one device, as in this case, then we can use the Skip ROM command 0xCC to tell all i.e the one, devices on the bus to be active.
Using this we can send a Skip ROM command using:
writeByte(RPI_GPIO_P1_07, 0xCC);
You can see the pattern of bits sent on a logic analyzer:
Our next task is to send a Convert command 0x44.
This starts the DS18B20 making a temperature measurement.
Depending on the resolution selected this can take as long as 750ms.
How the device tells the master that the measurement has completed depends on the mode it is operating in but using an external power line, i.e. not using parasitic mode, the device sends a zero bit in response to a bit read until it is completed when it sends a 1.
This is new and it is how 1-wire devices that need time to get data read slow down the master until they are ready. The master can read a single bit as often as it likes and the slave will respond with a zero until it is read with the data.
As we already have a readBit function this is easy.
The software polls for the completion by reading the bus until it gets a 1 bit:
int convert(uint8_t pin) { int i; writeByte(pin, 0x44); for (i = 0; i < 1000; i++) { bcm2835_delayMicroseconds(100000); if (readBit(pin) == 1)break; }
return i; }
You can of course test the return value to check that the result has been obtained. If convert returns 500 then the loop timed out.
When the function returns the new temperature measurement is stored in the device's scratchpad memory and now all we have to do is read this.
Reading The Scratchpad
The scratchpad memory has nine bytes of storage in total and does things like control the accuracy of conversion and provide status information. However in our simple example the only two bytes of any great interest are the first two - which hold the result of a temperature conversion. However if we are going to check the CRC for error correct we need to read all nine bytes.
All we have to do issue a Read Scratchpad 0xBE command and then read the nine bytes that the device returns.
However, to send the new command we have to issue a new initialization pulse and a Skip ROM 0xCC command followed by a read scratchpad command 0xBE:
presence(RPI_GPIO_P1_07);
writeByte(RPI_GPIO_P1_07, 0xCC);
writeByte(RPI_GPIO_P1_07, 0xBE);
Now the data is ready to read.
We can read all nine bytes of it or just the first two. The device will keep track of where the read is so if you come back later and read more bytes you will get the first one not read. If you issue another initialization pulse then the device aborts the data transmission.
As we do want to check the CRC for errors we will read all nine bytes:
int i; uint8_t data[9]; for (i = 0; i < 9; i++) { data[i] = readByte(RPI_GPIO_P1_07); }
Now we have all of the data stored in the scratch pad and the CRC byte. We can now check for errors:
uint8_t crc = crc8(data, 9);
As before crc will be zero if there are no transmission errors.
Getting the Temperature
We only really need the first two bytes which are the least and most significant bytes of the 11-bit temperature reading as a 16-bit, 2-complement integer.
int t1 = data[0];
int t2 = data[1];
t1 holds the low order bits and t2 the high order bits.
All we now have to do do is to put the two bytes together as a 16-bit integer. As the Pi supports a 16-bit int we can do this very easily:
int16_t temp1 = (t2 << 8 | t1);
Notice that this only works because int16_t really is a 16-bit integer. If you were to use:
int temp1= (t2<<8 | t1);
Then temp1 would be correct for positive temperatures but it would give the wrong answer for negative values because the sign bit isn't propagated into the top 16 bits. If you want to use a 32-bit integer then you will have to propagate the sign bit manually:
if(t2 & 0x80) temp1=temp1 | 0xFFFF0000;
Finally we have to convert the temperature to a scaled floating point value - divide by 16 to get centigrade.
float temp = (float) temp1 / 16;
Now we can print the crc and the temperature:
printf("CRC %hho \n\r ", crc); printf("temperature = %f C \n", temp);
A Function to get the temperature
Packaging all of this into a single function is easy:
float getTemperature(uint8_t pin) { if (presence(pin) == 1) return -1000; writeByte(pin, 0xCC); convert(pin); presence(pin); writeByte(pin, 0xCC); writeByte(pin, 0xBE); int i; uint8_t data[9]; 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; }
Notice that the function returns -1000 if there is no device and -2000 if there is a CRC error. These are values outside of the range of temperature that can be measured.
Other Commands
As well as the commands that we have used to read the temperature the DS18B20 supports a range of other commands.
There are two command concerned with when there are more devices on the bus - Search ROM 0xF0 is used to scan the bus to discover what devices are on the bus and Match ROM 0x55 is used to select a particular device.
You can also read the devices unique 64 bit code using the Read ROM command 0x33. This makes the slave transmit the 8 byte of data that the iButton transmits - a single byte device family code 0x28 for the DS18B20, six bytes of serial number and a single CRC byte. You can use the read iButton function in given in the previous chapter to read the serial number:
uint8_t code[8]; int i; int p = readiButton(RPI_GPIO_P1_07, code); if (p == 1) { for (i = 0; i < 8; i++) { printf("%hhX ", code[i]); } printf("\n\r"); fflush(stdout); }
As well as the read scratch pad command that we used to read the temperature there is also a Write ScratchPad command 0x4E.
The format of the scratchpad is:
The first two bytes are the temperature that we have already used. The only writeable entries are bytes 2, 3 and 4. The Write ScratchPad command transfers three bytes to these locations. Notice that there is no CRC and no error response if there is a transmission error. The data sheet suggests that you read the scratchpad after writing it to check that you have been successful in setting the three bytes - of course reading the scratchpad does include a CRC.
The third byte write to the scratchpad is the configuration register.
Essentially the only thing you can change is the resolution of the temperature measurement.
Configuration Register Resolution Time
0x1F 9 bits 93ms
0x3F 10 bits 175ms
0x5F 11 bits 375ms
0x7F 12 bits 750ms
The time quoted is the maximum time for a conversion at the given precision. You can see that the only real advantage of decreasing precision is to make conversion faster. The default is 0x7F and 12 bits of precision.
The first two bytes of the write scratchpad set a high and low temperature alarm. This is not a much used feature but you can set two temperatures that will trigger the device into alarm mode. Note you only set the top eight bits of the threshold temperatures. This is easy enough but the alarm status is set with every read so if the temperature goes outside of the set bounds and then back in the alarm is cleared. The second problem is that to discover which devices are in alarm mode you have to use the Alarm Search 0xEC command which works like the Search ROM command but the only devices that respond are the ones with an alarm state.
The alarm feature might be useful if you have a lot of devices and simply want to detect an out of band temperature. You could set up multiple devices with appropriate temperature limits and then simply repeatedly scan the bus for devices with alarms set.
You may notice that the scratchpad also has an EEPROM memory connected. You can transfer the three bytes of the scratchpad to the EEPROM using Copy Scratchpad 0x48 and transfer them back using the Recall EEPROM command 0xB8. You can use this to make the settings non-volatile.
Finally there is the Read Power Supply 0xB4 command. If the master then reads the bus a zero indicates that there are parasitic powered devices on the bus.
This is more or less all there is to the DS18B20 and the 1-wire bus in general if you restrict yourself to a single slave device on the bus. However if you want to have multiple slave devices you don't need any more hardware but you do need some more software - this is the subject of the next chapter.
Now On Sale!
You can now buy a print or ebook edition of Raspberry Pi IoT in C from Amazon.
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
-
Introducing Pi (paper book only)
-
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.
-
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? -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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. -
SPI MCP3008/4 AtoD (paper book only)
-
Serial (paper book only)
-
Getting On The Web - After All It Is The IoT (paper book only)
-
WiFi (paper book only)
Related Articles
Real Raspberry Pi - Getting Started And Custom NOOBS