I2C Encoder V2

Upgraded version of the small board for connect multiple rotary encoders on the I2C bus.

Public Chat
Similar projects worth following
This is the upgraded version of our original project: We added some new improvement. Most inportants are the support of the RGB encoders, extension of the addressing to 7bit and a new shape

After the first version of the I2C encoder we collected all the suggestions and we made this new upgraded version!

New features:

  • It's support the standard rotary encoder and the RGB encoder
  • It's possible to set all the 7bit of the I2C address trough a SMD jumper
  • Dimension of 25x25mm or 0.98x0.98in
  • With the castellated holes is possible to connected several boards on the 4 sides
  • Possibility to solder the pull-up resistor on the I2C bus
  • 3 General Purpose pins. (GP pins)
  • 256byte of internal EEPROM divided in the 2 bank of 128 byte
  • Advanced configuration respect the first version
  • The maximum frequency of the A/B signal is 150Hz.


This new version is powered by the PIC16F18345. Respect to the MCU on the first version, it has more GPIO and the EEPROM memory.

The new design of the board support both the standard encoders and the illuminated RGB encoders.

Moreover, there are the castellated holes on all the 4 sides of the PCB, in this way will be possible to connect multiple board by soldering them.

There are also 3 configurable GPIOs organized with the same footprint of a RGB LED. They are called GP1 GP2 and GP3.

But in case you are using the RGB encoder, the configurable GPIOs are only 2: GP1 and GP2.

GPIO configuration:

  • PWM: In this way you can add a RGB LED
  • OUTPUT: You can use the pins as standard digital output.
  • Analog: The pins are connected to the internal ADC of the PIC. In this way you can add sensors or potentiometer 
  • Input: You can use the pins as standard digital input. Plus you can configure also the interrupt on the edges

I2C Adress setting

The I2C Encoder V2 is a I2C slave, it's possible to the set 127 different addresses. All of the 7-bit address can be customized by soldering the jumpers A0 - A6 on the bottom of the board.
When the jumper is open, it means a logic 0. if jumper is shorted it means a logic 1.

The I2C Encoder V2 has I2C pull-up resistors, by default they are not soldered. It's possible to solder two resistors R1 and R2. This must be done in case that the master doesn't have these resistors and must be enabled only one I2C Encoder V2 in a chain. A typical value of this resistors are 4.7k.

Interrupt configuration:

The INT pin is an open-drain output, and it's used to send an interrupt to the master.

The interrupt is active low, and have multiple sources where it's possible to mask.

Interrupt source:

  • Encoder push button pressed
  • Encoder push button released
  • Encoder push button is double pushed
  • Encoder is moved clockwise 
  • Encoder is moved counterclockwise
  • The counter value reach the maximum value
  • The counter value reach the minimum value
  • The GP pins changed when configure as digital input

I2C registers:

This is the internal registers accessible on the I2C bus

It's possible to write those register on the fly during the normal operation.

It's possible to confgure several option by setting the registers with the I2C bus. With the GCONF register, it is possible to configure several parameters.
In the configuration, it's possible to set the polarity of the encoder quadrature signals and also to set if the outputof the encoder is X1 or X2.

For reading the rotary encoder movement, there are 4 32bit registers: CVAL, CMAX, CMIN and ISTEP.
All of these 4 registers can be configured to work as 32bit INT or as IEEE 754 floating number, this format can be set in the GCONF register.

Every time the encoder moves one step, the value of the CVAL register is increased or decreased of the value of ISTEP. The direction of the rotation decides if ISTEP is added or subtracted from CVAL.
CMAX and CMIN are used for setting a minimum and maximum thresholds of CVAL. In the GCONF register, there is WRAPE bit. This bit is used to enable or disable a wrap functionality of CVAL when it exceeds from the thresholds.

For example, if i con gure the I2C Encoder V2 as following:

  • CVAL= 0
  • CMAX = 5
  • CMIN = -5
  • ISTEP= 1

I will have CVAL is incremented of 1...

Read more »

I2C Encoder V2 FW

MPLAB X project with all the source files

x-zip-compressed - 467.59 kB - 10/20/2018 at 20:35


I2C Encoder V2 HW

Gerber files, BOM and assembly files

x-zip-compressed - 319.23 kB - 10/20/2018 at 20:35


x-zip-compressed - 10.59 kB - 10/20/2018 at 20:35



Datasheet of the I2C Encoder V2

Adobe Portable Document Format - 2.22 MB - 10/20/2018 at 20:33


  • 1 × PIC16F18345-I/GZ 8-bit Microcontrollers - MCU 8-BIT MCU 14KB Flash 1KB RAM, 256 EEPROM
  • 5 × Resistor 10k 0402
  • 5 × Resistor 47r 0402
  • 2 × Capacitor 47n 0402
  • 1 × Capacitor 100n 0402

View all 7 components

  • New FW release!

    Saimon09/20/2021 at 10:31 0 comments

    I have made an update about the detection of the encoder rotation.

    In the previous version for detecting the encoder steps and direction it was used the CLC feature of the PIC16F18345., this method works but sometime some errors can appear.

    In the Mini version the detection is made in FW with the lookup table method, this way is very efficient and it make very few mistake. For this reason i have decided to use the same way also in the I2C Encoder V2. The CLC are still used but for debouncing the encoder signals.

    I have found this application note from Microchip where it use two CLC module and a timer for creating a robust debouncing circuit. 

    Following the application note i have used the CLC 1 and 3 for the A signal and the CLC 4 and 2 for the B signal. The timer 0 is used for feeding the filp-flops

    Here the logic:

    The frequency of the timer 0 is configurable with the 8bit register ANTBOUNC, before this register was used for a similar function.  The range is from 0.192ms to 49.152ms that correspond to a frequency 5200Hz to 20Hz. Each step correspond to 0.192ms.

    The old functionality of the ANTBOUNC is removed, so there is no the delay when the direction it changed. This make the encoder more reactive.

  • OSHWA Certification

    Saimon10/14/2019 at 20:50 0 comments

    This project got the OSHW certification!

  • Version 2.1 is released!

    Saimon08/07/2019 at 11:19 0 comments

    One year ago we release the I2C encoder V2, now is time to add some improvement! 

    We have made the I2C encoder V2.1  the new feature are the following:

    Hardware changes:
    • Added the jumper SJ8 for enable the pull-up resistor in the I2C bus.
    • R1 and R2 are already soldered on the board with a 0402 footprint.
    • Changed the value of R3 and R4 with 42.2
    • Changed the value of R5 with 107
    Firmware changes:
    • Added the gamma correction feature.
    • Added the unique code and the version registers.
    • Added the possibility to enable/disable the I2C clock stretch.
    • Added and extra delay after the detection of the double-push.
    • Added the possibility to count absolute or relative steps
    • Decreased of the 33% the power consumption.
    • Changed the default value of ISTEP from 1 to 0.

    Of course all the source file are available on GitHub

    And the board is available on Tindie!

  • New Arduino library release!

    Saimon03/13/2019 at 13:25 0 comments

    Thanks to several suggestions, especially codebeat-nl, we have updated the Arduino library:

    The major update is the callback functionality with the I2C Encoder interrupts, and also the better integration with the Arduino IDE.

    We will also use that repository for integrating the libraries of our future project!

  • Hidden feature on the push button detection

    Saimon10/17/2018 at 11:59 0 comments

    On the I2C Encoder V2, it's possible to detect when the encoder switch is pushed, released and double pushed. But i have found out that i have accidentally introduced the functionality of detecting also a long push button press. 

    Lets check this Arduino code:

    Encoder.writeDoublePushPeriod(50);  /*Set a period for the double push of 500ms */
    if ( Encoder.readStatus(PUSHP)&& (Encoder.readStatus(PUSHR)==false)) {
            Serial.println("Long push!");
            /* Write here your code */
          if ( Encoder.readStatus(PUSHP) && Encoder.readStatus(PUSHR)) {
            Serial.println("Fast push!");
            /* Write here your code */
          if ( Encoder.readStatus(PUSHD)) {
            Serial.println("Double push!");
            /* Write here your code */

    In the code i'm setting a double push period of 500ms. This means that when i push the encoder the internal counter starts to count and after the 500ms i have an interrupt about the behavior of the switch.

    if i have both the flag of pushed and released, means that in the 500ms i have pushed and released the encoder switch, so i have done a fast push.
    But of i have only the flag of pushed, means that i have only pressed the switch ad maintained the switch pressed for at least 500ms. Then i did a long push!

  • Project source file released

    Saimon10/14/2018 at 20:12 0 comments

    Since the project is complete we have released all the documents regarding the I2C Encoder V2.

    On GitHub there are all the source files of the firmware and of the hardware!

    Under the folder Firmware we have uploaded the hole MPLAB X project. While under the folder Hardware there all the production document, we have also added the 3D .STEP file.

    The PCB project is available on CircuitMaker.

    The final I2C register map is the following:

    Respect to the original version we have changed several register, like the status registers, period register and the fade registers.

  • Final production version

    Atika09/29/2018 at 16:32 0 comments

    The final version of I2C Encoder V2 is ready, we changed the color of the solder mask from blue to matte black.

    Also the final version of firmware is completed, we will post soon on GitHub.

     I2C Encoder V2 will be available in our Tindie store very soon, stay tuned!

  • Datasheet updated

    Saimon09/14/2018 at 07:56 0 comments

    We have updated the datasheet with the all the feature, and we have added more descriptions. The datasheet is available on GitHub

    We have changed the register status and we have added a configurable anti-bouncing register for the rotary encoder.

    The firmware is still under progress. We will publish it once it is ready.

  • Feature updated

    Saimon08/21/2018 at 20:44 0 comments

    We have updated the datasheet with the final feature. The datasheet is available on GitHub

    There is an extra feature where it is possible to detect also a double fast push.

    On GitHub, there is also the production file of the hardware

  • Auto fading feature

    Saimon07/20/2018 at 06:32 0 comments

    Some baker asked to add an auto-fading feature on the LEDs. And here we are!

    We have added two new registers:

    These two registers make it possible to set the speed of fading. FADERGB is about the RGB led of the encoder FADEGP is related to the GP pins. The value that you write inside is the step speed of the fading ramp, and it's in ms. If the value is 0, the fade feature is disabled. It meas that when the new value of PWM is written, it is immediately updated to the output.

    The fading process starts when the FADE register is written. Fading process is complete according to the PWM value you have set. When the FADE PWM value is the same of the PWM value(it means that when fading is complete), an interrupt will be generated.

    The following image shows the fading process according to the PWM value set.

    Example of the fading process
    Example of the fading process

    Let's make an example:
    At the startup, i write as:

    • RLED=0
    • GLED=0
    • BLED=0
    • FADERGB=1
    In above case, the LEDs of the encoder are off and the fade has 1ms step size.

    Now i write as: RLED=0xFF
    And this moment, the ramp of the RLED starts and reaches the value 0XFF in 255ms. (the other LEDs remain OFF)
    When the ramp rreaches 0xFF, i get an interrupt from the INT pin. 

    After this, i write as: RLED=0x00. At this point, the fade ramp starts again and it will turn off the LED in 255ms, and an interrupt will be generated the end.

    Here you can see an example:

    In the video, every time when the encoders are rotated, the green LED turns on, while red LED (for RGB Encoder) and blue LED (for the normal Encoder) turn on when the thresholds are reached.

    The yellow channel is the INT pin, while the other channels are the I2C bus. As you can see, there is no I2C traffic during the fading. There is I2C traffic only when i move the encoder and when the fading reaches the target value. I have uploaded the code example showed in the video in GitHub:

View all 15 project logs

  • 1
    Get the PCB

    To make one I2C Encoder V2, the first thing that you need is the PCB. On GitHub or in the Files section, there are the gerber files.
    The PCB is 4 layer, this makes a bit complicated to do at home, for this reason the best option is to send the gerber file to some PCB manufacturing.

    Once you have the PCB, you can start to solder the components.

  • 2
    Start to solder the MCU

    The first component that you need to solder is the MCU. The MCU is the PIC16F18345 and the package is a 20 pin QFN package.
    There are many tutorial about soldering the QFN chip, my technique is the following:

    1. Tinning all the pad 
    2. Add flux
    3. Place the chip
    4. with the hot air melt the tin
    5. Necessary to add extra tin if some pads looks not soldered, or remove the extra tin
    6. Clean 
    Tin pads
    All the pads of the MCU must be soldered.
    With the hot air to finish to solder the chip

    MCU successfully soldered and cleaned
  • 3
    Start to solder the passive componets

    Once the MCU is soldered, it means that  the difficult part is done.

    Now you can start to solder the capacitors and the resistors. My method is the following:

    1. Apply the solder only to one pad
    2. Solder the component on that pad
    3. Solder the other pad

    Generally i start with the 10k resistor and the 47nF capacitor.
    That components are R8, R9, R10, R11, R12, C3 and C4.

    Components are soldered on one side
    Components are soldered on the both sides

View all 8 instructions

Enjoy this project?



Deltaprintr wrote 08/13/2019 at 05:47 point

I'm trying to set up automatic feeders for OpenPNP and each feeder has dual sensors of the VCNT2020CT-ND. Is it possible to incorporate this optical sensor in place of your encoders? I am trying to figure out how to wire all the feeders parallel so 2 sensors x 30 feeders only take up 2 pins on the Arduino (Output A, Output B from each feeder). Thoughts?

  Are you sure? yes | no

duck stock wrote 06/24/2019 at 22:37 point

Hello, -- thanks for an awesome project! 

Will this work with an optical encoder? For example --

  Are you sure? yes | no

Saimon wrote 06/25/2019 at 14:40 point

I think yes, but you need to manually solder the wires.
The footprint are not compatible. 

  Are you sure? yes | no

duck stock wrote 06/25/2019 at 14:46 point


  Are you sure? yes | no

fpgiovanini wrote 04/09/2019 at 23:57 point

Dear Mr Saimon, congratulations for the i2c rotary encoder V2 project.

Could you please , tell me which programmer I have to use to program the PIC?

Is the PICKIT 3 suitable for it?

  Are you sure? yes | no

UHF wrote 03/28/2019 at 13:35 point

I'm very pleased with my four encoder boards and the new Arduino library. Would you consider implementing acceleration and perhaps a more reliable long push detection with a minimum time? Many thanks for your hard work!

  Are you sure? yes | no

Philip wrote 02/05/2019 at 09:09 point

Hi, first of all: nice project! As a non-expert on electronics, I am a little stuck, however and would really appreciate some help. So I soldered everything together using the standard encoder and the rgb led that came with it. (according to the pictures here on this site)

With the Encoer I2Cv2_Basic example (Arduino), the encoder works just fine. But how can I make the led shine in pretty colors? Also, do I need a 4.7k pull-up from the GP pin to +5V? 

Best, philip  

  Are you sure? yes | no

Saimon wrote 02/09/2019 at 20:31 point

Hello Philip,
For using external RGB LED, you have to use the GP pins.

First configure the GP1, GP2, GP3 as PWM, with writeGP1conf( GP_PWM )
Second write the PWM value to the GPs register: writeGP1(   ):

If you use the GP pins as pwm you don't need the pull-up

  Are you sure? yes | no

ian wrote 11/04/2018 at 14:50 point

Does the INT pin need to go to the Arduino / Raspberry Pi? If so any GPIO line?

  Are you sure? yes | no

Saimon wrote 11/04/2018 at 14:58 point

Yes you can connect the INT pin to any GPIO pin. In my example i have used the pin A3 for the Arduino, while the pin 4 for the Raspberry.

  Are you sure? yes | no

ian wrote 11/04/2018 at 16:12 point

Thanks. Example RPi code working with one board, now to get both I2CEncoderV2 boards running...

  Are you sure? yes | no

ian wrote 10/24/2018 at 11:58 point

Does anyone have an example code for RaspberryPi, C or python?

  Are you sure? yes | no

Saimon wrote 10/24/2018 at 20:38 point

I'm working on it :)

  Are you sure? yes | no

ian wrote 10/25/2018 at 19:28 point

great, I’ve go my 2 boards wired up on a wall switch, just need to write some code. 

Excellent project. 

  Are you sure? yes | no

Saimon wrote 10/30/2018 at 22:02 point

I have just uploaded a Python library, in the next days i will add the documentation. Anyway is very similar to the Arduino lib.

  Are you sure? yes | no

ian wrote 11/04/2018 at 21:27 point

I could only get the RPi example to work when changed .writeMax(100.0) to .writeMax(100). Changing encconfig to FLOAT_DATA did not help.

I get the following error if .writeMax(100.0) is used:

Traceback (most recent call last):

  File "I2CEncoderV2/", line 26, in <module>


  File "/home/pi/I2CEncoderV2/", line 365, in writeMax

    self.writeEncoder32(REG_CMAXB4, max)

  File "/home/pi/I2CEncoderV2/", line 473, in writeEncoder32

    s = struct.pack('>i',value)

  Are you sure? yes | no

Giorgio Croci Candiani wrote 07/05/2018 at 08:23 point

Hi Saimon & Atikaimu, congratulations on this project, it seems well thought out and carefully designed.

If I can venture to make a little suggestion, there are two (software) features that I think it would make sense to add.

First of all, the encoder should have support for fast rotation, with additional parameters to configure the speed threshold and the magnitude of the effect on the output count. You probably have thought of this already, I just wanted to mention it because I think it's fairly important for a device like this.

The other feature would be an alternative interfacing method. 
The interrogation sequence you chose is typical for I2C; however, despite the meaningful presence of the IRQ line, it still involves a potentially long polling time if there are many connected devices.
You could also offer a multi-master arrangement as an alternative option; each encoder unit could be configured as master, and only send its data to a fixed destination address (or as broadcast) when an event occurs.
Of course, if you want to prevent collisions (and the need for a collision management which is anything more than trivial), this would imply that no more than one encoder can be activated at the same time; however, since this is very unlikely even in a large group of encoders, this constraint could be acceptable in most cases, making this option attractive.

I hope that you can find these considerations interesting for thought... keep up the very good work!

  Are you sure? yes | no

H00GiE wrote 05/15/2018 at 10:17 point

Can't wait, this is exactly what I need for my project. 

  Are you sure? yes | no

Atika wrote 06/28/2018 at 10:01 point

Hello HOOGIE, thank you for your comment! This project is LIVE in kickstarter now:

  Are you sure? yes | no

H00GiE wrote 06/28/2018 at 11:23 point

wow, pretty reasonable price as well... expect a backing from me for sure... 

  Are you sure? yes | no

H00GiE wrote 06/28/2018 at 17:08 point

UGH, i just found a downside of kickstarter: They ONLY accept creditcards and supported debet cards, here we regularly don't use any of the supported ones... is there a way to use a different method of payment?? Like paypal?

  Are you sure? yes | no

Orlando Hoilett wrote 04/15/2018 at 21:39 point

This project shows a lot of ingenuity. Well done.

  Are you sure? yes | no

Atika wrote 06/28/2018 at 10:01 point

Hello Oriando, thank you so much! In case you are interested, this project is LIVE in kickstarter now:

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates