Posts Tagged ‘ lcd

Scrolling the Adafruit 16×2 LCD+Keypad

I wanted to add additional functionality to my Raspberry FM project by having any long station names \ song names scroll on the Adafruit 16×2 LCD+keypad for Raspberry Pi: While the lcd Python module Adafruit provides has methods to scroll the text (scrollDisplayLeft, scrollDisplayRight, autoScroll), I was unable to get them to work nor find any good examples.  Maybe it’s completely possible with what they provide, but I had no luck with it.  If anyone does know how, please comment! :)

Why not write my own?  That’s a fun thing to do on a Saturday afternoon, right? 😉  Find a snapshot of the Python source below, and a link to the most current version on Bitbucket here: lcdScroll.py

To see it in action:

It’s a standalone module I designed to work with any sized lcd:  It’s really just a text formatter:  All the drawing to the LCD would be handled by some other application.  A very simple example can also be found on Bitbucket here: lcdScrollTest.py Or of course you could check out the Raspberry FM source here:  raspberryFm01.py

For either the top or bottom line, if they are longer than 16 characters (which is completely adjustable based on the type of lcd used), they will auto-scroll.  If less than 16 characters, no scrolling happens.  So you can have completely independent scrolling on any line based on their length.

#!/usr/bin/python
"""
lcdScroll.py
Author             :  Eric Pavey 
Creation Date      :  2014-02-08
Blog               :  http://www.akeric.com/blog

Free and open for all to use.  But put credit where credit is due.

OVERVIEW:-----------------------------------------------------------------------
Create scrolling text on a LCD display.  Designed to work on the the 
Adafruit LCD  + keypad, but it's not tied to any specific hardware, and should
work on a LCD of any size.

See lcdScrollTest.py for simple example usage.
"""

class Scroller(object):
    """
    Object designed to auto-scroll text on a LCD screen.  Every time the scroll()
    method is called to, it will scroll the text from right to left by one character
    on any line that is greater than the provided with.
    If the lines ever need to be reset \ updated, call to the setLines() method.
    """
    def __init__(self, lines=[], space = " :: ", width=16, height=2):
        """
        Instance a LCD scroller object.

        Parameters:
        lines : list / string : Default empty list : If a list is passed in, each 
            entry in the list is a  string that should be displayed on the LCD, 
            one line after the next.  If a string, it will be split by any embedded 
            linefeed \n characers into a list of multiple lines . 
            Ultimately, the number of entries in this list must be equal to or 
            less than the height argument.
        space : string : Default " :: " : If a given line is longer than the width
            argument, this string will be added to the end to help designate the
            end of the line has been hit during the scroll.
        width : int : Default 16 : The width of the LCD display, number of columns.
        height : int : Default 2 : the height of the LCD, number of rows.
        """
        self.width = width
        self.height = height
        self.space = space
        self.setLines(lines)

    def setLines(self, lines):
        """
        Set (for the first time) or reset (at any time) the lines to display.
        Sets self.lines

        Parameters:
        lines : list : Each entry in the list is a string
            that should be displayed on the LCD, one line after the next.  The 
            number of entries in this list must be equal to or less than the 
            height argument.
        """
        # Just in case a string is passed in, turn it into a list, and split
        # by any linefeed chars:
        if isinstance(lines, basestring):   
            lines = lines.split("\n")
        elif not isinstance(lines, list):
            raise Exception("Argument passed to lines parameter must be list, instead got: %s"%type(lines))
        if len(lines) > self.height:
            raise Exception("Have more lines to display (%s) than you have lcd rows (%s)"%(len(lines), height))            
        self.lines = lines
        # If the line is over the width, add in the extra spaces to help separate
        # the scroll:
        for i,ln in enumerate(self.lines[:]):
            if len(ln) > self.width:
                self.lines[i] = "%s%s"%(ln,self.space)

    def scroll(self):
        """
        Scroll the text by one character from right to left each time this is
        called to.

        Return : string : The message to display to the LCD.  Each line is separated
            by the \n (linefeed) character that the Adafruit LCD expects.  Each line
            will also be clipped to self.width, so as to not confuse the LCD when
            later drawn.
        """
        for i,ln in enumerate(self.lines[:]):
            if len(ln) > 16:
                shift = "%s%s"%(ln[1:], ln[0])
                self.lines[i] = shift
        truncated = [ln[:self.width] for ln in self.lines]
        return "\n".join(truncated)

Raspberry FM Part 2, the sequal

Nearly a year ago I wrapped up a project I called “Raspberry FM“:  A Raspberry Pi based internet radio streamer coupled with a MaKey MaKey as the interface.   Why the name Raspberry FM?  It’s a mashup of a Raspberry Pi, and my favorite internet radio station, Soma FM.   It worked, but had a few problems:

  • There was no visual feedback, the MaKey MaKey was purely an input.
  • I was having a hard time getting Python to interface with with MPlayer (probably due to my own ignorance), the audio player I had chosen, so everything was done via bash.

Fast forward nearly a year:  After learning about the Mpd audio player/server, and (one of) its clients Mpc, it re-piqued my interest in programming a Python app to play music on the Pi.  Second time around, it all turned out really well:  New software combined with new hardware and a 3d-printed case turned it into a good looking compact unit.

Overview of the whole process below (pic on top, video on bottom).  During the development I decided to write my own Python music player around mpc & Adafruit’s LCD library.  At this point there are several others online, but I enjoyed coding it from scratch.

adaLcdCase

Raspberry FM Features:

  • Auto-on with the Pi
  • If no internet is present or it drops out, app will go into holding pattern until it returns.
  • Can change between any number of stations. (left\right buttons).
  • Stations are easy to add via the commandline:  No need to update Python source: SSH into the Pi, and add\remove what you need.
  • Increase\decrease volume (up\down buttons).
  • Station and song info is displayed and auto-scrolls.
  • Shutdown Pi  (select+left) or turn off program (for debugging, select+right)
  • Lots of color changing when buttons are pressed!

Hardware needed:

  • Raspberry Pi (I used a B model)
  • Adafruit RGB 16×2 LCD+Keypad Kit : Solder and install!
  • Optional:  Custom 3D printed case I designed (well, I designed the top part), download on Thingiverse.  Print & install!  Took me about an 1:20 on my Makerbot Replicator (1).
  • I stream the internet radio over cat5, but I’ve also had success with wifi.
  • I use the headphone jack for audio out.

Software needed:

  • This was all done on the Raspbian distro via NOOBS.
  • My “Raspberry FM” Python program.  Find on Bitbucket.
  • You’ll need Adafruits CharLCDPlate library.
  • FYI, I coded this all via the Adafruit WebIDE, I’d recommend anyone else to use it as well to help manage the various Python modules on the Pi.
  • MPD & MPC:  sudo apt-get install mpc mpd

Steps:

  • I presume you already have your Pi setup.  If not, see my notes here on the general steps needed to get a Pi live and kicking.
  • Setup Pi to auto-login.  See notes here.
  • Download the Raspberry FM Python program to a folder of your choosing.  Since I coded this via the WebIDE, both the creation of my code and the integration of the Adafruit LCD modules was all handled via the WebIDE.  Make sure you download all the Adafruit CharLCDPlate modules as well and put them in the same directory.
  • Install MPD & MPC.
  • Add stations to MPC.  This is super easy on the commandline.  May I recommend anything from SomaFM?
    mpc add http://ice.somafm.com/groovesalad
  • Setup Pi to auto-run a program on start.  See notes here.  You will point that script to wherever you saved the Raspberry FM Python script.  For example, my startup.sh script looks like:
  • #!/bin/bash
    echo startup.sh : Launching raspberryFm.py
    sudo python /usr/share/adafruit/webide/repositories/my-pi-projects/Adafruit_CharLCDPlate/raspberryFm01.py
  • Restart the Pi and listen to the music!

The final result in action:

Arduino talks to Processing, Python…

Note:  This post has been updated, see notes below.

Been enjoying tinkering around with the Arduino and the Electronic Brick Kit I recently got. It’s easy to send data from the computer to the Arduino and run sketches there, but what about the other way? The Arduino can talk back with the computer over its serial port, so at that point any language that can read the serial port can read the Arduino’s data.  Since I’m most comfortable with Python and Processing, that’s what the below code covers.

Few things to note:

  • Python has no built-in serial modules\packages (to my knowledge), but I found several references to pySerial, that appears to be the go-to source for cross-platform serial info in Python.  So you will need that.
  • Update:  After a bit of thinking, I have got Python working independently from Pygame, see notes below.
    • (Old subject):  I couldn’t get Python working directly:  When I’d run a loop to capture the serial data, it would hang the shell.  I figured this was because it wasn’t ‘advancing in time’ (just looping endlessly on the first item), so I popped the code into Pygame, and it started working flawlessly.  There probably is a way to do this in Python, but this is my first stab at reading any kind of serial data.  So the Python example is implemented via Pygame.
  • This is all authored on  Win2k OS.  Serial ports on different OS’s are handled differently.  For the Python and Processing code I define a variable that sets which com port the Arduino is on (in my case, it’s COM5), which is something that you should have already defined via the Arduino IDE.  Just make sure those values match.  And again, if you’re on Linux of Mac, the serial port values will be different.
  • On both the Processing and (the old)  Python examples, they will draw a window with a circle inside that will change in size based on the passed in serial data. Based on the sensor I was using (twist sensor) on an analog pin, this maps the voltage of the sensor into values 0-1023, which are easy to then map into the on-screen graphics. The code will also print out to the shell\IDE the captured serial values.
  • Finally, I should point out I pulled a lot of info from the book Getting Started with Arduino.

Dependencies:

Begin:

Arduino:

Here is the Arduino code.  I have a rotational sensor on analog pin 5.  But you can use any kind of sensor that you want.  I picked the rotational sensor since it’s easy to see the values change.

/**
serialSensor
Eric Pavey 2009-12-26

Sketch will print the value from the sensor to the serial port
*/

#define SENSOR 5

int val = 0;

void setup(){
  Serial.begin(9600);
}

void loop(){
  val = analogRead(SENSOR);
  // Print to the serial port:
  Serial.println(val);
  delay(100);
}

When that is uploaded to the Arduino you can hit the ‘Serial Monitor’ button in the IDE, which will pop up a new window that shows the values captured from the serial port:  When I twist my rotational sensor, I see the result print in the serial monitor.  Pretty straight-forward.  Be sure to close the serial monitor window before you run any of the below code, or they will be blocked from accessing the serial port.

Processing:

The IDE’s for Processing and Arduino are very similar; the Arduino docs say it was ‘built on’ Processing, and the resembelence is strong.  When executed, will create a window, with a white circle inside thats size is controlled by the sensor data passed through the serial port.

Here is the Processing code:

/**
 * readArduinoSerial
 * Eric Pavey 2009-12-26
 * Read data from the computers serial port, that is being fed
 * by an Arduino sketch.  It's expecting values from 0->1023.
 */

import processing.serial.*;

// Create object from Serial class
Serial myPort;  

// Converted data received from the serial port
float val = 1.0;
float prevVal = 1.0;
int minSerial = 0;
int maxSerial = 1023;
// Define which port the Arduino is on:
String arduino = "COM5";

void setup(){
  smooth();
  frameRate(30);
  size(200, 200);
  myPort = new Serial(this, arduino, 9600);
}

void draw(){
  if ( myPort.available() > 0){
    String portVal = myPort.readString();
    // Trim off any extra chars that have no meaning
    // to our sketch.  If we don't do this, we can get
    // NaN float vals when converted.
    String trimmed = portVal.trim();
    if(trimmed.length() > 0){
      // if we have a valid value, update it:
      val = float(portVal);
    }
  }
  if(val != prevVal){
    println("New val: " + val);
    prevVal = val;
  }

  background(0);
  float mapVal = map(val, minSerial, maxSerial, 1, width);
  fill(255);
  ellipse(width/2, height/2, mapVal, mapVal);
}

Python / Pygame:

Like Processing, when executed, will create a window, with a white circle inside thats size is controlled by the sensor data passed through the serial port.

Here is the Python / Pygame code:

"""
readArduinoSerial.py
Eric Pavey - 2009-12-27

Read data from the computers serial port, that is being fed
by an Arduino sketch.  It's expecting values from 0->1023.
"""

import serial
import pygame
from pygame.locals import *
pygame.init()

WIDTH = 256
HEIGHT = 256
FRAMERATE = 30
# Define which com port the Arduino is on:
ARDUINO = "COM5"

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Arduino Serial Com.")
clock = pygame.time.Clock()

ser = serial.Serial(ARDUINO, timeout=1)
floatVal = 1.0
prevVal = 1.0

def main():
    global floatVal
    global prevVal
    cirCol = Color("white")
    looping = True

    while looping:
        clock.tick(FRAMERATE)
        screen.fill(0)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                looping = False

        # Read the serial value
        ser.flushInput()
        serialValue = ser.readline().strip()

        # Catch any bad serial data:
        try:
            floatVal = float(serialValue)/8.0
            if floatVal != prevVal:
                # Print the value if it differs from the prevVal:
                print "New Val: ", floatVal
                prevVal = floatVal
        except ValueError:
            pass
        pygame.draw.circle(screen, cirCol, (WIDTH/2, HEIGHT/2), floatVal)

        # update our display:
        pygame.display.update()

if __name__ == "__main__":
    main()

Update: Here is the new Python code sans-Pygame. It will print results directly to the shell it was executed from:

import serial

ARDUINO =  "COM4"

def main():
    ser =  serial.Serial(ARDUINO, timeout=1)
    prevVal = None
    while 1:
        # Read the serial value
        ser.flushInput()
        serialValue = ser.readline().strip()
        # Catch any bad serial data:
        try:
            if serialValue != prevVal:
                # Print the value if it differs from the prevVal:
                print "New Val: ", serialValue
                prevVal = serialValue
        except ValueError:
            pass

if __name__ == '__main__':
    main()

In Conclusion…

So while the implementation in Processing \Python is pretty simple, it opens doors into what could be.  Another interesting observation is that the Processing sketch was really ‘jumpy’:  It seems to capture values that aren’t being reported by the Arduino sketch, causing the circle to ‘jump’ in size occasionally.  However, the Pygame (and updated pure Python) app seems pretty rock solid, and no ‘jumping’ is reported.

Merry Christmas!

DSC05512

…and a happy new year, from my family to yours.

Arduino + Electronic Brick Chassis V1.1 + Electronic Brick LCD 16*2 + below code = above image.

// It's an Arduino LCD Christmas
// AK Eric - 2009-12-24
#include <LiquidCrystal.h>

LiquidCrystal lcd(10, 11, 12, 13, 14, 15, 16);

void setup(){
    lcd.begin(16, 2);
    lcd.clear();
    lcd.print("MERRY CHRISTMAS!");
    lcd.setCursor(0,1);
    lcd.print(" www.AKEric.com");     
}

void loop(){
}