I2C Rotary encoder
Version : I2C-ROT-003 V01.00
This document shows an example of an Arduino library module to interface with the Rotary encoders. It also includes a couple example programs to show how to use the library to read the status of the rotary encoders.


How to connect to your arduino

The rotary module can be connected to your arduino using a 4-wire cable. All you need is power and the I2C bus.

Power
You can use the power from your Arduino to power the rotary module. Just connect +5V and GND.
I2C bus
The bus consists of 2 wires. SDA (data) and SCL (clock). Most Arduino motherboards have these pins.
Board SCL SDA
Arduino UNO R3 A5 A4
Arduino Nano A5 A4
Arduino Pro Mini A5 A4
Arduino Mega 2560 21 20
Arduino Leonardo 3 2
Please check your Arduino documentation for other boards.
I2C pull-up resistors
The I2C bus needs pull-up resistors on both SDA and SCL. These resistors are provided on the I2C-Rotary-003 board. The value of these resistors is 10K. Low enough to support 400 kB communication over short distances, and high enough to allow multiple boards in parallel on the same bus.

I2C-Rotary-003 Library

Reading the status of the rotary encoders will require a set of interface functions. There are many ways to write an interface module. This library will implement such functions.

For a detailed interface specification: See RotaryEncoder Specification

Installation


Installation using Arduino library manager


Manual installation


To check for successful installation


If you want to use the library without installing the library, then you can extract the library from the I2C_Rotary_003.zip and include it in your arduino project directory.

You need "I2C_Rotary_003/src/I2C_Rotary_003.h".


I2C_Rotary object

class I2C_Rotary

Defines a class I2C_Rotary. Can be used to declare rotary objects. Each class object will contain the administration of a single rotary encoder, so you typically need 3 objects. One or each rotary encoder.

I2C_Rotary member functions

   I2C_Rotary(byte I2C_Address, byte SwitchId)

Constructor function for I2C_Rotary objects.

   byte Button();

Member function. Sends a message to the rotary module to check if the button was pressed.

   int Clicks();

Member function. Sends a message to the rotary module to check if the shaft was turned. Returns number of clicks. Negative if turned backwards.

   void SetDirection(byte NewDirection)

   void SetAccelleration(byte Accelleration)

If enabled, each click is multiplied by an accelleration value if the clicks are fast enough:
   byte ChangeAddress(byte OldAddress, byte NewAddress)

This function will change the I2C address from the module with the oldAddress to the NewAddress. This may fail if the address was already changed earlier. The new Address is registered in EEprom and remains effective after power-down.

I2C_Rotary member variables

All member variables can be updated by the application. Just set a new value.

   byte  m_Address;    // I2C address of rotary controller

I2C address. The library uses an 8-bit address. Lowest bit is ignored.

The Arduino Wire library uses a 7-bit address. So this Address is shifted by one position when used.

   byte  m_Id;         // Switch Nr 1 .. 3
Contains a Switch number. Range 1 .. 3.
Selects one of the 3 switches on the board.
   float m_MaxValue;
   float m_MinValue;   // Limits

User application can set the minimum and maximum value. Any value is supported, positive or negative, as long as m_MaxValue is larger than m_MinValue.

   float m_StepSize;

User application can set the value of a single click.
Each click is multiplied by m_Stepsize, so you can select microsteps by setting a small stepsize (like 0.001) or giant steps by setting a large stepsize (like 1000.0) or anything inbetween.
You can also modify stepsize on the fly, for example by using a button to select between coarse/fine updates.

   byte  m_XmitStatus; // Communicatio0n status

Contains the Wire status from the most recent message. Can be used for diagnostics purposes.

   byte  m_RecvCount;  //

Number of bytes of the most recent response message.


Examples


Reading Rotary Status

Here is an example .ino application that uses the above library. This application prints the rotary status to the serial port, so you can see what happens when you turn the knobs.

#include <I2C_Rotary_003.h>
I2C_Rotary Rotary_SW1(0xA0, 1);  // SW1
I2C_Rotary Rotary_SW2(0xA0, 2);  // SW2
I2C_Rotary Rotary_SW3(0xA0, 3);  // SW3

   // Set parameters for Rotary_SW1
   Rotary_SW1.m_MinValue =   0.0;
   Rotary_SW1.m_MaxValue = 100.0;
   Rotary_SW1.m_StepSize =   1.0;
   Rotary_SW1.m_Value    =   0.0;
   Rotary_SW1.SetDirection(0);
   Rotary_SW1.SetAccelleration(1);

   // Set parameters for Rotary_SW2
   Rotary_SW2.m_MinValue =  40.0;
   Rotary_SW2.m_MaxValue =  50.0;
   Rotary_SW2.m_StepSize =   0.1;
   Rotary_SW2.m_Value    =  40.0;
   Rotary_SW2.SetDirection(1);
   Rotary_SW2.SetAccelleration(0);

   // Set parameters for Rotary_SW3
   Rotary_SW3.m_MinValue =  -40000.0;
   Rotary_SW3.m_MaxValue =  +40000.0;
   Rotary_SW3.m_StepSize =     100.0;
   Rotary_SW3.m_Value    =   10000.0;
   Rotary_SW3.SetDirection(0);
   Rotary_SW3.SetAccelleration(0);

Set limits and configuration for each rotary encoder. Note that this example sets different direction and Stepsize for the 3 encoders, just to show the possibilities. You should adjust these values to your needs.

   // Scan status of the rotary buttons:
   byte Button1 = Rotary_SW1.Button();
   byte Button2 = Rotary_SW2.Button();
   byte Button3 = Rotary_SW3.Button();

Read the status of each rotary button and save the value is a local variable.

   // Scan status of the rotary clicks:
   int  Clicks1 = Rotary_SW1.Clicks();
   int  Clicks2 = Rotary_SW2.Clicks();
   int  Clicks3 = Rotary_SW3.Clicks();

Read the rotation clicks of each rotary and save the value is a local variable.

   // Just write the result to the serial port
   if(Button1)
   {  Serial.print( F("SW1 : Button = ") );
      Serial.println( Button1 );
   }
   if(Button2)
   {  Serial.print( F("SW2 : Button = ") );
      Serial.println( Button2 );
   }
   if(Button3)
   {  Serial.print( F("SW3 : Button = ") );
      Serial.println( Button3 );
   }

Print a message when a button is pressed

   if(Clicks1)
   {  Serial.print( F("SW1 : Clicks = ") );
      Serial.println( Clicks1 );
      Serial.print( F("SW1 : Value  = ") );
      Serial.println( Rotary_SW1.m_Value );
   }
   if(Clicks2)
   {  Serial.print( F("SW2 : Clicks = ") );
      Serial.println( Clicks2 );
      Serial.print( F("SW2 : Value  = ") );
      Serial.println( Rotary_SW2.m_Value );
   }
   if(Clicks3)
   {  Serial.print( F("SW3 : Clicks = ") );
      Serial.println( Clicks3 );
      Serial.print( F("SW3 : Value  = ") );
      Serial.println( Rotary_SW3.m_Value );
   }

Print the clicks of each rotary encoder.


Scanning Bus

You can change the I2C address of the rotary module. The module will then no longer respond to its default address. In that case you can use this scanner application to check if you have any modules connected on the bus.

This application will try to communicate with all possible addresses on the bus and will try to interprete the response. Our I2C-Rotary module will respond if its address matches, and will return its name and version on request.

   // Scan the bus to find all rotary controllers
   byte Count = 0;
   for(uint8_t Address = 8; ; Address += 2)
   {
      if(Address == 0)
      {  // End of the list
         if(Count == 0)
         {  Serial.println( F("Rotary controller not found") );
         }
         break;
      }

      byte Response[20];
      Rotary_SW1.m_Address = Address >> 1;
      Rotary_SW1.ReadRegister( 0xF1, Response, 20 );
      if(Rotary_SW1.m_XmitStatus == 0)
      {  if(    (Rotary_SW1.m_RecvCount > 5)
             && (Response[0] == Address)
             && (Response[1] == 0xF1) )
         {  Serial.print( F("Bingo. Address = 0x") );
            Serial.println(Address, HEX);
            Serial.print( F("Model   = ") );
            Serial.println((const char *) &Response[2]);

            Rotary_SW1.ReadRegister( 0xF2, Response, 20 );
            Serial.print( F("Version = ") );
            Serial.println((const char *) &Response[2]);

            Rotary_SW1.ReadRegister( 0xF3, Response, 20 );
            Serial.print( F("Release = ") );
            Serial.println((const char *) &Response[2]);

            Count += 1;
         }
         else
         {  Serial.print( F("Unknown device at Address = 0x") );
            Serial.println(Address, HEX);
         }
      }
   }

Tries all even addresses from 0x08 to 0xFE to see if there is a response. If a response is found, and it is a rotary module, then it reads module and version info and write a message on the serial port.


Change I2C Address

void ChangeAddress(byte OldAddress, byte NewAddress)
{
   Serial.println();
   Serial.println();
   Serial.print( F("Change Address from 0x") );
   Serial.print( OldAddress, HEX );
   Serial.print( F(" to 0x") );
   Serial.print( NewAddress, HEX );
   Serial.println();

   switch( Rotary_SW1.ChangeAddress(OldAddress, NewAddress) )
   {  case 0:  Serial.println( F("- New address already set") ); break;
      case 1:  Serial.println( F("- New address occupied")    ); break;
      case 2:  Serial.print  ( F("- Switched to new address 0x") );
               Serial.println( NewAddress, HEX ); break;
      case 3:  Serial.println( F("- Address-change failed")   ); break;
   }
}

Implementation of a function that changes the address of a module on the bus. The module with the OldAddress is updated to NewAddress.

void loop()
{
   ChangeAddress(0xA0, 0xA2);
   delay(500);
}

Here we change address 0xA0 (default) to 0xA2.

The user should connect a single module with default address 0xA0, an that module's address is updated to 0xA2.

Once updated, it will no longer respond to an update request because its address does not match OldAddress anymore.

Prints a message on the serial port to indicate success or failure.