Thursday, September 11, 2014

Make your own dual programmer in AVRDUDE

Modified 9/16/2014

Those of you who have programmed an Arduino through the Arduino or AVR Studio IDE may have noticed the utility that is really doing the work: AVRDUDE (AVR Downloader/UploaDEr).  This is a powerful program that can facilitate programming new sketches on top of a bootloader, load a brand new bootloader or chip image, capture the current firmware programmed on the chip, and set fuse bits (which can render your chip unusable without special tools if you're not careful).


You mean I could have been doing this the whole time?


The LEDgoes USB Communicator supports both programming over serial (bootloader must be present) or via ICSP bitbang (very slow).  The ICSP operation is identical to Adafruit's FTDI Friend product.  The serial programming is identical to the Arduino, except that in my case, I'd like to be able to program two ATmega chips at the same time without switching cables.  What's the best way to do this?

My original train of thought (from Mixing Logic With ISP Circuitry For Programming Firmware) involved using a switch and AND gates to decide which chip would actually get the bits.  Granted, that article was really geared for SPI programming, but the concept is even more applicable to serial programming since our UART lines (TX & RX) are common among both chips on the board.  Trying to program one chip without holding the other in RESET will cause a failure to write the program, as they will both try to send serial output on the same wire & confuse each other.  However, the logic gates used for switching still required either manual intervention or use of a Raspberry Pi which didn't really work out for me at the time.  I thought it was going to come to me needing to get yet another microcontroller just to handle one single bit of output from AVRDUDE to control the RESET lines, which seemed really stupid.  It was getting very annoying to wire up the boards two different ways to program both of the chips, though, so I still drove to find a solution.

After examining a serial port's configuration, and seeing which pins were still available after Arduino's serial programming application had been implemented, I decided it'd be simpler to use AVRDUDE to hold one chip in RESET while the other is programmed.


What if I don't have LEDgoes?


Good news!  You can use an Arduino to do this as well
.  If you haven't familiarized yourself with the ICSP header pins on the Arduino board, you'll get a crash course here.  The "RESET" header pin you can tap into is, obviously, electrically connected to the ATmega RESET pin, but it's also connected to the "RST" pin (#5) on the ICSP header.  AVRDUDE maps this RST signal to the "DTR" (Data Terminal Ready) serial signal coming from the FTDI USB/serial chip.  This is part of the mechanism used under the hood each time you upload a sketch.  However, this new AVRDUDE programmer defined below will also activate the MOSI pin on the ICSP header (#4), which is linked to the RTS signal from the FTDI chip (Request to Send).  Between these two pins (RST <- DTR, and MOSI <- RTS), we can hold one chip in reset while the other one is being programmed.

One small catch: you need to take off the on-board ATmega chip if you don't plan to use it.  For folks with SMD edition Arduinos, you cannot program two external chips without making some adjustments.  The code below assumes you have exclusive use of the RST line (i.e. the "RST" ICSP header used in the diagram below, or the Arduino "RESET" pin).  However, the SMD chip's reset pin is hooked up to this same RST line.  Thus, if you connect an external chip to this RST line while the on-board chip is still in place, the two chips will be programmed at the same time, and that always causes problems.  Usually when this happens, the chips start talking over each other loudly enough to make AVRDUDE fail.  In this case, AVRDUDE passes but the program on the external chip will be all screwed up.

To circumvent this, if you have an SMD-edition Arduino, you'll need to find (or perhaps write) yet another function in AVRDUDE to control a pin *besides* DTR and RTS.  You could pick the TXD pin (which leads to pin 3 / SCK on the ICSP header) or CTS (which goes to ICSP pin 1 / MISO).  Of course, there's no need to take these precautions if you're looking to program the SMD chip and one external chip; just make sure the external chip's reset is only hooked up to RTS (ICSP pin 4 / MOSI).

It's good to keep that in mind anyway, since with those extra functions to utilize TXD & CTS, you could program up to four ATmegas (or 16 if you wanted to get fancy with combinational logic).

Here's what the breadboard setup looks like (for the non-SMD-edition crowd):


Depending on what jumper cables you have around, you can route the pink cable into the RESET pin on the regular Arduino headers instead of the RST pin on the ICSP headers.  It doesn't matter which chip is hooked into RST or MOSI as long as you properly track which one gets programmed when.


Building Your Own AVRDUDE In Linux

To get started with this, I had to download the code and load a bunch of dependencies for it to compile.  After having looked at MinGW for Windows, I thought it'd be a little bit less effort to get it going in Linux.  So here's roughly how it went:


  • Checked out the SVN repository from http://savannah.nognu.org/svn/?group=avrdude
  • Learned about autoconf, a cross-platform build tool (and what AVRDUDE uses to get built)
  • Ran autoconf configure.ac
  • Fought with a "error: possibly undefined macro: AM_INIT_AUTOMAKE" (for which this post suggested the correct solution for fixing this issue):
    • Add AC_CONFIG_MACRO_DIR([m4]) to configure.ac -- it was already present in my case
    • libtoolize --force
    • aclocal
    • autoheader
    • automake --force-missing --add-missing
    • autoconf
  • Installed missing dependencies, such as developer libraries for libusb & libftdi (the AVRDUDE configure script will tell you what you're missing), plus flex (but not its friend bison), and yacc
  • Ran autoconf configure.ac again after these missing dependencies were satisfied
  • sudo ./configure; sudo make; sudo make install
Thus was born my very own AVRDUDE!


Cloning the Arduino Programmer into "BritebloxUSB"


Since my programmer is basically an "arduino" programmer (but I wanted to program two devices at once instead of just one), I decided to base my code heavily around theirs.  After poking around to study how it was implemented and tied in with the whole application, I was able to produce the desired behavior using the following files:

  • arduino.c (saved as briteblox.c)
  • arduino.h (saved as briteblox.h)
  • pgm_type.c
  • Makefile.am

Later on, you will see what I did to these files to get the programmer working as desired.  I also should have modified the following files (but took the lazy man's way out since I was only looking for Linux support at the time):

  • ser_posix.c
  • ser_win32.c

I'll explain why I need to modify these files later.  After each time I'd change any of these files, I would run:

autoconf; sudo make; sudo make install; sudo cp /usr/local/etc/avrdude.conf.stevo /usr/local/etc/avrdude.conf

This rebuilds the AVRDUDE binary and also reinstates the changes I need into the configuration file so it recognizes "britebloxusb" as a programmer.

AVRDUDE allows programmers to send the signals used for programming to different output pins than expected.  For example, the pulse sent to reset the AVR chips goes through the DTR and RTS pins from the FTDI chip.  By splitting up the function of the DTR & RTS pins to behave separately, I can "lightly tap" one chip into RESET (so it will be programmed) and hold the other chip in RESET (so it will "sleep through" all the instructions being sent to program its neighbor).  This was achieved by modifying the _open() function, and adding a new function called briteblox_set_dtr_rts().  (This new function needs to be modified to fit nicely into ser_posix.c and ser_win32.c.)

The programmer accepts an optional argument (through the _parseextparms() function) that allows you to specify which signal gets held down the entire time.  Without specifying "-x reverse", DTR is held low the entire time.  When this parameter is included, though, RTS is held low the entire time.  This way, to program both AVRs without rearranging the cables, all you need to enter on the command line is:

avrdude -p atmega168 -c britebloxusb -P /dev/ttyUSB0 -D -U <what to do>; avrdude -p atmega168 -c britebloxusb -P /dev/ttyUSB0 -D -U <what to do> -x reverse

And if you ever write anything invalid for -x, the britebloxusb programmer will politely remind you what options are supported by -x.  Right now, it's just help and reverse.


Code


While this isn't quite perfect nor polished yet (the output from _close() and _teardown() still needs to be reconciled a bit too), here it is for your enjoyment and edification.  Soon I hope to commit this (cleaned up) into the mainline AVRDUDE source code for enjoyment by all.

britebloxusb.c: 


/*
 * avrdude - A Downloader/Uploader for AVR device programmers
 * Copyright (C) 2009 Lars Immisch
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* $Id: $ */

/*
 * avrdude interface for britebloxusb programmer
 *
 * The britebloxusb programmer is mostly a STK500v1, just the signature bytes
 * are read differently.
 */

#include "ac_cfg.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include "avrdude.h"
#include "libavrdude.h"
#include "stk500_private.h"
#include "stk500.h"
#include "britebloxusb.h"

/* flags */
#define BBX_FLAG_REVERSE          (1<<0)

/* set dtr & rts separately; the usual "serial" function lib makes you do them together */
/* FIXME: This will only work in POSIX-land!!! */
static int briteblox_set_dtr_rts(union filedescriptor *fdp, int dtr_on, int rts_on)
{
  unsigned int ctl;
  int           r;

  r = ioctl(fdp->ifd, TIOCMGET, &ctl);
  if (r < 0) {
    perror("ioctl(\"TIOCMGET\")");
    return -1;
  }

  if (dtr_on) {
    /* Set DTR */
    ctl |= TIOCM_DTR;
  }
  else {
    /* Clear DTR */
    ctl &= ~TIOCM_DTR;
  }

  if (rts_on) {
    /* Set RTS */
    ctl |= TIOCM_RTS;
  }
  else {
    /* Clear RTS */
    ctl &= ~TIOCM_RTS;
  }

  r = ioctl(fdp->ifd, TIOCMSET, &ctl);
  if (r < 0) {
    perror("ioctl(\"TIOCMSET\")");
    return -1;
  }

  return 0;
}

/* read additional params */
static int britebloxusb_parseextparms(struct programmer_t *pgm, LISTID extparms)
{
  LNODEID ln;
  const char *extended_param;
  char reset[10];
  char *preset = reset;   /* for strtok() */
  int spifreq;
  int cpufreq;
  int serial_recv_timeout;

  for (ln = lfirst(extparms); ln; ln = lnext(ln)) {
    extended_param = ldata(ln);
    if (strcmp(extended_param, "reverse") == 0) {
      pgm->flag |= BBX_FLAG_REVERSE;
      avrdude_message(MSG_INFO, "%s: Reversing reset signals for this run...\n", progname);
      continue;
    } else if (strcmp(extended_param, "help") == 0) {
      avrdude_message(MSG_INFO, "%s: britebloxusb: Available Extended Commands:\n"
                      "\thelp\tPrints this help message\n"
                      "\treverse\tHolds down RTS instead of DTR throughout programming\n", progname);
      return -1;
    } else {
      avrdude_message(MSG_INFO, "%s: extended parameter %s is not understood.  Use \"-x help\" for all options.\n", progname, extended_param);
      return -1;
    }
  }
}

/* read signature bytes - britebloxusb version */
static int britebloxusb_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
{
  unsigned char buf[32];

  /* Signature byte reads are always 3 bytes. */

  if (m->size < 3) {
    avrdude_message(MSG_INFO, "%s: memsize too small for sig byte read", progname);
    return -1;
  }

  buf[0] = Cmnd_STK_READ_SIGN;
  buf[1] = Sync_CRC_EOP;

  serial_send(&pgm->fd, buf, 2);

  if (serial_recv(&pgm->fd, buf, 5) < 0)
    return -1;
  if (buf[0] == Resp_STK_NOSYNC) {
    avrdude_message(MSG_INFO, "%s: stk500_cmd(): programmer is out of sync\n",
progname);
return -1;
  } else if (buf[0] != Resp_STK_INSYNC) {
    avrdude_message(MSG_INFO, "\n%s: britebloxusb_read_sig_bytes(): (a) protocol error, "
                    "expect=0x%02x, resp=0x%02x\n",
                    progname, Resp_STK_INSYNC, buf[0]);
return -2;
  }
  if (buf[4] != Resp_STK_OK) {
    avrdude_message(MSG_INFO, "\n%s: britebloxusb_read_sig_bytes(): (a) protocol error, "
                    "expect=0x%02x, resp=0x%02x\n",
                    progname, Resp_STK_OK, buf[4]);
    return -3;
  }

  m->buf[0] = buf[1];
  m->buf[1] = buf[2];
  m->buf[2] = buf[3];

  return 3;
}

static int britebloxusb_open(PROGRAMMER * pgm, char * port)
{
  union pinfo pinfo;
  strcpy(pgm->port, port);
  pinfo.baud = pgm->baudrate? pgm->baudrate: 19200;
  if (serial_open(port, pinfo, &pgm->fd)==-1) {
    return -1;
  }

  /* Set DTR & RTS to reset both chips */
  briteblox_set_dtr_rts(&pgm->fd, 1, 1);
  usleep(250*1000);
  if ((pgm->flag & BBX_FLAG_REVERSE) == 0) {
    /* (Normal) Clear only RTS in order to resume communication with the desired chip */
    briteblox_set_dtr_rts(&pgm->fd, 1, 0);
  } else {
    /* (Reversed) Clear only DTR in order to resume communication with the desired chip */
    briteblox_set_dtr_rts(&pgm->fd, 0, 1);
  }
  usleep(50*1000);

  /*
   * drain any extraneous input
   */
  stk500_drain(pgm, 0);

  if (stk500_getsync(pgm) < 0)
    return -1;

  return 0;
}

static void britebloxusb_close(PROGRAMMER * pgm)
{
  /* Release the other chip from reset */
  briteblox_set_dtr_rts(&pgm->fd, 0, 0);
  serial_close(&pgm->fd);
  pgm->fd.ifd = -1;
}

static void britebloxusb_teardown(PROGRAMMER * pgm)
{
  britebloxusb_close(pgm);
}

const char britebloxusb_desc[] = "britebloxusb dual AVR programmer";

void britebloxusb_initpgm(PROGRAMMER * pgm)
{
  /* This is mostly a STK500; just the signature is read
     differently than on real STK500v1 
     and the DTR signal is set when opening the serial port
     for the Auto-Reset feature */
  stk500_initpgm(pgm);

  strcpy(pgm->type, "britebloxusb");
  pgm->read_sig_bytes = britebloxusb_read_sig_bytes;
  pgm->open = britebloxusb_open;
  pgm->close = britebloxusb_close;

  /* Optional functions */
  pgm->parseextparams = britebloxusb_parseextparms;
  pgm->teardown = britebloxusb_teardown;

}


britebloxusb.h:


/*
 * avrdude - A Downloader/Uploader for AVR device programmers
 * Copyright (C) 2009 Lars Immisch
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* $Id: $ */

#ifndef britebloxusb_h__
#define britebloxusb_h__

extern const char britebloxusb_desc[];
void britebloxusb_initpgm (PROGRAMMER * pgm);

#endif


Additions to Makefile.am:


libavrdude_a_SOURCES = \
...
avrftdi_tpi.c \
avrftdi_tpi.h \
avrpart.c \
bitbang.c \
bitbang.h \
britebloxusb.c \
britebloxusb.h \
buspirate.c \
buspirate.h \
butterfly.c \
butterfly.h \
config.c \

confwin.c \
...


Additions to pgmtype.c:


#include "avrftdi.h"
#include "britebloxusb.h"
#include "butterfly.h"
...
const PROGRAMMER_TYPE programmers_types[] = {
        {"arduino", arduino_initpgm, arduino_desc},
        {"avr910", avr910_initpgm, avr910_desc},
        {"avrftdi", avrftdi_initpgm, avrftdi_desc},
        {"britebloxusb", britebloxusb_initpgm, britebloxusb_desc},
        {"buspirate", buspirate_initpgm, buspirate_desc},
...


Additions to avrdude.conf.stevo:


programmer
  id    = "arduino";
  desc  = "Arduino";
  type  = "arduino";
  connection_type = serial;
;

programmer
  id    = "britebloxusb";
  desc  = "BriteBlox USB Dual Serial Programmer";
  type  = "britebloxusb";
  connection_type = serial;
;
# this will interface with the chips on these programmers:
#

# http://real.kiev.ua/old/avreal/en/adapters

Thursday, August 7, 2014

Applying old properties to new files in Windows

A while back, I used some kind of "cp -r" command in Linux to copy files from an old IDE hard drive to a newer SATA drive.  The IDE drive is 10 years old, and thus many of the files on there are of that vintage.  Unfortunately, I was not aware that cp does not properly transfer file attributes between Windows files (as these are NTFS partitions).  I could have simply used Windows to copy the files, but I didn't want this to happen:



Hence why the choice to use Linux.  Nevertheless, being somewhat of an archivist at heart, I was interested in retaining some of these original file properties, namely Created, Modified, and Accessed times.  I didn't want all my files looking like they came from 12/2/2013 when some of them are as old as 2002.  So, I went back into Windows to solve my problem.

The idea for this was derived from one of many related StackOverflow posts on the topic, plus some additional information was acquired in order to walk the directory trees recursively.  I have pasted the code below.  One issue I haven't been able to work through is that directory attributes are not copied properly, claiming you do not have enough permissions to modify the file.  I even tried running VS 2013 as Administrator, but it didn't help.  So, I solved this problem by taking any errors you encounter along the way and writing them to a CSV file which you can sort & search later on.

As for the IDE hard drive, it's been zeroed out and will be donated along with other old drives targeted in my downsizing plan.

I'm sure it could be much better-optimized for speed & multitasking, but nevertheless, the code below is for Visual C# 2013:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public long filesCount;
        public StreamWriter writer;

        public Form1()
        {
            InitializeComponent();
            filesCount = 0;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Recursively go through all the files on the source drive/folder
            
            //File.SetCreationTime(tgtFile, File.GetCreationTime(srcFile));
            //File.SetLastAccessTime(tgtFile, File.GetLastAccessTime(srcFile));
            //File.SetLastWriteTime(tgtFile, File.GetLastWriteTime(srcFile));
            string driveLetter = "E";
            writer = new StreamWriter("c:\\Notes-On-Migration-Of-" + driveLetter + ".csv");
            DirectoryInfo dirSrc = new DirectoryInfo(driveLetter + ":\\");
            WalkDirectoryTree(dirSrc, "D:\\Win7\\");
            writer.Close();
            lblStatus.Text = "Number of files: " + filesCount;
        }

        private void WalkDirectoryTree(System.IO.DirectoryInfo root, string dirTgt)
        {
            System.IO.FileInfo[] files = null;
            System.IO.DirectoryInfo[] subDirs = null;

            DateTime ss = File.GetCreationTime(root.FullName);
            DateTime ee = File.GetLastAccessTime(root.FullName);
            DateTime xx = File.GetLastWriteTime(root.FullName);
            // First, process all the files directly under this folder 
            try
            {
                files = root.GetFiles("*");
                filesCount += files.LongLength;
                lblStatus.Text = "On " + root.FullName + "; " + filesCount + " files done";
                Application.DoEvents();
            }
            // This is thrown if even one of the files requires permissions greater 
            // than the application provides. 
            catch (Exception e)
            {
                // This code just writes out the message and continues to recurse. 
                // You may decide to do something different here. For example, you 
                // can try to elevate your privileges and access the file again.
                //writer.WriteLine(e.Message);
                textBox1.Text = textBox1.Text += e.Message + "\r\n";
            }

            if (files != null)
            {
                /*
                foreach (System.IO.FileInfo fi in files)
                {
                    // In this example, we only access the existing FileInfo object. If we 
                    // want to open, delete or modify the file, then 
                    // a try-catch block is required here to handle the case 
                    // where the file has been deleted since the call to TraverseTree().
                    try
                    {
                        Console.WriteLine(fi.FullName);
                        File.SetCreationTime(dirTgt + fi.Name, File.GetCreationTime(fi.FullName));
                        File.SetLastAccessTime(dirTgt + fi.Name, File.GetLastAccessTime(fi.FullName));
                        File.SetLastWriteTime(dirTgt + fi.Name, File.GetLastWriteTime(fi.FullName));
                    }
                    catch (Exception e)
                    {
                        writer.WriteLine(e.Message);
                        textBox1.Text = textBox1.Text += e.Message + "\r\n";
                    }
                }
                 * */

                // Now find all the subdirectories under this directory.
                try
                {
                    subDirs = root.GetDirectories();
                }
                catch (Exception e)
                {
                    writer.WriteLine("Corrupt directory," + root.FullName);
                    textBox1.Text = textBox1.Text += e.Message + "\r\n";
                }

                foreach (System.IO.DirectoryInfo dirInfo in subDirs)
                {
                    // Write the directory attributes too.
                    try
                    {
                        //MessageBox.Show("Target: " + dirTgt + dirInfo.Name + "\r\nSource: " + dirInfo.FullName);
                        File.SetCreationTime(dirTgt + dirInfo.Name, File.GetCreationTime(dirInfo.FullName));
                        File.SetLastAccessTime(dirTgt + dirInfo.Name, File.GetLastAccessTime(dirInfo.FullName));
                        File.SetLastWriteTime(dirTgt + dirInfo.Name, File.GetLastWriteTime(dirInfo.FullName));
                    }
                    catch (System.UnauthorizedAccessException e)
                    {
                        // Write to the CSV file the "Access Denied" tag, what we were trying to modify, and its original properties from the source hard drive
                        writer.WriteLine("Access denied," + dirTgt + dirInfo.Name + "," + File.GetCreationTime(dirInfo.FullName) + "," + File.GetLastAccessTime(dirInfo.FullName) + "," + File.GetLastWriteTime(dirInfo.FullName));
                        textBox1.Text = textBox1.Text += e.Message + "\r\n";
                    }
                    catch (System.IO.FileNotFoundException e)
                    {
                        // Attempt to copy the file from the source to the destination
                        try
                        {
                            File.Copy(dirInfo.FullName, dirTgt + dirInfo.Name, false);
                            string ifei = "kjbhwe";
                        }
                        catch (Exception e2)
                        {
                            // This happens when we don't have enough permission to copy the file for some reason
                            writer.WriteLine("File copy denied," + e.Message);
                            textBox1.Text = textBox1.Text += e.Message + "\r\n";
                        }
                    }
                    catch (Exception e)
                    {
                        // We don't really know what happened, so just write it down
                        writer.WriteLine(e.Message);
                        textBox1.Text = textBox1.Text += e.Message + "\r\n";
                    }
                    // Recursive call for each subdirectory.
                    WalkDirectoryTree(dirInfo, dirTgt + dirInfo.Name + "\\");
                }
            }
        }
    }
}

Thursday, July 10, 2014

Waking the Sore, Groggy Dog

Well, it's reached the point where I finally had enough of computers and everything I was doing on them for just a bit, so I closed the lid of my laptop on Tuesday right at 5 PM and sat outside in the lawn chair, doing absolutely nothing for about 30 minutes until my dog started panting up a storm.  I let her back inside and she gulped down tons of water quickly.  Still not knowing what to do with myself while trying to stay away from the computer, I just about dozed off on the couch when inspiration struck me: I needed to do something physical to stay awake.

Now I'm a huge fan of sitting, but sitting still in one position at a desk for a long time leads to unhappy muscles.  Plus, I find tedious or uninteresting work to be soporific, especially if I just had a heavy lunch or didn't get much sleep the night before.  However, taking a nap usually leads to undesirable outcomes: I wake up about 2 or 3 hours later, still feeling groggy, and often incapable of accomplishing my desired tasks for the night.  But what am I supposed to do if I don't want to be on the computer but still want to sit?

How about take a bicycle ride?

Perfect!

In this hot weather, my dog isn't capable of going more than about a mile before she gets utterly & completely pooped, so I just went solo.  (The reason she gets so tired is because she hates being behind me; she's a dominant little creature and likes to lead the way.  However, I can always ride as fast as she can run, thus she burns herself out very quickly at the beginning and never goes quite so fast until the next walk.)  So on my own, I was able to log over 5 miles in about 35 minutes -- not too bad, considering my bike (and I) aren't in the best of shape, it was about 100 degrees outside (since I was riding at about 6 PM), and I had to cross at a busy street a couple times and wait on a long light.

Anyway, I guess I'll shut up now.  I haven't been feeling incredibly prolific with regards to writing, and none of the draft articles in my queue to finish really struck my fancy.  Stay tuned for an article called "Speech Pipe: My First (And Probably Last) Windows Phone 8 App"... but, alas, I can't really remember too much about the pain points I encountered that made that experience so fraught with tedium.  Let's see what has happened since then:
  • My last blog post was one of those pesky "draft" posts that I write when I'm feeling a surplus of creativity so that I can save it for days when I don't have enough time or don't feel like writing anything on Thursday night.  I submitted it after arriving at the hotel in San Antonio for the SoHacks hackathon, and after our flight had been delayed for several hours (thus we probably could have just driven to San Antonio by the time our plane landed), doing anything besides laying in bed and watching TV didn't seem very appealing.
  • It was good that I'd written that canned article ahead of time, but I didn't have anything technical to write about because I'd been working on Speech Pipe.  I was trying to get the app done in time for the DVLUP Day Challenge in order to be eligible for a Nokia Lumia 1020, but two things went against me: I had a lot of work to do for LEDgoes as well, plus I didn't just want to crank out yet another turd-ball type of application for Windows Phone, which many of these challenges seem to inspire since people always try to do the bare minimum necessary in order to win points and get free stuff.  Nevertheless, I've since received a different model of Windows Phone 8 device from the local Developer Evangelist, and Speech Pipe is currently making money in the app store.  So there. >-D
  • You may have seen how I posted the game of Tetris those kids developed with LEDgoes during SoHacks.  If not, what are you waiting for -- check that link!  And you can read more about SoHacks from DoesItPew's perspective and from Randy Arnold's perspective.  Apparently, one of the kids wrote this about me as a mentor:

    Stephen is f#@king smart!
    How eloquent. :-P
  • I completely redesigned the LEDgoes Battery POWAH board, in cahoots with some overseas engineers at TI who have helped me fine-tune the schematic and will shortly be helping me fine-tune the PCB layout.  I also did like four other big things on that project, which you can see on our latest Kickstarter update to date.
  • The freaking Udoo board that I hooked up to the Yamaha for my IoT project stopped working.  I dunno what the deal is, but the screen is all garbled and it's not responding to MQTT messages anymore.  I think it needs to be rebooted, but really I think there's a memory leak that needs to be fixed with the Python MQTT client that's running on there.  Or in an ideal world, I'd just make it into an app you can run on XBMC and make the Udoo run XBMC instead of Ubuntu or whatever distro the Udoo has on it now.
  • I have helped close to a half dozen people track down long-lost episodes of The Price is Right over the past month or so by referring them to a contact I made at FremantleMedia who works in their tape vault, or sending them links to their shows on YouTube, or what have you.  It turns out that my "Price Is Right Episode Guide" (if you can call it that) has been very popular on Google lately, usually ranking among the top 10 whenever you search for "the price is right <specific-production-number>".  It's really nice to be able to help so many people track down their shows, and what's even more fun is if they manage to find their own long-lost copies and share bits of them with me!
  • I started taking a class on Lean Six Sigma, Green Belt.  @DoesItPew has already taken her green belt, so of course she sounds like a know-it-all in the class. :-P  Not to mention, she's a vocal person anyway, but she thought there was a "continuing education" component to keeping your Six Sigma green belt certification alive, and if she's right, this'll help her keep/retain it.
  • We have had jam sessions, beer sessions, attended classes and talks held by other sucessful startups, had friends poke holes in their success stories and claim to be able to do in 3 days what took them 3 years, but we're taking a break from that: 3 out of the 5 weekdays this week will have involved some sort of evening drinking or huge dinner, and only 1 was class (the Six Sigma).  It's been nice to cut loose.
  • My dad celebrated 30 years of working for Mercedes-Benz last week.  It's amazing anyone can stay in the car business that long these days, much less work for the same company.  At this point, even after he retires, he'll be able to keep getting the "friends & family" discount on Mercedes vehicles for the rest of his life.
So... until next time... keep your pants on at work!

Thursday, June 12, 2014

The original Super Mario Bros Theme... for Bass Drum!

Back in high school, marching band took up even more time than I spent on homework in any given week. Being a member of the drumline added yet another level of demands and responsibility -- but that's all part of what it takes to play on a line that, if memory serves me correctly, would have beaten the University of North Texas' drumline at PASIC's drumline competition in 2002 had we not been penalized for crossing the physical boundary lines of the competition at some point.  (Nevertheless, we still won among high schools.)  Even though it has been almost 10 years since I graduated high school, their drumline is still kicking butt and constantly taking top honors at every contest they enter.

In middle school, I discovered I had the ability to play along with songs by ear as long as I heard them emough times and knew how they went.  In 9th grade, the realization that I had perfect (or really darn good relative) pitch always fascinated the other band members; I was often tested with random notes or chords, and never failed to impress.  My drum instructor would even ask me for the pitches of the drums -- yes, drums have pitches too, especially timpani & bass drums!  (Now I will admit it's much harder to tell a snare drum's pitch, so usually I'd jokingly say it's a "Bang Flat.")

I also became married to the bass drum in marching band, and became well-known as the one who carried arguably the heaviest instrument on the field -- the 30" bass.  Only the tuba section or tenor drums came close in weight, and this group of players carried a special level of respect for each other.  As I played the 30" bass for my second, and then my third, year, I was used as an inspiration tool on the field (i.e. called out as an example by the directors & techs) to those who carried a lighter instrument yet whose marching technique was not as good.  One of the drum majors admitted to watching my feet to get the tempo on one of the songs we played --- Wow!!!  I thought they always watched the center snare drum player (who is usually the highest position or best player on the line) to get the time, so I took that as a very high honor.

Ok, enough gloating.  During that time, I also dabbled with music composition on the side (more on that in a future post perhaps ;), and found inspiration one football-game night when it sounded like the opposing team was playing a few bars from the Super Mario Bros. theme.  I thought to myself, "Why aren't we doing this?"  Over the summer, after the long, sweaty rehearsal hours, I brought my microphone to the drumline equipment room and took samples of most of the drums to make a drumline "SoundFont."  This soundfont was the basis for my experimentation in MIDI and composition of the Super Mario Bros. Theme for bass drum, which you will find below.

I then formally notated it using whatever version of Sibelius was around 10 years ago.  You're probably better off simply transcribing it for yourself from the image here, because me trying to find the actual Sibelius file along with installing some archaic software on hardware that has long since moved on, would likely be an exercise fraught with disappointments.

So, at last, here it is!



So, one note about reading this:

The score is written for 5 bass drums, with the biggest (lowest-pitch) one notated below the staff ( partially highlighted -- makes it easier to spot your notes), and the other drums are on each "space" on the staff in decreasing-size (increasing-pitch) order.  A note on the middle line (bass clef "D") means that all basses will play.  Thus, it's not the real notes of the Mario Bros. theme. 

Unfortunately, one of the bass drummers left the drumline right after I rolled this out, so we never got to play it.  You just can't do Mario very well with only four bass drums, so maybe it got passed down over the years and played by others.  I really don't know what happened with it since.  However, this copy still sits in a page protector in my marching music binder, which has pretty much remained untouched since I graduated.

For the record, the (C) 2004 is just for show.  It makes things look a little more official (which is cool to do when you're in high school), but it's more for posterity's sake than actually claiming any rights on anything.  You should know I'm way bigger into open-source and open sharing than copyrights anymore.