Tuesday, 12 March 2013

Panelolu2 for Melzi

Panelolu2 LCD display and control solution working with Melzi Electronics.

The previous post outlined the functionality of the Panelolu2 - broadly similar to the Panelolu but with a reduced pin count allowing it to interface with more controllers. In order to simplify adding the Panelolu2 to your printer the design includes adapter boards and the second adapter board we have produced is for the Melzi controller:



This runs Marlin firmware which has been specifically adapted to support an I2C controller, a number of people are working to incorporate the I2C LCD control code into the standard Marlin build.

The adapter board schematic is below:


The adapter boards will be supplied fully assembled by default:




As the picture above shows they can do more than just provide an interface between the Melzi electronics and the Panelolu2. There are two FETs mounted which can be used to switch a 12V supply, useful for adding additional fans to you printer. Unlike the Sanguinololu the Melzi does not provide 12V on the expansion header so an additional 12V input is needed (The easiest way to do this is to use a short lead to provide the 12V supply from the 12V input to the Melzi board).

The expansion header allows access to the I2C pins so additional devices can be added to the bus, along with the two pins connected to the FETs and the click encoder pins. In normal use the click encoder pins are used for the encoder and cannot be used for anything else. However, as explained in the previous post, running the encoder over I2C may be supported in software in the future which would free these pins up to be used elsewhere. The board has solder jumpers to select between these two modes of operation, with the default being the normal, directly connected mode.




Using the additional FETs

The additional FETs are connected to pins A3 and A4 which map as arduino pin numbers 27 and 28. Pin 27 is the LED pin - setting that pin high turns on the FET and the LED at the same time.

Currently these FETS are not directly controlled in firmware (there is no menu command to turn them on and off) but that is in the pipeline. In the meantime they can be controlled in GCODE by using the M42 command (for example in your startup and shutdown code). Once I get a chance to wire it in I plan to use this in the end of print gcode to drive a fan that will cool the heated bed rapidly (originally Nophead's idea and incorporated into the Mendel90 design)

M42 S255 P27 ;turn on the fan on Transistor 2
G4 S240 ; wait for 4 minutes
M42 S0 P27 ;turn off the fan on Transistor 2

The other FET could be used to drive a fan to cool the stepper drivers or not used at all and the pin used on the expansion header on the adapter board for some other function.

SD Card

The firmware with the P2 changes is setup to use the P2 SD card slot rather than the built in SD card slot on the Melzi. The difference is in the definition of the SDSS pin in pins.h in Marlin. The specific line in pins.h is:

#define SDSS 30 //to use the SD card reader on the Panelolu2 rather than the melzi board

Comment this line out to us the SD card on the melzi board instead. It is not currently possible to use both and select between them after the firmware is compiled.

Summary

Update, added this section to summarise the process of getting the Panelolu2 working on a Melzi where the Melzi was previously running a "non standard" version of Marlin such as Nophead's version for M90 or RepRapPro's version of Huxley/Mendel. Please note that if you are using RepRapPro's Multicolour Mendel you need to use their custom variation of Marlin to control the multiple extruders and at this time the Panelolu2 is not compatible with this setup.


  1. If you are not already using it get the standard version of Arduino-0023 from the Arduino website, note that the T3P3 version of Marlin has not been tested with Arduino 1.0 or higher.
  2. Update the avrdude.conf file in the standard Arduino-0023 to include a definition of the atmega 1284p, I have used this one for a year now. Be sure to put it in arduino-0023\hardware\tools\avr\etc\ and rename or overwrite the old one. More detail in my first blog post
  3. Get T3P3 Marlin from github.
  4. Add the "Marlin\ArduinoAddons\Arduino_0.xx\Sanguino" directory to "arduino-0023\Hardware\" directory. This provides the Sanguino extensions required for Sanguinol and Melzi.
  5. Get LiquidTWI2 as mentioned in my Panelolu2 blog post, note that configuration changes are no longer required in the latest version of LiquidTWI2.
  6. Modify the configuration.h of the Marlin to fit your printer setup (setting like controller board, axis dimensions, thermistors, etc). Its best to copy these from the configuration.h of your custom firmware. Update: This blog post should help.
  7. Confirm Marlin compiles and uploads with //#define PANELOLU2 still commented out in configuration.h.
  8. Then uncomment #define PANELOLU2 , along with #define EEPROM_SETTINGS and #define EEPROM_CHITCHAT and confirm it compiles and uploads. Note that with the #define PANELOLU2 uncommented, you must have the Panelolu2 connected or Marlin will hang and not finish initialising.
  9. Check the printer operation and calibration to ensure you have edited the Marlin configuration.h properly.


Availability

Update: Now available on our webstore as well as eMakershop and eBay

Friday, 15 February 2013

Panelolu2

Panelolu2 - An I2C control solution for Sanguinololu, Melzi and more

The original Panelolu was designed to provide a simple user interface for a 3d printer controlled by a Sanguinololu and similar electronics - enabled by the Ultipanel code within the Marlin firmware. By connecting to the right pinouts it also works with Printrboard and RAMPS.

I have developed a new version which uses an I2C port expander to drive the LCD screen and adds a piezo buzzer and indication LEDs. This version uses less pins, making it compatible with the Melzi electronics and even leaves enough spare pins to drive a couple more outputs (such as an additional extractor fan and stepper driver cooling fan).


Panelolu2 Front View
  
In the photo above the grid covers the piezo buzzer and the H,E and F LEDs indicate that the Heated bed, Extruder and Fan (or second extruder) are on. It would be relatively simple to change the firmware to have these LEDs turn on for another reason. The R for reset and the encoder wheel are the same as in the original Panelolu although I have made the encoder wheel about 1/3 smaller to fit better. 

Circuit board

The Panelolu2 design simplifies the assembly by mounting all components on one circuit board that can be soldered directly to an LCD screen. The standard way to connect it to the screen is with a single row of 2.54mm headers as shown below. This row of headers could be replaced by wires (ribbon cable would be best) - enabling a variey of case designs, such as Printbit's Panelolu Box for Mendel Max.


This simplification also extends to the wiring which is now a 2x6 IDC plug on both ends of a ribbon cable. The circuit uses a combination of through hole and surface mount components; the other side of the prototype circuit board is shown below. The brightness and contrast pots are now on the back of the board and can be adjusted through the rear of the case.



I have tidied up the Schematic: the components are the MCP23017 port expander, the SD card circuitry, buzzer, click encoder and LEDS.

 

Of course the whole project is Open Source Hardware and the eagle files are available on github.

Case design

As mentioned above, the circuit board and LCD allow for multiple case designs. I have made a simple case with a back leg allowing the Panelolu2 to sit on the bench alongside the printer.
 
Panelolu2 Side View 

The case design is all done in OpenSCAD with both .scad and .stl versions of the files, available on Thingiverse. One of the great things I have found about Thingiverse is it allows you to embed the Thingiviews of the STL files, so here are the front and back case views:

Front
 
 
Back
 



The OpenSCAD file also allows the back leg to be generated to support the case at your chosen angle. For the rotary encoder I adapted Miserybot's "Spinner Knob" design to make it about 1/3 smaller and a lot thinner - the .stl is on Thingiverse and I will upload the OpenSCAD code once it is cleaned up.


Adapter Boards

One of the aims of the Panelolu2 was to reduce the complexity involved in wiring it up. I have designed adapter boards to allow compatibility of the Panelolu2 with various electronics. The picture below shows the adapter board connected to a Sanguinololu.



Update added the Sanguinololu and Melzi adapter schematics:



As can be seen from the schematic the two mosfets for driving additional light loads such as fans are on the PWM and A3 pins on the Sanguinololu. These pins can be accessed as Arduino digital pins 4 for the pin labelled PWM and 28 for the pin labelled A3. In Marlin firmware to turn on the fan connected to Transistor 1:

M42 S255 P4

And transistor 2

M42 S255 P28

In overview the picture below shows the inputs and outputs from the prototype Melzi adapter board.


The Schematic for the Melzi adapter board is:



The same logic as described the solder jumper operations on the Sanguinololu adapter board applies to the Melzi board. For the two additional outputs they are on pin A4 and A3 which are digital pins 27 and 28 respectively.In Marlin firmware to turn on the fan connected to Transistor 1:

M42 S255 P28

And transistor 2

M42 S255 P27

For basic operation the ISP and expansion header plug into the Melzi and the Panelolu plugs into its 2x6 connector. If desired FETs can be mounted to switch a 12V supply, for example to drive external fans.

As an alternative to the adapter board, standard female housings with crimp connectors could be used. I intend to design boards for Printrboard and RAMPS in the near future.
(update: done, see this blog post)

I2C encoder option 

Instead of linking the encoder and switch directly to pins on the Sanguinololu, Melzi or other electronics, the hardware has the option to run the encoder and switch over I2C. This would free up a couple more pins but the option is not yet supported by firmware. The Panelolu2 circuit board and adapter boards have solder jumpers, shown below on the back of the Melzi adapter board



Currently the firmware does not support routing the encoder signals over I2C so by default these jumpers will be set for normal operation.


Firmware 


The Panelolu2 only runs on Marlin firmware at the moment. Excellent work was done by Daid in the Marlin code to split the LCD screen hardware from the user interface. This made it straightforward to implement the Panelolu2 code. As mentioned in previous posts LiquidTWI2 by Lincomatic is required; download it from github, rename the directory from "LiquidTWI2-master" to "LiquidTWI2 and put it in the

arduno-0023/libraries/

sub-folder.

UPDATE: - This step is no longer required in the latest version of LiquidTWI2 (1.2.4) the library will work out the box with no configuration changes required.

By default the Panelolu2 is disabled in LiquidTWI2, after saving the directory, edit LiquidTWI2.h to remove the comments on line 10:

 //  #define PANELOLU2 //only possible....


To

    #define PANELOLU2 //only possible....
 

Continue following the instructions from here:

Then download the T3P3 fork of Marlin, make the changes you need to configuration.h to suit your printer and ensure :

#define EEPROM_SETTINGS
and

#define PANELOLU2

are uncommented within configuration.h then upload as normal. The compiled Marlin is ~100k, a very similar size to Marlin compiled for the original Panelolu, so the electronics board will need enough space (a 1284P is fine, a 644P does not have enough space).


Summary

Update, added this section to summarise the process of getting the Panelolu2 working Sanguinololu.


  1. If you are not already using it get the standard version of Arduino-0023 from the Arduino website, note that the T3P3 version of Marlin has not been tested with Arduino 1.0 or higher.
  2. If you are not already using a Sanguinololu with 1284P then update the avrdude.conf file in the standard Arduino-0023 to include a definition of the atmega 1284p, I have used this one for a year now. Be sure to put it in arduino-0023\hardware\tools\avr\etc\ and rename or overwrite the old one. More detail in my first blog post.
  3. Get T3P3 Marlin from github.
  4. If you are not already using a Sanguinololu with 1284P add the "Marlin\ArduinoAddons\Arduino_0.xx\Sanguino" directory to "arduino-0023\Hardware\" directory. This provides the Sanguino extensions required for Sanguinol and Melzi.
  5. Get LiquidTWI2 as mentioned above, note that configuration changes are no longer required in the latest version of LiquidTWI2.
  6. Modify the configuration.h of the Marlin to fit your printer setup (setting like controller board, axis dimensions, thermistors, etc). Its best to copy these from the configuration.h of your existing firmware. Update: This blog post should help.
  7. Confirm Marlin compiles and uploads with //#define PANELOLU2 still commented out in configuration.h.
  8. Then uncomment #define PANELOLU2 , along with #define EEPROM_SETTINGS and #define EEPROM_CHITCHAT and confirm it compiles and uploads. Note that with the #define PANELOLU2 uncommented, you must have the Panelolu2 connected or Marlin will hang and not finish initialising.
  9. Check the printer operation and calibration to ensure you have edited the Marlin configuration.h properly.

Availability

As always the hardware is open source so the design files are available on Github: https://github.com/T3P3/Panelolu2.

Update: Now available on our webstore and on eMakershop and eBay 

Tuesday, 5 February 2013

OpenSCAD Bitmap Font

An (almost) 32 bit font for OpenSCAD.

I have used Tony Buser's bitmap OpenSCAD font to add 3D letters to various objects. I recently wanted to to get higher resolution than 8 bits but did not want the more complicated workflow (of which there are a number of examples 1 2 ) of creating the letters outside of OpenSCAD as a DXF, then importing them and linear extruding to get the desired 3D objects.

The solution was to use "The Dot Factory" which is designed for creating bitmaps for graphics LCDs. It allows you to import a system font (I used Lucida Console at 24pts) and export that as a text file with each character mapped to a number of bytes representing the bitmap. For example the letter "a"

    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00011111, 0b11000000, //    #######    
    0b01111111, 0b11110000, //  ###########  
    0b01100000, 0b01111000, //  ##      #### 
    0b00000000, 0b00111000, //           ### 
    0b00000000, 0b00111000, //           ### 
    0b00000000, 0b00111000, //           ### 
    0b00000000, 0b00111000, //           ### 
    0b00001111, 0b11111000, //     ######### 
    0b00111111, 0b11111000, //   ########### 
    0b01111000, 0b00111000, //  ####     ### 
    0b11110000, 0b00111000, // ####      ### 
    0b11100000, 0b00111000, // ###       ### 
    0b11100000, 0b00111000, // ###       ### 
    0b11100000, 0b00111000, // ###       ### 
    0b11110000, 0b11111000, // ####    ##### 
    0b01111111, 0b10011111, //  ########  #####
    0b00011111, 0b00001111, //    #####    ####
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //               
    0b00000000, 0b00000000, //


 
With some text manipulation (in my case using notepad++, which is excellent) this is converted into:
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
    0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
    0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
    0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,
    0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0



The matrix above is the format that Tony Buser's open scad bitmap drawer uses, define the number of bits per row and you are away:

Still a bit chunky but less so than the original 8 bit version:
The full list of characters created are:


ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789

 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~


Depending on the character the bitmaps are 8,16,24 or 32 bits wide and the bitmaps are all 31 bits high.

If you render them all at once they can use quite a bit of processing power, best to leave extensive 3d lettering until the rest of the object is designed.

The OpenSCAD script is available on github.
 
Update: Inkscape has a plug-in to export direct to OpenSCAD so this may make that method easier - one to check next time!

Thursday, 3 January 2013

I2C Port Expander & LCD working with Marlin

Previously...

The I2C port expander proved capable of driving a 20x4 LCD screen, reading the status of a quadrature encoder with click switch, driving some LEDs and a piezo buzzer.

Here is a quick video of this configuration working with Marlin firmware:


I had to make a few changes to marlin to make this happen which are detailed below.

Marlin Firmware integration

Lincomatic has forked Marlin and edited it to use his LiquidTWI2 library. This is a fast I2C LCD library designed for the adafruit RGB LCD shield, I added a couple of functions for this setup which Lincomatic has incorporated and updated the version number of LiquidTWI2 to 1.1.1. I used these as a start state as Lincomatic's version of Marlin was already proven with an I2C LCD -  all the line numbers below refer to this version.

The changes required are:

to configuration.h (line 292) add:

   #define PANELOLU2

   #ifdef PANELOLU2
   #define VERSAPANEL

   #define I2CENCODER //define this if you want to use the encoder over I2C
  #endif


This allows using the Panelolu2 with the encoder connected to either the port expander or directly to the main board.

to pins.h add (at line 657) :

  #ifdef ULTRA_LCD
    #ifdef NEWPANEL
      #define BEEPER -1

    #ifdef PANELOLU2
      #ifndef I2CENCODER
        #define BTN_EN1 10
        #define BTN_EN2 11 
        #define BTN_ENC 30  //the switch
      #endif
    #else
        //we have no buzzer installed
        #define BEEPER -1
        //LCD Pins
        #define LCD_PINS_RS        4
        #define LCD_PINS_ENABLE    17
        #define LCD_PINS_D4        30
        #define LCD_PINS_D5        29
        #define LCD_PINS_D6        28
        #define LCD_PINS_D7        27

        //The encoder and click button
        #define BTN_EN1 11
        #define BTN_EN2 10
        #define BTN_ENC 16  //the switch
      #endif //PANELOLU2

      //not connected to a pin
      #define SDCARDDETECT -1

      //from the same bit in the RAMPS Newpanel define
      //encoder rotation values
      #define encrot0 0
      #define encrot1 2
      #define encrot2 3
      #define encrot3 1
    
      #define BLEN_C 2
      #define BLEN_B 1
      #define BLEN_A 0

    #endif //Newpanel
  #endif //Ultipanel


to temperature.cpp replace

  #ifdef ULTIPANEL
    buttons_check();
  #endif

with (4 times at lines 956,980,1001,1022) :

  #ifdef ULTIPANEL
    #ifndef I2CENCODER
      buttons_check();
    #endif

  #endif

This is because Marlin uses a timer interrupt to run the temperature check code. Each time this interrupt runs, the status of the buttons on a Ultipanel-compatible controller are checked. If connected over I2C the encoder & click buttons cannot be checked within this interrupt routine as the Arduino wire library is blocking it. So we exclude this button check call and will check the encoder at another point.

On to ultralcd.pde. In the beep() and beepshort() functions replace

  //return;
  #ifdef ULTIPANEL

with (2 times at lines 189, 207) :
 
  //return;
  #ifdef PANELOLU2
     lcd.buzz(300,4000);
  #elif defined ULTIPANEL

This uses the buzzer on the port expander whenever beep() or beepshort() are called. Still in ultralcd.pde, in the lcd_status() function replace:

  #ifdef MCP23017_LCD
    if (lcd.readButtons() & BUTTON_SELECT)
      buttons |= EN_C;
  #endif


with (at line 225) :


  #ifdef MCP23017_LCD
    #ifndef I2CENCODER
      if (lcd.readButtons() & BUTTON_SELECT)
        buttons |= EN_C;
    #endif
  #endif


and


 //  buttons_check(); // Done in temperature interrupt

with (at line 252) :

  #ifdef I2CENCODER
    buttons_check(); //Done in temperature interrupt except if using I2C buttons
  #endif


As mentioned earlier Marlin uses the temperature check routine to call buttons_check() but as that is inside an interrupt routine we can't check it there - hence the check here.

Still within ultralcd.pde, the buttons_init() function has been edited so as not to throw errors if the buttons are not defined in pins.h (from line 281)



  #ifdef NEWPANEL
    #ifdef BTN_EN1
      pinMode(BTN_EN1,INPUT);
      WRITE(BTN_EN1,HIGH);
    #endif

    #ifdef BTN_EN2
      pinMode(BTN_EN2,INPUT);
      WRITE(BTN_EN2,HIGH);
    #endif

    #ifdef BTN_ENC
      pinMode(BTN_ENC,INPUT);
      WRITE(BTN_ENC,HIGH);
    #endif

    #if (SDCARDDETECT > -1)
    {
      pinMode(SDCARDDETECT,INPUT);
      WRITE(SDCARDDETECT,HIGH);
    }
    #endif

 
Still within ultralcd.pde the buttons_check() function has been edited to handle the different cases of an encoder connected via I2C or directly. The first part of the function was changed to the following (from line 310):


void buttons_check()
{
  uint8_t newbutton=0;
  #ifdef I2CENCODER
    uint8_t lrb = lcd.readButtons();
    if (lrb & ENCODER_A) newbutton |= EN_A;
    if (lrb & ENCODER_B) newbutton |= EN_B;
    if (lrb & ENCODER_C) newbutton |= EN_C;
  #elif defined(NEWPANEL)
    #ifdef BTN_EN1
      if(READ(BTN_EN1)==0)  newbutton|=EN_A;
    #endif

    #ifdef BTN_EN2
      if(READ(BTN_EN2)==0)  newbutton|=EN_B;
    #endif

    #ifdef BTN_ENC
      if((blocking<millis()) &&(READ(BTN_ENC)==0))
        newbutton|=EN_C;
    #endif     
    buttons=newbutton;


These changes allow for two configurations which can be switched between using the #define I2CENCODER within configuration.h :

with I2CENCODER defined:
  outputs (LCD, Buzzer, LEDs) use I2C
  inputs (Encoder with click switch) use I2C

with I2CENCODER not defined:
  outputs (LCD, Buzzer, LEDs) use I2C
  inputs (Encoder with click switch) are directly connected on the pins defined in pins.h

The reason I have included the option of directly connecting the encoder is because of the way marlin checks the buttons. Currently it's quite possible for encoder rotations to be lost if connected over I2C.


Next Steps

Since starting on this project Marlin has been updated to change how lcd displays are handled, splitting the menu system from the display implementation. There is currently a header file for the "standard" implementation using an LCD directly like the Panelolu. The obvious next step is to write a similar header file to use an I2C LCD using LiquidTWI2. In addition I need to add an option to use an interrupt on a pin to check the encoder status rather than a timer interrupt - this will ensure steps are not missed and the encoder can be safely used over I2C.

Tuesday, 18 December 2012

I2C port expander driving an LCD screen, click encoder and more

Adventures with the MCP23017 I2C port expander

Since working on the Panelolu I have been planning a project using the MCP23017 port expander. This chip gives 16 additional I/O ports which can be accessed using the I2C protocol, an easy to use bus communication protocol that is supported in the Arduino environment using the Wire library. My first exposure to I2C was during development of the Panelolu when I came across the Hobbytronics I2C LCD backpack, Adafruit also do a fancy 3 colour backlight LCD. I started this project in August but then put it on the back burner due to other commitments.

I re-invigorated the project recently as we get a lot of emails from people wanting to use the Panelolu with a Melzi or a Sanguinololu where pins on the expansion header are dedicated to other things. In both these cases the original Panelolu took too many pins. Added to this Nophead has announced he will be using Melzi on the Mendel90 which I feel is the best printer design out there right now.

Hardware setup.

The setup I have tested on uses a Sanguinololu but any similar reprap controller should work. The ISP header and 4 more pins are required:


Leaving 6 pins for other uses.

The circuit is setup as in the schematic




The Sanguinololu ISP and Expansion header pins are allocated as follows:
  • SCL and SDA are connected to pin 12 and 13 of the MCP23017
  • MOSI, MISO, SCK and A0 provide the SD card signals as they do in the Panelolu and SDSL.
  • A1 is the interrupt pin and I added an LED to this to help with debugging which is not illustrated in the schematic.
  • 5V and GND are distributed as required
from the MCP23017 the LCD display, Buzzer, Click Encoder and 3 LEDs are connected. There are 2 spare pins so further LEDS or something else could be connected.

Software Setup

I began testing in August with the library from Adafruit which does a great job of exposing the basic read and write functions of the expander. Recently Lincomatic updated the LiquidTWI2 library which extends the Adafruit library and I switched to that. It does not provide access to the interrupt functionality of the MCP23107 so I added some additional functions to the library:


//check registers
uint8_t LiquidTWI2::readRegister(uint8_t reg) {
  // read a register
  Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
  wiresend(reg);   
  Wire.endTransmission();
 
  Wire.requestFrom(MCP23017_ADDRESS | _i2cAddr, 1);
  return wirerecv();
}

//set registers

void LiquidTWI2::setRegister(uint8_t reg, uint8_t value) {
    Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
    wiresend(reg);
    wiresend(value);
    Wire.endTransmission();
}


In addition I added a function to switch the buzzer pin rapidly at a certain frequency to make a sound

//cycle the buzzer pin for a certain duration (ms) and at a certain freq (hz)
void LiquidTWI2::buzz(long duration, uint8_t freq) {
  int currentRegister = 0;
  // read gpio register
  Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
  wiresend(MCP23017_GPIOA);   
  Wire.endTransmission();
 
  Wire.requestFrom(MCP23017_ADDRESS | _i2cAddr, 1);
  currentRegister = wirerecv();
  duration *=1000; //convert from ms to us
  int period = (1.0 / freq) * 1000000; //*1000000 as the delay is in us
  long elapsed_time = 0;
  while (elapsed_time < duration)
  {
        Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
        wiresend(MCP23017_GPIOA);
        wiresend(currentRegister |= M17_BIT_BZ);
        while(Wire.endTransmission());
        delayMicroseconds(period / 2);
        Wire.beginTransmission(MCP23017_ADDRESS | _i2cAddr);
        wiresend(MCP23017_GPIOA);
        wiresend(currentRegister &= ~M17_BIT_BZ);
        while(Wire.endTransmission());
        elapsed_time += (period);
   }
}



I need to get up and running on GitHub until I do so the T3P3 version of LiquidTWI2 is available here. just download the as a zip and extract into the libraries subdirectory of the arduino folder so it looks something like:

C:\arduino-0023\libraries\LiquidTWI2\

At this time I have only tested it with Arduino 0023.

A1 is not a hardware interrupt pin so the PinChangeInt library for the Arduino is required. I used version 2.19, just download it according to the instructions and put in the libraries subdirectory of the arduino folder.

Example Sketch

I adapted the HelloWorld_i2c sketch that comes with LiquidTWI2, the full sketch is in the example sub directory of the T3P3 version of LiquidTWI. In essence the program uses the LiquidTWI2 library to setup communication with the MCP23017, configure and display information on the LCD and set the buzzer and LED pins.

It also sets interrupts on the click encoder pins and switch which trigger an interrupt on A1 every time the click encoder is turned or switch is pressed.

(from setup() )
...
    lcd.setRegister(MCP23017_GPINTENA,0x07); //enable interrupts on 3 pins
  lcd.setRegister(MCP23017_DEFVALA,0x07); //set the default values as 1

  //set to compare with register values set to 1 with DEFVALB 
  lcd.setRegister(MCP23017_INTCONA,0x07);
  lcd.setRegister(MCP23017_IOCONA,0x02); //enable active high for interrupt pin
  //read the interrupt capture register to reset it
  uint8_t reg = lcd.readRegister(MCP23017_INTCAPA);
  pinMode(interruptPin, INPUT); //set A1 (pin 30) as input

  //attach an interrupt using the pinChangeInt library to interruptPin
  PCintPort::attachInterrupt(interruptPin, &quickInt, RISING);

... 


The state of the click encoder and switch pins are are captured at the interrupt point and read, determining which way the encoder is turned or if the switch was pressed.

  void handleInterrupt() {
    //reset the flag
    rotating=false;  
    //get the interrupt status of the pins
    //this will clear the flag allowing further interrupts
    intCap=lcd.readRegister(MCP23017_INTCAPA);
    uint8_t test = intCap & 0b00000111;

    //only deal with the situation where the encoder is turned without the
    //click button held down
    if (test == 0b101) encoderPos += 1;
    if (test == 0b110) encoderPos -= 1;
    //mask out the encoder bits and look at the click button
    test &= 0b100;
    if (test == 0b000) clickButton = LOW;


    // while interrupts are still occurring clear the register (switch bouncing) 
    while(digitalRead(interruptPin) > 0)
    {
      
      lcd.readRegister(MCP23017_INTCAPA);
      delay (10);
    }
  }


The program does not make use of the SD card part of the design - that is planned for later when this is integrated into Marlin firmware. Also further testing on the buzzer is required as I believe the frequency is fairly limited by the I2C protocol and associated processing.

The video below demonstrated the example in operation.

Now off to make it work with Marlin firmware.