You are on page 1of 10

Programming the PixelPusher

version0.5

JasStrong<jasmine@heroicrobotics.com>

0. Introduction

ThePixelPusherisasimplebeast.Ittakespixelsfromthenetwork,anditpushesthemoutto
intelligentLEDsasfastasitcan.IthassomesoftwareinsideitthatrunsasimpleTCP/IP
networkstack,andanumberofmodulesthattellithowtotalktodifferentLEDcontrolchips.

Thesophisticationinthissystemcomesfromthedesignofthenetwork.PixelPusheris
designedtobeplugandplaytoabstractthecomplexityofthetaskawayfromtheuserto
allowartistsandarchitectstoconcentrateontheinterestingpartofthedesignequation.

Weprovideasoftwarestackthatiscomprisedofthreecomponents:

1.Firmware

Thefirmwareisapieceofsoftwarethatisloadedintotheflashmemoryofthe
PixelPushercontrollercards.Itcontainsabout100kilobytesofmachinecode.Itcontainsa
realtimeoperatingsystem,anetworkstack,aFATfilesystem,aUSBstack,andasignalling
stacktotalktothestripsorpixelsyouhaveattached.

2.Thelibrary

ThelibraryisapieceofJavacodewhichrunsonthecontrollingcomputer.Itconsistsof
asetofclasseswhichcollectivelyfind,makeorderly,andtalktooneormorePixelPusher
controllers.

3.Examples

Theexamplesareprovidedinthelibrarypackage.Theyaregenerallyquitesimple
implementationsoftherequiredsoftwaretomakeaPixelPusherarraywork.Ifyouhaveinstalled
thelibrary,youcanfindtheminProcessingsExamples...dialogue,underContributed
Libraries.


1. To begin...

1. Thefirstthingyoullneedisacomputer.ThesoftwareisbasedonProcessing,sohead
tohttp://processing.org/andpickuptherightversionforyourmachine.Itisavailablefor
PC,MacandLinux,soyoushouldbeprettywellcovered.

2. Nextyouneedtoinstallthelibrary.Youcanfindinstructionsonhowtoinstalllibraries
intoProcessingathttp://wiki.processing.org/w/How_to_Install_a_Contributed_Library
notethatatthetimeofwriting,PixelPusherisnotyetincludedintheAddlibrary...tool,
soyouwillhavetodoitmanually.YoucanfindthePixelPusherlibraryat
http://forum.heroicrobotics.com/notethatitisfrequentlyupdated,sobesuretokeepup
todate.

3. Thirdly,youshouldupdatethefirmwareonyourPixelPusher.Itisquitelikelythatnew
firmwareversionshavecomeoutsinceyourproductshipped,aswefollowacontinuous
developmentmethodology.Dontworryitsquiteeasytodo,andallyouneedisamini
USBcable.YoucanfindinstructionsonhowtodothisintheHowToPushPixels
document,alsoavailablefromtheHeroicRoboticsforum.

4. Andlastly,youwillneedtowriteaconfigurationfileforeachPixelPusherinyoursystem,
saveeachoneontoaUSBmemorystick,andinsertitintotheappropriatePixelPushers
USBhostport.Then,tomakethemreadtheirconfiguration,presstheresetbuttonor
powerthePixelPusheroffandthenon.Theconfigurationfileis,again,explainedinthe
HowToPushPixelsdocument.

YoullneedtoconnectallthePixelPusherstothesamenetworkyourcomputerison,ofcourse.

Onceyouvedonethat,youcanopenoneofthePixelPusherexamplesinProcessing,hitrun
andyoushouldseeitpickupallyourPixelPushersinafewseconds.

2. How does all this work?

PixelPusherisdesignedtoautomaticallyconfigurethearray,anditdoesthisusingtwotypesof
message.OneissentfromacontrollingcomputertothePixelPushers,andtheotherissent
fromthePixelPusherstothecontrollingcomputer.Skipthenextpageifyourenotcuriousabout
howthisworks.

A technical aside:

The Universal Discovery Protocol

ThemessagesthataresentfromthePixelPushertothecontrollingcomputerfollowaformatwecallthe
UniversalDiscoveryProtocol.ThesepacketsaresentonceasecondbyallthePixelPushersonthe
network.TheycontainseveralpiecesofinformationaboutthePixelPusher,andarepickedupandstoredin
adatabasebythelibrary.Thepiecesofinformationinclude:

thePixelPushersIPaddressandMACaddress
twonumberscalledthegroupordinalandcontrollerordinal,assignedbytheuser
howmanystripsareattachedtothisPixelPusher
howmanypixelsareineachstrip
howlongittakesthisPixelPushertoprocesseachpacketofdata
howmanypacketsthePixelPushermissedinthelastsecond
howmanystripscanfitintoasingledatagram(packet).
aflagbyteforeachstrip,describingthepixellayout(RGBorRGBOW,24bitor48bit,
renormalizedorlinear).
thesoftwareversionthePixelPusherisrunning
aflagwordforthePixelPusher,denotingwhetheritrequirespackedstripdatagrams

Thelibraryusesthisinformationtobuildarepresentationofthearrayinmemory.This
representationispopulatedwithPixelPushersastheyareseen,andkeptuptodateasdiscovery
packetsarrive.Theapplicationsoftwarecanthenaskthelibraryforpiecesofthis
representation,andmodifythepixelsinthemodelthelibraryhandlessendingupdatesoutto
thePixelPushersautomaticallyandasynchronously.Todothis,itusesthesecondformof
traffic.

The Strip Update Message Protocol

Stripupdatesarepackedintodatagramsandsentbythisprotocol.Thepacketcontains
informationrelatingtospecificstrips,including:

apercardsequencenumberforthecurrentpacket
astripnumberfromzerotoseven
anumberofRGBtripletsappropriatetothenumberofpixelsinastrip

SincethememoryavailabletotheethernetcontrolleronPixelPusherislimited,onlyalimited
numberofpixelscanbesentinasingledatagram.Withourusualmaximumof240pixelsper
strip,twostripscanbesentineachdatagram.Eachdatagramhasasinglesequencenumber,
followedbyavariablenumberofrepetitionsofthestripnumberfollowedbythepixeldata.The
timinginformationandthemissedpacketdatafromthediscoverypacketareusedtothrottlethe
packetsbeingsenttotheavailablesystembandwidth.

3. Groups and Controllers

WeanticipatethatyoumightwanttohavemorethanonesectiontoyourPixelPusherarray.
Maybeyourelightingdifferentareas,oryouwanttohaveabigbillboardsizedvideodisplaythats
separatefromyour3dsculpture.Thatswhyweimplementedgroupsandcontrollers.

WhenthelibraryenumeratesaPixelPusheritreadsfromittwonumbers,whicharecalledgroup
andcontroller.Eachgroupisacollectionofcontrollers,andwithineachgrouptheyareordered
bythecontrollernumber.Youcanselectcontrollersbygroup,oryoucanjustselectallofthem
atonce,inwhichcasetheyareorderedfirstbygroupandthenbycontrollerso,forexample,
group2controller1comesbeforegroup2controller2,butgroup1controller37comesbefore
either.Iftwocontrollershavethesamenumber,theethernethardwareaddressisusedasa
tiebreaker.Sothereisathreelevelhierarchy:

4. Nuts and bolts

Asyoullseefromourexamples,thereareafewbitsandpiecesyoullneedinyourProcessing
sketchinordertomakethesystemwork.Heresaquickrundown.

Theimports

PixelPusherdefinesanumberofclassesthatprovideessentialfeatures.Atthetopofthesketch
youllseeablockthatlookslikethis:

importcom.heroicrobot.dropbit.registry.*
importcom.heroicrobot.dropbit.devices.pixelpusher.Pixel
importcom.heroicrobot.dropbit.devices.pixelpusher.Strip
importjava.util.*

Thisimportstheregistry,whichisthesoftwarecomponentthatlistensforthediscoverypackets,
thePixelPusherabstractionsforPixelsandStrips,andtheJavastandardutilityclasseswhich
areusedforcollections.

Theregistry

ThenextthingwedoistodeclaretheDeviceRegistry.

Theobserver

PixelPushersregistryusesanObservermodeltotellthesketchwhenPixelPushersappearand
disappear.Theresasimpleimplementationofthisbelow.

classTestObserverimplementsObserver{
publicbooleanhasStrips=false
publicvoidupdate(Observableregistry,ObjectupdatedDevice){
println("Registrychanged!")
if(updatedDevice!=null){
println("Devicechange:"+updatedDevice)
}
this.hasStrips=true
}
}

Thenwedeclareone,likethis:

TestObservertestObserver

Asyoucansee,itsnotabigclass,andjustimplementsamemberyoucanusetocheckwhen
therearestripsavailabletothesystem.Theobserverwillgetcalledandupdatedwheneverthe
arraysshapeorbehaviourchangessignificantly.

Setup

Thentherearesomebitsandpiecesweneedtodointhesketchssetup()method.Thisisa
methodthatrunsbeforethesketchstartsinearnest,anditsagoodplacetoinstantiatethebits
andpiecesweneedforPixelPusher.Thecodeweuselookslikethis:

registry=newDeviceRegistry()
testObserver=newTestObserver()
registry.addObserver(testObserver)

Thisinstantiatestheregistry,gettingitrunningandlisteningforPixelPusherstoappear.It
instantiatestheobserver,andthentellstheregistryaboutit.

Pushingthosepixels

Havingdoneallthissetup,weregoodtogo.Wedoouractualpushinginthesketchsdraw()
method.Firstwehavetowaituntilthereactuallyaresomestripsinexistence!Itmighttakea
secondortwoafterthesketchstartsuntilaPixelPusherannouncesitself.Wewaituntilsome
makeanappearance.

if(testObserver.hasStrips){
//...thePixelPushercodeiswrappedupinhere
}

Whentheymaketheirappearance,wemakeacalllikethistomaketheregistrystarttalkingto
thePixelPushers:

registry.startPushing()

Thenextthingweneedtodoistogetthestrips.TheStripclassisarepresentationofastringof
LEDpixelsoraflexibleLEDstrip.Aswesawearlier,therearegroupswhichcontaincontrollers
eachofwhichcontainsstrips.Sincewalkingthishierarchyistedious,weprovideawaythatyou
canbypassthisandhavetheregistrydoitforyou.

List<Strip>strips=registry.getStrips()

Thisreturnsalistofallthestripsknowntotheregistry.Alternatively,ifyouonlywanttotalkto
oneparticulargroup,youcandothis:

List<Strip>strips=registry.getStrips(some_group_number)

anditwillreturnalistofallthestripsbelongingtocontrollerswhosegroupnumberisgiven.

Thenallyouneedtodoistoloopthroughthestripsandsetthepixelswithinthemtothe
appropriatecolour.Bewarnedthatnotallthestripsmaybethesamelength!TheStripclass
givesyouamethodtodeterminethelength.strip.getLength()willreturnthenumberof
pixelsinthestrip.Tosetthecolourofapixel,youcancallstrip.setPixel(color,
number)wherecolourcaneitherbeaProcessingcolorobjectoraPixelPusherPixelobject.

5. Every day Im scrapin

Inmostofourexamples,wescrapepixelsfromtheProcessingsketchwindowandsendthem
tothearraydirectly.Thecodetodothislookslikethis:

intstripy=0
List<Strip>strips=registry.getStrips()
intyscale=height/(strips.size())

for(Stripstrip:strips){
intxscale=width/strip.getLength()
for(intstripx=0stripx<strip.getLength()stripx++){
colorc=get(stripx*xscale,stripy*yscale)
strip.setPixel(c,stripx)
}
stripy++
}

6. Tweaking the rate

Normally,theregistrykeepstrackofhowmanypacketsarebeinglost(theygetdroppedifthere
isntenoughbandwidthorifyoureusingawirelessnetworkandtheresnoise)andhowlong
eachPixelPusherissayingittakestopushoutacompletepackettothestrips,andadjuststhe
rateatwhichpacketsaresentautomatically.However,ifyoureonaveryslownetwork,likea
verylongwirelesslinkoracellularmodem,youmightwanttoaddanextraslowdown.Thats
whattheregistry.setExtraDelay()methodisfor.

registry.setExtraDelay(milliseconds)

Thisaddsanextradelaytoeverypacketthatissentbytheregistrysworkerthreads.Ifyousetit
tozero,onlythestandardratelimitingapplies.WithfulllengthHeroicRoboticsstripsandthe
defaultupdaterate,eachPixelPusherconsumesbetween5and10megabitspersecondof
bandwidth.Inthespecialturbomode,aPixelPusherwithtwostripsrunningatmaximumspeed
willconsumeabout52megabitspersecondofbandwidth.

Ifyoureonanetworkwithahigherrorrate,likesomewirelessnetworks,orpoorlyinstalled
ethernet,youmightfindthatyougetpersistentlyhigherrorratesandtheupdatefrequencydrops
uncontrollably.Inthiscase,youmaywanttodisabletheautothrottlingentirely.Weprovidea
registrymethodtodothis.registry.setAutoThrottle(true)willturnautothrottlingon,
andregistry.setAutoThrottle(false)willturnitoff.

Asof20130325,thelibrarydefaultstoautothrottlingbeingturnedoff,soyouwillneedtomake
theabovemethodcalltoturniton.

7. Getting the colours right

Ifyoureusingvideosourcematerialandeverythinglookswashedout,youmaywanttoapplythe
antilogcurvecorrection.PixelPusherfaithfullysendseverythingyoutellittoouttothestrips,
otherwise.Asofthe20130608versionofthelibrary,thereisacalltotellittocorrectthe
luminancecurve:registry.setAntiLog(true)willsetthecurvecorrectionon.Thevalue
falsewillturnitoff.ThanksareduetoRusMaxhamforcontributingthiscode.

8. A note on architecture

ThewayPixelPusherslibraryactuallyworksisthatthereareseveralthreads,allrunning
asynchronously.Thedatastructuresusedinternallyarealllockless,sogoodrealtime
performanceispossible.Thethreadsarenamed,soifyouwanttouseVisualVMtoprofile
whatsusingCPUtime,youcaneasilyidentifythem.Thedifferentprocessesarethese:

TheUDPcallback(akaDiscoveryListener)

Theresasmallroutinethatgetscalledeverytimeadiscoverypacketarrives,whichupdatesthe
PixelPusherdatastructures.Itkeepsthestructuresuptodate,andalsonoteshowlongits
beensinceanygivenPixelPusherhasbeenseen.Iftheydisappearformorethanafew
seconds,theyareremovedfromthestructure.Iftheysaythattheyhavemissedpackets(as
indicatedbydiscontinuitiesinthesequencenumbers)thenthisroutineisresponsiblefor
changingtheappropriateratelimitingvariables.

Thepreeningthread

Thisrunseveryfewsecondstocleanthedatastructuresandremoveanysadlydeparted
PixelPushersfromthesystem.

Themarshallingthread(akaSceneThread)

Thisrunscontinuously,andtakescareoftheworkerthreads,makingsurethatthereisone
workerthreadforeveryPixelPusher.ItsresponsibleforkillingthreadswherethePixelPusher
hasdisappeared,andstartingnewoneswhennewPixelPushersappear.

Theworkerthreads(withnameslikeCardThreadfor00:04:13:a3:a6:5c)

EachPixelPushercardhasanassociatedworkerthread.Theworkerthreadissimpleit
checkstheRegistrytoseehowthecardisdoing,andthenitchecksthestripstructures
associatedwithitscard.Ifanyofthemaremarkedasupdated,itwalksthroughthem,sending
themtothecardattheratethathasbeencalculated,andmarksthemasnotupdated.

Thereareseveraldatastructuresinternaltothelibrarywhichareusedtokeeptrackofwhich
cardsarewhich,whichstripsbelongwhere,andwhichstripsarewhichlength.Itisstrongly
recommendedthatyousticktousingthesoftwareinterfaceslistedinthisdocument,ratherthan
modifyinganyofthestructuresyourself,sincethesehavechangedinthepastandwilllikely
changeinfuturewhichwouldrequireyoutowritenewcode!

9. Multi-head operations

Becausetheworkerthreadsonlysendpacketstoupdatepixelsthathavechanged,itispossible
tohavemultiplecontrollingPCs(orMacs,Androiddevices,etc.)onasinglePixelPusherarray,
aslongastheyareeachcontrollingseparatesubsetsofPixelPushers.Itisstrongly
recommendedthatyourestrictthissharingtoonecontrollingPCperPixelPusher,since
otherwisetherearelikelytobetimingconflictsandyoumayendupfloodingonepoor
PixelPusherwithtwiceasmuchnetworktrafficasitdeserves.Ifyoujustcantkeepthe
architecturethissimple,usetheExtraDelayfeaturedescribedafewpagesagotoreducethe
signallingrate.Youwillalsoneedtodisabletheautothrottlesystem,sincethesequence
numberswillnotbesynchronizedbetweendifferentcontrollingPCs.

You might also like