WeAct F411 + MicroPython + NeoPixels

Further to the Canaduino STM32 boards with MicroPython writeup, I thought I’d start showing how you’d interface common electronics to the WeAct F411 boards. First off, NeoPixels!

Rather than use the Adafruit trade name, these are more properly called WS2812 LEDs. Each one contains a tiny microcontroller and it only takes three connections to drive a long chain of addressable colour LEDs. The downside is that the protocol to drive these is a bit of a bear, and really needs an accurate, fast clock signal to be reliable.

The STM32F411 chip does have just such a clock, and the generic micropython-ws2812 library slightly misuses the SPI bus to handle the signalling. The wiring’s simple:

  • F411 GND to WS2812 GND;
  • F411 3V3 to WS2812 5V;
  • F411 PA7 (SPI1_MOSI) PB15 (SPI2_MOSI) to WS2812 DIn

Next, copy ws2812.py into the WeAct F411’s flash. Now create a script to drive the LEDs. Here’s one to drive 8 LEDs, modified from the library’s advanced example:

# -*- coding: utf-8 -*-

import time
import math

from ws2812 import WS2812

ring = WS2812(spi_bus=2, led_count=8, intensity=0.1)

def data_generator(led_count):
    data = [(0, 0, 0) for i in range(led_count)]
    step = 0
    while True:
        red = int((1 + math.sin(step * 0.1324)) * 127)
        green = int((1 + math.sin(step * 0.1654)) * 127)
        blue = int((1 + math.sin(step * 0.1)) * 127)
        data[step % led_count] = (red, green, blue)
        yield data
        step += 1

for data in data_generator(ring.led_count):
    ring.show(data)
    time.sleep_ms(100)

Previously I said you’d see your WS2812s flicker and shimmer from the SPI bus noise. I thought it was cool, but I suspect it was also why the external flash on my F411 board just died. By pumping data into PA7, I was also hammering the flash chip’s DI line …

Blinking on and off with the 74LS42

I want to make an edge-lit numeric display. These were a common technology before numeric LEDs were available. They use 10 illuminated slides to display individual numbers. Here’s my first try at the display:

Homebrew edge lit numeralsThe 74LS42 logic chip (4-Line BCD to 10-Line Decimal Decoder) seems a likely candidate to drive such a display. You feed it a 4-bit binary-coded decimal input, and the chip activates one of ten outputs. It’s a low-voltage version of the old 7441 chip used for driving Nixie tubes. Here’s what I got working as a demo of the 7442, driven by an Arduino:

(Video link for the iframe-averse: https://www.youtube.com/watch?v=cETGB2M8iUw)

From the video you can see that:

  1. The 7442 only passes inputs from 0 (0000b) to 9 (1001b). All other inputs result in no output.
  2. The outputs are really more like 1–10 than 0–9, as a zero input activates the first output.

Making a clean breadboard layout for this circuit was a little more work than I’d anticipated. It just fits on a half-sized breadboard:

Simple 7442 demo circuit, showing BCD inputs (green) and decimal outputs (orange)
Simple 7442 demo circuit, showing BCD inputs (green) and decimal outputs (orange)

Because the 7442 will only activate one output at a time, it’s okay to use a single current-limiting resistor for all ten output LEDs. The chip also uses active low outputs: the outputs go from high to low when activated. The negative side of each LED goes to an output pin, and the chip sinks current when an output is selected, lighting the LED.

The components get in the way of seeing the wiring, so here’s another picture from Fritzing with just the wires and the breadboard:

7442-no_componentsApart from the 74LS42 chip itself, the components I used were 10× 3mm orange LEDs on the outputs and 4× 3mm green LEDs on the inputs. I don’t have a spec for them (they were from a bargain selection box from PCBoard.ca) so my use of ridiculously precise 332 Ω 1% resistors to limit current is a little unnecessary. (I have a bunch of these precision resistors from a project that didn’t go ahead, so using them is cheaper for me than digging about for a 5% one.)

Finally, here’s the Arduino sketch I wrote to drive the chip for the demo video. All it does is cycle through digital outputs 4–7, incrementing a bit every half second.

/*
   SeventyfourFortytwo - Arduino demo of a
   74LS42 - 4-line BCD to 10-line decimal decoder
   Steps through 0-15, two steps per second
   Shows the 74LS42's blocking of values 10-15 as invalid

   Wiring:
   Arduino  74LS42
   ======== =======
    4      - 15  Input A (bit 0)
    5      - 14  Input B (bit 1)
    6      - 13  Input C (bit 2)
    7      - 12  Input D (bit 3)

   scruss - 2016-09-23
*/

int n = 0;

void setup() {
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);
}

void loop() {
  digitalWrite(4, n & 1);
  digitalWrite(5, n & 2);
  digitalWrite(6, n & 4);
  digitalWrite(7, n & 8);
  n++;
  if (n > 15) n = 0;
  delay(500);
}

If you felt really fancy, you could drive the LED inputs through PWM, and come up with just the right of flicker to make this look like a Nixie tube. You should also be able to chain the inputs through some shift registers, too.

Work-in-progress: Sayso Globord audio decoding

You may still be able to get surplus Sayso Globord programmable LED signs in surplus stores. It’s a 7×24 LED scrolling sign that you can program with a lightpen or with audio input.

sayso-001

The unit comes with no software, but has a link to https://www.dropbox.com/sh/q1q9yhahwtblb23/AACpMeXQjYyD8ZWC-65vNgcxa printed on the box. It’s an archive of the programming software, manual, and canned audio files for a whole bunch of standard messages. Here’s an archive if the dropbox link goes away: SaySo.zip

The audio files used for programming the display are clearly FSK-encoded, but I haven’t quite worked out the relationship between the tones and the display bits. Here’s what I’ve worked out so far:

  • Files are made up of 12 audio blocks, each about 0.9 seconds long. Each block appears to correspond to one 7×24 display screen.
  • Mark (1 bit): Three cycles, 96 samples at 44100 Hz: 1378.125 Hz
  • Space (0 bit): Four cycles, 256 samples at 44100 Hz: 689.0625 Hz

The editor runs nicely under DOSBox, so you can experiment and save samples as WAV files. Here’s a sample display with its corresponding audio linked underneath:

sayed1_0_003

I’m not sure how much extra work I have time or inclination to put in on getting this working, but I hope that my preliminary work will be useful to someone (maybe this person).

python + Arduino + Tk: Toggling an LED

Whoa! This is so old I don’t even know where to start!

  • It’s using Python 2, so if it works at all it probably won’t for much longer, and Tkinter is something completely different under Python 3
    (grrreat planning there, Python guys …)
  • pyfirmata is likely ancient history too.

Phil sent me a note last week asking how to turn an LED on or off using Python talking through Firmata to an Arduino. This was harder than it looked.

It turns out the hard part is getting the value from the Tkinter Checkbutton itself. It seems that some widgets don’t return values directly, so you must read the widget’s value with a get() method. This appears to work:

#!/usr/bin/python
# turn an LED on/off with a Tk Checkbutton - scruss 2012/11/13
# Connection:
# - small LED connected from D3, through a resistor, to GND

import pyfirmata
from Tkinter import *

# Create a new board, specifying serial port
# board = pyfirmata.Arduino('/dev/ttyACM0') # Raspberry Pi
board = pyfirmata.Arduino('/dev/tty.usbmodem411') # Mac

root = Tk()
var = BooleanVar()

# set up pins
pin3 = board.get_pin('d:3:o') # D3 On/Off Output (LED)

def set_led():  # set LED on/off
    ledval = var.get()
    print "Toggled", ledval
    pin3.write(ledval)

# now set up GUI
b = Checkbutton(root, text = "LED", command = set_led,
                variable = var)
b.pack(anchor = CENTER)

root.mainloop()

This is explained quite well here: Tkinter Checkbutton doesn’t change my variable – Stack Overflow. I also learnt a couple of things about my previous programs:

  • You don’t really need to set up an Iterator unless you’re reading analogue inputs
  • My “clever” cleanup-on-exit code actually made the script hang on Mac OS.

much improved HSV colour cycling LED on Arduino

There were some flaws in the post HSV colour cycling LED on Arduino. This does much more what I wanted:

/*
HSV fade/bounce for Arduino - Stewart C. Russell - scruss.com - 2010/09/19

Wiring:
LED is RGB common cathode (SparkFun sku: COM-09264 or equivalent)
    * Digital pin  9 → 165Ω resistor → LED Red pin
    * Digital pin 10 → 100Ω resistor → LED Green pin
    * Digital pin 11 → 100Ω resistor → LED Blue pin
    * GND → LED common cathode.
*/

#define RED                9 // pin for red LED; green on RED+1 pin, blue on RED+2 pin
#define DELAY              2

long rgb[3];
long rgbval, k;
float hsv[3] = {
  0.0, 0.5, 0.5
};
float hsv_min[3] = {
  0.0, 0.0, 0.4 // keep V term greater than 0 for smoothness
};
float hsv_max[3] = {
  6.0, 1.0, 1.0
};
float hsv_delta[3] = {
  0.0005, 0.00013, 0.00011
};

/*
chosen LED SparkFun sku: COM-09264
 has Max Luminosity (RGB): (2800, 6500, 1200)mcd
 so we normalize them all to 1200 mcd -
 R  1200/2800  =  0.428571428571429   =   109/256
 G  1200/6500  =  0.184615384615385   =    47/256
 B  1200/1200  =  1.0                 =   256/256
 */
long bright[3] = {
  109, 47, 256
};

void setup () {
  randomSeed(analogRead(4));
  for (k=0; k<3; k++) {
    pinMode(RED + k, OUTPUT);
    rgb[k]=0; // start with the LED off
    analogWrite(RED + k, rgb[k] * bright[k]/256);
    if (k>1 && random(100) > 50) {
      // randomly twiddle direction of saturation and value increment on startup
      hsv_delta[k] *= -1.0;
    }
  }
}

void loop() {
  for (k=0; k<3; k++) { // for all three HSV values
    hsv[k] += hsv_delta[k];
    if (k<1) { // hue sweeps simply upwards
      if (hsv[k] > hsv_max[k]) {
        hsv[k]=hsv_min[k];
      }    
    }
    else { // saturation or value bounce around
      if (hsv[k] > hsv_max[k] || hsv[k] < hsv_min[k]) {
        hsv_delta[k] *= -1.0;
        hsv[k] += hsv_delta[k];
      }
    }
    hsv[k] = constrain(hsv[k], hsv_min[k], hsv_max[k]); // keep values in range
  }

  rgbval=HSV_to_RGB(hsv[0], hsv[1], hsv[2]);
  rgb[0] = (rgbval & 0x00FF0000) >> 16; // there must be better ways
  rgb[1] = (rgbval & 0x0000FF00) >> 8;
  rgb[2] = rgbval & 0x000000FF;

  for (k=0; k<3; k++) { // for all three RGB values
    analogWrite(RED + k, rgb[k] * bright[k]/256);
  }
  delay(DELAY);
}

long HSV_to_RGB( float h, float s, float v ) {
  /*
     modified from Alvy Ray Smith's site:
   http://www.alvyray.com/Papers/hsv2rgb.htm
   H is given on [0, 6]. S and V are given on [0, 1].
   RGB is returned as a 24-bit long #rrggbb
   */
  int i;
  float m, n, f;

  // not very elegant way of dealing with out of range: return black
  if ((s<0.0) || (s>1.0) || (v<0.0) || (v>1.0)) {
    return 0L;
  }

  if ((h < 0.0) || (h > 6.0)) {
    return long( v * 255 ) + long( v * 255 ) * 256 + long( v * 255 ) * 65536;
  }
  i = floor(h);
  f = h - i;
  if ( !(i&1) ) {
    f = 1 - f; // if i is even
  }
  m = v * (1 - s);
  n = v * (1 - s * f);
  switch (i) {
  case 6:
  case 0: // RETURN_RGB(v, n, m)
    return long(v * 255 ) * 65536 + long( n * 255 ) * 256 + long( m * 255);
  case 1: // RETURN_RGB(n, v, m) 
    return long(n * 255 ) * 65536 + long( v * 255 ) * 256 + long( m * 255);
  case 2:  // RETURN_RGB(m, v, n)
    return long(m * 255 ) * 65536 + long( v * 255 ) * 256 + long( n * 255);
  case 3:  // RETURN_RGB(m, n, v)
    return long(m * 255 ) * 65536 + long( n * 255 ) * 256 + long( v * 255);
  case 4:  // RETURN_RGB(n, m, v)
    return long(n * 255 ) * 65536 + long( m * 255 ) * 256 + long( v * 255);
  case 5:  // RETURN_RGB(v, m, n)
    return long(v * 255 ) * 65536 + long( m * 255 ) * 256 + long( n * 255);
  }
} 

HSV colour cycling LED on Arduino

Pretty much everyone tries the RGB colour cycler when they get their first Arduino. This variant cycles through the HSV colour wheel, though at fixed saturations and values.

Code:

// HSV fade/bounce for Arduino - scruss.com - 2010/09/12
// Note that there's some legacy code left in here which seems to do nothing
// but should do no harm ...

// don't futz with these, illicit sums later
#define RED       9 // pin for red LED
#define GREEN    10 // pin for green - never explicitly referenced
#define BLUE     11 // pin for blue - never explicitly referenced
#define SIZE    255
#define DELAY    10
#define HUE_MAX  6.0
#define HUE_DELTA 0.01

long deltas[3] = {
  5, 6, 7 };
long rgb[3];
long rgbval;
// for reasons unknown, if value !=0, the LED doesn't light. Hmm ...
// and saturation seems to be inverted
float hue=0.0, saturation=1.0, value=1.0;

/*
chosen LED SparkFun sku: COM-09264
 has Max Luminosity (RGB): (2800, 6500, 1200)mcd
 so we normalize them all to 1200 mcd -
 R  1200/2800  =  0.428571428571429   =   109/256
 G  1200/6500  =  0.184615384615385   =    47/256
 B  1200/1200  =  1.0                 =   256/256
 */
long bright[3] = {
  109, 47, 256};

long k, temp_value;

void setup () {
  randomSeed(analogRead(4));
  for (k=0; k<3; k++) {
    pinMode(RED + k, OUTPUT);
    rgb[k]=0;
    analogWrite(RED + k, rgb[k] * bright[k]/256);
    if (random(100) > 50) {
      deltas[k] = -1 * deltas[k]; // randomize direction
    }
  }
}

void loop() {
  hue += HUE_DELTA;
  if (hue > HUE_MAX) {
    hue=0.0;
  }
  rgbval=HSV_to_RGB(hue, saturation, value);
  rgb[0] = (rgbval & 0x00FF0000) >> 16; // there must be better ways
  rgb[1] = (rgbval & 0x0000FF00) >> 8;
  rgb[2] = rgbval & 0x000000FF;
  for (k=0; k<3; k++) { // for all three colours
    analogWrite(RED + k, rgb[k] * bright[k]/256);
  }
  delay(DELAY);
}

long HSV_to_RGB( float h, float s, float v ) {
  /* modified from Alvy Ray Smith's site: http://www.alvyray.com/Papers/hsv2rgb.htm */
  // H is given on [0, 6]. S and V are given on [0, 1].
  // RGB is returned as a 24-bit long #rrggbb
  int i;
  float m, n, f;

  // not very elegant way of dealing with out of range: return black
  if ((s<0.0) || (s>1.0) || (v<0.0) || (v>1.0)) {
    return 0L;
  }

  if ((h < 0.0) || (h > 6.0)) {
    return long( v * 255 ) + long( v * 255 ) * 256 + long( v * 255 ) * 65536;
  }
  i = floor(h);
  f = h - i;
  if ( !(i&1) ) {
    f = 1 - f; // if i is even
  }
  m = v * (1 - s);
  n = v * (1 - s * f);
  switch (i) {
  case 6:
  case 0:
    return long(v * 255 ) * 65536 + long( n * 255 ) * 256 + long( m * 255);
  case 1:
    return long(n * 255 ) * 65536 + long( v * 255 ) * 256 + long( m * 255);
  case 2:
    return long(m * 255 ) * 65536 + long( v * 255 ) * 256 + long( n * 255);
  case 3:
    return long(m * 255 ) * 65536 + long( n * 255 ) * 256 + long( v * 255);
  case 4:
    return long(n * 255 ) * 65536 + long( m * 255 ) * 256 + long( v * 255);
  case 5:
    return long(v * 255 ) * 65536 + long( m * 255 ) * 256 + long( n * 255);
  }
}

The circuit is very simple:

  • Digital pin 9 → 165Ω resistor → LED Red pin
  • Digital pin 10 → 100Ω resistor → LED Green pin
  • Digital pin 11 → 100Ω resistor → LED Blue pin
  • GND → LED common cathode.

The different resistor values are to provide a limited current to the Triple Output LED RGB – Diffused, as each channel has different requirements. The 165Ω resistor is actually two 330Ω in parallel; I didn’t have the right value, and this was the closest I could make with what I had.

first LED

Saw my first retail LED domestic bulb today – $14 at Rona. Looked more like a novelty than anything else, but it did have a stated 100,000 hour life…

All the printers I’ve ever owned …

bird you can see: hp print test

  • An ancient (even in 1985) Centronics serial dot-matrix printer that we never got working with the CPC464. The print head was driven along a rack, and when it hit the right margin, an idler gear was wedged in place, forcing the carriage to return. Crude, noisy but effective.
  • Amstrad DMP-2000. Plasticky but remarkably good 9-pin printer. Had an open-loop ribbon that we used to re-ink with thick oily endorsing ink until the ribbons wore through.
  • NEC Pinwriter P20. A potentially lovely 24-pin printer ruined by a design flaw. Print head pins would get caught in the ribbon, and snap off. It didn’t help that the dealer that sold it to me wouldn’t refund my money, and required gentle persuasion from a lawyer to do so.
  • Kodak-Diconix 300 inkjet printer. I got this to review for Amiga Computing, and the dealer never wanted it back. It used HP ThinkJet print gear which used tiny cartridges that sucked ink like no tomorrow; you could hear the droplets hit the page.
  • HP DeskJet 500. I got this for my MSc thesis. Approximately the shape of Torness nuclear power station (and only slightly smaller), last I heard it was still running.
  • Canon BJ 200. A little mono inkjet printer that ran to 360dpi, or 720 if you had all the time in the world and an unlimited ink budget.
  • Epson Stylus Colour. My first colour printer. It definitely couldn’t print photos very well.
  • HP LaserJet II. Big, heavy, slow, and crackling with ozone, this was retired from Glasgow University. Made the lights dim when it started to print. Came with a clone PostScript cartridge that turned it into the world’s second-slowest PS printer. We did all our Canadian visa paperwork on it.
  • Epson Stylus C80. This one could print photos tolerably well, but the cartridges dried out quickly, runing the quality and making it expensive to run.
  • Okidata OL-410e PS. The world’s slowest PostScript printer. Sold by someone on tortech who should’ve known better (and bought by someone who also should’ve known better), this printer jams on every sheet fed into it due to a damaged paper path. Unusually, it uses an LED imaging system instead of laser xerography, and has a weird open-hopper toner system that makes transporting a part-used print cartridge a hazard.
  • HP LaserJet 4M Plus. With its duplexer and extra paper tray it’s huge and heavy, but it still produces crisp pages after nearly 1,000,000 page impressions. I actually have two of these; one was bought for $99 refurbished, and the other (which doesn’t print nearly so well) was got on eBay for $45, including duplexer and 500-sheet tray. Combining the two (and judiciously adding a bunch of RAM) has given me a monster network printer which lets you know it’s running by dimming the lights from here to Etobicoke.
  • IBM Wheelwriter typewriter/ daisywheel printer. I’ve only ever produced a couple of pages on this, but this is the ultimate letter-quality printer. It also sounds like someone slowly machine-gunning the neighbourhood, so mostly lives under wraps.
  • HP PhotoSmart C5180. It’s a network photo printer/scanner that I bought yesterday. Really does print indistinguishably from photos, and prints direct from memory cards. When first installed, makes an amusing array of howls, boinks, squeals, beeps and sproings as it primes the print heads.

the great lost opportunity

I’ve always thought that Adobe missed a great opportunity when they didn’t make their basic PDF writer freely available for Windows. Other OSs now have transparent print-to-PDF options. If you’re lucky, a corporate PC might have MS Office Document Image Writer installed, but a 300dpi monochrome TIFF can’t compare to a PDF.

Still, one can always install PDFCreator (if you have admin rights to the PC, of course). It’s a shame they decide to bundle a marginally dodgy toolbar/spyware package with it, but you don’t get that if you use the MSI installer package.

providing cannon fodder for empire since 1867

Prime Minister Stephen Harper says the toll of Canadian soldiers killed in Afghanistan is the price Canada is paying for playing a leadership role in world affairs.

I reckon that if I took a random street poll anywhere (anywhere outside Canada, that is), no more than 3 out of 10 people would consider Canada as having a leadership role. I do not wish to make light of the soldiers’ plight; I just don’t want them there in my name.

(I was going to make a comment about the nearest thing to a role to most Canadians would be a Swiss Chalet 1/4 chicken dinner, but that doesn’t work in a written context, and barely works when spoken.)

a tiny stub-tailed birdlet

Our tree is filled with Ruby-crowned Kinglets, and the title is Peterson’s poetic description of them. I guess they’re feeding up to migrate a bit south. Give news of yourselves when you bring spring back with you!

not the smartest loaches in the tank

Came home, said hello to the fish, and did a quick count; I was one loach down, and the CO2 generator had an orange tail …

Seems that one of the loaches had decided it was way cool to get wedged up the back of the gas generator, and couldn’t get back out. I gingerly pulled off the device from the side of the tank, and the loach fluttered off, a little dazed.

No sooner had I put the generator back did another loach zoom up and get jammed. It must’ve been told that you got a “wicked headrush, dude”.

And for this reason, loaches don’t rule the earth.