Christmas Lights

Each year I setup a display of Christmas lights in Narara.

This year (2015) I pretty much started from scratch as I moved to all pixels (each light is individually controlled)

Here are videos of my lights this year.

Jingle Bells pon de Floor

Light Of Christmas

What’s This

Wizards of Winter

Technical Details

For those that are interested this is all controlled by a Sandevices E682 and Advatek Pixlite4 sitting in a large plastic tub.

IMG_20151127_140104

All the sequences were created in xLights running on Linux – and the show is controlled by FPP running on a Raspberry Pi

If anyone is interested in the sequences used here is a zip file containing all the sequences and my xlights_rgbeffects.xml file

2015_Sequences

USB-Rubber Ducky scripts on Arduino/Leostick

The other day I was at Jaycar and saw that they are now selling small USB sticks that are arduino compatible.
It is called the LeoStick and is made by Freetronics down in Melbourne.
Seeing that it could pretend to be a USB HID device (ie keyboard/mouse) I wondered if I could do the sort of thing that the USB Rubber Ducky from Hak5 can do
As it turns out the answer is YES 🙂

Since it was possible I spent an hour or two writing a quick shell script which can convert ducky script payloads into a sketch suitable for uploading to the LeoStick (or any arduino that has USB-HID capability)   The end result is a small bash script which can be downloaded from compile_payload.sh

Usage is fairly simple – you run the script with two options – the first being the payload file, and the second being the arduino script output.

ie: compile_payload lock_prank.txt lock_prank.ino

Various payloads can be found linked from the USB-Rubber-Ducky wiki

As a bit of fun I changed the lock_prank payload to work on Gnome/Linux and it also plays the mission impossible theme once done 😉
Grab it from lock_prank.ino

Also note that to get this working you need to edit the arduino libraries so that the sendReport function is marked as public.

To to this edit the USBAPI.h file which can be found in ${ARDUINO_DIR}/hardware/arduino/cores/arduino directory.
This may be /usr/share/arduino/hardware/arduino/cores/arduino/USBAPI.h or similar
If you installed the LeoStick board stuff from their website then it will be under your sketches directory as hardware/LeoStick/cores/arduino/USBAPI.h

Open that file and find

private:
    KeyMap* _keyMap;
    void sendReport(KeyReport* keys);
    void setKeyMap(KeyMap* keyMap);
public:
    Keyboard_();
    virtual size_t write(uint8_t);

Then change that to

private:
    KeyMap* _keyMap;
    void setKeyMap(KeyMap* keyMap);
public:
    void sendReport(KeyReport* keys);
    Keyboard_();
    virtual size_t write(uint8_t);

Then everything should work fine.

Freetronics DMD – Screen transitions

I was driving past one of those led advertising signs sitting on the side of the road the other day and thought to myself ‘I can do that’
So I’ve hooked up 6 of the freetronics DMD modules (in a 2×3 layout) and got down to coding up a way to do it.
In the end I got it working by adding support for multiple buffers into my DMD library – github.com/cjd/DMD
Since I have the ability to have multiple buffers I can now transition between buffers presentation-style.
There are 8 transitions – wipe left/right/up/down, box in/out and cross in/out
The following video demonstrates some of the transitions
It is my sons first birthday in a few days so I thought it nice to create a moving sign for his party 🙂
[youtube_sc url=”s4Qjski-WMY”]

For the hardware I mounted the displays on a wooden frame (hold on by hot-glue) and then routed power down each side and signal was daisy-chained from bottom-right (bottom-left in photo) to each display.

Source code below:

/*---------------------------------------------------------------
 Includes
 ----------------------------------------------------------------*/
#include <SPI.h>        //SPI.h must be included as DMD is written by SPI (the IDE complains otherwise)
#include <DMD.h>        //
#include <TimerOne.h>   //
#include "Arial_black_16.h"
//Fire up the DMD library as dmd
DMD dmd(2,3,1);

long mDelay=30;
long timer=-1;
long oldtimer=-1;
int centreX=32;
int centreY=24;
int trans=0;
int i=0;

/*--------------------------------------------------------------------------------------
 Interrupt handler for Timer1 (TimerOne) driven DMD refresh scanning, this gets
 called at the period set in Timer1.initialize();
 --------------------------------------------------------------------------------------*/
void ScanDMD()
{ 
  dmd.scanDisplayBySPI();
}

/*--------------------------------------------------------------------------------------
 setup
 Called by the Arduino architecture before the main loop begins
 --------------------------------------------------------------------------------------*/
void setup(void)
{
  randomSeed(analogRead(0));
  //initialize TimerOne's interrupt/CPU usage used to scan and refresh the display
  Timer1.initialize( 5000 );           //period in microseconds to call ScanDMD. Anything longer than 5000 (5ms) and you can see flicker.
  Timer1.attachInterrupt( ScanDMD );   //attach the Timer1 interrupt to ScanDMD which goes to dmd.scanDisplayBySPI()
  //clear/init the DMD pixels held in RAM
  dmd.clearScreen( 0 );   //true is normal (all pixels off), false is negative (all pixels on)
  dmd.selectFont(Arial_Black_16);
  dmd.setupBuffer(3);
}

/*--------------------------------------------------------------------------------------
 loop
 Arduino architecture main loop
 --------------------------------------------------------------------------------------*/
void loop(void)
{
  lookaround();
  dmd.setBufferEdit(0);
  smile();
  dmd.setBufferDisplay(0);
  delay(1000);
  wink();
  delay(300);
  dmd.copyBuffer(2,0);
  dmd.setBufferEdit(0);
  dmd.setBufferDisplay(0);
  char stringa[]="Happy Birthday Lucas";

  dmd.drawMarquee(stringa,strlen(stringa),64,16,0xFF,0);
  while (!dmd.stepMarquee(-1,0)){delay(30);}

  dmd.setBufferEdit(1);
  dmd.clearScreen(0);
  dmd.drawString(8,0,"Happy",5,1,0);
  dmd.drawString(0,16,"Birthday",8,1,0);
  dmd.drawString(6,32,"Lucas!",6,1,0);

  dmd.copyBuffer(0,2);
  dmd.setBufferDisplay(2);  
  transition(0,1);
  delay(1000);
  
  dmd.setBufferEdit(0);
  smile();
  transition(1,0);
  delay(500);
  
  dmd.setBufferEdit(1);
  dmd.clearScreen(0);
  dmd.drawString(8,0,"Lucas",5,1,0);
  dmd.drawString(26,16,"is",2,1,0);
  dmd.drawString(16,32,"One!",4,1,0);
  dmd.copyBuffer(0,2);
  dmd.setBufferDisplay(2);

  transition(0,1);
  delay(1000);

  dmd.setBufferEdit(0);
  dmd.clearScreen(0);
  dmd.drawString(16,0,"Born",4,1,0);
  dmd.drawString(0,16,"28 April",8,1,0);
  dmd.drawString(16,32,"2011",4,1,0);

  transition(1,0);
  delay(1000);
  
  dmd.copyBuffer(1,0);
}

void transition(byte from, byte to)
{
  int i=0;
  trans=random(8);
  dmd.copyBuffer(from,2);
  while (dmd.transition(from,to,2,trans,i)) {i=i+1;}
}

void wink()
{
  dmd.setBufferEdit(0);
  dmd.clearScreen(0);
  dmd.setBufferDisplay(2);  
  head();
  eyes();
  mouth_smile();
  dmd.drawFilledBox(centreX-8,centreY-7,centreX-6,centreY-5,1);
  dmd.copyBuffer(0,2);

  int winkspeed=100;  

  dmd.setBufferEdit(1);
  for (int i=0;i<=8;i++) {
    dmd.copyBuffer(0,1);
    if (i<=4) {
      dmd.drawFilledBox(centreX+6,centreY-7,centreX+8,centreY-5,1);
    } else if(i==5) {
      dmd.drawFilledBox(centreX+6,centreY-6,centreX+8,centreY-5,1);
    }
    int width=0;
    if (i==0 || i==8) {
      dmd.drawLine(centreX+7-1,centreY-11+i,centreX+7+1,centreY-11+i,1);
    } else if(i<=2 || i>=6) {
      dmd.drawLine(centreX+7-3,centreY-11+i,centreX+7+3,centreY-11+i,1);
    } else {
      dmd.drawLine(centreX+7-4,centreY-11+i,centreX+7+4,centreY-11+i,1);
    }
    dmd.copyBuffer(1,2);
    delay(50);
  }
  for (int i=8;i>=0;i--) {
    dmd.copyBuffer(0,1);
    if (i<=4) {
      dmd.drawFilledBox(centreX+6,centreY-7,centreX+8,centreY-5,1);
    } else if(i==5) {
      dmd.drawFilledBox(centreX+6,centreY-6,centreX+8,centreY-5,1);
    }
    int width=0;
    if (i==0 || i==8) {
      dmd.drawLine(centreX+7-1,centreY-11+i,centreX+7+1,centreY-11+i,1);
    } else if(i<=2 || i>=6) {
      dmd.drawLine(centreX+7-3,centreY-11+i,centreX+7+3,centreY-11+i,1);
    } else {
      dmd.drawLine(centreX+7-4,centreY-11+i,centreX+7+4,centreY-11+i,1);
    }
    dmd.copyBuffer(1,2);
    delay(50);
  }
  dmd.setBufferEdit(2);
}

void lookaround()
{
  //head
  dmd.setBufferEdit(0);
  dmd.clearScreen(0);
  dmd.setBufferDisplay(2);  
  head();
  eyes();
  mouth_flat();
  dmd.copyBuffer(0,2);
  
  // eyelids
  dmd.drawLine(centreX-5,centreY-10,centreX-10,centreY-10,1);
  dmd.drawLine(centreX+5,centreY-10,centreX+10,centreY-10,1);

  dmd.setBufferEdit(1);
  for (int i=0;i<=2;i++) {
    dmd.copyBuffer(0,1);
    dmd.drawFilledBox(centreX-8+i,centreY-7,centreX-6+i,centreY-5,1);
    dmd.drawFilledBox(centreX+6+i,centreY-7,centreX+8+i,centreY-5,1);
    dmd.copyBuffer(1,2);
    delay(100);
  }
  for (int i=2;i>=-2;i--) {
    dmd.copyBuffer(0,1);
    dmd.drawFilledBox(centreX-8+i,centreY-7,centreX-6+i,centreY-5,1);
    dmd.drawFilledBox(centreX+6+i,centreY-7,centreX+8+i,centreY-5,1);
    dmd.copyBuffer(1,2);
    delay(100);
  }
  for (int i=-2;i<=0;i++) {
    dmd.copyBuffer(0,1);
    dmd.drawFilledBox(centreX-8+i,centreY-7,centreX-6+i,centreY-5,1);
    dmd.drawFilledBox(centreX+6+i,centreY-7,centreX+8+i,centreY-5,1);
    dmd.copyBuffer(1,2);
    delay(100);
  }
  dmd.setBufferEdit(2);
}


void smile()
{
  dmd.clearScreen(0);
  head();
  eyes();
  mouth_smile();
  dmd.drawFilledBox(centreX-8,centreY-7,centreX-6,centreY-5,1);
  dmd.drawFilledBox(centreX+6,centreY-7,centreX+8,centreY-5,1);
}

void head()
{
  dmd.drawCircle(centreX,centreY,20,1);
}

void eyes()
{
  dmd.drawCircle(centreX-7,centreY-7,5,1);
  dmd.drawCircle(centreX+7,centreY-7,5,1);
}

void mouth_flat()
{
  dmd.drawLine(centreX-12,centreY+8,centreX+12,centreY+8,1);
}

void mouth_smile()
{
  dmd.drawLine(centreX-12,centreY+6,centreX-4,centreY+11,1);
  dmd.drawLine(centreX-4,centreY+11,centreX,centreY+11,1);
  dmd.drawLine(centreX+4,centreY+11,centreX,centreY+11,1);
  dmd.drawLine(centreX+12,centreY+6,centreX+4,centreY+11,1);
}


MTPFS 1.1 – Now supporting multiple storage areas

I am now the proud owner of an Asus Transformer tablet (not the prime) and so wanted to update mtpfs to work better with it.

The biggest change was to add support for multiple storage areas.  Now when you mount a MTP device which has internal storage and a MicroSD card (or similar) it shows up as two separate directories which you can access.

The download is available from mtpfs page

Freetronics DMD – Games

I had a little while free and so ported a bunch of games I wrote for the Peggy2 to work with a pair of DMD’s
In doing so the games now run at 32×32 and are controlled by a Wii nunchuck.
The games are snake, pong, breakout and race.
The Arduino code is available at DMD_games.zip
The two screens are daisy-chained but as the cable connecting them is quite short the top one is upside-down.
To get this working the modified DMD library is setup to handle rows of displays where odd-numbered rows are upside down.
See this diagram for how it is laid out (for 2×2 case)

Playing is fairly straight-forward.  Only thing to remember is that the ‘z-button’ on the nunchuck is used to select and the ‘c-button’ is used to exit back to the menu

Have a look at the video below to see them in action.
[youtube_sc url=”5WW9ZmvjSA4″]