Thursday, July 24, 2014

Unity 3D and Phidgets: Text LCD displays

LCD displays have a wide variety of applications, from industrial to amusement and redemption games. Phidgets offers a very solid range of displays with plenty of options, and great controllers that can communicate with two at a time. Today, we'll tackle some of the ins and outs of working with them in Unity.

First, you'll want to check out our basic Phidgets setup tutorial to get the hardware talking in Unity. Once you have your DLL loaded and your basic script ready to go, plug in your display controller and move forward.

Like other Phidgets devices, the general pattern is this: you instantiate a new object relating to the type of hardware you want to talk to, you add the event handlers, then you communicate with them. In general, I like to instantiate the device immediately, add the event handlers and open the device in Start (), initialize the device in the attach method, and then communicate with the device in custom methods, making sure the device is attached, of course.

We're going to assume you have one Text LCD controller connected, which will allow you to talk to two displays. Here is the code:

using Phidgets;
using Phidgets.Events;
using UnityEngine;
using System.Collections;

public class PhidgetsController : MonoBehaviour
{
    private TextLCD textLCDController = new TextLCD ();


    private int arbitraryCounter = 0;

    void Start ()
    {
       
textLCDController.Attach += new AttachEventHandler (lcdControl_Attach);
       
textLCDController.Detach += new DetachEventHandler (lcdControl_Detach);
       
textLCDController.Error += new ErrorEventHandler (lcdControl_Error);
       
textLCDController.open ();
    }

    void lcdControl_Attach(object sender, AttachEventArgs e)
    {
        Debug.Log ("Attached text LCD controller. Serial #" +
textLCDController.SerialNumber.ToString () + ".");
       
textLCDController.open ();
       
textLCDController.screens[0].Backlight = true;
       
textLCDController.screens[1].Backlight = true;
       
textLCDController.screens[0].Brightness = 255;
       
textLCDController.screens[1].Brightness = 255;
       
textLCDController.screens[0].Contrast = 200;
       
textLCDController.screens[1].Contrast = 200;
       
textLCDController.screens[0].ScreenSize = TextLCD.ScreenSizes._2x20;
       
textLCDController.screens[1].ScreenSize = TextLCD.ScreenSizes._2x20;
       
textLCDController.screens[0].initialize ();
       
textLCDController.screens[1].initialize ();
       
textLCDController.screens[0].rows[0].DisplayString = "  DISPLAY ATTACHED";
       
textLCDController.screens[1].rows[0].DisplayString = "  DISPLAY ATTACHED";
    }
   
    void lcdControl_Detach(object sender, DetachEventArgs e)
    {
        Debug.Log ("Detached.");

        textLCDController.close ();
    }
   
    void lcdControl_Error(object sender, ErrorEventArgs e)
    {
        Debug.Log ("Phidgets display error: " + e.ToString ());
    }

    void Update ()
    {
        if (Input.GetKeyDown (KeyCode.Alpha0))
        {
            arbitraryCounter++;
            if (
textLCDController.Attached)
            { 

                textLCDController.screens[0].rows[0].DisplayString = "DISPLAY 0";
                textLCDController.screens[0].rows[1].DisplayString = "          " + arbitraryCounter.ToString ();
            }
        }
        if (Input.GetKeyDown (KeyCode.Alpha1))
        {
            arbitraryCounter++;
            if (
textLCDController.Attached)
            { 

                textLCDController.screens[1].rows[0].DisplayString = "DISPLAY 1";

                textLCDController.screens[1].rows[1].DisplayString = "          " + arbitraryCounter.ToString ();
            }
        }


    }
}


Let's go through a bit at a time and explain it all.

As per usual, we're including using statements at the top of our C# script for the "Phidgets" and "Phidgets.Events" namespaces. This allows us to talk to our Phidgets devices and use their event system.

We instantiate a TextLCD () object next. This is the object we'll use for communicating with the device. The "arbitraryCounter" int is just a number we'll increment and display on the screen so you can see that the output is working.

In Start (), we attach our event handlers. These events will fire when the device is attached or detached, or an error is thrown. We then open the device for communication.

The "lcdControl_Attach" method, which is fired when the device is attached, is where we can do our setup and initialization of the device. Keep in mind this will also fire if the devices are already connected when the program starts. First, we log that the device is attached along with its serial number. This is just for debugging, and having the serial number logged is nice if we end up working with multiple devices of the same type, which we touch on below. We open the device -- we opened it in Start (), but we can do it here as well so that if the device is detached and attached while the program is running, we can be sure that it's ready to talk to. We then run the initialization that we want -- here, I've turned the backlights on for both displays, set the brightness, contrast, and screen size, run the initialize () method (which clears all text from the display), and displayed "DISPLAY ATTACHED" on each display.

We should note a couple of things here. One potential gotcha is that you have to set the screen size before you send a string to be displayed. The screen size's default value is none, so if you send a string without setting the screen size, you'll get an exception. The screen that I have is a 2x20, meaning it has two rows, and each row has twenty characters. You'll want to make sure that you select the correct parameter for your display.

Also, notice how we reference each screen and each row. They are 0-based indices, meaning that the first screen/row is 0, and the next is 1. We always have to reference the screen we want to set for backlight, contrast, etc., and we have to reference the row number when we send a string to be displayed. Screens 0 and 1 are marked on the board itself. Rows are indexed from the top, so the top is 0, the next is 1, etc.

In "lcdControl_Detach", we just log that the device was detached, and close it for communication. When the device gets re-attached, the attach event will fire lcdControl_Attach (), where we open it again.

"lcdControl_Error" logs any errors that are thrown by the device.

In Update (), we just wrote a simple little keyboard-controlled test. If you press the 0 key at the top of your keyboard (not the number pad), you'll see the display number and our arbitrary number on screen 0; the 1 key sends the same data to screen 1. Each time you press either of those keys, the arbitrary number is incremented by one, so you'll get a new number each time you hit a button. This confirms that everything is still working.

Notice that we check textLCDController.Attached each time we send a command. This is because if we try to send a command to a device that is not attached, we'll get an exception. Again, pay attention to how we're referencing the screens and rows.

This is a simple test, largely taken from Phidgets' own test C# program, that will get you basic functionality on your LCD displays. While this program is only designed for one controller, one great thing about Phidgets is that you can reference different boards of the same type by their serial number. To do that, you'd just instantiate a TextLCD () object for each device you have, and instead of calling textLCDController.open () with no parameters, you pass it your serial as an int, like so:

textLCDController.open(123456);

This will open that specific controller board for communication. You can get a board's serial number programmatically (TextLCD.SerialNumber), and they are also printed right on the board. For usability, you'll want a place for end users or operators to enter the correct serials, or possibly even work up a calibration routine to assign them to the right spots. We've done all that before with great success, but it's outside of the scope of this article.

1 comment: