This is the second in a series of articles in which we explore using the 8th programming language with embedded as well as desktop and server systems, in an integrated project. The first in the series is "8th": Embedded and more (part 1).
It is not intended to be a tutorial on how to use 8th: for that, you can see the 8th tutorials, the 8th manual, the 8th forum, or you can start here: 8th - a Gentle Introduction to a Modern Forth.
In the previous article in this series we created a very simple framework application which admittedly did nothing but give you a feel for how the data-collection code might work. This time we'll flesh that part out, but to do that we also need to assemble the hardware. So without further ado, let's begin!
We need to connect the LM75A Temperature Sensor (henceforth "board") to the Raspberry Pi 2 (or 3) using the I2C connection on the RPI. First we need to assemble the sensor, since it comes with a header separate from the board. Simply solder the header to the board. Then, jumper the pins as follows:
The "OS" pin on the board need not be connected to anything. Likewise, for the moment we won't jumper anything on the back of the board (doing so changes the I2C address of the board).
You need to have installed at least the Hobbyist version of 8th on your RPI in order to run this code, since it has support for I2C. For a limited time you can purchase the current Professional version and automatically receive the Embedded version as soon as it's released. More details here.
You'll also need to enable I2C on your RPI. Start raspi-config, option 5, then P5. Or you can manually enable the kernel modules "i2c-bcm2708" and "i2c-dev". If you've installed "i2c-tools" on your RPI, you can test the board's connection by typing i2cdetect -y 1, which should show a "48" amongst a field of dashes. That "48" is the hex address of the board, as currently configured.
raspi-config
i2cdetect -y 1
So to test this with 8th, start 8th, and type in the console:
1 0x48 hw:i2c0 false hw:i2c@reg.s bye
1 0x48 hw:i2c
0 false hw:i2c@reg
.s bye
You should see something like:
2 n: 017d2620 1 327861 hw: 017ee000 1 unknown
2 n: 017d2620 1 32786
1 hw: 017ee000 1 unknown
That means that the word (2-byte) value 32786 was read from the I2C device at address 0x48, register 0, and was put on TOS (top of stack). The 'hw' item below that is 8th's I2C handle for the board. If you see 'null' instead of a number, then the number you gave for the address is incorrect, or possibly your board is defective or not wired up correctly, or it's unhappy because you're touching it. Double-check all of that!
NOTE: the board is very sensitive to being touched. A slight amount of skin oil on the back surface can change the address it uses. Touching the sensor can freak it out so it returns 'null'. Be careful!
The full code is here so you don't have to type it all in. Usual practice is to put the code in a file (in this case, 'data.8th') and then load it using 8th data.8th.
8th data.8th
We'll just discuss some of the highlights of the code. Notice, first of all, that the code's main loop is mostly the same as the previous (non-functional) version:
: app:main \ time-stamp log output: true log-time "Starting" log init-devices repeat collect-data null? if \ Couldn't read; ignore drop else save-data send-data then 1 sleep again ;
The differences are that we tell 8th we want the 'log' word to output a time-stamp, we added a "init-devices" word, and we check to see if the "collect-data" word returned valid data. And it's the "collect-data" word that's more interesting:
\ Read the device:: collect-data \ -- tempC LM75A @ \ Read a 2-byte word from the device: 0 false hw:i2c@reg nip null? not if \ Convert the data to a real value: cvt-raw then ;
As the comments state, this reads a word from the device and then converts it from the raw data format into a temperature in degrees Celsius. The data-sheet for the LM75A is useful for figuring out how to do this. The "cvt-raw" word does the hard work:
\ Take raw data from device and convert to actual number:: cvt-raw \ n1 -- n2 \ reverse the bytes and shift off the insignificant: bswap 5 n:shr \ if top bit set, it's negative: dup %10000000000 band if 2comp n:neg then \ value is eighths of a degree C 8 n:/ ;
The "bswap" and "2comp" words aren't part of 8th, but they're in the full source file.
When you run the sample code on a properly configured RPI and board, you'll get something like this:
Note the full time-stamp which we enabled with "true log-time". Also note we print the Fahrenheit as well as Celsius values. The temperature rose during this run because I put my thumb on the sensor, and some data points are missing precisely because my thumb's presence caused the sensor to not return a value.
In the next blog post, we'll take a look at hooking up the 7-segment display and interfacing it with 8th. The inspiration for this series is taken from this post though some of what's written there proved to be incorrect.