Tutorial: Adding the SSD1306 OLED Screen to an Arduino Logger (without a library)

An SSD1306 OLED screen mounted on a climate station build.

This is the third installment in a series on adding output screens to Cave Pearl Data loggers. It builds on the Nokia 5110 LCD tutorial and the post describing how I store fonts in the Arduino’s internal EEprom to save program memory – so you might want to have a look at those two posts before diving in here.

While the Nokia 5110’s are a cheap, low-power option with great visibility in full sun, they reacted badly to pressure directly on the LCD surface.  Since this project deploys loggers in an underwater environment I went looking for something more robust and the SSD1306 OLED’s caught my attention. (@ ~$3.50 on eBay) These little screens are showing up in hacked toyscompasses, GPSanalog meters, ECG’s, and theres even a tiny oscilloscope project at Hackaday. But those applications typically use the u8g2 library which is fantastic for graphic output, but also quite memory intensive; what I need is a bare bones solution that uses the fewest system resources on a unit that’s already near memory limits. Though these 0.96 inch displays are quite small, 128×64 pixels lets you render several lines of readable text.

While the I2C variants of this screen are easy to use, the SPI version lets me re-purpose unused analog lines to drive this display without interfering with the sensor or SD card bus because it can be driven via shift-out on any pins that are available. Powering the screen from A0 brings the screen down to zero current while the logger is sleeping, and also lets me get rid of the Reset & Cable Select lines. The only thing to watch out for is that you bring all the control lines low when you cut power so that reverse bias doesn’t end up “back powering” the screen controller.

Connecting the OLED:                                {Click any image to see larger versions.}

The data sheet for the SSD1306 controller specifies that the reset input needs to be LOW, during initialization, after which the pin should be HIGH for normal operation. To achieve this low -> high  transition, I tie the Reset line to the middle of an RC bridge made from a 104 (0.1uF) capacitor and a 10k resistor. When you add the 25k inside the 328p used to pull A0 high, you get a 63.2% time constant of about 3.5ms.

Begin by tinning the pins, and bending them 90 degrees for alignment with the RC bridge which also connects to the CS pin.  Once that delayed rise circuit is soldered in place, also jumper the incoming GND line over to Cable Select. You would not do this if the display was on the normal hardware SPI bus, because it would exclude other devices on the SPI bus. But since we are using a separate set of wires for the display we can get away with this trick.

The connections for the SSD1306 OLED display are the same as the ones for the Nokia 5110: (D0=clock, DC=command, D1=data in)  Note that I’m not using the 3.3v rail from the pro-mini,  and YOU MUST BE USING A 3.3V ARDUINO FOR THIS WIRING TO WORK UNLESS YOUR OLED BOARD IS 5V TOLLERANT.  Some of the breakouts  from eBay are OK at 5v, and some are not – check THIS before you buy if you are using a 5V Arduino.  (Note that  most breakouts sold by Adafruit include regs & level shifters to handle both voltages.)

When all the incoming lines have been added to the board, thread them through the housing and verify that the screen is still working. The analog A0-A5 lines on an Arduino can be re-purposed as digital I/O so I usually break them out with a 6-pin Deans Micro Plug (incl. GND & Vcc)  In this case the screens Vcc line is being re-routed to pin-powering from A0 so the pro-mini’s rail voltage is not used.  Cut away or heat-shrink over the unused Vcc pass-through on the screen side of the connector (which is still visible & exposed in the photo on the right) as you don’t want it accidentally contacting something later on inside the housing.

After the operation test, cork the pass-through hole with a bead of plumbers epoxy putty  about the size of your fingertip. Wrap that putty so that it is on both sides of the wires before smoothing it down on the housing.  (The sensor cap shown below has four housing penetrations sealed by this method)  On the top of the sensor cap, wrap a grape-sized lump of well worked putty around the wires on the back of the screen. Then carefully press the screen down into the PVC well so that the putty compresses into a support pillar that holds the screen as near level as possible. The putty hardens in about 10 minutes and then you can pour the first layer of potting epoxy. Here I’ve used Loctite HYSOL E-60NC, with a 50ml applicator & MA6.3-21s mixing nozzles. The air space for the reflective back light forced me to use clear epoxy with the 5110 LCD screens, but since these OLED’s emit their own light, you can bring that black epoxy right to the edge of the display – covering the board & any ugly soldering… 🙂  Check the epoxy over the next couple of hours and pop any bubbles that rise to the surface with a pin. (unless you have a vacuum chamber)

Always TEST before potting!

Let that initial layer of epoxy cure for 24 hours, then drill a 7/32″ hole at a safe distance away from the screens edge. Mount an RGB indicator led inside the housing and seal it in place with plumbers putty. At this point you can simply add a second layer of clear epoxy to protect the screen, but in our experiments with the Nokia LCDs, seawater caused a serious fogging problem.  So I recommend a top surface of more chemically resistant material for that kind of application. Here I’ve used a 1/4″ thick plexiglass disk (~$0.35 each on eBay)
First apply a thin layer of clear epoxy over the screens surface so that it self-levels to thickness of about 1-2mm. Then hold the edge of the acrylic disk and carefully tilt it over the epoxy film with an even contact edge that moves across the disk slowly enough to let the air escape.  Don’t make that first layer of clear epoxy too thick or it will over-top the edge when the plexi settles into place and this will form blobs on the surface. Once the acrylic is in place, you can slowly add drops of epoxy on the side to bring the level up to match the edge.  Give the clear epoxy another 24 hours to cure.

The display draws more current as you increase the number of pixels turned on.  As a point of reference: the screen output shown above (generated from the code on Github ) draws 3.4 mA.  And I haven’t seen any nasty current surges that might exceed the pin limit when the display is first powered up.

The shore hardness of the plexiglass is not much higher than cured epoxy, but the optical clarity is considerably better. The photos in this post don’t capture it well, but this mounting method really shows off the razor-sharp output you get from an OLED, and it’s readable from just about any angle.  For surface loggers a 1/8″ disk should provide sufficient protection, but for the underwater units I will use 1/4″ thickness to resist the compression at depth. I won’t be able to test those underwater versions in the real world for a few months, but if the acrylic fails I will change to  a stiffer glass overlay.

A few might wonder why I added an indicator LED when the logger now has full display capability. There are plenty of situations where brief LED pips communicate the progression of duty cycle events that are too fast and too numerous for screen output. I also use the LED as a noise generator when I want to squeeze higher resolution from the ADC through oversampling.  These days I add an NTC thermistor to every build to capture ambient temperature.

The Code Example on Github uses a cascading method to break text strings up for single character rendering with : oledWriteString–>oledWriteCharacter–>oledWriteData  and I’ve co-opted that with a new split-character method to print larger numbers to the screen in a two-pass method.  The shift-out code for those functions is basically the same as that used for the Nokia 5110. I went over all that in some detail in the earlier posts so I wont re-fry those beans here. The key thing to keep in mind is that this new screen driving script uses EEPROM.read which assumes you’ve already loaded the font definition(s) into your Arduinos internal eeprom with this helper utility.  That utility loads a ‘reduced’ font set to leave 500 bytes of space for file header data in the EEprom as well. (so the 5×7 font is caps only, and large characters are #’s only).  If you don’t need that level of memory optimization in your build, you can switch back to simply storing the screen fonts in program memory;  just transfer the PROGMEM based string->character->data functions (and the font arrays) over from in the NOKIA 5110 code example.

The housekeeping functions that take care of  initializing the screen, clearing the memory, and setting the XY position are more complex for the OLED than those for the PCD8544 Nokia because the SSD1306 controller has more operating parameters.  But the overall approach transferred easily between the two controllers and I suspect this method could be re-used with most of the larger SPI OLED screens on the market (like the SH1106) provided they stay below the pin current limit on A0.

Time to go shopping!  🙂

Addendum 2018-10-19:

A helpful commenter has informed me that it’s possible to try a similar approach with I2C screens because you can bit-bang I2C devices over any two spare wires.  Now that we’ve pulled off this isolation trick with SPI, I’ll look into bang’n as a way to shut down high current I2C sensors when they are not in use.  I’d want those off the regular I2C bus because it necessarily has a few “always on” devices that might not respond well to a lump of dead wood hanging off the same wires.

Addendum 2019-04-23:

A year long experiment in OLED burn-in. Significant dimming of the most used pixels after 4 weeks, but the screens were still operational after one year.

Addendum 2020-11-15:

Two I2C 0.96″ OLED displays make a highly useful addition to the basic three module logger

I finally got around to updating this eeprom/fonts method for use with I2C screens


12 thoughts on “Tutorial: Adding the SSD1306 OLED Screen to an Arduino Logger (without a library)

  1. Pingback: Tutorial: Adding the SSD1306 OLED screen to an Arduino logger (without a library) – gStore

    1. edmallon Post author

      Yep, my observation was that tweaking the charge pump/frequency settings brought it down to about 50% the current of the default settings. However at that point visibility in high light settings (ie full sun outdoors) is pretty bad so it’s really a question of where the screen is being viewed. But it’s good to know when you have pin current limitations that you can double the amount of pixels in use and then pull the current draw back down to increase your safety margin.

    1. edmallon Post author

      Because we are pulling all the control lines down to ground (including the A0 pin that supplies power) the screen draws zero current while the logger sleeps. The logger itself draws different amounts based on how many of the optimization steps you add to the basic 3-module logger. The basic build (from 2016) typically draws about 0.20 mA, and pin powering the RTC brings that down to about 0.1mA. Adding a power mosfet to the SD card reduces that further to about 0.02-0.03mA.

      1. Fredrich

        I’m using the RTC: RV-1805-V7. Its a 20nA on battery current consumption device.
        I’m using it in a logger design, with a 170nA consumption. Maybe its an upgrade to your current solution. If you drive your solution directly on a Li-Fe battery you do not get any power losses in the design!

        As an ref to how small 170nA are, a cr2032 lithium battery have a self discharge level of 250nA. If you don’t use all output/input signals you might get closer to the promised 20nA.

        If you switch the SD(flash) memory to FRAM(ferro ram) you would save even more power. flash are very time and power hungry. 😃

        1. edmallon Post author

          Thanks for the tip about that RV-1805! It looks like the first alternative RTC that can deliver the ±2.0 ppm spec of the $1 DS3231 boards I’ve been using thus far. Since I am powering the DS3231 via Vbat, they only draw 3uA, and it could be a while before I get the rest of the system into the nA range… Right now I’m pretty happy with 20-ish µA with always on sensors in tow.

          I’ve already been considering FRAM, since the main CPU has to stay awake during the relatively slow save & retrieval events and that uses almost as much power as saving the data directly to the SD card. (savings from eeprom buffering is only about 20% on typical runtime events). The problem with FRAM is cost vs longevity for our particular application. The I2C eeproms that come free with those cheap RTC boards have a standby current of about 2uA, while FRAM often lists standby in the 27 μA range. Given that the duty cycle for typical environmental monitoring applications needs only one reading every 15 minutes, the super fast read/write times available from FRAM don’t seem to deliver enough gain to justify doubling the cost of the entire build. However if I build a unit for a for an application that really needs that fast I/O, I’ll switch over to FRAM on the SPI bus.

          1. Fredrich Lindberg

            I turn completely off my application with the RTC -> it works like a charm! , then the standby current do not matter of the sensors. Then the high speed write to the FRAM saves al lot of power… In SLEEP mode the RTC(only running device) consumes only …nA. When I run the board and collects values for ..ms it do not mather so much what power they consumes, anyway today I do select sensors that are in the low uA range to save even more power while running. If you use the Fe-Lion(it have a flat 3V topology) battery technology you do not waste any power in power conversion devices. All USB to serial devices are not on this board to save even more power(they consume a lot of mA’s). No LED’s to. If you need a display I would consider a E-ink display -> no power at all with the information on the display preserved between you logg events.

            With an accelerometer you could program interrupt levels to vake the board up when something hapends with the currents, that could save logging events when there are no changes to the currents. A STM accelerometer LIS2DE12 have a power consumtion of 2uA whith 1Hz update rate. This accelerometer you connect to the WDI-RTC pin to wake up the board when you get an interrupt from the accelerator. I do not know how the currents are behaving in the caves if there are times of “nothing/steady state” changing, if there are, you would have “power” to save with this approach.
            Im using this device and it works like a charm 🙂

            If the currents are heavy time to time you could mount a turbine in the cave to generate power to your sensors 🙂 There are turbines to buy on ebay,
            A wide tube with this in the end should do the trick.


            Have a happy designing!

            Buy they way, Im a PADI openwater diver too, I have never dived caves, that would be very interesting, I have no issues with smal dark places or high wide spaces….
            I love the water, when I was a kid I dreamed that I could bread in water… I have always free dived water, even before I could swim properly, you only have to dip down your head to float 🙂

          2. edmallon Post author

            Those are all super ideas. I’ve been thinking about having the RTC alarm control a power mosfet for a while now, but haven’t built one yet due questions about re-initializing everything so many times. Some of the sensors I’ve been looking at have very long init sequences when you boot them (esp those with heating elements & self calibration). I think Grier Andersen uses the alarm/power method on his tindie boards which even let you do logging with power hungry UNOs.

            The other thing is that our education focused project has multiple goals (in addition to operating lifespan.) So we are looking for the venn diagram overlap between low cost, lifespan, and ease of assembly for beginners. Like Fram, E-paper is very tempting in terms of power optimization, but it’s not the kind thing you can afford to buy 20 of without blowing your classroom materials budget. Another thing that we strive for is having the generalized code so sensors can be swapped with minimal changes to the compile. This again usually ends up sacrificing power performance.

            The accelerometer idea is interesting, but we already have to do significant amounts of sample smoothing/filtering to deal with vortex induced wobbles during high flow conditions (most of our systems are coastal so see a huge daily range). I suspect those eddys would play havoc with any change-triggered interrupt unless the fifo buffer applied some pretty heavy filtering internally. We are using the change-interrupt idea in our Drip Sensors, but those are simple event counters rather than tilt sensitive devices.

            I am going to order a couple of the turbines just to play with them. The might even make better water flow sensors than the Hall effect sensors I’ve been using so far.

          3. Fredrich

            You have 3 versions of the turbines, 2regulated 5@12V and one unregulated, you get 0-80v depending on the flow. Im going to try out them all. Yes you are right I starting in the low power corner and are designing my own customized pcb’s, to make them small as possible. Right now my boards are 25x25mm and I get 160 pcb’s for 17USD. Then I reflow them at home, smallest part like the accelerator have a 0.5mm pith and works great to silder. It actually fit’s behind your Oled display you are using in your project.

            Have a nice day.

    1. edmallon Post author

      Thanks for that info! It wouldn’t be the first time I came up with a solution by noodling around, that re-traces work that someone else has already sorted. I will check out that zero ram text-only version to see how well it integrates with my base code. Bit banging is on the longer to-do list…

Comments are closed.