I2C Continuum

Updated August 07. 2016: This article which started off discussing the “new addition” of i2c to the home control software, is now NOT ONLY about an I2c  2/4 line LCD display facility recently added to the ESP8266 boards – but there’s a PARALLEL version as well, making use of GPIO 4,5,12,13,15 and 16. And NOW – I’m adding a pretty crude interface for the Seeeed OLED displays with other OLEDs to follow.

Comments below about format apply equally to the I2c and parallel versions – for the latter, simply use device 255. Note when you do that you lose GPIO13 as an indicator automatically until next power-up.

Success with I2c – once I realised there was something up at the Arduino end which I could work around, my ESP8266 I2c endeavours have been coming on in leaps and bounds!

And here is my test rig – or one of them – I have one for I2c display and another for parallel display  - the latter is just as messy and has more wires:


So what you’re looking at there from top to bottom – is a prototyping ESP8266 board (the author will recognise it) just because I had it handy – ESP-12 based, running my rapidly developing software (which now has loads of spare RAM thanks to the SDK 2.0). I put a simple set of i2c commands in there then promptly realised that a series of commands sent by MQTT would not be a lot of use. I then proceeded to waste the day, having already talked successfully to an 8-bit port expander, trying to get one of those back-of-an-LCD I2C boards to work – as it was based on the same chip.

Parallel versionAfter several hours of considering taking up brick-laying I realised the damned thing was bust – and went off in search of my  cheap Chinese port expander – that’s the red thing you see in the middle. The only difference is this does not have a transistor to power an LCD backlight so I just hardwired that. At the bottom – the original test 4-line LCD. Over on the right, an irrelevant Arduino-type board which is also talking I2c successfully.

Having decided I liked the idea of plugging one of the cheap LCDs onto the odd home control board for information purposes, I set about doing something I’ve never done before – reading the Hitachi data sheet. These things are quite cute once you get over starting in 8-bit mode then switching to 4-bit to save on wires etc… (so in total you only need 6 signals to talk to the LCD) and after some timing experimenting (clear screen command takes a while – over 1.5ms) as you can see I finally have a working LCD – and by the look of it, rock-solid reliable.

So what is driving that display (which is updating every second virtually instantly)…



and inside that inject?

Topic: freddy/toesp

Payload: {hitachi:39,"$1MQTT test$2$i$3Time $t$4Date $d"}

That’s all. So assuming you’re familiar with MQTT (if not – look at other articled in here) the destination is the little board “freddy” – and the new command I’ve just added – “hitachi” talks to an i2c board (the expander) device #39 and sends out that string.

Rather than have a boatload of commands which would be unwieldy in the home control setup – or special characters which might cause issues – I used the dollar as an escape character. Here’s what I’ve implemented up to now

  1. $$ -  well that shows a dollar on the screen !!
  2. $s -  setup – sets up a virgin LCD and clears the screen
  3. $c  - clear the screen
  4. $1 – set the cursor to line 1 -  ($2 $3 $4)
  5. $t  -  fire out the time
  6. $d – fire out the date
  7. $i  -  fire out the current ip address

I’ll probably settle on a 2-liner as they are sub-£2 from China – indeed for 20-off just over £1 – and at £1.34 for the port expander – around £3 to add a nice little display to some of the ESP8266 boards – can’t be bad. indeed, using one of the port expanders for an ultra-low-cost i2c keypad isn’t a bad idea. 45p for the keyboard, £1.34 for the expander… Sub-£2 keypad.

Clearly one way to wipe a line would be “$1               $1” which is probably fast enough for most purposes. I’ll likely think of some more commands.

A reminder that the right hand side of this blog contains links to the Home Control 2016 project, ROMs etc.

And now I have this big decision.. whether to spend £1.34 on a PCF-base port expander… which I need to poll regularly if attaching a keyboard – and no spare pins for a beeper – OR to spend £1.68 on an Arduino Nano which can be turned into an I2c peripheral (granted I might need 2 pull-up resistors) with keyboard buffering and a beep facility…..   OR…. go the whole hog and use another ESP8266 to make a completely wireless keypad.

OLED from SeeedAnd on that latter note – if you abandoned serial I/O which would net one more output – that gives up to 10 control pins – enough for a keyboard and beeper and light. Hmm…£1.43 for an ESP12, 17 pence for a 0.1” adaptor board.   Fully buffered keypad…. Big decision…

And finally – device 255 is reserved for  a PARALLEL version of this using 6 GPIO pins – 3,4,12,13,15 and 16 – operation is identical. All in the code – including the OTA ROMS. Documented in the WORD manual.

Update: On the right you see a Seeed OLED – I starting with this library, code intended for Arduino, I’ve heavily modified it to run in the normal ESPRESSIF SDK environment – but I have to say – the original Arduino version was SLOW, so VERY slow and this is still a little slow – especially the screen clear – I’ve pulled that into one function with an inner loop of I2c bytes, way faster than the original  but even THEN it is hardly nippy – but the important thing is that “Hello World” works – so soon I’ll add the above commands and then figure out a way to speed it up. Right now I can manage {seeed:”Hello world”} or similar. Current software and ROMs are on the web.


59 thoughts on “I2C Continuum

    1. That is the VERY keyboard I was thinking of - adhesive backed as well. Not tried it yet but it seems to me I should be able to make a scanning keyboard without any extra components as a simple background job running, say every 25ms without creating any significant overhead. I love Aliexpress - only downside of course is it takes weeks to get something and I want to have a go NOW. I could have sworn I had one of those things lying around.

  1. Hi - yes that's the keypad. I think I may make that a separate thing altogether... with 8 pins on the i2c expander it would be easy to add the keyboard but then the ESP has to poll etc, it could be that using a NANO instead of the expander, I could do a very swish keypad with flashing LED, beeper and key buffering....

    I don't think you put a link to code anywhere on your video - were you meaning for people to make use of what you'd done?

      1. Yup - that looks good - thanks for that - I'm sure people in here will be interested... I think I may go for the £1.50 nano solution to handle both keypad and display at some point.

  2. The parallel version of this incidentally - I had some timing issues at first as it is a LOT faster than the i2c version - sorted - I put some delays in to match manufacturer's spec and I've had a 4-line board running flat out on my desk all afternoon - perfect. Ok, limited use but considering the 2-liner blue ones are under £2 from China - and no other hardware is needed other than a resistor... yes, I could see this ending up on some boards. If nothing else, each of the ESP boards in my setup knows the right time, date and dusk and dawn as well as it's own status.

    1. Hi there

      Well I was given the breakout boards as samples - indeed we're looking to making our own as I'd like at least 2 pins for each output - more for the i2c pins, and lots of ground, 3v3 and 5v... but - that Wemos certainly is a nice little board for the price - thanks for bringing it to everyone's attention.

      The keypad- not what I'm after but others may want to look at it as it is also neat - and cheap but as you say could need reprogramming. The final button pad looks nice also - I'm after a standard 0-9 job - one on the way. I'm sure all of this will trigger people off thinking.

      Right now I'm sitting here re-writing the serial routines. One of the first things I put in - and I know better ways now (rotary buffer, in and out pointers).

  3. Pete,
    Have you checked graphical OLED screens (SSD1306) as an option already? I suggest that the price is more or less the same (3$) as a 4-line-LCD, it is i2c already and the optical quality and possibilities are totally different. All the routines are available to manage it.

    I use it with a rotary encoder as an human I/O for ESP thermostat program.

    1. Hi there George - well I was just looking at some code for this -originally meant for the Arduino I think.... just not familiar enough to know how many variations there are...

      I have one of these

      The library for that looks easy enough to convert .

      But it looks like that uses a different chip and so presumably a different set of commands - want to point me to the model you're thinking of?

      Is it this


      Mind you - replicating the adafruit library on my code sounds like a lot of hard work for little return for just doing text and the odd line - I'd have to find a simpler library I could modify...

      1. The comprehensive description of usage:

        Only an example: the cheapest one from AliExpress:

        I converted the Adafruit libraries. You are right that it is a bit more complicated to use it for simple text but you can emphasize the more important data with larger fonts. You can present graphics if you want. It is nice.
        All the neccessary sw. components are available if you want it to try.

        1. Oh I beat that - marginally more expensive but free post - I think I just paid £3 all in. Yes, I plan to give it a shot. I think I may also have come across some winner SPI code - need something trivial to test that on. Will be a little while before the displays turn up.

        1. Yes indeed Ozay - that looks good - except I think toward the end the author mentions some problems...but a good starting point - once I can clear screen and set and clear a point - the rest is straightforward. Right - EBAY next.

  4. I see you tried one of those LCM1602 IIC boards with your LCD, well I hooked up my working one and .. nothing.
    I then remembered the address code with A0-A2 linked was 20h .. entered {hitachi:32,"$2$i$3Time $t$4Date $d"} and all working !, so maybe worth trying that address on your one again.
    On another matter Nathan Chantrell referred to http://www.esp8266.com/viewtopic.php?p=4311#p4311 as the source of the library he used for an 8266 OLED project .

    1. Hi there - in my case it was not the address - the bottom lines worked no problem - it was the board itself. I could easily control the bottom 4 lines - so I figured either that board was bust or the LCD. I carefully desoldered the little board and used a standard ic2 board with the same LCD... sure enough - worked a treat. I'll order another as no doubt about it, it is nice to have a board properly soldered to the back of the LCD. There's another way from what I recall of my Arduino experimenting days - a shift register - I seem to recall that was down to using 2 wires as well.

      1. The one I use from aliexpress is a plugin version so no soldering needed, but there are several different base addresses in use and easily overlooked.
        I have created a simple function to pass incoming payload out to the lcd:

        var newMsg = msg.payload;
        msg.payload = '{hitachi:32,"$1'+newMsg +'"}';
        return msg;

        I have also experimented with your $ coding and added $o to add the degree symbol (0xDF) and $x to provide line clear (leaving cursor at pos 16), so $1$x$1 is needed to get to start of line 1.

        code added:

        case 'x' : tb=tmb;
        os_sprintf(tmb," ");
        while (*tb) hitachiHiLo(*tb++,1); continue;
        case 'o' : tb=tmb;
        os_sprintf(tmb,"%c", 0xDF);
        while (*tb) hitachiHiLo(*tb++,1); continue;

        so using topic 'fromesp/auto_temperature' I now have my function node as:

        var newMsg = msg.payload;
        msg.payload = '{hitachi:32,"$1$x$1'+newMsg + '$oc'+ '"}';
        return msg;

        note: 32 is my address for IIc board

        1. Hi there Alan... slightly simplified and with more commands - get the latest software -and thanks for triggering me to add the percentage... I've also catered for 20 character displays, added more - updated manual.

          1. Thanks Peter, I'm out and about at the moment, so will catch up with it later, will also provide that link.

          2. All new hitachi commands working fine Peter.
            I thought it would be useful to add a command to turn on/off the LCD backlight, and with node-red arrange to have it flash, to perhaps indicate a warning or update of information.
            $F = on
            $f = off

            here's the code added/replaced in 1.5.53 :

            64 #define LCD_BL_PIN 0b1000
            275 uint8_t lcdBacklightStatus=0;

            724 flag &= 1;
            725 if (lcdBacklightStatus){
            726 flag |= LCD_BL_PIN;
            727 }

            734 i2c_master_writeByte(data|4|flag);
            734 // i2c_master_writeByte(data|4|(flag&1));
            735 i2c_master_checkAck();
            736 i2c_master_writeByte(data|flag);
            736 // i2c_master_writeByte(data|(flag&1));
            737 i2c_master_checkAck();

            1815 case 'f' : lcdBacklightStatus = 0; // LCD backlight off
            1816 i2c_master_writeByte(0);
            1817 i2c_master_checkAck();
            1818 continue;
            1819 case 'F' : lcdBacklightStatus = 1; // LCD backlight on
            1820 i2c_master_writeByte(LCD_BL_PIN);
            1821 i2c_master_checkAck();
            1822 continue;

            Regarding the i2c adapter, aliexpress now shows that page as error404, so no longer available from that supplier, but the others around to 80p mark looked identical.

  5. There is a I2C LCD library for ESP8266 at esp8266.nu, in the stable version at the bottom of the page. It is for the Arduino ide and works well. Biggest problem I had is the LCD nad board are 5v and did not have a level shifter included. After I added a level shifter it works great. Only problem is the button to turn it on for 15 seconds turns the light on for 15 seconds, It probably works fine on the Oled, but those are small for me to read. The Arduino ide for ESP8266 has come a long way in the 8 months I have used it.

    1. Rather too much conversion on the Arduino versions. I'll come up with a fresh library - however - a short break - I've ordered the LCD displays, a 16 bit A/D convertor and a 16 channel 12-bit PWM unit - all dirt cheap from China - so in 2-4 weeks I will attempt to add all of these as instructions to my code. I think I've also ordered one of those combined temperature/humidity/pressure units and will have a go at that too.

  6. Alan I've implemented your changes - only marginally changed to simplify a tiny amount. It's in the code etc but I don't have one of those back boards to test - mine was bust and I'm using a simple i2c port expander. Can you give it a go and confirm it is working and I'll add those two commands to the manual... good thinking.


    1. I can confirm that is all working fine and backlight is off by default - thanks for implimenting it so quickly, from the timestamp I can see you were an earlier riser !
      I also have that OLED display, so will try later today and advise.

      1. Thanks for that Allan - just a dummy routine that should put some text up - once I know it is working - I will add code from there....

        Up early thanks to a Scorpion who decided to have a go at me. He's dead - I'm fine.

        1. I fired up the OLED and ... nothing, but serial showing no i2c device seen.
          Mine like most have 2 address options showing as 0x78 & 0x7a - 0x78 normally being the active one, which equates to 0x3c - so I changed oled_addr to 0x3c in ssd1306.c and the screen came alive ... with random dots.
          Then to prove if it was seeing the text ( scrambled), I removed it, but the random pattern was EXACTLY the same.
          Thats as far as it's got at present, will try and look a bit deeper later.

          1. Funny enough - the newer chip has address 3c. Are you SURE that's an SSD1306 you have there? Mind you - I got nothing - zilch from my little SEEED board so with dots you're one up on me anyway.

  7. Under the previous related blog entry called I2c conundrums (I thought I was writing in here) - got a library now for the AliExpress type OLED displays based on SSD1306 - the code is all updated and command {oled:0} is in place to test - don't have the display yet to test it with but if anyone is running this stuff and has a display and wants to test - it should come up with 3 lines of test.. if it works I'll take the dummy out and put a proper string parameter in there.

    1. Interested. But I don't use ESP8266 SDK. I use these Aliexpress SSD1306 OLEDs with u8glib on Arduino and happy with the library (the Adafruit library OTOH bloated the code so much). Interested in a barebones C library so that I can use the OLED on ESP8266 or STM32 for my next project.

      1. I have a lib for C but it could be a week or so before my Aliexpress boards turn up - at which point I'll be working to get the board running and speed will be a priority - as the Seeed boards are slow.

  8. I've used 50 or so ssd1306 with the unofficial SDK for my iotbox project. They work really well. I'm in the process of switching over to the arduino ide and Squix’s library for my new projects

  9. Peter, I use those little LCD serial back packs and they work a treat with my 4*20 LCD modules, I had to use a logic level converter as I couldn't get the LCD module to run at 3.3 volt. I thought I would share a link about the various back pack pin connections in case it is helpful. http://arduino-info.wikispaces.com/LCD-Blue-I2C. I found some useful information there. some of my back packs are address 0x20 and some 0x27.
    As usual thanks for all your efforts, this is a really good thread.

  10. Peter - Just tried to compile v 1.5.57 and the compiler spat out an error, saying it was trying to write to my cd ! .. the culprit was in the makefile:
    I have # out the 2 offending lines and all ok again :-

    @mkdir -p $@

    # @cp -f c:/Espressif/esp8266_sdk/bin/blank.bin D:\Git\esp-mqtt-dev\firmware
    # @cp -f c:/Espressif/esp8266_sdk/bin/esp_init_data_default.bin D:\Git\esp-mqtt-dev\firmware

    1. Hi

      Well that Makefile is written for my own use and I am NOT an expert at Makefiles (or I would have it print out memory information in English and in better detail).

      I just added those so that the bin files would be included with the firmware for flashing purposes. I've no idea how to set that to "only if this is pete's computer".

      1. I've just played around and this should work for your setup.

        if test -d D:\Git\esp-mqtt-dev\firmware; \
        then cp -f c:/Espressif/esp8266_sdk/bin/blank.bin D:\Git\esp-mqtt-dev\firmware; \
        cp -f c:/Espressif/esp8266_sdk/bin/esp_init_data_default.bin D:\Git\esp-mqtt-dev\firmware;\
        I substituted directories for my setup and it saved the files correctly.

        1. Well, it didn't crash and it compiled ok - but the files did not appear.

          @mkdir -p $@

          if test -d D:\Git\esp-mqtt-dev\firmware; \
          then cp -f c:/Espressif/esp8266_sdk/bin/blank.bin D:\Git\esp-mqtt-dev\firmware; \
          cp -f c:/Espressif/esp8266_sdk/bin/esp_init_data_default.bin D:\Git\esp-mqtt-dev\firmware; \

          # @cp -f c:/Espressif/esp8266_sdk/bin/blank.bin D:\Git\esp-mqtt-dev\firmware
          # @cp -f c:/Espressif/esp8266_sdk/bin/esp_init_data_default.bin D:\Git\esp-mqtt-dev\firmware

          1. Strange... I have just change mine to the following again and it saved both files to my e:temp directory ok

            if test -d e:\temp; \
            then cp -f c:/Espressif/esp8266_sdk/bin/blank.bin e:\temp; \
            cp -f c:/Espressif/esp8266_sdk/bin/esp_init_data_default.bin e:\temp; \ fi

      1. This should work now.. all forward slashes for the directories

        if test -d d:/Git/esp-mqtt-dev/firmware; \
        then cp -f c:/Espressif/esp8266_sdk/bin/blank.bin d:/Git/esp-mqtt-dev/firmware; \
        cp -f c:/Espressif/esp8266_sdk/bin/esp_init_data_default.bin d:/Git/esp-mqtt- dev/firmware; \

          1. That's good news ..got there in the end !

            I've been playing around with the LCD commands again - initially because I was using a 20x4 display labelled QY-2004A and the line addressing is different to the 16x4 display and using $3 $4 did not take the cursor to the beginning of the lines.
            So the commands in use now are:
            $s - setup sets up virgin screen and clears screen for 16x4 display
            $$ - setup sets up virgin screen and clears screen fro 20x4 display

            I also decided to split each line, as some of my data to display is less than 8 or 10 characters, making it easier to write or clear either half of a line.
            $1 as before
            $2 as before
            $3 as before
            $4 as before
            $5 cursor to pos 9 or 11 line1 (
            $6 cursor to pos 9 or 11 line2 (
            $7 cursor to pos 9 or 11 line3 ( auto selected when using $s or $S)
            $8 cursor to pos 9 or 11 line4 (
            $X clears line of 16 or 20 char displays
            $x clears either 8 or 10 char from a line ( auto selected when using $s or $S)
            $1-$4 = line 1-4 , cursor posn 1
            $5-$8 = line 1-4 , cursor posn 9 or 11

            Here's the code:

            uint8_t hitachi_line_3, hitachi_line_4;
            uint8_t hitachi_offset;

            case 'x' : if (hitachi_offset ==8){
            hitachiHiLoS(" ",1); continue; // 8 spaces
            else {
            hitachiHiLoS(" ",1); //10 spaces

            case 'X' : hitachiHiLoS(" ",1); continue; //existing code

            case 'S' : hitachi_line_3=148; //20x4 LCD
            hitachi_line_4=212; //20x4 LCD
            goto initLCD;
            case 's' :
            hitachi_line_3=144; // 16x4 LCD
            hitachi_line_4=208; // 16x4 LCD
            hitachiByte(0x30,0); os_delay_us(38); // existing code
            hitachiByte(0x30,0); os_delay_us(38); // existing code
            hitachiByte(0x30,0); os_delay_us(38); // existing code
            hitachiByte(0x20,0); os_delay_us(38); // existing code
            hitachiHiLo(8,0); os_delay_us(38); / / existing code
            hitachiHiLo(0x28,0);os_delay_us(38); // existing code
            hitachiHiLo(1,0); os_delay_us(2000); // existing code
            hitachiHiLo(0x0c,0); os_delay_us(38); // existing code
            continue; // existing code

            case '1' : hitachiHiLo(128,0); os_delay_us(38); continue; //existing code
            case '2' : hitachiHiLo(192,0); os_delay_us(38); continue; //existing code
            case '3' : hitachiHiLo(hitachi_line_3,0); os_delay_us(38); continue;
            case '4' : hitachiHiLo(hitachi_line_4,0); os_delay_us(38); continue;
            case '5' : hitachiHiLo(128 + hitachi_offset,0); os_delay_us(38); continue;
            case '6' : hitachiHiLo(192 + hitachi_offset,0); os_delay_us(38); continue;
            case '7' : hitachiHiLo(hitachi_line_3 + hitachi_offset,0); os_delay_us(38); continue;
            case '8' : hitachiHiLo(hitachi_line_4 + hitachi_offset,0); os_delay_us(38); continue;

    1. I know you're out for the day, just noticed a typo in the manual -
      $x - clear a line of 16 or 20 characters (depending on setup)
      should read
      $x - clear a line of 8 or 10 characters (depending on setup)
      Thanks, enjoy your day.

  11. Tested both formats and working correctly, but the $x & $X are transposed in the manual.
    Thanks for adding these commands and code tweaking - I like the extra debug info you have added, makes life easier when experimenting. Just a thought, but would it be possible to store the setup commands for LCD/OLED in flash, so it survives a reboot - as the display would normally be unchanged.
    Last thing ... I noticed the timestamps on this site are gmt +2 , it that intentional?

  12. Hi Peter,

    My displays arrived today, some 2 line and 4 line with i2c boards already attached to the back. with great enthusiasm i connected them up but without success. The message i receive from the esp12e is Badi2c.

    I have checked the address of the boards using one of the many i2c scanners available; I then though perhaps it was because i didn't have a logic level converter, so i have put one in place for the data lines as the display is 5v. But i still have the same issue.

    any ideas or suggestions and can you confirm the two pin connection for the i2c from the esp?

    Many thanks,


        1. I have three of these apps on the phone - testing right now actually - I made a node-red timed event to send a sequential message every 15 seconds - NONE of these apps continue to receive messages after turning the phone power off and then on - I have written to the authors and two have already come back to me to offer to update the apps - I will let people know when fully tested.... I just don't think the authors thought about this - but MQTT will make for a great messenger but it has to work regardless once set up.

Leave a Reply

Your email address will not be published. Required fields are marked *