Sunday, 27 July 2014





Just bought one of those small scrolling LED badges from Maplins for £20!
Up till 4 am reverse engineering the BADGE set up code included on the CD - so I could use it with a tomcat7 server on my Pi. Hopefully one of you out there - may find it useful!


import jssc.SerialNativeInterface ;
import jssc.SerialPortList;
import jssc.SerialPort ;
import jssc.SerialPortException;
import java.io.ByteArrayOutputStream ;
import java.io.IOException ;

//javac -cp jssc.jar LEDBadge.java
//java -cp jssc.jar:./ LEDBadge "TEST 1" 

// Example Class showing how we can set up a Maplin's LED Scrolling Badge in Java
// Using JSSC Serial Port class 
// Johnny Wilson, Brighton July 2014

public class LEDBadge {


static public  int style  = 0 ;
static public  int speed  = 0 ;

static public  void log(String s) {
System.out.println("log : " + s) ;
}

  public int unsignedint(byte b) {
return (b < 0 ? (int)b + 256 : (int)b);
}



// buildScrollingMessage - build byte array to send to our Maplin's LED Scrolling Badge
// @message  250 size max string - 
// @style -1 is HOLD, 0 is SCROLL, 1 = STAR DROP, 2 = FLASH
// @speed 0 is fast , 4 is slow

byte [] buildScrollingMessage(String message, int style, int speed) throws IOException
{

int msgBlockSize = 0 ;
int sum = 0;

ByteArrayOutputStream ba = new ByteArrayOutputStream() ;
byte [] finalHeader = {(byte)0x02, 0x33, 0x01} ;
byte[] blockHeader = {0x02, 0x31, 0x06} ;

byte [] bmessage = message.substring(0,Math.min(250,message.length())).getBytes() ; // Just the 1st 250 characters will be used
int messageSize = bmessage.length ;
log("Messaage : " + message) ;
log("Message size of " + message.length() + " clipped to " + messageSize) ;

ba.write(0x00) ;
ba.write(blockHeader) ;
ba.write(0x00) ;
ba.write((-speed  + 0x35)) ; // speed
ba.write(0x31) ; // Memory block
ba.write((style  + 0x42)); // display style
ba.write(messageSize) ; // total size of message

// 1st Block of text
msgBlockSize = Math.min(messageSize,60) ;
sum = 0 ;
for (int i = 0 ; i <  msgBlockSize ; i++) {
ba.write(bmessage[i]) ;
int val = unsignedint(bmessage[i]) ;
sum = sum + val ;
}
sum = sum  + messageSize  - 33 + style - speed ; // this is the check sum formula for scrolling messages at speed 5

// Write out remaining zeros
for(int i = 0 ; i < 60 - msgBlockSize  ; i++) {
ba.write(0x00) ;
}
// 69th pos
ba.write(sum) ; // Save 1st Check Sum


// 2nd block

ba.write(blockHeader) ;
ba.write(0x40) ;

msgBlockSize = Math.max(0,Math.min((messageSize - 60),64)) ;

sum = 0  ;
for (int i = 0 ; i < msgBlockSize ; i++) {
ba.write(bmessage[i + 60]) ;
int val = unsignedint(bmessage[i + 60])   ;
sum = sum + val ;
}
sum = (sum + 0x77)   ;

// Write out remaining zeros
for(int i = 0 ; i < 64 - msgBlockSize  ; i++) {
ba.write(0x00) ;
}

// 138th Position
ba.write(sum) ; // Save 2nd Check Sum




// 3rd Block
ba.write(blockHeader) ;
ba.write(0x80) ;

msgBlockSize = Math.max(0,Math.min((messageSize - 60 - 64),64)) ; // I'll leave you to tidy these bits up - 

sum = 0  ;
for (int i = 0 ; i <  msgBlockSize ; i++) {
ba.write(bmessage[i + 60 + 64]) ;
int val = unsignedint(bmessage[i + 60 + 64])  ;
sum = sum + val ;
}
sum = sum + 0xb7;

// Write out remaining zeros
for(int i = 0 ; i < 64 - msgBlockSize  ; i++) {
ba.write(0x00) ;
}


// 207th Position
ba.write(sum) ; // Save 3rd Check Sum

// final 4th block

ba.write(blockHeader) ;
ba.write(0xC0) ;
msgBlockSize = Math.max(0,Math.min((messageSize - 60 - 64 - 64),62))  ;

sum = 0  ;
for (int i = 0 ; i < msgBlockSize ; i++) {
ba.write(bmessage[i + 60 + 64 + 64]) ;
int val = unsignedint(bmessage[i + 60 + 64 + 64])  ;
sum = sum + val ;
}
sum = sum + 0xf7;

// Write out remaining zeros. 
// Note although we only allow 250 chars in total [62 chars in final block]
//- we must write out 64 bytes for final block 
for(int i = 0 ; i < 64 - msgBlockSize ; i++) {
ba.write(0x00) ;
}

// 276th position
ba.write(sum) ;

// Final few bytes
ba.write(finalHeader) ;

// final size of array should be 280 bytes!
//log("Size = " + ba.size()) ;

return ba.toByteArray() ;
}

static public void main(String [] args)
{
SerialNativeInterface sni = new SerialNativeInterface() ;
//log("SCROLLING MAPLIN LED BADGE") ;
//log("jssc Version " + sni.getLibraryVersion()) ;
String[] portNames = SerialPortList.getPortNames();

if (portNames.length == 0) {
log("No Serial Ports to print to!") ;
return ;
}

//log("Message " + args[0]) ;

for (String name : portNames) {
log("Port : " + name) ;
}

  SerialPort serialPort = new SerialPort(portNames[0]); // FUDGE here - Assume we are using the 1st Serial Port in our list.

  try {
           serialPort.openPort();//Open serial port
           serialPort.setParams(SerialPort.BAUDRATE_38400, 
                                SerialPort.DATABITS_8,
                                SerialPort.STOPBITS_1,
                                SerialPort.PARITY_NONE);


LEDBadge badge = new LEDBadge() ;
// I have seen the serial writes fail on some occassions -
// My guess is that we are missing some kind of flow control
// from our set up - to tell the badge - that we are about to send.
// The failures are exacerbated when the BADGE is doing some more complicated 
// display (style) format - such as the falling 'star drop'.
// Please drop me a line if you manage to make your serial sends - more robust.
// Thanks - Johnny, johnnyw66 at gmail dot com
           serialPort.writeBytes(badge.buildScrollingMessage(args[0],LEDBadge.style,LEDBadge.speed));//Write data to port

           serialPort.closePort();//Close serial port
       }
       catch (SerialPortException|IOException ex) {
           System.out.println(ex);
       }
        

}

}

Saturday, 19 July 2014

DataManager finding mean, mode (assuming single value) and median

import java.lang.Number ;
import java.util.Comparator ;

public class ValueFrequency<T extends Number> implements Comparator<ValueFrequency<T>> {

private T value ;
private long frequency ;

public void setFrequency(long frq) { frequency = frq ; }
public void setValue(T val) { value = val ;}

public long getFrequency() { return frequency ; }
public T getValue() { return value ; }

public ValueFrequency(T v) {
setValue(v) ;
setFrequency(0) ;
}
public void bump() { frequency += 1 ;}

public float getValueFreq() { return value.floatValue() * frequency ; }

@Override
public String toString() {
return "ValueFrequency: " + getValue() + " freq " + getFrequency();
}

public int compare(ValueFrequency<T> a, ValueFrequency<T> b) {
        return a.getValue().intValue() - b.getValue().intValue();
    }
    

}


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

import java.lang.Number ;
import java.util.Collections ;

// Shiva, this is the basic outline of what we discussed last night
// I have not checked it and it is certainly unfinished - with space for big improvements!
// Compile this, together with ValueFrequency.java and the run this class
// javac ValueFrequency.java
// javac DataManager.java
// java DataManager

public class DataManager<T extends Number> {

private List<ValueFrequency<T>>  vfList = new ArrayList<>() ; // This is crap! Some nice OrderedList Object with a very fast sort
private Map<T,ValueFrequency<T>> map = new HashMap<>() ; // Quick way of getting to our ValueFrequency Objects
private float mean ;
private long numsamples ;
private ValueFrequency<T> mode ; 


public void addValue(T value) {
ValueFrequency<T> vf ;
if (!map.containsKey(value)) {
vf = new ValueFrequency<T>(value) ;
map.put(value,vf) ;
vfList.add(vf) ;
} else {
vf = map.get(value) ;
}

vf.bump() ;

if (mode == null) {
mode = vf ;
} else {
// @TODO
// Assuming only ONE 'mode' exists here - this needs improving for a 'Set' of possible 'MODEs'
if (vf.getFrequency() > mode.getFrequency()) {
mode = vf ;
}
}
// Update mean based on last mean calculation

mean = (mean * numsamples + value.floatValue()) / (numsamples + 1) ;
numsamples +=1 ;

}

public float getMean() { return mean ; }

public T getMode() { return mode != null ? mode.getValue() : null; } // assuming one MODE value

public float getMedian() {

if (mode == null) 
return 0 ;

Collections.sort(vfList, mode) ;

if ((numsamples & 1) == 0) {
// Even number in our current sample size
// (numsamples) / 2 is our required index 
// ()(numsamples) / 2 ) + 1 is our 2nd required index 
// Median is the average of these two values 
ValueFrequency<T> vf1 = getValueFrequencyByIndex((numsamples/2) - 1) ;  // minus 1 cos the array indexes in Java start at 0!
ValueFrequency<T> vf2 = getValueFrequencyByIndex((numsamples/2)) ; 
// @TODO - Check for null return - maybe catch?
return (vf1.getValue().floatValue() + vf2.getValue().floatValue()) / 2 ;

} else {
System.out.println("Odd Number");
// Odd number in our current sample size
// (numsamples + 1) / 2 is our required index 
ValueFrequency<T> vf1 = getValueFrequencyByIndex((numsamples + 1)/2  - 1 ) ;
// @TODO - Check for null return 
return vf1.getValue().floatValue() ;
}

}

//@TODO Maybe this is NOT needed if we add cummFrequency to ValueFrequency and calculate
// this as we add/insert values?
// Assumed that vfList is sorted!
public ValueFrequency<T> getValueFrequencyByIndex(long index) {
System.out.println("getValueFrequencyByIndex Index " + index) ;
long cummFrequency = 0 ;
for(ValueFrequency<T> vf : vfList) {
cummFrequency += vf.getFrequency() ;
if (index < cummFrequency) 
return vf ;
}
return null ;
}

public long getTotalFrequency()  { return numsamples ; }

@Override
public String toString() {
return "mean: " + getMean()  
+ " mode: " + getMode() 
+ " meanian " + getMedian() 
+ "\n" ;
}

public void debug() {
long index = 0 ;
long cummFrequency = 0 ;

if (mode != null) {
Collections.sort(vfList, mode) ; // Use one of our objects - for comparison function
}


for(ValueFrequency<T> vf : vfList) {
cummFrequency += vf.getFrequency() ;
System.out.println(" ValueFrequency at index " + index + " = " + vf + " cumm Freq = " + cummFrequency) ;
index++ ;
}

}


static public void main(String [] args)
{
DataManager<Integer> dm = new DataManager<Integer>() ;

// Throwing Dice 20,000,000 times!
Random rnd = new Random(10) ;  // start off with same seed each time - remove 
for (int i = 0 ; i < 20000000 ; i++) {
// Throw two die and make a note of the sum! i.e a 'throw' should produce a result between 2 and 12
dm.addValue((rnd.nextInt(6) + 1) + (rnd.nextInt(6) + 1)) ;
}

dm.debug() ;
System.out.println(dm) ;
}


}