Category Archives: Uncategorized

The finished panel, showing a green build

Building an Azure DevOps Build Pipeline Monitor from Salvage

How this one got started

Every so often a project starts with a piece of hardware that somebody else has already given up on. This one started in a pile headed for the trash.

The part that caught my eye was the front panel off a Christie Phoenix Quad-T, which has been end-of-support for a while now. The chassis itself was done, but it has a neat front-panel — a half-rack wide and one rack unit tall textured plate with curved corners, a little window for a 128×32 OLED, a strip of holes for indicator LEDs, and a frosted hexagon dead center.

For a while it just sat on the bench while I tried to decide what it should be. The OLED and the LED board were still intact, which meant whatever I built could show both text and color. The obvious answer, the one I kept coming back to, was a build monitor. At work we’ve got a handful of Azure DevOps pipelines that I care about, and “is anything red right now?” is a question I answer by opening a browser tab and looking at a dashboard. A physical thing on a shelf that just tells me seemed like a better answer.

A couple of decisions fell out of that pretty quickly:

  • The center hexagon was too good to waste on a single LED, so I added a 7-pixel NeoPixel Jewel behind it. Seven pixels, seven pipelines. To diffuse it I cut two layers out of a gallon milk jug and stacked them behind the hexagon window — I fully intended to replace that with something proper later, and of course I never did, because it works perfectly.
  • It had to be wireless. A device that needs an Ethernet run is a device that lives wherever the Ethernet run is, and I wanted this thing to be able to sit anywhere.
  • I have a bunch of small embedded boards lying around, and landed on using a Raspberry Pi Pico W running MicroPython. It’s cheap, tiny, has WiFi built in, and is quick to iterate on.
Early bring-up on the bench
Early bring-up on the dining room table

That’s the first time it really looked like something. I had it cycling through some colors for testing ahead of wiring up the real application, but everything worked and looked pretty sweet (albeit crazy bright which I ended up dialing down in software later).

Building the hardware

Here’s the whole thing on paper:

Wiring diagram
Wiring diagram

Parts in the panel

  • Raspberry Pi Pico W — the brains, and about $6.
  • Adafruit SSD1306 128×32 SPI OLED (#661) — this is the stock panel used in the panel’s window. It now runs off the Pico’s 3.3V output.
  • Adafruit NeoPixel Stick — 8 WS2812 pixels, though only 4 of them line up with holes in the bezel. The other 4 are wired, powered, and permanently invisible (more on that later).
  • An Adafruit NeoPixel Jewel — 7 pixels, and exactly the right size to fill the center hexagon.
  • Two silicon diodes — cheap, and the entire reason this works. More below.
  • A 220µF (or larger) electrolytic cap — across 5V/GND at the first pixel, to soak up power-on inrush.
  • A 5V / 2A external supply — the Pico W pulls 100-150mA with WiFi spikes on top, and 11 NeoPixels at full white can hit ~660mA. 2A leaves comfortable headroom.

The OLED is straightforward SPI — clock, data, chip select, D/C, reset, plus 3.3V and ground straight off the Pico. The NeoPixels are a single data line off GPIO15, daisy chained from the panel’s original board into the Jewel.

Power is where it gets slightly more interesting. Rather than run the Pico off USB and the LEDs off a separate supply, I run one 5V supply and “T” it: one leg to the LED chain, one leg to the Pico’s VSYS pin. That’s pin 39, and it’s the input the onboard regulator is designed to accept external power on. Not VBUS (pin 40) — that one’s tied to the USB rail, and I learned that feeding it can damage the Pi board. Splitting a single supply this way also hands you a shared ground for free, which the NeoPixels very much require. And because there’s a diode between VBUS and VSYS on the Pico, you can leave USB plugged in at the same time while you’re developing without the two supplies fighting which ended up being massively helpful while tweaking the software on this thing.

The diode trick

Here’s the one that I spent a lot of time on and almost gave up over. WS2812 pixels want a logic-high somewhere north of 0.7 × VDD. The Pico’s GPIO is 3.3V. On a true 5V rail, that’s under spec, and the symptom is maddening: the pixels get data — I could see it on my oscilloscope — but they mostly ignore it. Sometimes the first pixel lights, usually nothing, and sometimes it worked fine for a few minutes and then stopped.

The fix that finally worked was two silicon diodes in series in the LED’s +5V feed — not in the data line — dropping the pixel rail to roughly 4.2-4.4V and giving that 3.3V signal actual margin. I tried one diode first, and it almost worked, which is the worst outcome. Forward voltage sags at low current, and this chain spends most of its life dim or idle, so a single ~0.6-0.7V drop kept wandering back out of spec. Two diodes, rock solid ever since. This is apparently common knowledge for smart people, and I saw a lot of posts online about it (after circling in on it by chatting with ChatGPT).

Worth checking your supply too, incidentally — some USB bricks like the one I’m using measure 5.4-5.5V under light load, which makes the margin problem worse than the initial math would suggest.

About that soldering

The board on the bench, mid-assembly
The board on the bench, mid-assembly

The pictures above and below show the wired-up mess at the back of the panel, and admittedly it’s messy. Every joint on that Pico is a small monument to enthusiasm exceeding technique — blobby, over-tinned, and held in position by optimism and hot glue. In my defense: the panel’s backing plate is the mounting surface, everything has to sit flat enough to still fit behind a curved bezel, and there was not a lot of room in there to be elegant.

The back of the assembled panel
The back of the assembled panel

It doesn’t look like much, but it hasn’t dropped a connection yet, so I’ve decided it’s a feature.

Breadboard testing with a spare display
Breadboard testing with a spare (unrelated) display

I did most of the ugly experimentation using a breadboard — which is where I dialed in that diode business — before committing anything to the panel itself. This picture above was the last part where I was figuring out the diode thing.

3D printing a chassis

The panel is a curved face with nothing behind it, so it needed a body. I designed one in Tinkercad and printed it on my Prusa Core One, which I will take any excuse to mention because I love that printer. Big enclosed build volume, quiet, and it just prints — I’ve stopped thinking about first layers entirely, which is the highest compliment I can pay a 3D printer.

The case design in Tinkercad
The case design in Tinkercad

The design is deliberately dumb: an open-backed tray that the panel drops onto face-out, with all the electronics hanging down inside it. The panel is held on with velcro rather than screws, which means getting back to the Pico’s USB port is a matter of peeling the face off.

It took three revisions to get right, which I think is about par:

Three printed revisions with notes
Three printed revisions with notes
  • Revision 1 was the naive version — a tray, a hole in the bottom for the power connector, and two corner tabs to hold velcro. It worked, and immediately told me what was wrong with it.
  • Revision 2 added “diamond” vent holes along the walls so the Pico and the LED chain aren’t sealed in a black plastic box (I noticed it getting warm with the first rev), mounting brackets on the back wall so it can hang, and a lot more flat surface area for the velcro to actually grab.
  • Revision 3 was one specific annoyance: the back-wall brackets only worked with the power connector oriented one way. Reworked them so it hangs correctly with the cable exiting either direction.

The STL is in the repo under case-stl/ if you happen to have a Quad-T front panel lying around, which, statistically, you do not.

Building the software

This is the part I actually enjoyed, and the part where the design decisions mattered.

The firmware should be as dumb as possible

The single most important constraint came from the case: updating this thing is a pain. To reflash it I have to peel the panel off the case, find the USB cable, plug into the Pico, push files with mpremote, and put it all back together. That’s a five minute round trip for even a simple change.

In addition to the physical update process, the reality is that this is a simple IoT device that I might repurpose for anything, and instead of writing tedious code specific for one use case on the target, making it a wireless display and allowing external code to control it feels like the more natural fit. In my case I have a homelab Kubernetes cluster providing more than enough infrastructure to provide messaging and application hosting for the Azure build monitor today and whatever else I might want tomorrow.

So the firmware knows nothing about builds, and it doesn’t know what Azure DevOps is. It stores no credentials beyond the WiFi password, makes no outbound calls to anything but the local MQTT broker, and listens on no inbound ports. It is a generic display device: it connects to WiFi, subscribes to one MQTT topic, and renders whatever JSON shows up there. All the interesting logic lives somewhere I can redeploy in thirty seconds.

That MQTT message contract is the whole design, and it looks roughly like this:

{
  "display": {
    "drawMode": "text2Line",
    "textLine1": "EchoCommon",
    "textLine2": "Success: 2 Days ago",
    "textLine1Align": "left",
    "textLine2AutoScroll": true,
    "oledBurnInProtectionMode": "invertDisplay",
    "oledBurnInProtectionInterval": 60
  },
  "leds": [
    { "r": 0, "g": 255, "b": 0 },
    { "r": 255, "g": 0, "b": 0, "displayMode": "blinkThruBlack", "blinkDuration": 0.75 }
  ],
  "transitionDuration": 2.5,
  "transitionType": "smooth",
  "pixelBrightness": 1.0
}

The goal was high-level but rich — one message should be able to express an entire visual state, and the device should own everything about how that state gets rendered over time. Some of what it can do:

  • Text, one or two lines, each independently left-aligned or centered, and each able to ping-pong scroll if it’s wider than the screen (with a dwell at each end, because scrolling that instantly reverses looks nervous).
  • Raw pixels — you can also just hand it 512 bytes of hex and paint the 128×32 framebuffer yourself.
  • Per-pixel color as a simple array. Position 0 is the first externally visible LED.
  • Transitionsimmediate, smooth (blend from current to target), or thruBlack (fade down, then up into the new color), with a duration.
  • Per-pixel blinkingblink for hard on/off, or blinkThruBlack for a gentle pulse, which is what “this pipeline is running right now” uses.
  • Global brightness, as a single multiplier applied at the moment of writing to hardware.
  • OLED burn-in mitigation, because this display is on 24/7 and may showing nearly the same thing which can get burned into the display permanently. The protocol supports configuration to automatically can invert the panel colors, nudge the text around DVD-logo style, or do nothing.

A few details are worth calling out that evolved organically while I was working on this project. First, the burn-in inversion is motion aware — it only flips once the screen has held genuinely static content for a while, and any new content immediately un-inverts. Since the build monitor rotates through builds anyway, this just looked annoying and served no purpose. The one caveat is that if for some reason the application driving display messages stops updating the panel, the panel will protect itself automatically by using the invertion technique. Second call out is for the hidden LEDs: the panel’s board has 8 pixels but only 4 are visible, so the firmware keeps a map from logical position to physical position and never exposes the hidden ones over MQTT at all. Everything upstream sees a clean chain of 11 addressable pixels and has no idea there’s anything else on the wire. Had I thought about this before hot glueing everything together I would have popped the NeoPixel strip out and drilled 4 extra holes, but it’s simply not worth it to me anymore (but since it’s software configurable if I make another one down the road it’s a simple code change to re-enable it).

The device also publishes its last applied state back to a retained state topic, plus an online/offline status topic wired up as an MQTT Last Will — so anything that cares can tell at a glance whether the panel is actually alive.

Getting real build data to it

How a build result reaches the panel
How a build result reaches the panel

With the panel reduced to “renders JSON from a topic”, the rest is a plumbing problem:

  1. Azure DevOps fires a run.statechanged service hook whenever a build changes state.
  2. n8n catches that webhook and republishes it onto my MQTT broker. n8n is already running in my homelab and is very good at exactly this sort of glue, so it saved me from writing and deploying my own webhook receiver just to turn one POST into one MQTT message.
  3. A small Go service (azureBuildMonitor, containerized and running in my k8s cluster) subscribes to that topic, keeps track of the world, and publishes display commands to the panel.

The Go service is where all the real behavior lives, and it works on two timescales at once. It does a full refresh every hour — one API call that fetches the latest 4 builds for every pipeline and overwrites its in-memory view with the authoritative answer. That’s the drift-correction backstop. Then, for responsiveness, every webhook event patches that view in place immediately.

The webhook payload alone turned out not to be trustworthy enough to use directly, which was a fun little discovery: it carries no branch name, and its pipelineId field is actually the run ID rather than the build definition ID that everything else is keyed on. So each event triggers exactly one “get build by ID” call to resolve the truth, and that result is what lands in the store. Events make it fast; the hourly sweep makes it correct.

What you actually see

Every 5 seconds the service publishes a fresh command, and that’s what drives the little show:

  • The 7 hexagon pixels are the pipelines — one each, in a fixed order. Each one always shows its own true status color, so the hexagon as a whole is an at-a-glance health check of everything at once. Green is good, red is failed, blue means running (and pulses through black so it’s obviously live), gray is cancelled.
  • The pipeline being highlighted right now is shown at full brightness while the other six are scaled down to about a quarter. It cycles.
  • The OLED shows the highlighted pipeline’s name on line 1, and cycles line 2 between its status (“Success: 2 Days ago”) and its branch. Once a pipeline has shown all its messages, the highlight moves to the next pipeline and the hexagon follows along.
  • The 4-pixel strip on the right is the highlighted pipeline’s last four builds, newest first. Three green and a red tells you something quite different from four reds, and it’s the fastest possible answer to “is this a blip or a trend?”

Colors and blink timings are all configurable per status, and — this detail matters more than it sounds like it should — configurable separately for the hexagon and the strip. The hexagon sits behind two layers of milk jug and the strip pixels don’t, so the same RGB triple reads as two completely different colors depending on which group it lands on.

Two bugs that weren’t where I thought they were

The panel would connect to MQTT and then get its socket killed immediately, with the broker logging Bad socket read/write and the client reporting a CONNACK “identifier rejected”. I spent an embarrassing amount of time on client IDs. The actual cause: umqtt’s default keepalive is 0, and this particular mosquitto build accepts the CONNECT and then hangs up. Send a real keepalive and ping on it, and everything’s fine.

Worse — and later — well-formed clients started getting rejected with strange, inconsistent errors again. This time it genuinely wasn’t my code. My broker’s Kubernetes Service had externalTrafficPolicy: Cluster, which let MetalLB’s L2 speaker answer ARP from a node that wasn’t the one running the mosquitto pod, forcing an extra kube-proxy SNAT hop that corrupted connections from at least one client. One word changed to Local and it’s been quiet ever since. I’d already rewritten a working MQTT library twice by then, chasing a bug that lived three layers below it.

MQTT Connected
MQTT Connected

So where are we, and what’s next?

The panel in its natural habitat
The panel in its natural habitat

This one landed a lot better than I expected. Most of my projects are fun first and useful second, if at all — this one is genuinely, boringly useful. It sits on a shelf above my little cluster of boards, and every morning I glance at it on my way past and know instantly whether anything needs my attention. No tab, no dashboard, no login.

The unexpected part is what that’s done to my behavior. When build health is a thing sitting in my peripheral vision, a red pixel is a small irritation that I want to make go away, and I fix things the same day instead of discovering them on Thursday. I did not build this expecting to become a more disciplined engineer, and yet.

Next up, most likely: moving off my local broker onto a cloud-hosted message queue, so the panel doesn’t need to live on the same network as the service driving it. Then I can hang it on my cubicle wall at the office, which was sort of the point all along. Or I’ll just build a second one — assuming I can find another junk front panel, which is honestly the hard part.

Everything’s up on GitHub if you want a closer look — firmware, the Go service, the tester CLI, and the case STL:

github.com/dsmithson/quad-t-mqtt-display

If you run into problems or have ideas, filing an issue on the repo is by far the best way to reach me — I have a bad habit of not checking blog comments for months at a time.

The great garage refresh of 2018

In December I pulled the trigger and ordered a new Tesla Model X. Being more of a computer on wheels than a car with a cool computer and autopilot system, this car speaks to me on a level deeper than I can comprehend. The time from order to delivery was a little over three months, and it became apparent immediately that it would need a worth garage to call it’s home. This blog post is a chronicle of the creation of just such a garage.

Where we Started

The garage had years worth of stuff packed into every corner, and hadn’t seen a car actually parked in it for quite some time. I wouldn’t allow my shiny new car to bake in the driveway, so initially it seemed obvious that a clean-out and maybe some paint on the walls and cabinets was in order.

First Attempt

At first all I really wanted to do was paint the walls and cabinets, and clean up the junk that I had accumulated over the years. As the space cleared out and I went to painting, I started noticing how the cabinets weren’t really in great shape, and were starting to crumble. I went to painting anyway, but realized quickly that there wasn’t going to be much of an improvement. I got through a couple walls, but didn’t even get to painting the cabinets. I knew this was going to become a much bigger project…

Tearing it all Down

The cabinets were garbage. I ripped out all the cabinetry, and then was faced with bare studs that would need some drywall. The garage had previously been a carport, and so the walls it did have were exterior walls or just bare studs. These exterior walls were 1/2″ wood with slats, and I finally broke down and ripped all that out as well. The covering of the uneven wall along the house came out and revealed a door and a window that had been covered over on the other side during a previous remodel. One wall after another, I kept tearing more and more down until I had pulled almost all walls, with the exception of half the wall running along the house, down to the studs.

Adding more Power

While the studs were exposed, I added electrical outlets to the outside and back walls, and ran the power line up to the attic. I also added a pair of 8′ LED strip lights which look like fluorescent fixtures to give the room a brilliant white light. This lighting alone was a major improvement both for working in the garage as well as the finished product.

I had an electrician come in to wire up the outlets to a new 20-Amp breaker, as well as wire a new circuit to an 80-Amp Tesla wall charger unit which would live on the back wall of the garage. We had a few surprises – my electrical box is 3-phase, wasn’t grounded, and needed some work to fit some new breakers. We got through it in a few days, but between the Tesla wall charger, parts, and labor the whole endeavor set me back just over $2,000.

Building it Back Up

I framed out the half of the previously uneven wall against the house. The garage is notoriously an oven in the summer, so I decided to insulate all the walls and the attic while I had the opportunity. The ceilings are 8′ high, so I initially put in drywall strips vertically (they come 8′ x 4′). About the time I finished I found out that I should be putting a 1/2″ gap between the floor and the drywall, and also using special ‘green board’ drywall which is treated to resist moisture. I ripped all my hard work out and started again with new green board. There were some ceiling gaps that I fixed up along the back wall due to the prior 1/2″ wood boards being thicker than my new 1/4″ drywall.

To mud and tape the drywall together, I hired a professional. That is an art in itself, and I figured for all my work I wouldn’t want to do a bad job here and not get the benefit of clean/smooth walls. Had him mud the entire ceiling while he was in there, to get rid of the prior texture and some other holes and cracks that needed to go. $950 later I had something starting to resemble a legitimate garage again.

Finishing the Job

Painting the walls was easy, but painting the ceiling was much more difficult than expected. I stayed up many nights until 2AM just painting, and the excitement as it started to look like a showroom fueled me to push hard. With the walls painted, the floor became an instant eye-sore. It quickly became apparent I was going to need to go all the way and epoxy the floor. The idea of a showroom look had taken hold, so I committed to going with a white color.

The prep work and application of the epoxy floor took as much time as the walls. I ground down the floors with a large grinder rented from the Home Depot. I ground down along the walls by hand using a hand grinder. Then I acid washed the entire garage to prep the concrete. Then I spent hours sealing cracks in the concrete to ensure a smooth surface. This process alone spanned weeks and cost a couple hundred dollars. The actual epoxy (two coats) and associated sealer/hardener (also two coats) cost around $600 and also spanned over a week due to curing time between coats.

The very final step was trim, which I waited several weeks before knocking out. Simple white, pre-primed trim pieces went up in an afternoon, and after a quick bead of silicon caulking I was finally done.

Completed Garage Renovation

Overall Thoughts Now that it’s done

At the outset if you told me I would spend over $5,000 to renovate my garage, I likely wouldn’t have started at all. But that’s not how great projects work. This garage is awesome, and makes me happy. Sometimes I go out to my car, stop, and pull out my phone to take a picture or two. It looks walking into a showroom every day. It’s worth it, and I hope for everyone to have something that brings them recurring joy like this. It’s a prefect compliment to the car, which also brings me boundless joy.

For those wondering or hating on the white floor – I mop it once a week and it looks brand new again after three months. The car doesn’t drip random oil or other fluids on the ground, so at worst you end up with some tire tracks after a few days. If you live somewhere rainy, your mileage may vary. In Phoenix Arizona it’s not bad.

Future Stuff

I would very much like to install three of the Tesla PowerWalls and some solar. Beyond the energy benefits, the visual design of those PowerWalls would really look amazing along that red wall in the garage.

I need to build a home for my bike, which is now living in the back yard. It’s covered, but it’s also an expensive machine that shouldn’t be left outside.  I already have some plans for this – look for that in another post soon.

Garage Door Opener Project: Part 1

So a few weekends ago I decided that I’d like to embark on a fun project of making a network enabled garage door opener.  A few weeks ago I was walking home from a neighbors, and found myself wishing I could open the garage from my phone, or ideally from my Band.  This seemed like enough of a pretense to embark on a new project, and after a few weeks of tinkering I’ve emerged with the following working home accessory:

The Plan

When I started this project, I laid out a few key requirements:

  1. Build an application targeting an existing Wi-Fi enabled Raspberry Pi I had laying around.
  2. Build and debug the application on the Pi, and write it in modern C++.
  3. Setup Git on the Pi, and keep the code up in GitHub.
  4. Breadboard the hardware required to switch the garage light and door.
  5. Integrate a temperature/humidity sensor to allow me to play with garage cooling options over the upcoming summer months.
  6. Add a garage door closure sensor so the device can tell when the door is open or closed, so the app can provide open/closed status back to network clients.
  7. Integrate my X10 Firecracker dongle, to allow me to control my existing X10 outlets/lights

The C++ requirement came partly from a desire to tackle a ‘real’ project with the language using modern language features like smart pointers, and to try out some of the Boost library’s programming constructs for asynchronous networking.  I’ve also been playing around with building basic electronic circuits, and making some necessary hardware for the Pi seemed like a fun time.

The Hardware

It took a while to get the electronics working, and I learned a decent amount along the way (even though there really isn’t much to this).  I started trying to trigger my 3.3v relays directly using the GPIO pins on the Pi, and when that didn’t work I had to go back and figure out that GPIO pins can’t drive the level of current required.  I was also using a shift register initially to reduce the number of pins coming off of the Pi, but it ended up being unnecessary and was eventually removed.

Testing first iteration using shift registers

 

The second iteration two introduced some diodes and transistors, and then I was up and running.  I added a couple momentary contact switches to provide local light and door control, and picked up some DHT11 to do my temperature/humidity sensing.

I put everything together on a breadboard, with a plan to solder everything together and put it into a clean enclosure with my couple of surface mounted switches, but I quickly fell in love with the idea of just putting an acrylic top over it, and mounting the Pi and breadboard together on another sheet of acrylic (spray painted black on the back), as-is.  It has a fun look to it, and that might be what I like about the whole project most.

Second iteration of electronics using transistors, diodes, and opto-couplers

Second iteration of electronics using transistors, diodes, and opto-couplers

The one part that sadly hasn’t made the cut yet is the X10 firecracker.  This is a control device I used to have connected to my PC (back when I had a serial port), which works in conjunction with a wireless receiver to send On/Off power commands to outlets and switches throughout my house.  I found and installed an RS-232 shield on the Pi, but later found out that it doesn’t connect the RTS/CTS lines used to power and signal the Firecracker.  Oh well, I’ll think of something in the future…

The Software

I chose CodeBlocks as my IDE of choice on the Pi, after seeing a few recommendations online for people coding in C++ on the Pi.  I was able to target C++ 11 features, and although it took a while to figure out how to get the Boost libraries to link up to my project, the process wasn’t too bad.  In general, however, I wouldn’t recommend that people try to write code directly on the Gen 1 Raspberry Pi, at least when you’re trying to do it using an IDE on top of x-windows.  It’s really slow, not only when trying to compile (which takes a long time even for trivial programs), but even typing code into the editor is noticeably delayed.  I eventually started writing and testing as much as I could in Visual Studio, and then bringing the code files over to the Pi for final testing and implementation.  For final debugging/testing I eventually ended up editing files using VI over SSH to skip the UI overhead on the PI, and that seemed much more usable.

Running CodeBlocks on the Pi

Running CodeBlocks on the Pi

I have the project checked into GitHub, and you can check it out here.  With any luck I’ll actually come back and check in some schematics for the hardware portion of the application, but for now at least the software is up there in case the Pi dies in the extreme heat of my garage over the summer.

Trying it Out

I was pretty excited the first time I was able to trigger the door and light manually using the new Pi-based controller, and even more so when triggering it from my desk.  The design has two LEDs, one that blinks on/off at roughly a one second interval, and one that lights red when the door is open.

Talking to the garage door using Putty

What’s Next?

In the next post, I’ll cover writing Windows Phone and hopefully a  Microsoft Band apps to talk to our garage door.

Open Source release of C# Spyder control library

The software library used for the Spyder Client for the Windows Store is now available for use as a nuget package (for .Net developers), and the source has been made available up on GitHub.  This library provides an implementation of the documented UDP control protocol, and additionally provides some of the ‘secret sauce’ used by richer control clients.  From the GitHub readme:

Key Features

  • Full implementation of the published Spyder UDP control protocol
  • System Config File parsing – provides full Spyder data object model to clients
  • Drawing Data (live view) data stream deserialization (Supports 2.10.8 – 4.0.4)
  • Spyder quick file transfer procotol (QFT) implementation. List, get, and set files
  • Thumbnail Manager – provides background downloading, multi-resolution scaling, and memory lifetime management of system thumbnail images

Consuming the Nuget Package (.Net Developers)

The Nuget package is the easiest way for .Net developers to consume the Spyder client library, and can be used to target Windows Phone 8.1 or later, Windows RT / Store apps, or .Net 4.5 or later desktop applications.  Get it from Visual Studio using the Nuget package manager (see screenshot below).

Nuget Package Info Page:  https://www.nuget.org/packages/SpyderClientLibrary/

Getting the Spyder Nuget Package

Getting the Source

GitHub Project Link:  https://github.com/dsmithson/SpyderClientLibrary

If you want to build the library yourself, peruse the source, or even port it to another platform, head over to GitHub to get access to the source code (link above).  This source uses the Apache 2.0 license, so you can do whatever you want with it.  Please do use the issue tracker or submit pull requests if you have anything you’d like to add or fix, and have fun!

What’s Next?

In future posts, we’ll explore some sample applications that use the library to do common things like control command keys, perform file backup/restore, and possibly even create a display simulator control that works from the Spyder server’s real-time data stream.  I’ll also be working on some general documentation that I’ll post on the GitHub site, so keep an eye on the project’s GitHub page.

Spyder Client Command Key Visualization

My favorite feature in the Spyder Client for Windows 8.1 is the command key thumbnails it displays.  These thumbnails are smart, in that they combine the live look on screen with the expected execution plan for the command key, resulting in a true thumbnail preview of what the screen(s) can be expected to look like after the command key has executed.  In the video below, I give you a quick overview of this feature in action.