Sunday, September 16, 2018

The Incredible, Recordable, Blinkable, Micro:Bit

The Phoenix 2.5 is a clever device used by soldiers for marking locations during night ops. The light snaps on to a 9 volt battery and generates a sequence of flashes. The Phoenix makes use of IR Leds, so only those with night vision goggles will see the strobe. What makes the Phoenix 2.5 especially interesting is that it's field programmable. Using a coin, a soldier can setup a new flash pattern.

For just a few bucks you can let fellow soldiers know that it's you, their buddy, and not some hostile force sneaking around in the dark.

While this model of strobe isn't very expensive, I don't tend to run a lot of night ops. Heck, I don't run a lot of days ops. So while I think they're nifty, I can't justify buying one. Still, the idea of field programmable strobe has stuck with me.

While I may have to pass on the military grade version, I realized I could program something similar with my micro:bit. Like the Phoenix, my version has two modes: playback and recording. While recording, pressing A, B or A and B, lights up different combinations of LEDs. Letting go of the buttons turns the LED grid off. After setting up the sequence of flashes, you switch back to playback mode where your pattern repeats.

Switching modes is accomplished by twisting (technically rolling) the device to either the left or right. To start recording, twist the device to the left and hit A. To start playback, twist the device to the right and hit B.

While this light beacon implementation isn't going to be deployed to a war zone, it does show how to combine a number of micro:bit features in a somewhat novel way. As always, what the micro:bit lacks in power it makes up for in fun.

Here's the code that powers the beacon:

let Modes = { Recording: 0, Playing: 1 };
let Symbols = { None: -1 };

let current = {
  symbol: Symbols.None,
  mode: Modes.Playing,
  started: input.runningTime(),
  step: 0,
  sequence: [
    { t: 0, s: IconNames.No },
    { t: 250, s: Symbols.None },
    { t: 500, s: IconNames.Target },
    { t: 750, s: Symbols.None },
    { t: 1000, s: IconNames.Square },
    { t: 1250, s: Symbols.None }
  ]
};

basic.forever(() => {
  if(current.mode == Modes.Playing && input.buttonIsPressed(Button.A)  && isTwistedLeft()) {
    startRecording();
  } else if(current.mode == Modes.Recording && input.buttonIsPressed(Button.B) && isTwistedRight()) {
    startPlaying();
  } else {
    if(current.mode == Modes.Recording) {
      if(input.buttonIsPressed(Button.A) && input.buttonIsPressed(Button.B)) {
        record(IconNames.Target);
      } else if(input.buttonIsPressed(Button.A)) {
        record(IconNames.No);
      } else if(input.buttonIsPressed(Button.B)) {
        record(IconNames.Square);
      } else {
        record(Symbols.None);
      }
    } else {
      let offset = input.runningTime() - current.started;
      if(offset > current.sequence[current.step].t) {
        render(current.sequence[current.step].s);
        if(current.step+1 < current.sequence.length) {
          current.step++;
        } else {
          startPlaying();
        }
      }
    }
  }
});

function render(symbol: number): void {
  if(current.symbol !== symbol) {
    current.symbol = symbol;
    if(symbol === Symbols.None) {
      basic.clearScreen();
    } else {
      basic.showIcon(symbol);
    }
  }
}

function record(symbol: number):void {
  if(current.symbol !== symbol) {
    current.sequence.push({ t: input.runningTime() - current.started,
                            s: symbol });
    render(symbol);
  }
}

function isTwistedLeft():boolean {
  return input.rotation(Rotation.Roll) < 45 ;
}

function isTwistedRight():boolean {
  return input.rotation(Rotation.Roll) > 45 ;
}

function startRecording():void {
  current.mode     = Modes.Recording;
  current.sequence = [];
  current.started  = input.runningTime();
}

function startPlaying():void {
  current.mode    = Modes.Playing;
  current.step    = 0;
  current.started = input.runningTime();
}

Grab a local copy of this project by running these commands:

pxt target microbit
pxt extract https://makecode.microbit.org/_F4MDcL8mHhh6

No comments:

Post a Comment