Python Forum
Thread Rating:
  • 1 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Rotary Encoder Jitter
#1
I've made an Internet Radio using a Raspbbery Pi, Rotary encoder,
and 20x4 LCD. Stations are selected with a rotary encoder, and the
switch is pressed to play the stream.

Each stream is decoded with linux mpc and any track/artist data sent
to the display.

It all works ecept as I turn the switch I encounter "jitter". The
count is diaplay on last line of 4 line display and if I rotate
too quickly digits are missed and turning anticlockwise is even
more erratic, sometimes skipping numbers altogether.

There may not be anything wrong with the code, the Raspberry Pi is the
fisrt Model B single core at 700MHz and it may just be too resource
hungry.
The tutorial for the Rspberry Pi is below:
https://www.modmypi.com/blog/how-to-use-...spberry-pi

I modified the code to use the switch on the rotary encoder, attached as
rotary_mod.py below:

rom RPi import GPIO
from time import sleep

clk = 22
dt  = 27
sw  = 17
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(clk, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(dt, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(sw, GPIO.IN, pull_up_down=GPIO.PUD_UP)
counter = 0
clkLastState = GPIO.input(clk)

def function():
                clkState = GPIO.input(clk)
                dtState = GPIO.input(dt)
                switch = GPIO.input(sw)
                global clkLastState,counter
                if clkState != clkLastState:
                        if dtState != clkState:
                                counter -= 1
                        else:
                                counter += 1
                clkLastState = clkState
                sleep(0.01)
                if counter == 20:
                        counter = 0
                return(counter,switch)  # return count pstn and switch on or off
The Radio program I have called test.py for now:

from RPi import GPIO
from time import sleep
import lcd_module
from lcd_module import *
import subprocess
import rotary_mod

lcd_init()
lcd_bl(1)
lcd_cls()
tk =""


def decode():
        track = subprocess.getoutput('mpc current')
        if track == "":
                return
        track = track.split(':')[-1]
        art = track.split('-')[0]
        tk = track.split(' -')[1:]
        print(tk)
        tk = ",".join(str(x) for x in tk)
        print (tk)
        #tk = (track.lstrip(' ')).split(' -')[1:]
        #tk = str(tk.replace(',' ,  ''))
        print(art,tk)
        lcd_string(art, line2)
        if len(str(tk)) > 20:
                scroll(tk,line3)
        else:
                lcd_string(tk, line3)

while True:
        #  function() in rotary_mod returns (a,b) where a=count, b= sw state
        counter=rotary_mod.function()[0]
        switch=rotary_mod.function()[1]
        #print (counter)
        lcd_string(str(counter), line4)

        if (counter == 0):
                lcd_string1("   Push to clear LCD",line1)
        if (counter == 0 and switch == 0):
                lcd_cls()
        if counter == 1:
                lcd_string1("   Cinemix",line1)
        if (counter == 1 and switch == 0):
                lcd_cls()
                subprocess.call('mpc play 1', shell=True)
                station = "Cinemix France"
                lcd_string(station)
                decode()
        if counter == 2:
                lcd_string1("   Smooth Jazz",line1)
        if (counter == 2 and switch == 0):
                lcd_cls()
                subprocess.call('mpc play 2', shell=True)
                station = "Smooth Jazz"
                lcd_string(station)
                decode()
        
        if counter == 9:
                lcd_string(   "Stop Music", line1)
        if (counter == 9 and switch == 0):
                subprocess.call('mpc stop', shell=True)
                lcd_cls()
                                
For clarity just two stations are selected from mpc playlist.
The functions lcd-init(), lcd_cls(), lcd_string() etc are from the
lcd_module , listing below:

import RPi.GPIO as GPIO
import time
# LCD to GPIO Mapping BCM numbering (Pin names after #)
LCD_RS = 7     # CE1
LCD_E  = 8     # CE0
LCD_D4 = 25    # P6
LCD_D5 = 24    # P5
LCD_D6 = 23    # P4
LCD_D7 = 4    # P1
# Backlight Control Pin (via BJT to control BL)
LCD_BL = 18     # P7


# Display Characteristics
#  Set LCD width 16 or 20 characters per line
LCD_WIDTH = 20
#LCD_CHR = True
#LCD_CMD = False
# Line address in RAM, if using 16x2 comment out line 3 and 4 
line1 = 0x80
line2 = 0xC0
line3 = 0x94
line4 = 0xD4
line45 = 0xDe
# LCD character true, lcd command set false
LCD_CHR = True
LCD_CMD = False

# Timing constants for OLED displays check data sheet
E_PULSE = 0.0005
E_DELAY = 0.0005

# Initialise GPIO ports 
def lcd_init():
  GPIO.setwarnings(False)
  GPIO.setmode(GPIO.BCM)       # Use BCM GPIO numbers
  GPIO.setup(LCD_E, GPIO.OUT)  # E
  GPIO.setup(LCD_RS, GPIO.OUT) # RS
  GPIO.setup(LCD_D4, GPIO.OUT) # DB4
  GPIO.setup(LCD_D5, GPIO.OUT) # DB5
  GPIO.setup(LCD_D6, GPIO.OUT) # DB6
  GPIO.setup(LCD_D7, GPIO.OUT) # DB7
  GPIO.setup(LCD_BL, GPIO.OUT) # BL Gnd 

  # Initialise LCD display parameters
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display
  time.sleep(E_DELAY)

#################### Display Functions ##############################

def lcd_byte(bits, mode):
  # Send byte to data pins,  bits = data
  # mode = True  for character, False for command
  GPIO.setwarnings(False)
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(LCD_RS, GPIO.OUT)
  #new
  GPIO.setup(LCD_BL, GPIO.OUT)

  GPIO.output(LCD_RS, mode) # RS

  # High bits
  GPIO.output(LCD_D4, bits&0x10==0x10)
  GPIO.output(LCD_D5, bits&0x20==0x20)
  GPIO.output(LCD_D6, bits&0x40==0x40)
  GPIO.output(LCD_D7, bits&0x80==0x80)
  lcd_toggle_enable()
# Low bits
  GPIO.output(LCD_D4, bits&0x01==0x01)
  GPIO.output(LCD_D5, bits&0x02==0x02)
  GPIO.output(LCD_D6, bits&0x04==0x04)
  GPIO.output(LCD_D7, bits&0x08==0x08)
  lcd_toggle_enable()


def lcd_toggle_enable():  # Enable Display by  toggling Pin 8
  time.sleep(E_DELAY)
  GPIO.output(LCD_E, True)
  time.sleep(E_PULSE)
  GPIO.output(LCD_E, False)
  time.sleep(E_DELAY)


def lcd_string1(message, line=line1):
  # Send string to display, defaults to line1
  message = message.ljust(LCD_WIDTH)
  lcd_byte(line, LCD_CMD) # print at line number set by line
  for i in range(LCD_WIDTH):
      lcd_byte(ord(message[i]),LCD_CHR) # convert each char to hex and

def lcd_string(message, line=line1):
  # Send string to display, defaults to line1
  message = message.ljust(LCD_WIDTH)
  lcd_byte(line, LCD_CMD) # print at line number set by line
  for i in range(LCD_WIDTH):
      if (ord(message[i])) == 233: # check utf e grave
        lcd_byte(0,LCD_CHR)   #replace custom charset 0
        continue
      lcd_byte(ord(message[i]),LCD_CHR) # convert each char to hex and

def scroll(scrollmsg, line=line1):
         # defaults to speed=0.4 and line1 takes 1 to 3 arguments 
    scrollmsg = (" "+scrollmsg+" ") # Add space to message
    for i in range (len(scrollmsg)):
    #for i in range (16):    
        lcd_string(scrollmsg[(LCD_WIDTH-i):i],line)  #reverse needs padding with spaces
        time.sleep(s)

def lcd_cls():  # clear all  lines of display
    lcd_byte(0x01,LCD_CMD)

def lcd_clr(line):  # Clear line 1 or 2 by writing spaces
    lcd_string("                ",line)

def lcd_bl(control):
    GPIO.output(LCD_BL, control)
I realise that this is not possible to try unless someone has same hardware,
but any ideas about the switch jitter are welcome.

Again its possible that the Pi is a little underpowered to handle
the data stream, update display and respond to rotary switch at the same
time.

Thanks in advance.
Reply
#2
This acts like a keyboard debounce problem, which probably applies to your rotary switch as well.
To debounce a key, you read the key, wait for a specified amount of time (device dependent) and
read the key again. If the results are the same, consider it a valid keypress.

You might want to try somethng similar. The amount of delay can be adjusted. If too long, you may miss
the press altogether, if too short, you may receive several instances of the key, even though only one was pressed.

I am only guessing here, if you feel that this is a logical possibility, you should experiment.
see: http://whatis.techtarget.com/definition/debouncing
Reply
#3
OK, thank you Larz60+
I'll experiment with some more sleep.time in the rotary encoder module,
and see if that helps.
Reply
#4
After the sleep time you need to re-read the switch to see if it's still active.
I'm thinking the delay should be around 200 ms.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  categorical encoder Scott 0 2,654 May-19-2018, 03:38 AM
Last Post: Scott

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020