Author Archives: Owen Lyke

Tracking Stars

via SparkFun: Commerce Blog

Like many people on Earth, I love stargazing and imagining all the possibilities that lie in the vast expanses of the universe. As a gearhead, tech nerd and maker I have taken that fascination and tried to express it through astrophotography. Until recently, I've been able to get increasingly clear, bright, and awe-inspiring shots just by improving my technique using tools I already have. Now, however, I have encountered a new foe: physics.

There's a lot of physics going on when you look at the stars. Fusion is releasing huge amounts of EM radiation from distant clumps of gas. Those waves travel through the vacuum of space, are bent by gravity, dispersed by vast clouds of gas and illuminate countless worlds. Our planet rotates on an axis while orbiting the Sun, which is caught in a milky whirl pool that floats lazily amongst trillions more. It's all quite messy, really!

Most of that stuff doesn't matter for astrophotography because exposures are typically short - from a few minutes to a few hours. The translation of the Earth over that period does not change the appearance of the sky, but one thing - the Earth's rotation - has a huge effect.

star trails
All In A Spin Star WikiMedia Commons

A lot of photons are needed in order to capture an image of a faint object in the sky. That means a lot of time spent photon-hunting (or gathering, whichever you prefer). And with the Earth spinning right round at 360 degrees per day, it won't be long until your star is smeared right across the image. This became my limiting factor, so I decided to build a solution.

Star Trackers

Star trackers are devices used to counteract the rotation of the Earth so long-period observations of the sky can be performed. The concept is simple: make a platform that rotates against the Earth at the same rate. Designs for star trackers range from Ford Pinto to Rolls Royce, and there are plenty of resources online about how to make your own. My design is of the "barn door" type, and is inspired by these projects: Makezine: 6 DIY Star Trackers for Perfect Night Sky Photos

In my build I add positional feedback so that the tracker can correct its motion over time - allowing (in theory) for increased precision.


Usually my projects don't have a complicated mechanical aspect, but this one sure did. It was a good opportunity to try out some of the new robotics-focused parts that SparkFun offers like gears, axles, hubs, bearings, motors and encoders.

Here are the SparkFun parts that I used:

SparkFun Logic Level Converter - Bi-Directional

SparkFun Logic Level Converter - Bi-Directional

SparkFun Qwiic Cable Kit

SparkFun Qwiic Cable Kit

SparkFun RedBoard Artemis Nano

SparkFun RedBoard Artemis Nano

Rotary Encoder - 1024 P/R (Quadrature)

Rotary Encoder - 1024 P/R (Quadrature)

SparkFun Qwiic Motor Driver

SparkFun Qwiic Motor Driver


Standard Gearmotor - 20 RPM (3-12V)

Set Screw Hub - 1/4" Bore

Set Screw Hub - 1/4" Bore

Gear - Pinion Gear (16T; 0.25" Bore)

Gear - Pinion Gear (16T; 0.25" Bore)

Gear - Hub Mount (84T; 0.5" Bore)

Gear - Hub Mount (84T; 0.5" Bore)


Two changes to this list are as follows:

  • added a level shifter breakout for better compatibility with the encoder
  • changed the gearmotor to a 20 RPM version to fix a math mistake I made early in planning

I chose the RedBoard Artemis Nano for its small footprint, great support for interrupts (for the encoder) and, admittedly, because I am fond of it. To drive the motor I decided to try out the Qwiic Motor Driver because it would be easy to hook up and get going.

I also needed a few miscellaneous pieces/parts from a hardware store:

  • three-foot section of 1/4" stainless steel threaded rod
  • one brass 1/4" nut
  • one 1/4" nylock nut
  • one 1/4" wing nut
  • two 1/4" ID washers
  • one 1/4" ID lock washer
  • approximately 3.5' maple 1x4

Figuring out how exactly to mount all these parts so that the gears would mesh correctly was perhaps the most challenging and time-consuming part of the project. I ended up making some rough 3D models of the main components and assembling them using OnShape (a very cool web-based 3D modeling application). Here are the public design files:


I tried to dust off some old carpentry/fabrication skills and more or less discovered that dust is all they were to begin with. That being said here was my general process:

To build barn doors:

  1. Cut two 18" sections of maple 1x4 stock to serve as the barn doors.
  2. Within one section, mark a center line and a perpendicular hinge line about 1" from the end.
  3. Measure 14" from intersection and mark as center for the threaded rod.
  4. Use spray adhesive to locate top template over the threaded rod hole center.
  5. Drill small (1/8") holes where the pinion gear axles would go with a drill press (you can use a hand drill but make sure it is straight).
  6. Using those two holes, you can locate and adhere the bottom template.
  7. Use a router to route out the top template (pay attention to the correct depth [0.312"] and ignore the center hole).
  8. Flip over and drill the axle holes for the encoder/motor all the way through (5/16" or 3/8" drill bits work here).
  9. Working from the smallest shapes to the largest shapes, mill out the two levels for both the motor and encoder holes. Do the smaller ones first, because the template is destroyed in the process - pay attention to using the correct depth.

To build the drive mechanism:

  1. Use whatever means you can to affix the brass nut to the aluminum hub so that the hub can be screwed into the large drive gear (this one is hard, I used JB Weld, but broaching a hexagonal hole would be ideal).

To make the drive rod:

  1. Use a string or other method to trace out about 60 degrees worth of a 14" radius arc.
  2. With hands at either end of the threaded rod, use a pure twisting motion to bend it to match the 14" arc (the center will likely be the best section).
  3. Identify your best section and cut it out - the more you have, the longer you will be able to run the star tracker at a time.

Electronics hookup:

  1. Solder wires to the motor terminals.
  2. Connect motor wires into terminals A1 and A2 of the Qwiic Motor Driver.
  3. Connect your 8V to 11V DC power supply to the VIN terminals of the Motor Driver.
  4. Connect the Qwiic Motor Driver to the RedBoard Artemis Nano using a Qwiic Cable.
  5. Connect the encoder's brown wire to the same voltage supply as your motor.
  6. Connect the encoder's blue wire to GND on the Artemis Nano.
  7. Use your level shifting solution to connect the encoder's black and white wires to the Artemis on any pins you like (mosfet level shifters are recommended - I used voltage dividers because I was caught off guard and needed to whip something up fast. If you do this beware that the encoder has a 2k output impedance. I wound up using 2M and 1M resistors in my dividers).
  8. Power the Artemis Nano with a USB cable - or provide some other 5V source to VIN on the dev board.


Though currently a work-in-progress, I plan to continue updating this project on GitHub: StarTracker.

Star trackers of this style don't necessarily even need software - a simple voltage regulator to adjust the motor's speed is often enough. In this case, however, I wanted to have a little fun and aim for high precision/reliability. For me that meant adding a feedback loop.

Earth's rotational rate is very stable (over the periods of time used to expose film for astrophotography), so you might wonder whether a control system is really necessary. In an idealized case you could set a motor off to do its own thing and it would also run at a constant rate. Any mismatch between the rate of the motor and the Earth's spin would be a constant error and stack up over time predictably. In fact, if you could get that difference to be small enough, then one could effectively hide it with other techniques (such as image stacking).

Here's an example of what that might look like (exaggerated)

alt text

  • Red: the measured error in rotational rate over time
  • Blue: the overall positional error that has accumulated over time - you would observe this in an image as trails beginning to form behind the stars.

What happens when dust/dirt clogs up the system? When the battery voltage sags in cold weather? When a heavy camera/lens combination strains the drive system? In those cases there may be error that changes over time and is much harder to predict and account for. Covering these scenarios is what an active feedback loop does very well.

Here's the same plot after adding proportional (P) feedback

alt text

This time, as the overall position error increases, a proportional "restoring force" is applied to bring it back to zero (in practice this means changing the speed at which the motor is set to run). You can see that the error now oscillates around zero rather than growing infinitely. This is good, but we want the error to settle down to zero - that's where derivative feedback comes into play.

And again after adding basic derivative (D) feedback

alt text

Finally, the error is controlled to zero. This is done by adding a "force" that counteracts motion toward the target.


Overall this was a fun way to combine my interests in astrophotography and control systems. The role of the latter in this project deserves a much more in-depth and precise examination, but it had to start somewhere! Hopefully this rig will be out there tracking stars very soon.

alt text

comments | comment feed

Enginursday: Switching Sensors with a WS2811 Addressable LED Driver

via SparkFun: Commerce Blog

Sometimes we sign up to do crazy things - like going skydiving with your best friend or running a marathon in flip flops. In this case I wound up hatching a plan to use 192 of the same I2C sensor in an interactive installation. As the sheer size and complexity of the goal sank in I grew increasingly curious if there was a simple way to wire all those sensors together. The result is elegant in theory, noisy in reality, and probably won't work at scale. However it is really fun to talk about so stick around to hear about using addressable LED drivers as software switches!

A Collaborative Staff

The task: create an interactive installation that allows young children to play with sound and color collaboratively. When the Museum of Boulder asked me and Joe Ryan create this we started thinking big! Joe's fantastic idea was to create a larger-than-life musical staff that kids could place notes on to craft their very own song. The color of the notes would determine what kind of instrument was used. After settling on a size (four measures, four positions per measure, and 12 notes per position) we needed to find a way to actually make it work.

Color Nodes

Sensing colors has been made easy by ICs such as the ISL29125. The general idea was to use one of these at every note position along with an addressable LED that could be used to both illuminate the notes for a reading and serve as a visual guide for getting started. A central system would take readings to determine which notes should be played and by which instruments. The challenge was to connect 192 I2C devices that all use the same address.

There are several ways to accomplish this task - perhaps using a tree of I2C multiplexers, using several independent sensing groups each with its own microcontroller, and even extravagant methods using wireless communication. I went in search of a solution that would minimize cost and installation complexity. What I found was the WS2811 addressable LED driver IC.

WS2811-Based Switch Design

Knowing that LED data would need to be sent to every node gave me an idea - what if the power to the color sensor could be controlled in the same way? Perhaps this would allow all the sensors to share one I2C bus, as long as only one was powered at a time. In retrospect it is hard to remember which search terms exactly led me to what I needed but eventually I found the datasheet for the WS2811. A companion to the popular WS2812B addressable led, the WS2811 encapsulates the same driver but instead breaks out the R, G and B output pins so that you can attach your own LEDs.

The datasheet indicated that the outputs were constant-current drains of up to 18.5 mA. Not ideal - a controllable voltage output would have been simpler to use but fortunately we know how to convert current to voltage and vice-versa.

V = I • R
I = 18.5 mA
V = 3.3 V (using ISL29125 supply voltage)
R = V / I = (3.3 / 0.0185) = 178.37 Ω

A 178.37 ohm resistor with 18.5 mA current flowing through it will drop 3.3 V. In other words a constant current driver set to pull 18.5 mA from 3.3 V through a 178.37 ohm resistor would need to set its output at 0V!

Now it's not quite that simple, unfortunately. It would be if the LED driver was a true "analog" constant current driver, but LED drivers typically use Pulse Width Modulation (PWM) to approximate constant current. The duty cycle of the PWM signal is what we perceive as the brightness of the LED. When trying to use an LED driver as a switch you run into a problem - your switch changes quite frequently, even when you have it set to just one value!

PWM Cuty Cycle Diagram
pwm duty cycle diagram
Image: Android Developer's Site

You might hope that asking the LED for either a 0% or 100% duty cycle would work well enough, but we can't guarantee how the driver operates. In the case of the WS2811 I found that a channel value of 0 results in a true 100% duty cycle (this makes sense for a common anode LED driver), but the max value (255) only approaches a 6% duty cycle.

The ability to reach a true 100% duty cycle is good for powering the sensor because it means minimal noise. The fact that the supply voltage will occasionally come up, however, is concerning because that may cause any one of the sensors to turn on and interfere with communication on the I2C bus. In order to combat this I used a low-pass RC filter that incorporates the current-limiting resistor.

The WS2811 uses a PWM frequency of 2 kHz, so a 6% pulse would have a width of (6 / 100) • (1 / 2000) = 30 μs. A 30 μs square wave pulse contains many frequency components, the slowest of which is (1 / 30 μs) = 33.3 kHz. In order to block that frequency one would choose a corner frequency below that value. Using a simulated circuit on, I played around with various capacitor values to get a good balance of high-frequency blocking (higher capacitor values) and the time it takes to power on a sensor (low capacitor values)

falstad circuit simulator for RC filter on LED driver output simulation of WS2811 switch

fc = 1 / (2 • π • R • C)
R = 220 Ω (choose a common resistor value greater than 170.37 Ω)
C = 1 μF
fc = 1 / (2 • π • 220 • 1 • 10-6) = 723 Hz

Using the design parameters from above I laid out a prototype schematic and board in Eagle.

circuit schematic in Eagle PCB


In an effort to move quickly I ordered PCBs and the WS2811 driver ICs at the same time. In fact my original design used a slightly more complicated circuit that I was able to simplify during testing. We've been discussing the simpified circuit but the boards contain a few extra components and a host of white-wire fixes. That's a lesson in why you should always do a 1:1 scale package check for any new parts that you intend on using! Below is an image of the prototype as tested:

prototype v01 with white wire fixes and simplified circuit

To test the board I wrote a quick test sketch. It uses the FastLED library to control the LED data line (for the WS2811 as well as the WS2812B illumiator LED), and the SparkFun ISL29125 Arduino library to read from the RGB sensor.


Test sketch for for turning on/off an ISL29125 RGB sensor using a WS2811 LED 
driver IC. 

Owen Lyke 
25 Mar 2020

FastLED library by Daniel Garcia
ESP32 based microcontroller such as the SparkFun ESP32 Thing Plus (

- Build your own color node based on the schematics shown at:
- Connect the I2C pins SCL and SDA to the Wire port of your ESP32 based board
- Connect the LED data line to pin 18 of the ESP32 on your board
- Connect GND and 5V lines

This code is beerware.
Distributed as-is; no warranty is given. 


#define DISPLAY_TITLES 1      // 1 to print field names to serial
#define DISPLAY_RGB 0           // 1 to print r g b values to serial
#define DISPLAY_HUE 1           // 1 to print computed h value to serial
#define DISPLAY_COLOR 1         // 1 to print nearest color to serial

// Define a type to hold detectable colors
typedef struct _color_t {
  float hue;
  const char* name;

// Here's the list of colors that the system can name on its own (you may add your own based on hue angle [0.0, 360] )
color_t colors[] = {
  {0.0, "Red"},
  {60.0, "Yellow"},
  {120.0, "Green"},
  {180.0, "Cyan"},
  {240.0, "Blue"},
  {300.0, "Magenta"},


// Includes
#include <math.h>
#include <Wire.h>
#include <FastLED.h>          // Click here to get the library: http://librarymanager/All#FastLED_Daniel_Gracia
#include "SparkFunISL29125.h" // Click here to get the library: http://librarymanager/All#SparkFun_ISl29125

// RGB sensor control
SFE_ISL29125 RGB_sensor;
unsigned int red = 0;
unsigned int green = 0;
unsigned int blue = 0;

// WS2811 / WS2812B control
#define DATA_PIN 18
#define NUM_LEDS 2    // Each color node has two 'leds' - one WS2811 to control the sensor power and one WS2812B for illumintation

void setup() {
  Serial.begin(115200);                                     // Start Serial 

  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);


  while(!RGB_sensor.init()){                                // Initialize the ISL29125 with simple configuration so it starts sampling
    Serial.println("trying to start the sensor!");
  Serial.println("Sensor Initialization Successful\n\r");

void loop() {
  /* Begin Taking a Reading */

  sensorPower(true);                // Turn on the sensor  
  setLED(200, 200, 255);            // Turn on the LED to illuminate the subject area;                   // ^- adjust the balance of white here... blue seems to need some help (higher Vf than other leds..)

  delay(2);                         // some time for sensor to come online
  RGB_sensor.init();                // now perform initialization since the sensor had been asleep

  delay(200); // sensor continuously runs ADC at ~ 10 hz so to be sure wait 0.2 seconds before reading

  delay(300); // delay to combat voltage sag from turning on all the leds...
              // I've experimentally determined that while there is no LED brightness that completely 
              // eliminates noise in detected color there is a minimum total delay between turning on
              // the leds and taking a sample that gets darn close. Its approx 500 ms total (including
              // time dedicated to letting the sensor read)

              // the final product may as well turn on all the leds, wait half a second, and then sample
              // all of the color sensors rapidly. 

  red = RGB_sensor.readRed();     // Sample the sensor
  green = RGB_sensor.readGreen();
  blue = RGB_sensor.readBlue();

  sensorPower(false);             // Turn off the sensor
  setLED(0, 0, 0);                // Turn off the LED;                 // Apply changes to the 'LED' data (includes WS2811 'switch' changes)

                                  // Now let's try to sample the sensor to show that it has really been shut down
  delay(1);                       // Time for the sensor VDD line to fall to 0

  printResults();                 // Show the results on the Serial monitor

  delay(200);                     // delay 200 ms before taking another reading

void sensorPower(bool on){
  LEDS.setBrightness(255);                                  // Ensure full brightness so that WS2811 controls are not scaled

    leds[0] = CRGB(0, 0, 0);                                // Turn on the sensor by writing a 0 value to the R channel
    leds[0] = CRGB(255, 0, 0);                              // Turn off the sensor by writing 255 to the red channel

void setLED(uint8_t R, uint8_t G, uint8_t B){
  leds[1] = CRGB(R, G, B);                                  // The LED is the second item in the 'led' array (the first is the WS2811 sensor switch)


float max3( float Rp, float Gp, float Bp, uint8_t* index ){
  // hacky way to find maximum of three (not tested well or even well-thought-through)...
  float Cmax = 0.0;
   if(Rp >= Gp){
      if(Rp >= Bp){
        Cmax = Rp;
        *index = 0;
    if(Gp >= Bp){
      if(Gp >= Rp){
        Cmax = Gp;
        *index = 1;
    if(Bp >= Gp){
      if(Bp >= Rp){
        Cmax = Bp;
        *index = 2;
    return Cmax;

float min3( float Rp, float Gp, float Bp, uint8_t* index ){
  // hacky way to find minimum of three (not tested well or even well-thought-through)...
  float Cmin = 0.0;
   if(Rp <= Gp){
      if(Rp <= Bp){
        Cmin = Rp;
        *index = 0;
    if(Gp <= Bp){
      if(Gp <= Rp){
        Cmin = Gp;
        *index = 1;
    if(Bp <= Gp){
      if(Bp <= Rp){
        Cmin = Bp;
        *index = 2;
    return Cmin;

void printResults( void ){
    float Rp = (float)red/255;
    float Gp = (float)green/255;
    float Bp = (float)blue/255;

    uint8_t max_ind = 0;
    uint8_t min_ind = 0;
    float Cmax = max3(Rp, Gp, Bp, &max_ind);
    float Cmin = min3(Rp, Gp, Bp, &min_ind);
    float delta = Cmax - Cmin;

    float hue = 0.0;
    if(Cmax == 0.0){
      hue = 0.0;
        case 0: hue = 60 * (fmod(((Gp-Bp)/delta), 6.0)); break;
        case 1: hue = 60 * (((Bp-Rp)/delta) + 2); break;
        case 2: hue = 60 * (((Rp-Gp)/delta) + 4); break;
        default: break;

    // search list of colors for the closest one 
    //  (todo: when overall lux levels are low just say nothing is there)
    const char* identified_color = NULL;
    float prev_diff = 360.0;                              // start with a high (impossibly so) difference
    float diff = 0.0;
    uint8_t num_colors = sizeof(colors)/sizeof(color_t);
    for(uint8_t indi = 0; indi < num_colors; indi++){     // loop through all the named colors
      diff = abs(hue - colors[indi].hue);                     // find the difference between the selected color hue and the calculated hue
      if ( diff >= 180.0 ){                                   // correct for differences over 180 degrees because the spectrum is circular
        diff = 360.0 - diff;
      if( diff < prev_diff ){                                 // if this difference is smaller change the detected color to this name
        prev_diff = diff;
        identified_color = colors[indi].name;

    Serial.print("B: "); 
    Serial.print(" ");

    Serial.print("R: "); 
    Serial.print(" ");

    Serial.print("G: "); 
    Serial.print(" ");

    Serial.print("Hue: ");
    Serial.print(" ");

    Serial.print("Color: ");
    Serial.print(" ");


Using a digital logic analyzer I was able to record some great data. You can see that the rise time of the sensor VDD line matches what is expected from the simulation, as well as the same small ripples when the sensor is turned off. You can also see that the sensor responds to I2C transactions while it is powered. Shortly after sending the 'off' command, the VDD line falls and the sensor no longer responds to I2C transactions. This suggests that turning off unused sensors should be enough to allow using more than one sensor on the same I2C bus.

digital logic analzer trace of sensor VDD line and I2C transactions

Further Considerations

This project is a work in progress. To scale this single test up to an array of 192 nodes will present more challenges such as:

  • Sample speed - the test above does not leave any time for the sensor to make ADC conversions, so the readings come out as 0. In reality each sensor will need to be on for 100 to 200 ms to get a good sample.
  • I2C Bus Capacitance - generally it is challenging to use I2C over long distances because of the open-drain topology.

These challenges will likely preclude the use of this method in the Rainbow Forest project. Still, using the WS2811 in creative ways could like this can be useful in a host of projects that already involve addressable LEDs such as model train layouts, DIY LED shows with special effects, and much more!

If you use the WS2811 in a project of your own let us know!

comments | comment feed

Enginursday: Exploring the Embedded Startup Process

via SparkFun: Commerce Blog

Artemis, my close friend, has urged me to learn much more about the bare-metal details of getting code running and supporting the familiar features that we often take for granted in C++. It is actually a very interesting topic and a beneficial thing to understand when working on embedded systems. Sadly, however, it is a fairly challenging topic to find information about on the internet - and what you can find is daunting, to say the least.

Since we are not fans of reinventing the wheel we've developed some examples you can follow along with using an Artemis board. After doing so you will know:

  1. How the linker organizes compiled code
  2. How startup code initializes variables
  3. How the stack and heap are managed
  4. How global/static object constructors are conveniently called before you get to main()

Check out the examples, explanation and instructions on the embedded-startup exploration repo!

Here are some useful tools to have along the way:

Artemis Boards

SparkFun RedBoard Artemis Nano

SparkFun RedBoard Artemis Nano

SparkFun RedBoard Artemis

SparkFun RedBoard Artemis

SparkFun Artemis Module - Low Power Machine Learning BLE Cortex-M4F

SparkFun Artemis Module - Low Power Machine Learning BLE Cortex-M4F


SparkFun Artemis SnowBoard



J-Link EDU Mini Programmer

J-Link EDU Mini Programmer

J-Link EDU Base Programmer

J-Link EDU Base Programmer

J-Link BASE Compact Programmer

J-Link BASE Compact Programmer



SparkFun Serial Basic Breakout - CH340C and USB-C

SparkFun Serial Basic Breakout - CH340C and USB-C

USB 2.0 Cable A to C - 3 Foot

USB 2.0 Cable A to C - 3 Foot


comments | comment feed

Enginursday: Playing with CRCs in Python

via SparkFun: Commerce Blog

Recently, the engineering team has needed to implement Cyclic Redundancy Checks (CRCs) for several different projects. The algorithm is easy enough to copy from the internet and forget, but my curiosity just couldn't quit there! CRCs have a very fascinating mathematical underpinning that relates to information theory, computer hardware and more. Trying to get a better understanding of CRCs eventually led me to discover the fantastic legendary ASCII text file called 'crc_v3.txt`. Dr. Ross Williams, the author, once hosted the file at, however it can't be found there any more 😔.

Don't fret - everyone knows nothing ever really disappears on the internet, and this is no exception. A work of art and a beacon for curious minds 'crc_v3.txt' will live on forever. You can go pay it a visit here.

Alone 'crc_v3.txt' is a great read, but there are still a few points that might be hard to follow. To satiate my curiosity I created a follow-along Python script to demonstrate the math. You can check it out on GitHub CRC_Exploration or by trying it out live in this post, thanks to the widget below. Just click the green 'run' arrow and peruse the output, then try changing the code yourself!

comments | comment feed

Enginursday: 3D Volumetric Display with Transparent OLED

via SparkFun: Commerce Blog

Not surprisingly, this all started when (perhaps jokingly) a clever colleague questioned, "Why don't you stack 'em up like a sub sandwich?" A merely mundane Monday morning quickly became a maniacal midnight mash-up for yours truly, mad scientist. A little planning and proving the power of this particular display was providential in the proceeding process.

Sub Sandwich GIF of ten stacked OLED displays

Initially it was imperative to test the inconceivably incredible display's transparency. With only one unit working wonderfully well, nine more non-functioning displays were stacked on top, one by one. When at last the image at the bottom faded from view, the battle was won but the total number was ten.

Breadboard view of the Super Speedy SPI Bus

Another astonishing achievement that empowered this futuristic artifact was the super speed of the SPI bus. Said bus, in coordination with ten tiny chip select lines, was capable of continually captivating the consciousness of the (possibly concerned) crowd in a blistering burst of brilliance.

3d image of the emerald moving around

Nevertheless, such an exceedingly embryonic idea would be irrefutably evanescent could an elegant software solution not be embraced. Since we can spy furthest standing upon the shoulders of cyclops, I silently sought a singular font of pseudocode. Upon applying Bresengham's arcane craft to an additional dimension, one artful enigma appeared.

Stacked OLEDS on breadboard, showing our Elegant Emerald

Moral of the Story

So what's the take away from this experience? Several things:

  • There's a very cool 3D graphics library for Arduino out there by M Rule that allows users to render STL files on a display. If you want to look further, you can also see the code as one of the examples for the transparent graphical OLED.
  • SPI is a very nice protocol when you've gotta go fast. The symmetry is also pretty nice, as you can see from the stack of boards that all share MOSI, SCLK, D/C, VCC and GND. The only wire that couldn't be shared was the CS line, which I protected from the other layers with a smidgin of kapton tape.
  • Drawing in 3D seems hard, right? Well it's actually easier (from a number-crunching perspective) than drawing something 3D on a 2D screen. When going to 2D, you also need to project your points and determine if there are any overlapping parts.
  • If you like algorithms you should check out Bresenham's line algorith applied to 3D.

comments | comment feed

Have You Seen HyperDisplay?

via SparkFun: Commerce Blog

What Is HyperDisplay?

alt text

HyperDisplay is perhaps the greatest graphics library I have ever written! And, erm, yeah I've toootally written like a lot of graphics libraries in my day...

Jokes aside, HyperDisplay is a graphics library that makes it easy to support and use new displays, and offers flexibility to users without unnecessary overhead. There are a lot of good embedded system graphics libraries out there, from the feature packed LittlevGL to the ever-popular Adafruit_GFX, but none of these libraries fit the bill of exactly what we wanted: a capable yet uncomplicated library that could easily be extended by SparkFun employees and customers alike.

Why Is HyperDisplay?

So, with that plug out of the way let's get to the personal side of the story, the part you won't hear about in the official documentation.

This summer I started working at SparkX alongside Nate, Nick, Jim and the amazing Ciara Jekel. Nate had visions of releasing a whole lot of cool new display-related technology, from RGB OLEDs to ePaper displays and touchscreen panels. Ciara and I both got started on our own products in a more or less isolated fashion - since reading datasheets is hardly a social activity. As is SparkFun fashion we also developed Arduino libraries to support our newborn products, and boy was that a process. I looked to past SparkFun products for inspiration, and also had a good time learning about some famous graphics algorithms like Bresenham's Line and the Midpoint Circle.

At some point Ciara and I started talking about our work and we realized that not only had we doubled our efforts, but we had also made it harder for users to learn our software because of the slight differences in naming, parameter arguments and even how various shapes are defined. Ciara observed that her ePaper and my RGB OLED were the same up to the point of deciding a color and passing actual signals to the device. It seemed like a no-brainer to pool our efforts and develop HyperDisplay.

How we designed HyperDisplay just wouldn't fit here -- it is worthy of its own whole discussion -- but I want to say thank you to Ciara for her invaluable input during the process. It was very rewarding to work as a team.

How to Learn HyperDisplay

A good place to start learning HyperDisplay is the tutorial that we've just released:

Everything You Should Know About HyperDisplay

There you can learn about how to use the drawing functions, easily make repeating patterns, draw in windows, and even allocate memory for persistent storage.

P.S. What's Next for HyperDisplay?

Try it out for real! HyperDisplay is for you. Though we will support our new display products, the really exciting prospect is that any person can support a new display and the possibilities are endless! I'd really like to see a display that draws in sand - so I'll leave that as a challenge to the reader. Most importantly, go start something!

comments | comment feed