Raspberry Pi RC Transmitter Phase 3 – Adding an OLED display

The Raspberry Pi RC transmitter with a 128x64 OLED display
Advertisements

With the addition of the easily configurable variables for centering, end-points, reversing etc it only makes sense to add a display to the transmitter to allow for in-the-field configuration. Since the idea of the transmitter is to be highly portable and self contained then it should be possible to change the setup without external tools and displays. An inbuilt display is almost unavoidable but when I was deciding what buttons would be needed I quickly realised that I already have a game-pad full of buttons.

I needed a display that would fit in the 50mm wide case that I had already 3D printed. A quick search on the internet turned up a very cheap but well documented SSD1306 based 0.96″ 128×64 resolution OLED display on ebay. A similar display was sold by Adafruit who also supply excellent documentation on connecting the display to a Raspberry Pi as well as Python libraries for its use.

After wiring the display to the Raspberry Pi directly via the I2C pins on the GPIO port and mounted the display in a 3D printed lid I played with the Adafruit examples and then began modifying my code. I started by removing all the display related code that came with the PyGame Joystick demo that I began with and added the initialisation code from the Adafruit examples. Several lines were then added to the end of the code to display the 8 output channel pulse length in micro-seconds.

Next step will be to add several selectable screens that each display the variables that I want to be able to change via the controllers D-pad.

import pygame
import signal
import threading
import pigpio
import ppm

import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

ppm_output = []

ppm_output_pin = 18									#Output pin for transmitter module

reverse_channel = [1, -1, 1, 1, 1, 1, 1, 1]					#set if each channel is reversed

center = [0, 0, 0, 0, 0, 0, 0, 0]	                #center value for each channel in mili-seconds

lower_end_point = [-1, -1, -1, -1, -1, -1, -1, -1]			#lower end point as a percentage

upper_end_point = [1, 1, 1, 1, 1, 1, 1, 1]			#upper end point as a percentage

expo = [1, 1, 1, 1, 1, 1, 1, 1]						#expo value. should be between 0.5 and 1 for negative expo and 1 to 2 for positive expo

channel_assignment = [['a', 3], ['a', 4], ['a', 1], ['a', 0], ['a', 5], ['a', 2], ['b', 0], ['b', 2]]

# Raspberry Pi pin configuration:
RST = None     # on the PiOLED this pin isnt used

# 128x64 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)

# Initialize library.
disp.begin()

# Clear display.
disp.clear()
disp.display()

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# Draw a black filled box to clear the image.
draw.rectangle((0,0,width,height), outline=0, fill=0)

# Draw some shapes.
# First define some constants to allow easy resizing of shapes.
padding = -2
top = padding
bottom = height-padding
# Move left to right keeping track of the current x position for drawing shapes.
x = 0


# Load default font.
font = ImageFont.load_default()

pi = pigpio.pi()
pi.set_mode(ppm_output_pin, pigpio.OUTPUT)        

if not pi.connected:
    exit(0)

pi.wave_tx_stop() # Start with a clean slate.

ppm = ppm.X(pi, ppm_output_pin, frame_ms=20)
        
pygame.init()
 
#Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# Initialize the joysticks
pygame.joystick.init()
    

# -------- Main Program Loop -----------
while done==False:

    # Draw a black filled box to clear the image.
    draw.rectangle((0,0,width,height), outline=0, fill=0)

    ppm_joystick_values = [0, 0, 0, 0, 0, 0, 0, 0]
    ppm_chanels_us = [0, 0, 0, 0, 0, 0, 0, 0]
    scaled_channel_value = [0, 0, 0, 0, 0, 0, 0, 0]
    
    # EVENT PROCESSING STEP
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done=True # Flag that we are done so we exit this loop
        
        # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
        if event.type == pygame.JOYBUTTONDOWN:
            print("Joystick button pressed.")
        if event.type == pygame.JOYBUTTONUP:
            print("Joystick button released.")
            
    joystick_count = pygame.joystick.get_count() 
    
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()
    
                
        for i in range(8):
            if channel_assignment[i][0] == 'a':
                ppm_joystick_values[i] = joystick.get_axis(channel_assignment[i][1])
            
            if channel_assignment[i][0] == 'b':
                ppm_joystick_values[i] = joystick.get_button(channel_assignment[i][1])
              
            if channel_assignment[i][0] == 'h':
                ppm_joystick_values[i] = joystick.get_hat(channel_assignment[i][1])

        
        for i in range(8):
            ppm_joystick_values[i] = ppm_joystick_values[i] * reverse_channel[i]
	    
            if ppm_joystick_values[i] < 0:
                scaled_channel_value[i] = -abs(ppm_joystick_values[i])**expo[i]) * abs(lower_end_point[i] - center[i]) + center[i]    #Processes the negative joystick values
                 
            if ppm_joystick_values[i] > 0:
                scaled_channel_value[i] = ppm_joystick_value[i]**expo[i] * (upper_end_point[i] - center[i]) + center[i]               #Processes the positive joystick values
              
            ppm_chanels_us[i] = int(round(1100 + (500 * scaled_channel_value[i])))
           
        ppm.update_channels(ppm_chanels_us)

    draw.text((x, top),       "Ch 1: " + str(ppm_chanels_us[0]), font=font, fill=255)
    draw.text((x, top+8),     "Ch 2: " + str(ppm_chanels_us[1]), font=font, fill=255)
    draw.text((x, top+16),    "Ch 3: " + str(ppm_chanels_us[2]), font=font, fill=255)
    draw.text((x, top+24),    "Ch 4: " + str(ppm_chanels_us[3]), font=font, fill=255)
    draw.text((x, top+32),    "Ch 5: " + str(ppm_chanels_us[4]), font=font, fill=255)
    draw.text((x, top+40),    "Ch 6: " + str(ppm_chanels_us[5]), font=font, fill=255)
    draw.text((x, top+48),    "Ch 7: " + str(ppm_chanels_us[6]), font=font, fill=255)
    draw.text((x, top+56),    "Ch 8: " + str(ppm_chanels_us[7]), font=font, fill=255)

    # Display image.
    disp.image(image)
    disp.display()
    
    # Limit to 20 frames per second
    clock.tick(20)
    
# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit ()

Leave a Reply