When we learn a new programming language, usually the first thing we do is to print the "Hello world" text. It seems like the equivalent in the IoT is to blink an LED. After I was successful in blinking an LED, I got curious about how to change the brightness of an LED. Then I started to think about actually printing the "Hello world" text somehow.
Many ways to output text
I had tons of electronic parts at hand from the starter kit I purchased when I first started playing with the IoT framework Nerves and Raspberry Pi. So it was a great opportunity for me to learn how to use them. Here are some options I came up with:
- Place many LEDs to make the hello text
- Seven-segment display
- Dot Matrix LED
- LCD Character Display
- Web interface
- Mobile interface
Eventually I want to do them all but for now I decided to use a Character LCD Display with I2C interface. Some determiners are:
- LCD Character Display with I2C interface requires only 4 wires
- LEDs requires so many wires and it seems messy
- Web and mobile interfaces are practical but I wanted to learn electronics now
I googled around and watched tons of Youtube vidoes to learn the basics. Here are some of the resources I found helpful:
- Hitachi HD44780 LCD controller datasheet
- Hitachi HD44780 LCD controller | Wikipedia
- How a Character LCD works Part 1 by The 8-Bit Guy | Youtube
- How to Use Character LCD Module | elm-chan.org
- Difference between interfacing character 16×2 lcd in 4-bit and 8-bit mode with microcontroller | engineersgarage.com
- Character LCDs | learn.adafruit.com
- I2C Serial Interface 1602 LCD Module User Guide
There was nothing difficult about the wiring; however, many tutorials just use certain libraries that communicates with an LCD when it comes to the programming. Many are written in C, C++ or Python.
On the other hand, I had trouble finding a good Elixir library for controling an LCD, other than Elixir Circuits. The ExLCD library does deal with LCDs; however as of writing it has not been maintained for many years and depends on old dependencies.
At first I forked ExLCD and attempted to build on it. Then I realized that there is nothing special in communicating with an LCD as I learned more about the Hitachi HD44780 LCD. All I need is to understand what its datasheet says. I decided to write my own from scratch, borrowing ideas from other libraries.
Some key things are:
- what each LCD pin does (RS, En, D4-D7)
- how to initialize the display
- 4-bit mode vs 8-bit mode
- instruction mode vs data mode
- how to send instruction
- how to send data
- the pin assignment between the I/O expander (e.g. PCF8574, MCP23008 etc) and the LCD
- the basics of I2C
Experiments by trial and error
I did experiments using a playground Nerves app. After all, all I needed was:
- understand roughly what the HD44780 datasheet says
- understand the pin assignment between the I/O expander and the LCD
- understand how to switch on/off the output of the I/O expander through I2C interface
I took me a while to be able to show the "Hello" text because I did not do the above-mentioned. It was frustrating when I saw a random text on the display.
Once I was able to show the "Hello" text on the display, I wrote tests, improved the code and refactored incrementally. I realized my experimental app can be split into:
- a library that knows how to communicate with an LCD; and
- a demo app that demonstrates how to use the library
- A target board (e.g. Raspberry Pi)
- Micro SD card
- LCD display (16x2)
- I2C interface module
- female to female Jumper wires
Burn firmware to an SD card
Set necessary environment variables. Here is a list of target tags that Nerves supports.
$ export WIFI_SSID=_____ # your WIFI id $ export WIFI_PSK=______ # your WIFI password $ export MIX_TARGET=rpi0 # your target board
$ mix deps.get
Here are some firmware-related commands.
$ mix help | grep firmware mix burn # Write a firmware image to an SDCard mix firmware # Build a firmware bundle mix firmware.burn # Build a firmware bundle and write it to an SDCard mix firmware.gen.script # Generates a shell script for pushing firmware updates mix firmware.image # Create a firmware image file mix firmware.metadata # Print out metadata for the current firmware mix firmware.patch # Build a firmware patch mix firmware.unpack # Unpack a firmware bundle for inspection mix upload # Uploads firmware to a Nerves device over SSH
$ mix firmware
Insert the SD card into your host machine.
Burn the firmware to that SD card.
$ mix firmware.burn
Insert the SD card into your target board.
Power your target board on.
Connect to your target board
Check the connection by pinging.
$ ping nerves.local
SSH into your target board, then interactive Elixir shell will start.
$ ssh nerves.local Interactive Elixir (1.11.2) - press Ctrl+C to exit (type h() ENTER for help) Toolshed imported. Run h(Toolshed) for more info. RingLogger is collecting log messages from Elixir and Linux. To see the messages, either attach the current IEx session to the logger: RingLogger.attach or print the next messages in the log: RingLogger.next iex(1)>
RingLogger.attach then log messages will be outputted.
Follow the instructions in mnishiguchi/lcd_display README to show "Hello" text.
It was a great experience for me to learn I2C allows me to communicate with a peripheral quite easily just reading the datasheets carefully. Reading the datasheet is super important.
I put together my study note on HD44780 LCD, I/O expanders, I2C interface etc as a separate post.