Adafruit library/backpack compatibility

Issue #29 new
Tomasz Jarzynka
created an issue

Can this library be made compatible with the adafruid i2c/spi backpack and their LiquidCrystal library? The pinouts are different than the ones used in the PCF8574-supported board, plus the R/W pin is not connected in the adafruit backpack. I've tried modifying the pin numbers passed to the constructor but that doesn't seem to be enough (the display controller is never initialized).

Comments (28)

  1. Francisco Malpartida repo owner

    The problem is that the Adafruit backpack uses a different I2C expansion module to the pcf8574 ASIC. This library will not work out of the box with it.

    Stay tuned for future library updates. In the mean time use the library provided by Adafruit.

  2. Tomasz Jarzynka reporter

    The MCP23008 should be more or less compatible with the PCF8574, I believe it's in fact a more powerful chip. But I guess the wiring of the adafruit backpack is somewhat different, especially the use of an additional shift register, is that correct?

  3. Francisco Malpartida repo owner

    There are several differences: 1. The way the ASIC is controlled, i.e. the commands it interprets. 2. The wiring to the LCD my be different to other LCDs but this is catered for in the library by calling the adequate constructors.

    There are differences in that "more or less" which means they are not the same nor compatible.

  4. Tomasz Jarzynka reporter

    I see, that's a shame since your library is more elegant (with an abstracd LCD class for one thing) and some other user-contributed code relies on it (like MENWIZ). Unfortunately I don't feel competent enough in electronics to port support for the backpack myself, maybe one day. PS. The R/!W pin of the display, is it used/required anywhere in the library? A quick grep found nothing, but maybe I looked improperly.

  5. Francisco Malpartida repo owner

    The R/W pin is not used, the driver relies on the fact that it is write only, therefore, setting the R/W to GND will work just fine. The driver doesn't read back the status from the LCD (this is one speed twick).

    As for the Adafruit driver support this may come soon. I think that Bill (contributor to this library) is getting one to play with and my do a porting to it.

    Thank you for your comments.

    We will keep this ticket open for the future.

  6. Bill Perry

    Yep, It is still on my "to list" so I'll definitely add support for the AdaFruit board. It is quite simple - shouldn't take more than an hour or two at the most. I don't have board yet. I need to get one ordered.

  7. Bill Perry

    Still working on the I2c portion, but here is the constructor for the adafruit board in serial mode (which adafruit mistakenly calls "SPI" mode). Solder the blob on the board for "spi" mode then use SR3 mode.

    LiquidCrystal_SR3W lcd (2, 3, 4, 2, 0, 1, 6, 5, 4, 3, 7, POSITIVE); // Adafruit I2C/SPI in "spi" mode

  8. Bill Perry

    I've actually got this up and working. Haven't yet decided on the exact constructors to use. I also can autodetect PCF8574 vs MCP23008 chips so I'm also considering doing auto detection to make it transparent. The new device layer code also handles the ic2 bus differently which nearly doubles the throughput for both i2c chips.

  9. Scott Rising

    If anyone needs testers for the MCP23008 code, let me know- I have serial LCDs with both PCF8574 and MCP23008 ASICs on them. I'd love to test out the autodetect functionality.

  10. Tomek Szafrański

    First of all, let me say that this version of LiquidCrystal library is really a great one! Not only I was able to replace all of LCD libraries I had because it supports 4bits, shift registers, I2C and everything is configurable, but what’s more, this is the only library that was able to work with my just bought I2C LCD board: see on Configuration is (please add to the list :-)

    LiquidCrystal_I2C lcd(0x20, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE) 

    OK, I was able to replace almost all of libraries… the only one I had to leave was Adafruit_LiquidCrystal because this board runs different chip. Like all Adafruit products, this is brilliant one (apart from being beautiful :-) it has both shift register (serial) and I2C modes. As mentioned in one of posts above - configuration for serial mode is:

    LiquidCrystal_SR3W lcd(dat,clk,str, 2, 0, 1, 6, 5, 4, 3, 7, POSITIVE)

    The configuration for I2C mode is:

    LiquidCrystal_I2C lcd(0x20, 2, 0, 1, 3, 4, 5, 6, 7, POSITIVE)

    Of course is doesn’t work due to different chip. MCP23008 is more complex then PCF8574 but from our point of view the main difference is that it has more internal registers and you have to specify which register to read/write before you actually read/write it.

    LiquidCrystal_I2C class uses only three methods from I2CIO class: begin(), portMode() and write(). In portMode() after setting _dirMask you have to add:

    #if (ARDUINO < 100)
          Wire.write(0x00); // MCP23008 IODIR register address

    In write() before Wire.write() data you should add:

    Wire.write(0x09); // MCP23008 GPIO register address

    The last one – begin() you don’t really have to change… it reads a byte to check whether I2C works. To be correct, before we Wire.requestFrom() we should Wire.write() register number we want to read. If we do not do this, what is read is random (or probably the first one) but this is check only, we don’t really care…

    That’s all. My LCD works with your LiquidCrystal and Adafruit board!

    Of course to make it right we should rewrite whole I2CIO class. It seems quite easy when you have a look at Adafruit_MCP23008 class, but I’m not sure how are you going to switch between those two versions. One of the posts mentioned automatic detection – that would be nice… Did you plan to subclass I2CIO? or to dynamically create it in LiquidCrystal_I2C?

  11. Bill Perry

    I actually have a new interface class that is up and running. It allows you to specify which i/o expander chip you are using in the constructor. It doesn't use I2CIO but is about twice as fast as it combines multiple i/o operations in a single i2c bus access. It also runs the MCP23008 in BYTE mode which makes it work more like a PCF8574 and avoids some extra control byte traffic which makes it faster. It also allows using canned entries in the constructor. Here are some example constructors for the IIC class:

     LiquidCrystal_IIC lcd({i2c_address}, IIC_MCP23008,2,0,1,3,4,5,6,7,POSITIVE);
     LiquidCrystal_IIC lcd({i2c_address}, IIC_UNKNOWN,2,0,1,3,4,5,6,7,POSITIVE); // auto figure out chip
     LiquidCrystal_IIC lcd({i2c_address}, 2,0,1,3,4,5,6,7,POSITIVE); // auto figure out chip
     LiquidCrystal_IIC lcd({i2c_address}, IIC_BOARD_ADAFRUIT292);
      LiquidCrystal_IIC lcd({i2c_address}, IIC_BOARD_WIDEHK);
      LiquidCrystal_IIC lcd({i2c_address}, IIC_MCP23008,7,5,4,0,1,2,3,6,POSITIVE);
      LiquidCrystal_IIC lcd({i2c_address}, 7,5,4,0,1,2,3,6,POSITIVE);
     LiquidCrystal_IIC lcd({i2c_address}, IIC_BOARD_EXTRAIO); // ElectroFun ExtraIO
     LiquidCrystal_IIC lcd({i2c_address}, IIC_PCF8574, 6,5,4,0,1,2,3); // ElectroFun ExtraIO
     LiquidCrystal_IIC lcd({i2c_address}, 6,5,4,0,1,2,3); // ElectroFun ExtraIO (auto detect PCF8574)
     LiquidCrystal_IIC lcd({i2c_address}, IIC_BOARD_YWROBOT);

    etc... You can specify chip types or let the library automatically figure it out. The constructor without the chip type will auto figure it out. The easiest for users is probably the canned entries. The library can also automatically locate the device if you use IIC_ADDR_UNKNOWN but that only works if there aren't other devices on the bus.

    I also have a class that works on LCDs with native i2c interfaces that don't use backpacks. It is yet another mode of operation.

    I need to finalize some things before I'm ready to generate a pull request. I have MANY updates for the library not just this class. All the other updates are fairly minor and are mostly comment documentation. The main thing is that I need to work out with fm how this fits into the library with respect to the i2c class. My feeling at this point is to avoid the "I2C" name as there are simply too many others with that name out there, which is why I chose IIC instead.

    --- bill

  12. Tomek Szafrański

    Yeah, LiquidCrystal_TWI ia already taken :-)

    The reason for my post (and workaround described there) was that I was afraid that glorious idea of new version of library supporting MCP died somewhere in the process – if not – I’m extremely happy and looking forward to new version of the library!

    As a intermediate user I would expect to have options to construct LiquidCrystal_IIC object in three ways - exactly as in second paragraph of your code above.

    I have four different I2C LCD interface boards, and ordered 3 more – and I think that is 99% of what is available. I think we could make LiquidCrystal_IIC to work with all of them "right out of the box" (using canned versions).

    Looking forward to new version coming soon!

  13. Bill Perry

    I've been tracking all the i2c lcd threads on the Arduino forum for quite some time. (Part of the reason I wrote the guesser sketch) I look at all the boards and and libraries that people use. Nearly all issues are user error from lack from lack of reading and following instructions or paying attention to details. (unfortunately, Arduino actually encourages not reading about or understanding the h/w and using a "just try something" mindset) However, from all the threads that I've seen and boards that I've looked at, I've seen 11 different identifiable boards. 8 use PCF8574 and 3 MCP23008. Of that, there are only 4 different wirings for the PCF8574 that vendors are using and 3 different wirings for MCP23008. 5 of the 8 PCF8574 based boards all use the same wiring. And 1 of the 8 uses the same wiring as the 5 but the backlight is the reverse polarity.

    The 3 MCP23008 boards are all different.

    I have canned entries for all of them, including separate entries for the 5 that are all the same wiring to try to make it easy to select the right one.

    That said, I'm in the process of working on a full auto detect that can figure out the wiring. The guesser sketch just guesses, "auto-detect" actually figures it out. For the PCF8574, I'm down to one difficult combination. My goal is to be able to probe the bus to find it, identify the chip, then figure out the wiring. So far, I can do that for 7 of 8 PCF8574 backpacks. (Currently it can't tell between the ElectroFun board and the mjkdz boards)

    But I think I should be able to get there with some additional code.

    I'm not sure I want to but the "auto-detect" stuff into the library vs make it a separate diagnostic sketch. My fear is that something goes wrong and creates additional issues.

    --- bill

  14. Tomek Szafrański

    Now I can see where the problem is :-) You are trying to produce a perfect library :-))

    Make auto-detect a separate sketch in examples sections. And yes you're right - auto-detect feature will eventually fail ("create additional issues"). China/HK will manage to come out with something you haven't forseen. But the value of this library isn't its auto-detect mechanism, its value is that it supports 11 boards (wow!!! my dreams come true!) and next year everyone possesing LCD will be using it. Majority of users will follow link to this site, search the internet, compare photos or read item description - to find out which canned entry you prepared to use. They don't really need autodetection.

  15. Bill Perry

    There is more to it. Like I said I wasn't really sure I wanted to put auto-detect in the actual library. I was leaning against it since I do feel eventually there will be issues. One issue is the Arduino 1.5.x releases. The Arduino team keeps changing things and unfortunately, the way they are changing things affects this library. This is because this library is "multi-headed" and builds all the modules regardless of whether they end up being used. The issue is include paths, particularly when the user installs the library in his personal libraries directory vs in the system/IDE libraries directory. So things like the path to the Wire library are not guaranteed to be on the include path, like when the user uses SR or 4bit and doesn't include Wire.h I've figured out how to handle it and it is all working now. At least until the Arduino team changes the way the library paths work again. I really wish that they would put the Wire library in the core library. That would solve so many issues for many other libraries out there as well.

  16. Tomek Szafrański

    I understand this. After a year with Arduino I'm still being suprised how libraries/includes behave. And when I get it, 2.0 will come out and change it. Do you know why did I post this code above (quite naive after what we have discussed so far)? Because the world doesn't know how to handle PCF/MCP LCD stuff in one code. No one has examined 11 boards. You have and you know how to do this. Make it simple. Document it well. Release it. Thank you. Tomek out.

  17. Francisco Malpartida repo owner

    Hi folks,

    I've been tied up with other commitments and silently reading these treads.

    Bill, I think that you contribution is great and the new I2C library class where you can specify specific chips is very good and would be a nice enhancement to the library. However, I am very reluctant on introducing the auto detection mechanism as a library feature. At some point it will fail and we will have to revisit and hence include a fair bit of maintenance.

    As far as including the extension I think that the best approach is to maintain the current interface and extend it. My take on this one is to have an abstract class where all the device specific inherit from. I haven´t given it a careful thought though.

    I also don´t like very much the bit masks that I added a while back and I am very much in favor of removing them.

    Bill, thank you very much for supporting the library after all this time.

    Tomek, thank you very much for your contribution and interest in the library.

  18. Bill Perry

    I'm not sure what you mean by: "maintain the current interface and extend it". That seems to be in conflict with: "I also don´t like very much the bit masks that I added a while back and I am very much in favor of removing them."

    There also is so much confusion over the LiquidCrystal_I2C class that I think it would be helpful to pick a new name.

    The auto chip detection was mainly an attempt to sanity check the user, to ensure that they didn't attempt to specify a chip that they didn't have. I was trying to prevent a user from blowing up their backpack by picking the wrong type and bits since the MCP23008 can drive pins while the PCF8574 can't. (PCF8574 can only sink current, it won't source it) It is unfortunate that many of backpack makers hook up the R/W line. This can create a potential bus collision. This hurts much worse on a MCP23008 than a PCF8574 since the MCP23008 can actually drive pins.

    In reality the auto chip detect isn't as big of potential issue as you might think. The constructors I created allow full specification of all the parameters, as well as auto chip detection. However, in practice, I think the users will be using one of the canned backpack types which fully specifies everything. So from their perspective they won't be using the auto-detect. They will simply specify an address and a backpack type which is clean and simple.

    That said, I wouldn't be opposed to removing the auto detect capability or perhaps limiting it to be a private function that is simply an internal sanity check of the users parameters.

    A bit more food your thoughts. There are two different types of i2c backpacks:

    1. ones that use i/o expanders (PCF8574, MCP23008, MCP23017, etc..)

    2. Native.

    The i/o expander chip are pretty similar, and currently I combined them into a single class. They need to configured with a expander chip pin to hd44780 pin mapping. There is no around that. The canned entries, hide this from the user. The Native ones I've seen are much simpler and don't have any pin mappings to deal with. They only need an address. For now I created a separate class for those. They could be combined but then the native i2c devices would be dragging in quite a bit of code that they don't use.

    The device classes that I created don't use I2CIO. The main reason is that to increase the performance you want to do both nibbles and E strobes during the same i2c BUS connection and I2CIO doesn't allow that. This can almost double the performance when compared to the current I2C device class.

    Also, what gave me fits is the new directory structure for Arduino 1.5x. The issue here is that this library is multi-headed. So all modules must be compiled regardless of whether they are used or not. The Stupid IDE sets the include paths based on what headers the user sketch includes. So if say the user is doing 4bit or SR mode then the Wire library is not included and the Wire library will not be on the path. The only way around this is hard code a relative path to the Wire library, which the current release does but does it relative to itself. This doesn't always work because, two other things come into play.

    1) the library can be installed in with the system libraries or the users sketchbook area

    2) The paths the Wire library are different on 1.x vs 1.5x

    I solved the first by using a path that is relative to something that is always there, like the core library. However, the relative path from there to get to the Wire library for 1.x vs 1.5x is different so there must be a conditional ifdef to pick the two different paths.

    This affects several modules. This could be cleaned up to only affect I2CIO but then i2CIO needs to be updated to support some additional features so that those modules that are currently use Wire could use I2CIO vs directly using the Wire interface themselves.

    Another issue, is DUE. The Arduino team has a few broken & missing macros in DUE. One affects FIO. There is no way to work around the DUE FIO issue I've already entered a bug for it:

    The other one affects code that uses _BV(), this one can easily be worked around with a local _BV() definition of _BV isn't defined:

    Just some additional food for thought.

    --- bill

  19. Francisco Malpartida repo owner

    Hi Bill,

    I think I need to sit on this email. What I do agree on is to change the name to IIC so that people are not confused.

    I was thinking in the lines of leaving the method (interfaces only) of the current library and adding the new ones. The idea behind the I2CIO was to abstract out any dependency from wire, where should things change again this would be the only file affected. I think this is a good candidate for rewriting and has a lot of room for improvement.

    I will go through your post and come back, I need to sit on it.

    Do you have your changes in a branch that I can look at ?



  20. john_plocher

    As long as you are talking about an I2C library abstraction, take pity on those of us who are using other I2C devices on the same chain as these LCDs - with their own Wire-aware libraries that drive them.

    The PCF8574, MCP23008, MCP23017 differences should be able to be handled by the lower level I2CIO IOexpander, such that the higher level LCD code doesn't need to be aware of any differences (8-bit, 16-bit, sink-vs-source, yadda yadda...), at which point there is a hope for a unified I2C/IO expander driver that can be reused by others...

  21. Bill Perry

    I not quite sure what your concern is.

    Were you concerned about how this library affects other libraries?

    Or were you wanting some new i2c abstraction library for use with i2c i/o expanders for general purpose use?

    With respect to this library: The higher level LCD code doesn't care what the lower interface is. (4bit, SR, i2c, etc..) The upper level LCD code doesn't know, and doesn't care. It also doesn't care how that lower level interface code talks to the h/w. Each lower level device interface class can talk to the hardware anyway it wants. With respect to the lower level i2c interface classes in this library, other libraries and other i2c devices on the i2c bus are unaffected whether the lower level interface class i/o expander code in this library uses the I2CIO code routines or talks to the Wire library directly.

    It is lower level device interface class code that has to know all the details of chip type and how the pins are mapped to the HD44780 pins. There is no way around that.

    The sink/source issue is a potential issue that I'm not sure how to ever fully resolve. The issue for i2c i/o expander backpacks is that, depending on how i2c i/o expander pins are wired up to the hd44780 pins and how the user configures the pin mappings, it can potentially damage the hardware. That is part of the reason I was working on auto detect capabilities - to try to prevent that.

  22. john_plocher

    It's late/early here, so I may not be as clear as I could be - sorry...

    Were you concerned about how this library affects other libraries? Or some new i2c abstraction for i2c i/o expanders?

    Yes and Yes :-)

    I have a generalized I2C IOExpander library of my own that I use to talk to various I2C devices, including IOExpanders, none of which have been in a backpack before now. It uses Wire, does Wire.begin, and generally presumes it is the bus master class, similar, but gratuitously different from the I2CIO class :-)

    How do I use the LiquidCrystal_I2C library (that pulls in I2CIO) and my library (that pulls in my I2CIO-equivalent) in the same sketch?

    I could, I presume, just try it and hope that the two libraries play well together without fighting over TWI-internal state, or I could refactor my I2C-equivalent out of my code and port it to use I2CIO, or I could hope that the gods of arduino (TM) would find a way to address the "libraries that depend on other libraries" problem in a better way. Since the chances of the latter happening are probably equivalent to getting a balanced budget out of the government and my winning the lottery, I'm back to the first two options.

    In a generalized I2C-expander at the low level and the LCD at a higher one world, the LCD code would (of course) be aware of pin mappings for D7-D4, E and RS (yadda yadda), but would not need to be aware of the differences between the register sets of a 8574 or MCP23017. It sounds like that is what is being done here, though your comments earlier in the thread imply a tighter/different coupling ("device classes that I created don't use I2CIO"), which led to my initial question...

  23. Bill Perry

    I think you are missing some understanding of how things fit together and work, in particular what the I2CIO code is. All of code in the library is using the Wire library regardless of whether the code is using the I2CIO code or not. This code isn't like some of the DUE code out there i've seen that actually stomps on and controls the i2c hardware directly. Those solutions don't play nicely with others. This code doesn't work that way.

    No matter how many entities are using the Wire library, it is the Wire library that is touching the i2c h/w so there shouldn't be an issue with multiple entities using Wire. Well as long as each Wire library user (sketch/library) does an end transmission when it is done using the bus. All the state information is tracked in the Wire object and there is only one of those.

    The LCD class code does NOT need to know anything about the pin mappings. And in this library it knows nothing of pin mappings, E, RS, or data lines. That isn't how the layer/class interfaces work in this library. The LCD class layer is dealing with things at a much higher level. It says write this 4/8 bit control value, or write this 4/8 bit data value. The lower device interface class code is responsible for making that happen and can use any means it wants or that is necessary. That is how this library supports 4bit, SR, i2c etc... Only the lower interface class knows how to talk to the actual hd44780 interface so it is the only entity that must know about and talk to the h/w as well as any potential pin mapping information. This allows the each layer to focus on what it does and knows how to do. The upper layer translates the primitives like clear(), home(), write(), etc, to write control, write data requests, and then the lower level takes those requests and does whatever it takes to get that to the LCD. Each low level device interface class is intentionally very dumb, as it only has to move the control and data information to the LCD and backlight circuit.

    And since a 8574 and a MCP23017 have different registers, different capabilities, and work differently, the lower level code must know how to talk to each one. That could be handled with two separate low level class interfaces or a slightly smarter one that knows how to talk to both.

    The I2CIO code in this library provides an abstraction layer on top of the Wire library. It provides a set of capabilities to provide a digitalWrite()/digitalRead() like interface on top of a PCF8574 chip as well as the ability to write/read all 8 pins a once. Currently, that code only knows how to talk to a PCF8574. It does not know how to detect or talk to a MCP23008 or MCP23017. The way it works it does a full bus transaction for each i/o update. i.e. if you update a pin or do a 8 pin update, it acquires the i2c bus does the i/o expander port update, then lets go of the bus.

    Since the Wire library is the actual code that controls the i2c h/w it is not necessary to use the I2CIO code to control the i2c h/w.

    The current LiquidCrystal_I2C class uses this code to control the PCF8574 but use the 8 pin mode as the separate pin mode would be much slower.

    The current LiquidCrystal_I2C_ByVac class does not; it talks to Wire directly.

    My current LiquidCrystal_IIC & LiquidCrystal_IIClcd classes also talk to Wire directly.

    The code in my device LiquidCrystal_IIC interface classes, while using the Wire library, does not use the I2CIO code in this library so it can support multiple i/o expanders like the PCF8574 and the MCP23007 (adding the MCP23017 would be extremely easy). Both of my new classes, also handle the i2c bus differently than the I2CIO code. They grab the bus and hold on to for more than a single update (data byte transfer). This allows things like the the nibble updates and the E strobe to handled in a single i2c bus acquisition vs multiple. This can substantially increase the transfer rate to the LCD.

  24. Andre Lini

    I found this discussion today after battling with two lcds with different I2C adapters (Adafruit's MCP23008 and a generic PCF8574). The last message was posted over a year ago.... any news? Cheers, Andre

  25. Francisco Malpartida repo owner

    Currently the library is in maintenance and no big features are being added to it. I have several things in the works, but haven't found the time to get them in.

    Unfortunately I don't own an Adafruit's LCD, so I can't do a library update with it.

  26. Log in to comment