Programming the NS-500UR Led Sign

I recently purchased the NS-500UR Led Sign from an Amazon reseller for the PLOS Office. I wanted to have it display the latest 10 terms users have searched for given the search engine on our website. It was a bit of headache tracking down documentation on how to communicate to the sign, so I’m publishing my results below.

I did my work in java, code samples below are in java, you could probably however, adapt it to any language. Also note, that this is my no means comprehensive, there is a lot more you can do with this sign then I am going to cover below. All I cared about was display a bunch of short text messages.

First: I used the commapi library to abstract serial communications to java. On linux, this is the command you want to get the source, compile and install into your local java jre:

export CVSROOT=:pserver:anonymous@qbang.org:/var/cvs/cvsroot
cvs login
CVS password: (blank)
cvs checkout -r commapi-0-0-1 rxtx-devel
cd rxtx-devel
./configure
sudo make install

Once that is set up, you’ll need to figure out what port to use. Code below, compile and execute. It will tell you availible ports, and confirm the above library is working.

package org.plos.ledstatus.util.gnuio;

import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.UnsupportedCommOperationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class SerialPort {
  private static final Logger log = LoggerFactory.getLogger(SerialPort.class);

  public static String[] getPorts() {
    List ports = new ArrayList();

    Enumeration portIdentifiers = CommPortIdentifier.getPortIdentifiers();

    while(portIdentifiers.hasMoreElements()) {
      CommPortIdentifier pid = (CommPortIdentifier) portIdentifiers.nextElement();

      ports.add(pid.getName());
    }

    return ports.toArray(new String[ports.size()]);
  }
}

Now that you have a list of ports, and you’ve plugged your sign into one of them. Here is a bit of code to open the port: (I placed this method in the above class)

  public static CommPortIdentifier getPort(String name) {
    CommPortIdentifier portId = null;  // will be set if port found
    Enumeration portIdentifiers = CommPortIdentifier.getPortIdentifiers();

    while (portIdentifiers.hasMoreElements())
    {
      CommPortIdentifier pid = (CommPortIdentifier) portIdentifiers.nextElement();

      log.info("PID: {}", pid.getName());
      log.info("PID Serial: {}", pid.getPortType() == CommPortIdentifier.PORT_SERIAL);

      if(pid.getPortType() == CommPortIdentifier.PORT_SERIAL && pid.getName().equals(name))
      {
        portId = pid;
        break;
      }
    }

    return portId;
  }

Pretty simple so far eh? This bit of code will send a string to the serial port. Again, I added this static to method to the same class

  public static void sendMessage(CommPortIdentifier portId, String message) {
    try {
      gnu.io.SerialPort port = (gnu.io.SerialPort) portId.open(
        "led-status", // Name of the application asking for the port
        10000   // Wait max. 10 sec. to acquire port
      );

      port.setSerialPortParams(9600, gnu.io.SerialPort.DATABITS_8, gnu.io.SerialPort.STOPBITS_1, gnu.io.SerialPort.PARITY_NONE);

      log.debug("Sending message: {}", message);

      PrintStream ps = new PrintStream(port.getOutputStream(), true, "UTF-8");
      ps.write(message.getBytes(Charset.forName("UTF-8")));

      port.close();

    } catch(PortInUseException e) {
      throw new RuntimeException(e.getMessage(), e);
    } catch(UnsupportedCommOperationException e) {
      throw new RuntimeException(e.getMessage(), e);
    } catch(IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }

Ok, so now we get to the LED specific sign language. There is a lot more here then I am covering, and your mileage may very pending your exact model and release date.

I had to reverse engineer a bit, but the language is quite simple and a message looks a bit like the following. Disclaimer, this was all totally guess work on my part, after watching how their window’s software behaves.

~128~f01NZ5Y1stest/r
/r/r

That will display one message with the text “test”. (/r represents a line feed)

    “~128~” Represents “begin message”

    “~f01” Represents “Message 1” (The unit has the ability to display multiple message sets)

    “N” is the transition type. More listed below

    “Z5” sets the transition speed (in seconds) values 1 to 8 are valid

    “Y2” sets the staytime of the message (in seconds) values 1 to 8 are valid

    “s” sets the font size of the message valid values are “s” or “q” (for smaller)

    “rr” End of message

Now I was able to append multiple messages to a given set like so:

~128~f01NZ5Y1stest/r
NZ5Y1stest one/r
NZ5Y1stest two/r
NZ5Y1stest three/r
/r/r

Here below are a list of available tranistions:

  • A – Cyclic
  • B – Immediate
  • C – Open from right
  • D – Open from left
  • E – Open from center
  • F – Open to center
  • G – Cover from center
  • H – Cover from right
  • I – Cover from left
  • J – Cover to center
  • K – Scroll up
  • L – Scroll down
  • M – Interlace to center
  • N – Interlace to cover?
  • O – Cover up
  • P – Cover down
  • Q – Scan line
  • R – Explode
  • S – Pacman (very cool!)
  • T – Fall and stack
  • U – Shoot
  • V – Flash
  • W – Random
  • X – Slide in

And that’s pretty much it! Here is a java enum of the transitions:

public enum TRANSITIONS {
  CYCLIC("A"), IMMEDIATE("B"), OPEN_FROM_RT("C"), OPEN_FROM_LT("D"), OPEN_FROM_CTR("E"),
    OPEN_TO_CTR("F"), COVER_FROM_CTR("G"), COVER_FROM_RT("H"), COVER_FROM_LT("I"),
    COVER_TO_CTR("J"), SCROLL_UP("K"), SCROLL_DOWN("L"), INTLCE_TO_CTR("M"), INTLCE_CVR("N"),
    COVER_UP("O"), COVER_DOWN("P"), SCAN_LINE("Q"), EXPLODE("R"), PAC_MAN("S"), FALL_AND_STACK("T"), SHOOT("U"),
    FLASH("V"), RANDOM("W"), SLIDE_IN("X");

  public static TRANSITIONS[] preferred = { COVER_FROM_CTR, COVER_FROM_RT, COVER_FROM_LT, COVER_TO_CTR,
    SCROLL_UP, SCROLL_DOWN, INTLCE_CVR, COVER_UP, COVER_DOWN, EXPLODE, PAC_MAN, FALL_AND_STACK };

  //Too jittery: CYCLIC, OPEN_FROM_RT, OPEN_FROM_LT, OPEN_FROM_CTR, OPEN_TO_CTR, INTLCE_TO_CTR
  //Annoying: FLASH
  //Takes too long, but cool: SCAN_LINE, SHOOT, SLIDE_IN

  private final String stringValue;

  private TRANSITIONS(final String s) { stringValue = s; }

  public String toString() { return stringValue; }
}

And another helper class for building up a message to send to the serial port:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Message {
  private List lines = new ArrayList();

  public static Message create() {
    return new Message();
  }

  public Message addLine(String message, int speed, int staytime, boolean small) {
    TRANSITIONS t = TRANSITIONS.preferred[(new Random()).nextInt(TRANSITIONS.preferred.length)];

    this.lines.add(new MessageLine(message, speed, staytime, t, small));

    return this;
  }

  public Message addLine(String message, int speed, int staytime, TRANSITIONS transition, boolean small) {
    this.lines.add(new MessageLine(message, speed, staytime, transition, small));

    return this;
  }

  public Message addLine(String message, TRANSITIONS transition, boolean small) {
    this.lines.add(new MessageLine(message, 7, 6, transition, small));

    return this;
  }

  public String render()
  {
    StringBuilder s = new StringBuilder();

    //Only support one message for now
    s.append("~128~f01");

    for(MessageLine line : lines) {
      //Z7Y6sTest-1
      s.append(line.toString());
    }

    s.append("rr");

    return s.toString();
  }

  private class MessageLine {
    String message;
    int speed;
    int staytime;
    TRANSITIONS transition;
    boolean small;

    public MessageLine(String message, int speed, int staytime, TRANSITIONS transition, boolean small) {
      this.message = message;
      this.speed = speed;
      this.staytime = staytime;
      this.transition = transition;
      this.small = small;
    }

    public String toString() {
      StringBuilder s = new StringBuilder();

      s.append(this.transition.toString());

      s.append("\Z");
      s.append(this.speed);

      s.append("\Y");
      s.append(this.staytime);

      if(this.small) {
        s.append("\q");
      } else {
        s.append("\s");
      }

      s.append(this.message);

      s.append("r");

      return s.toString();
    }
  }
}

photo1

…fin…

This entry was posted in programming, tech. Bookmark the permalink.

3 Responses to Programming the NS-500UR Led Sign

  1. ch00f says:

    I’m looking to get one of these that involves outputting a live feed of non repeating text. is there any way to determine if a string has finished scrolling on the display?

    Like

  2. Albert H says:

    Hey Joe,

    Are you still using this sign (or have it somewhere)? If so, could you contact me? I’m trying to use this sign, but I’m having a lot of issues…

    Thanks in advance!
    Albert

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s