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~f01N\Z5\Y1\stest/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)

    “\r\r” End of message

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

~128~f01N\Z5\Y1\stest/r
N\Z5\Y1\stest one/r
N\Z5\Y1\stest two/r
N\Z5\Y1\stest 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) {
      //\Z7\Y6\sTest-1
      s.append(line.toString());
    }

    s.append("\r\r");

    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…

Posted in programming, tech | Leave a comment

log4couch

I created my first github project. Log4Couch. It’s an appender for log4j that will write logging messages directly to a couch server.

https://github.com/joeo73/log4couch

Posted in internet, programming, stoopid | Leave a comment

New map feature in iOS6

The new map feature in iOS6 while looking pretty awesome. Is pretty much DOA for me. (Dead on Arrival). I use maps for public transit information. The feature is only supported in the sense of “use this other application” for public transit information and linking to third party apps on their app store. Pretty big oversite here.

Apple, please fix. :-)

Posted in apple sux, internet | Tagged | Leave a comment

Using Java’s HttpClient to send a PUT

After a lot of searching the net, I wrote this method a few weeks back to write a string (or file) to a web server using Java’s HttpClient. It seemed ridiculous that I couldn’t find an example to cut and paste. So here it is for the next person.

Cheers.


public boolean store (final String file) {
HttpClient client = new HttpClient();
PutMethod method = new PutMethod("http://localhost:80");

method.setPath("/path");

try {
RequestEntity re = new RequestEntity() {
@Override
public boolean isRepeatable() {
return false;
}

@Override
public void writeRequest(OutputStream outputStream) throws IOException {
PrintWriter pr = new PrintWriter(outputStream);
pr.println(file);
pr.flush();
}

@Override
public long getContentLength() {
return source.getBytes().length;
}

@Override
public String getContentType() {
return "application/json";
}
};

method.setRequestEntity(re);

//Execute the method
int statusCode = client.executeMethod(method);

switch(statusCode) {
case HttpStatus.SC_CREATED:
return true;

default:
log.error("Unhandeled result code {}", statusCode);
log.error(new String(method.getResponseBody()));
break;
}

return false;
} catch (HttpException ex) {
log.error("Fatal protocol violation: {}", ex.getMessage());
log.error(ex.getMessage(), ex);
} catch (IOException ex) {
log.error("Fatal transport error: {}", ex.getMessage());
log.error(ex.getMessage(), ex);
} finally {
// Release the connection.
method.releaseConnection();
}

return false;
}

Posted in internet, programming | Tagged , | 1 Comment

Bay Area dot coms

So today I spent the afternoon driving around Mountain View. After going to the Computer History Museum (which was awesome mind you) I stopped by Google, Apple and Facebook world headquarters. Food for thought, all of the campuses were non-descript. Nothing like the amazing and older IBM, Oracle or Microsoft campuses. Google had these what appeared to be free bikes everyplace for general use. What a neat idea… anyhow… what I found most interesting were the kind of cars parked in the parking lots. Apple and Google might have been anyplace. Mostly empty said for a few random normal vehicles. Facebook’s parking lot on the other hand was chocked full (and I mean really full) of Mini Coopers, Range Rovers and BMWs… on a Saturday afternoon at that.

Posted in culture, internet, life, tech | Leave a comment

The perception of vendor lock in

So I have this perception of my Android phone being more open and DRM free, and my Apple/iTunes account not being. I had an interesting experience this morning that I thought was best shared.

So, I just frustratingly bought an album a second time as apposed to trying to migrate it from my android phone to iTunes. About 6 months ago I purchased “O’ Brother Where Art Thou?” through my phone via Amazon.

I started by plugging my phone into my computer and browsing to the media folder. Given a list of 1000 or so songs, with no apparent way to grab one ‘album’ that I know I purchased from Amazon. I started browsing around for tools to sort through this, but all I really wanted to do was listen to a few songs from a particular album on my computer and not through my phone. After about 60 seconds, I’m just like “This isn’t worth it”. For 10$, I purchased the album again and had it playing 20 seconds later in iTunes.

I found it odd. For all of the openness and DRM freeness of the Android platform, I felt locked in, in this case by the lack of ease of use of the tools.

In iTunes, to copy an album, I create an MP3 versions of the track and then drag and drop. Would have taken 30 seconds.

Posted in internet, music, tech | Leave a comment