Last week, while checking out the quarter-finals for the DreamBoard competition here on element14, I noticed one of the competitors, the Mother HEN board. It had a few things on it that I would also love to have on a dev board – specifically the range of sensors that the creator, crakkerjakked decided to include. I've actually worked with these types of sensors before, and I've wanted to do a small project with some of them for a while now. My idea was to sort of 'simulate' the Mother H.E.N, using an ARM Cortex microcontroller with a sensor board add-on. A good fit for this was the Tiva C LaunchPad and the SensorHub BoosterPack , which contains the BMP180 barometric pressure sensor, the TMP006 infrared temperature sensor, the ISL29023 visible and infrared light sensor, the SHT21 humidity sensor, and the MPU9150 9-axis motion sensor. Quite a complement of capable sensors!
My idea for this project was to create a clock that displays current weather conditions, including humidity, pressure, and temperature. This is a project that I've had in the back of my mind for a while, and so I decided to throw together a prototype. I also wanted to add data logging, either offline or online, so that weather data could easily be archived and viewed. To create the finished project will take a while, so I decided to focus on just getting something up and running – a combination proof-of-concept and prototype.
The first thing to do was get the Tiva C Launchpad set up and get I2C communication up and running. I debated whether to use Energia or Code Composer Studio for this project, but ended up going with Code Composer, as I have more experience with it, and the debugging would certainly be vital to the success of the project. Getting Code Composer set up the first time can be slightly daunting for a beginner, so be sure to download and check out all the documentation that TI provides, as it can make the difference between spending ten minutes and two hours getting the IDE ready. The important thing to do is to get the TivaWare libraries installed and built. After this, take a close look at the settings of the included Project 0 – things like the include folders, and the predefined symbols and names will need to be copied into your project settings. If you run into trouble, ask around here on element14 or check out TI's E2E forums – they are frequented not only by TI employees and engineers, but also lots of other engineers and hobbyists asking for and offering help. In order to make replicating my effort easier, I've included the source code, as well as a .BIN file, which can be used in conjunction with TI's LM Flash Programmer app to instantly get the project working.
Once you've got the IDE up and running, I typically take the time to draw out a rough system diagram in my lab notebook. This is a step that many programmers skip over, but it's one that can save a lot of time and headache while writing code. In this case, I knew that I wanted our microcontroller to periodically get updated sensor readings, display them on whichever display is used, and also log data to an EEPROM if data logging is enabled. This sounds simple when it's broken down like this, but the reason I like to draw it out is because many parts of the code are interdependent, and require different parts to work in concert. Being able to write down the purpose of each function, along with how it should be executed, makes writing them much easier. It's great to be able to refer back to the system diagrams and function flow charts when you start to get bogged down in the code.
Also, it's important to get all your documentation together before starting to write code. In this case, I downloaded all the datasheets for these sensors, and spent a good amount of time reading them and familiarizing myself with their registers and functions. Remember that the I2C bus specifies how communication is handled, so much of the interface will be identical. The important things to write down are which sensors use which addresses, and what internal registers you'll need to write to and read from in order to get the sensors working. I find once it's time to write the code, I focus on one sensor at a time. Write a simple function that just gets the updated data from the sensor. Once you've got all these functions written, they can simply be called one after another, or if you like, you can merge them all into one big sensor update function, though it might end up messier that way.
In a project like this, where you have many sensors to interface to which all use the same interface, I find it's a good idea to build a single function to take care of all the interface handling. Though the TivaWare library includes many functions to make it easy to use the peripherals, calling these functions over and over inline ends up looking messy – I prefer to unify all the calls into a single function, which then gets called with whatever parameters are needed. This makes your source code much cleaner and easier to read. In the case of our I2C bus, this function is in the “includes/myi2c.h” file, and it's called i2ctransact(). The parameters are pointers to the address of the chip we're trying to talk to, along with pointers to that chip's send and receive buffers and a flag indicating the direction of data. This makes using the I2C bus easier in the other functions – any time you need to send or receive data, you just put it in the chip's send buffer and call the function, and voila! It's all taken care of. This also helps to make the code more portable – if I wanted to use this same code on a different chip, like an MSP432, all you need to change is the i2ctransact() function, instead of having to re-write all the functions that call it.
Because this project is going to have a clock function as well, we need to update the display at least once a second. I decided to use the SysTick to keep things simple. The SysTick is a built-in timer, which 'ticks' every processor clock cycle. Unlike other timers on the chip, the only thing you can change is the period – how often it 'rolls over' back to zero. We enable an interrupt on the rollover, and use this to do all the housekeeping tasks. Every few hundred milliseconds, the chip gets updated information from the sensors, and updates the display with the new values and the new time from the RTC. In order to support the data logging feature, you have to decide how often you want data points to be written, and then add a way to keep track of how much time has gone by. I did this with a rudimentary system timeout variable, though you could also use one of the timer peripherals with a long period, or you can use the output from the RTC. Most RTCs have a programmable alarm output, which can be programmed to fire every second. This could be tied to a pin change interrupt to replace the SysTick interrupt, but this adds more complexity to the project. Also, the SysTick timer can fire more frequently than once a second, enabling us to have a higher screen refresh rate. To help save power, as more time goes by, the display dims and the frequency of updates decreases, until eventually the whole system goes to sleep. It can then be woken with a push of a button.
Once I had the basic concept down, it was time to dive in and start coding! And boy, I sure cranked out a lot of code in a short period of time (for me, at least). It was a bigger project than I originally anticipated, and I ran into many major problems. First of all, getting communications established with the chips on the I2C bus wasn't terribly difficult – I was getting sensor readings from the humidity and pressure sensors within a few hours. Getting the display up and running was quite an effort. I had written my own library for a PIC32MX chip for the OLED display I was using, so I had a base to start with, but porting it to the Tiva C was a bit of work. I had to make some major changes to the fonts used so that the data could be displayed with proper units and symbols. The biggest issue, however, was getting the real time clock working. This stumped me for literally days – I spent hours looking at the output of my logic analyzer and referring to my favourite book on I2C, Mastering the I2C Bus by Vincent Himpe. Everything seemed fine, and I really couldn't figure out what the problem was. My only guess was that the first two bytes on the I2C bus (chip address and register) were not spaced apart. Because of how the Tiva C works, you can easily add delays between bytes after the first two, but the address and first data byte are sent together by the hardware and so, in order to take the guesswork out of it, I wrote a bit-bang I2C library just for the RTC. I know, it seems a bit crazy, but the clock function is pretty vital to the whole project, so I really needed to get it working before moving on. After a few attempts, the clock came to life and I started getting correct data! I don't know for sure what the problem really was, but at least I got it fixed!
So then I had to start crafting the interrupts and the main loop structure. I used a style that I learned from fellow element14 blogger/podcaster elecia White's book, Making Embedded Systems, which I highly recommend picking up if you do any work with embedded software or hardware. Essentially, the interrupts just set flags in a global variable, and pull the chip out of sleep. In our case, the interrupt sources are the SysTick timer, and the buttons being pressed. It then runs through the main while(1) loop and, depending on which flags were set, it refreshes the screen with the correct information. After it deals with all the interrupt flags, it checks to see if data logging is on, and whether it is time to take another sample. Then, depending on the state of the system power level, it either goes to a normal sleep, or a lower power deep sleep after shutting the OLED display off.
It's a very elegant and simple structure, and it worked really well for this project. Though I am far from a professional coder, I feel my grasp on the C language is fairly good, and after getting the basic structure created, it was easy to add on all the other features. The last step was getting the data logging working. As I mentioned earlier, I was split on whether to do online or offline data logging. Unfortunately, I don't have enough experience with the CC3100 BoosterPack yet to use it in Code Composer, so I decided to at least start with a local EEPROM for data storage, and leave expansion options open. Down the line, it probably wouldn't prove too difficult to add some Bluetooth or Wi-Fi peripherals and make this a true Internet of Things project.
In order to store the data, I had to create a very basic file system, if you can even call it that. My choice to use an external EEPROM instead of the built-in 2K of EEPROM in the TM4C123G was because of space. The EEPROM I used, a Microchip 24LC256 , has 32 768 bytes (or 256 thousand bits, hence the LC256), which is a lot of samples! Each sample takes up 26 bytes. Unfortunately, the way the EEPROM works, you cannot do a continuous write across the internal page boundaries. What this means is that you can only write between each 64 byte boundary in a single pass. In order to simplify the reading and writing, I decided to take the easier route and simply create a look-up table of safe addresses to write to. This wastes some internal space, but negates the requirement of re-writing the I2C function to watch for the page boundary and stop when it reaches it.
So, I just sat and did some simple math. I decided to reserve the first 16 bytes as a 'header' of sorts, to allow for possible future expansion into a real filesystem, so addresses 0x0000 – 0x000F can't be used for data storage. Each 64 byte boundary lands in an easily predictable place. The first page is 0x0000 to 0x003F, the second is from 0x0040 to 0x007F, etc. Each of our data packets is 26 bytes long, as I mentioned before – 6 bytes for the date and time, then 8 bytes each for the temperature and humidity, plus 4 bytes for the barometric pressure. Essentially, we can fit two packets per page with a few bytes left over. 26 is 0x1A, and 2 * 0x1A is 0x34 – just a bit smaller than a full page. So creating the look-up table was a matter of figuring out the pattern from 0x000F to 0x00FF, and then just copying and pasting it. You can see it at the top of “includes/eeprom.h”. I only put enough addresses for 128 packets, but this would be easily expandable if needed.
After a lot of late nights and scratching my head, I finally got everything working. It actually works even better than I had hoped. The OLED screen is quite small, but for a prototype it's perfect. It has great readability and high contrast, so it works in dark and bright lighting equally well. It would be quite trivial to move to a bigger display, such as the next step up from the 128x32 I am using – the 128x64 version offers double the vertical pixels and would make it easier to read from far away. Another option would be a vacuum fluorescent display , or VFD (yes, they're insanely expensive nowadays), which you can see an example of in the image to the left. I've got a two-line, 20 character one which accepts TTL serial and is very easy to use. It's really bright and quite big. The reason I didn't use it is the font is programmed into the ROM and therefore not all the characters I wanted (such as the degrees symbol) were available. I originally started off using a standard HD44780 2x20 LCD , but the I2C backpack I was using was giving me trouble and I'm not a fan of the look of those LCDs anyway. If you were to build this project into an enclosure, you could really put a lot more work into the user interface. For example, you could add a programmable delay between data samples, or an easier way to set the time. Both these things had to be hard-coded due to the fact that the user interface only has four buttons! However, even with just a few buttons it wouldn't be terribly hard to add a menu system. Adding an optical encoder or a BoosterPack with more buttons would be an option – or if you really wanted to go all out, a touchscreen LCD would be really cool!
The code is all available on GitHub. To clone it, simply run the command
git clone https://github.com/FrozenElectronics/motherhen.git
Or you can visit the web version of the repo here: https://github.com/FrozenElectronics/motherhen
You can even just download a .zip of the entire repo by clicking here:
https://github.com/FrozenElectronics/motherhen/archive/master.zip
I warn you in advance – I'm not exactly a professional programmer, so the way I organize my code isn't the greatest. I've still got some bad coding habits that I'm trying to get rid of. However, if you've got the parts available, I've included the .bin file which you can use to directly program the Tiva C LaunchPad in case you don't have Code Composer and TivaWare installed. It's under the Debug/ folder, and the file name is, amazingly, motherhen.bin.
Here's the code, embedded in GitHub Gists. While you can view the code here, the problem with Gists is that they don't update if I push new modifications to the code. So, if you're interested in the project, I highly recommend viewing the code through the web link above, as it will show you the most recent code. I had to rename the files to .c to get them to render properly -- don't use these filenames, they need to be .h for it to compile!