I can’t believe I’m having to write this article again. Back in 2004, I picked up an identical model of typewriter on Freecycle, also complete with the parallel printer option board. The one I had back then had an incredible selection of printwheels. And I gave it all away! Aaargh! Why?
Last month, I ventured out to a Value Village in more affluent part of town. On the shelf for $21 was a familiar boxy shape, another Wheelwriter 10 Series II Typewriter model 6783. This one also has the printer option board, but it only has one printwheel, Prestige Elite. It powered on enough at the test rack enough for me to see it mostly worked, so I bought it.
Once I got it home, though, I could see it needed some work. The platen was covered in ink and correction fluid splatters. Worse, the carriage would jam in random places. It was full of dust and paperclips. But the printwheel did make crisp marks on paper, so it was worth looking at a repair.
Note that there are lots of electronics projects — such as tofergregg/IBM-Wheelwriter-Hack: Turning an IBM Wheelwriter into a unique printer — that use an Arduino or similar to drive the printer. This is not that (or those). Here I’m using the Printer Option board plus a USB to Parallel cable. There’s almost nothing out there about how these work.
Connecting the printer
You’ll need a USB to Parallel adapter, something like this: StarTech 10 ft USB to Parallel Printer Adapter – M/M. You need the kind with the big Centronics connector, not the 25-pin D-type. My one (old) has a chunky plastic case that won’t fit into the port on the Wheelwriter unless you remove part of the cable housing. On my Linux box, the port device is /dev/usb/lp0. You might want to add yourself to the lp group so you can send data to the printer without using sudo:
sudo adduser user lp
The Wheelwriter needs to be switched into printer mode manually by pressing the Code + Printer Enable keys.
Printer Codes
As far as I can tell, the Wheelwriter understands a subset of IBM ProPrinter codes. Like most simple printers, most control codes start with an Esc character (ASCII 27). Lines need to end with both a Carriage Return (ASCII 13) and newline (ASCII 10). Sending only CRs allows overprinting, while sending only newlines gives stair-step output.
The codes I’ve found to work so far are:
Emphasized printing — Esc E
Cancel emphasized printing — Esc F (double strike printing [Esc G, Esc H] might also work, but I haven’t tried them)
Continuous underscore — Esc – 1
Cancel continuous underscore — Esc – 0 (technically, these are Esc – n, where n = ASCII 1 or 0, not character “1” or “0”. But the characters seem to work, too)
7/72″ inch line spacing — Esc 1
Set text line spacing to n / 72″ units — Esc A n (this one really matters: if you send “6” (ASCII 66) instead of 6, you’ll get 66/72 = 11/12″ [= 28.3 mm] line spacing instead of the 1/12″ [= 2.1 mm] you expected)
Start text line spacing — Esc 2
Text functions such as italics and extended text aren’t possible with a daisywheel printer. You can attempt dot-matrix graphics using full stops and micro spacing, but I don’t want to know you if you’d try.
Sending codes from the command line
echo is about the simplest way of doing it. Some systems provide an echo built-in that doesn’t support the -e (interpret special characters) and -n (don’t send newline) options. You may have to call /usr/bin/echo instead.
To set the line spacing to a (very cramped) 1/12″ [= 2.1 mm] and print a horizontal line of dots and a vertical line of dots, both equally spaced (if you’re using Prestige Elite):
IBM daisywheels typically can’t represent the whole ASCII character set. Here’s what an attempt to print codes 33 to 126 in Prestige Elite looks like:
The following characters are missing:
< > \ ^ ` { | } ~
So printing your HTML or Python is right out. FORTRAN, as ever, is safe.
Prestige Elite is a 12 character per inch font (“12 pitch”, or even “Elite” in typewriter parlance) that’s mostly been overshadowed by Courier (typically 10 characters per inch) in computer usage. This is a shame, as it’s a much prettier font.
Related, yet misc.
There’s very little out there about printing with IBM daisywheels. This is a dump of the stuff I’ve found that may help other people:
IBM didn’t make too many daisywheel printers. Two models were the 5216 Wheelprinter and 5223 Wheelprinter E, possibly intended for larger IBM machines. The 5216 Wheelprinter looks like it may use similar character codes. Here’s a (Printer Definition File?? An IBM thing, I think) for that printer that might help the interested: ibm5216_pdf
I don’t think I’ve had as much enjoyment for a piece of software for a very long time as I’ve had with BirdNET-Pi. It’s a realtime acoustic bird classification system for the Raspberry Pi. It listens through a microphone you place somewhere near where you can hear birds, and it’ll go off and guess what it’s hearing, using a cut-down version of the BirdNET Sound ID model. It does this 24/7, and saves the samples it hears. You can then go to a web page (running on the same Raspberry Pi) and look up all the species it has heard.
Our Garden
Not very impressive, kind of overgrown, in the wrong part of town. Small, too. But birds love it. At this time of year, it’s alive with birds. You can’t make them out, but there’s a pair of Rose-breasted Grosbeaks happily snacking near the top of the big tree. There are conifers next door too, so we get birds we wouldn’t expect.
We are next to two busy subway/train stations, and in between two schools. There’s a busy intersection nearby, too. Consequently, the background noise is horrendous
What I used
This was literally “stuff I had lying around”:
Raspberry Pi 3B+ (with power supply, case, thermostatic fan and SD card)
USB extension cable (this, apparently, is quite important to isolate the USB audio device from electrical noise)
Horrible cheap USB sound card: I paid about $2 for a “3d sound” thing about a decade ago. It records in mono. It works. My one is wrapped in electrical tape as the case keeps threatening to fall off, plus it has a hugely bright flashing LED the is annoying.
Desktop mic (circa 2002): before video became a thing, PCs had conferencing microphones. I think I got this one free with a PC over 20 years ago. It’s entirely unremarkable and is not an audiophile device. I stuck it out a back window and used a strip of gaffer tape to stop bugs getting in. It’s not waterproof, but it didn’t rain the whole week it was out the window.
I’ll put the recordings at the end of this post. Note, though, they’re noisy: Cornell Lab quality they ain’t.
What I learned
This is the first time that I’ve let an “AI” classifier model run with no intervention. If it flags some false positives, then it’s pretty low-stakes when it’s wrong. And how wrong did it get some things!
There are also false positive for Trumpeter Swans (local dog) and Tundra Swans (kids playing). These samples had recognizable voices, so I didn’t include them here.
The 30 positive species identifications
Many of these have a fairly loud click at the start of the sample, so mind your ears.
BirdNetPi doesn’t create combined spectrograms with audio as a single video file. What it does do is create an mp3 plus a PNG of the spectrogram. ffmpeg can make a nice not-too-large webm video for sharing:
as performed by the flite speech synthesizer and some shell scripts
Not quite as good as having the late Prof. Morgan recite it to you himself — one of the few high points of my school experience — but you take what you can get in this economy.
A while back, Seeed Studio sent me one of their Wio Terminal devices to review. It was pretty neat, but being limited to using Arduino to access all of it features was a little limiting. I still liked it, though, and wrote about it here: SeeedStudio Wio Terminal
There wasn’t any proper MicroPython support for the device as it used a MicroChip/Atmel SAMD51 ARM® Cortex®-M4 micro-controller. But since I wrote the review, one developer (robert-hh) has worked almost entirely solo to make SAMD51 and SAMD21 support useful in mainline MicroPython.
Hey! Development is still somewhere between “not quite ready for prime time” and “beware of the leopard”. MicroPython on the SAMD51 works remarkably well for supported boards, but don’t expect this to be beginner-friendly yet.
I thought I’d revisit the Wio Terminal and see what I could do using a nightly build (downloaded from Downloads – Wio Terminal D51R – MicroPython). Turns out, most of the board works really well!
What doesn’t work yet
Networking/Bluetooth – this is never going to be easy, especially with Seeed Studio using a separate RTL8720 SoC. It may not be entirely impossible, as previously thought, but so far, wifi support seems quite far away
RTC – this is a compile-time option, but isn’t available on the stock images. Not all SAMD51 boards have a separate RTC oscillator, and deriving the RTC from the system oscillator would be quite wobbly. RTC works now! It may even be possible to provide backup battery power and have it keep time when powered off. VBAT / PB03 / SPI_SCK is broken out to the 40-pin connector.
What does work
Display – ILI9341 320×240 px, RGB565 via SPI
Accelerometer – LIS3DHTR via I²C
Microphone – analogue
Speaker – more like a buzzer, but this little PWM speaker element does allow you to play sounds
Light Sensor – via analogue photo diode
IR emitter – PWM, not tied to any hardware protocol
Internal LED – a rather faint blue thing, but useful for low-key signalling
Micro SD Card – vi SPI. Works well with MicroPython’s built-in virtual file systems
Switches and buttons – three buttons on the top, and a five-way mini-joystick
I²C via Grove Connector – a second, separate I²C channel.
I’ll go through each of these here, complete with a small working example.
LED
Let’s start with the simplest feature: the tiny blue LED hidden inside the case. You can barely see this, but it glows out around the USB C connector on the bottom of the case.
MicroPython interfaces: machine.Pin, machine.PWM
Control pin: Pin(“LED_BLUE”) or Pin(15), or Pin(“PA15”): any one of these would work.
# MicroPython / Seeed Wio Terminal / SAMD51
# Wio-Terminal-LED.py - blink the internal blue LED
# scruss, 2022-10
# -*- coding: utf-8 -*-
from machine import Pin
from time import sleep_ms
led = Pin("LED_BLUE", Pin.OUT) # or Pin(15) or Pin("PA15")
try:
while True:
led.value(not led.value())
sleep_ms(1200)
except:
led.value(0) # turn it off if user quits
exit()
IR LED
I don’t have any useful applications of the IR LED for device control, so check out Awesome MicroPython’s IR section for a library that would work for you.
# MicroPython / Seeed Wio Terminal / SAMD51
# Wio-Terminal-IR_LED.py - blink the internal IR LED
# scruss, 2022-10
# -*- coding: utf-8 -*-
# Hey! This is a completely futile exercise, unless you're able
# to see into the IR spectrum. But we're here to show you the pin
# specification to use. For actual useful libraries to do stuff with
# IR, take a look on https://awesome-micropython.com/#ir
# So this is a boring blink, 'cos we're keeping it short here.
# You might be able to see the LED (faintly) with your phone camera
from machine import Pin, PWM
from time import sleep_ms
ir = PWM(Pin("PB31")) # "IR_CTL" not currently defined
try:
while True:
ir.duty_u16(32767) # 50% duty
ir.freq(38000) # fast flicker
sleep_ms(1200)
ir.duty_u16(0) # off
sleep_ms(1200)
except:
ir.duty_u16(0) # turn it off if user quits
exit()
Buttons
There are three buttons on top, plus a 5-way joystick on the front. Their logic is inverted, so they read 0 when pressed, 1 when not. It’s probably best to use machine.Signal with these to make operation more, well, logical.
Control pins: Pin(“BUTTON_3”) or Pin(92) or Pin(PC28) – top left; Pin(“BUTTON_2”) or Pin(91) or Pin(PC27) – top middle; Pin(“BUTTON_1”) or Pin(90) or Pin(PC26) – top right; Pin(“SWITCH_B”) or Pin(108) or Pin(PD12) – joystick left; Pin(“SWITCH_Y”) or Pin(105) or Pin(PD09) – joystick right; Pin(“SWITCH_U”) or Pin(116) or Pin(PD20) – joystick up; Pin(“SWITCH_X”) or Pin(104) or Pin(PD08) – joystick down; Pin(“SWITCH_Z”) or Pin(106) or Pin(PD10) – joystick button
# MicroPython / Seeed Wio Terminal / SAMD51
# Wio-Terminal-Buttons.py - test the buttons
# scruss, 2022-10
# -*- coding: utf-8 -*-
# using Signal because button sense is inverted: 1 = off, 0 = on
from machine import Pin, Signal
from time import sleep_ms
pin_names = [
"BUTTON_3", # Pin(92) or Pin(PC28) - top left
"BUTTON_2", # Pin(91) or Pin(PC27) - top middle
"BUTTON_1", # Pin(90) or Pin(PC26) - top right
"SWITCH_B", # Pin(108) or Pin(PD12) - joystick left
"SWITCH_Y", # Pin(105) or Pin(PD09) - joystick right
"SWITCH_U", # Pin(116) or Pin(PD20) - joystick up
"SWITCH_X", # Pin(104) or Pin(PD08) - joystick down
"SWITCH_Z", # Pin(106) or Pin(PD10) - joystick button
]
pins = [None] * len(pin_names)
for i, name in enumerate(pin_names):
pins[i] = Signal(Pin(name, Pin.IN), invert=True)
while True:
for i in range(len(pin_names)):
print(pins[i].value(), end="")
print()
sleep_ms(100)
Buzzer
A very quiet little PWM speaker.
MicroPython interfaces: machine.PWM
Control pin: Pin(“BUZZER”) or Pin(107) or Pin(“PD11”)
# MicroPython / Seeed Wio Terminal / SAMD51
# Wio-Terminal-Microphone.py - print values from the microphone
# scruss, 2022-10
# -*- coding: utf-8 -*-
from time import sleep_ms
from machine import ADC
mic = ADC("MIC")
while True:
print([mic.read_u16()])
sleep_ms(5)
Grove I²C Port
The Wio Terminal has two Grove ports: the one on the left (under the speaker port) is an I²C port. As I don’t know what you’ll be plugging in there, this example does a simple bus scan. You could make a, appalling typewriter if you really wanted.
# MicroPython / Seeed Wio Terminal / SAMD51
# Wio-Terminal-Grove-I2C.py - show how to connect on Grove I2C
# scruss, 2022-10
# -*- coding: utf-8 -*-
from machine import Pin, I2C
# NB: This doesn't do much of anything except list what's
# connected to the left (I²C) Grove connector on the Wio Terminal
i2c = I2C(3, scl=Pin("SCL1"), sda=Pin("SDA1"))
devices = i2c.scan()
if len(devices) == 0:
print("No I²C devices connected to Grove port.")
else:
print("Found these I²C devices on the Grove port:")
for n, id in enumerate(devices):
print(" device", n, ": ID", id, "(hex:", hex(id) + ")")
LIS3DH Accelerometer
This is also an I²C device, but connected to a different port (both logically and physically) than the Grove one.
Library: from MicroPython-LIS3DH, copy lis3dh.py to the Wio Terminal’s small file system. Better yet, compile it to mpy using mpy-cross to save even more space before you copy it across
# MicroPython / Seeed Wio Terminal / SAMD51
# Wio-Terminal-Accelerometer.py - test out accelerometer
# scruss, 2022-10
# -*- coding: utf-8 -*-
# based on
# https://github.com/tinypico/tinypico-micropython/tree/master/lis3dh%20library
import lis3dh, time, math
from machine import Pin, I2C
i2c = I2C(4, scl=Pin("SCL0"), sda=Pin("SDA0"))
imu = lis3dh.LIS3DH_I2C(i2c)
last_convert_time = 0
convert_interval = 100 # ms
pitch = 0
roll = 0
# Convert acceleration to Pitch and Roll
def convert_accell_rotation(vec):
x_Buff = vec[0] # x
y_Buff = vec[1] # y
z_Buff = vec[2] # z
global last_convert_time, convert_interval, roll, pitch
# We only want to re-process the values every 100 ms
if last_convert_time < time.ticks_ms():
last_convert_time = time.ticks_ms() + convert_interval
roll = math.atan2(y_Buff, z_Buff) * 57.3
pitch = (
math.atan2((-x_Buff), math.sqrt(y_Buff * y_Buff + z_Buff * z_Buff)) * 57.3
)
# Return the current values in roll and pitch
return (roll, pitch)
# If we have found the LIS3DH
if imu.device_check():
# Set range of accelerometer (can be RANGE_2_G, RANGE_4_G, RANGE_8_G or RANGE_16_G).
imu.range = lis3dh.RANGE_2_G
# Loop forever printing values
while True:
# Read accelerometer values (in m / s ^ 2). Returns a 3-tuple of x, y,
# z axis values. Divide them by 9.806 to convert to Gs.
x, y, z = [value / lis3dh.STANDARD_GRAVITY for value in imu.acceleration]
print("x = %0.3f G, y = %0.3f G, z = %0.3f G" % (x, y, z))
# Convert acceleration to Pitch and Roll and print values
p, r = convert_accell_rotation(imu.acceleration)
print("pitch = %0.2f, roll = %0.2f" % (p, r))
# Small delay to keep things responsive but give time for interrupt processing.
time.sleep(0.1)
Control Pins: Pin(“SD_SCK”), Pin(“SD_MOSI”), Pin(“SD_MISO”) for SD access. Pin(“SD_DET”) is low if an SD card is inserted, otherwise high
Library: copy sdcard.py from micropython-lib to the Wio Terminal’s file system.
Rather than provide a small canned example (there’s one here, if you must: Wio-Terminal-SDCard.py) here’s my boot.py startup file, showing how I safely mount an SD card if there’s one inserted, but keep booting even if it’s missing:
# boot.py - MicroPython / Seeed Wio Terminal / SAMD51
import sys
sys.path.append("/lib")
import machine
import gc
import os
import sdcard
machine.freq(160000000) # fast but slightly jittery clock
gc.enable()
# mount SD card if there's one inserted
try:
sd_detected = machine.Signal(
machine.Pin("SD_DET", machine.Pin.IN),
invert=True,
)
sd_spi = machine.SPI(
6,
sck=machine.Pin("SD_SCK"),
mosi=machine.Pin("SD_MOSI"),
miso=machine.Pin("SD_MISO"),
baudrate=40000000,
)
sd = sdcard.SDCard(sd_spi, machine.Pin("SD_CS"))
if sd_detected.value() == True:
os.mount(sd, "/SD")
print("SD card mounted on /SD")
else:
raise Exception("SD card not inserted, can't mount /SD")
except:
print("SD card not found")
The Wio Terminal may have an XPT2046 resistive touch controller installed, but I haven’t been able to test it. There are LCD_XL, LCD_YU, LCD_XR and LCD_YD lines on the schematic that might indicate it’s there, though.
# MicroPython / Seeed Wio Terminal / SAMD51
# Wio-Terminal-Screen.py - output something on the ILI9341 screen
# scruss, 2022-10
# -*- coding: utf-8 -*-
from time import sleep
from ili9341 import Display, color565
from machine import Pin, SPI
def wheel565(pos):
# Input a value 0 to 255 to get a colour value.
# The colours are a transition r - g - b - back to r.
# modified to return RGB565 value for ili9341 - scruss
(r, g, b) = (0, 0, 0)
if (pos < 0) or (pos > 255):
(r, g, b) = (0, 0, 0)
if pos < 85:
(r, g, b) = (int(pos * 3), int(255 - (pos * 3)), 0)
elif pos < 170:
pos -= 85
(r, g, b) = (int(255 - pos * 3), 0, int(pos * 3))
else:
pos -= 170
(r, g, b) = (0, int(pos * 3), int(255 - pos * 3))
return (r & 0xF8) << 8 | (g & 0xFC) << 3 | b >> 3
# screen can be a little slow to turn on, so use built-in
# LED to signal all is well
led = Pin("LED_BLUE", Pin.OUT)
backlight = Pin("LED_LCD", Pin.OUT) # backlight is not a PWM pin
spi = SPI(
7, sck=Pin("LCD_SCK"), mosi=Pin("LCD_MOSI"), miso=Pin("LCD_MISO"), baudrate=4000000
)
display = Display(spi, dc=Pin("LCD_D/C"), cs=Pin("LCD_CS"), rst=Pin("LCD_RESET"))
display.display_on()
display.clear()
led.on() # shotgun debugging, embedded style
backlight.on()
# use default portrait settings: x = 0..239, y = 0..319
dx = 3
dy = 4
x = 3
y = 4
i = 0
try:
while True:
# display.draw_pixel(x, y, wheel565(i))
display.fill_hrect(x, y, 3, 4, wheel565(i))
i = (i + 1) % 256
x = x + dx
y = y + dy
if x <= 4:
dx = -dx
if x >= 234:
dx = -dx
if y <= 5:
dy = -dy
if y >= 313:
dy = -dy
except:
backlight.off()
led.off()
display.display_off()
MIDI seems to be absurdly complex. In all the files I looked at, there didn’t seem to be much of a standard in encoding whether the note duration was in the NOTE_ON event or the NOTE_OFF event. Eventually, I managed to fudge a tiny single channel file that had acceptable note durations in the NOTE_OFF events. Here is the file:
# extremely crude MicroPython MIDI demo
# MicroPython / Raspberry Pi Pico - scruss, 2022-08
# see https://github.com/bixb922/umidiparser
import umidiparser
from time import sleep_us
from machine import Pin, PWM
# pin 26 - GP20; just the right distance from GND at pin 23
# to use one of those PC beepers with the 4-pin headers
pwm = PWM(Pin(20))
led = Pin('LED', Pin.OUT)
def play_tone(freq, usec):
# play RTTL/midi notes, also flash onboard LED
# original idea thanks to
# https://github.com/dhylands/upy-rtttl
print('freq = {:6.1f} usec = {:6.1f}'.format(freq, usec))
if freq > 0:
pwm.freq(int(freq)) # Set frequency
pwm.duty_u16(32767) # 50% duty cycle
led.on()
sleep_us(int(0.9 * usec)) # Play for a number of usec
pwm.duty_u16(0) # Stop playing for gap between notes
led.off()
sleep_us(int(0.1 * usec)) # Pause for a number of usec
# map MIDI notes (0-127) to frequencies. Note 69 is 440 Hz ('A4')
freqs = [440 * 2**((float(x) - 69) / 12) for x in range(128)]
for event in umidiparser.MidiFile("lg2.mid", reuse_event_object=True):
if event.status == umidiparser.NOTE_OFF and event.channel == 0:
play_tone(freqs[event.note], event.delta_us)
This isn’t be any means a general MIDI parser, but is rather specialized to play monophonic tunes on channel 0. The result is gloriously awful:
More Micropython programmers — and especially beginners — should know about Awesome MicroPython. It’s a community-curated list of remarkably decent MicroPython libraries, frameworks, software and resources. If you need to interface to a sensor, look there first.
For example, take the INA219 High Side DC Current Sensor. It’s an I²C sensor able to measure up to 26 V, ±3.2 A. It does this by measuring the voltage across a 0.1 ohm precision shunt resistor with its built-in 12-bit ADC. I got a customer return from the store that was cosmetically damaged but still usable, so I thought I’d try it with the simplest module I could find in Awesome MicroPython and see how well it worked.
I guess I needed a test circuit too. Using all of what was immediately handy — a resistor I found on the bench and measured at 150.2 ohm — I came up with this barely useful circuit:
The INA219 would be happier with a much higher current to measure, but I didn’t have anything handy that could do that.
Looking in Awesome MicroPython’s Current section, I found robert-hh/INA219: INA219 Micropython driver. It doesn’t have much (okay, any) documentation, but it’s a very small module and the code is easy enough to follow. I put the ina219.py module file into the /lib folder of a WeAct Studio RP2040 board, and wrote the following code:
# INA219 demo - uses https://github.com/robert-hh/INA219
from machine import Pin, I2C
import ina219
i = I2C(0, scl=Pin(5), sda=Pin(4))
print("I2C Bus Scan: ", i.scan(), "\n")
sensor = ina219.INA219(i)
sensor.set_calibration_16V_400mA()
# my test circuit is 3V3 supply through 150.2 ohm resistor
r_1 = 150.2
r_s = 0.1 # shunt resistor on INA219 board
# current is returned in milliamps
print("Current / mA: %8.3f" % (sensor.current))
# shunt_voltage is returned in volts
print("Shunt voltage / mV: %8.3f" % (sensor.shunt_voltage * 1000))
# estimate supply voltage from known resistance * sensed current
print("3V3 (sensed) / mV: %8.3f" % ((r_1 + r_s) * sensor.current))
with everything wired up like this (Blue = SDA, Yellow = SCL):
Running it produced this:
I2C Bus Scan: [64]
Current / mA: 22.100
Shunt voltage / mV: 2.210
3V3 (sensed) / mV: 3321.630
So it’s showing just over 22 mA: pretty close to what I calculated!
It feels like forever, but it was probably 2018 that the roads through Kennedy Station were closed to dig the huge hole for the Eglinton Crosstown. So for all that time, I’ve been braving the deadly intersection at Kennedy and Eglinton to get back home from my daily bike ride.
But as of late last week, the roads reopened and I’ve been enjoying the wheee! instead of constant fear of dying. There’s still some work needed, so it’s not all sunbeams and cucumbers:
there’s still a misplaced Buses Only sign eastbound on North Service Road. Since this is countermanded by bike and taxi signs on South Service Road continuing the same way, it’s clearly a mistake
Transway Crescent is in a horrible state, with nearly six years of heavy construction traffic turning the surface into a knobbly mess. This can, and must, be fixed
Perhaps most worrying is permanent signs that suggest the sidewalks will be designated shared-use. They’re far too narrow for that, and have none of the necessary safe entry/exit points. As I’m a road user, the road I will use
You still (as before) have to mix it with buses. Bus drivers are professionals, though, and part of the solution.
This message from an unknown caller has sat on our landline answering machine since 2020 or 2021. No idea who or what sent it. All I know is it came in just before noon on a Tuesday morning. The entirely synthesized voice makes me think it’s a junk call, but there’s no scam attached. Just this message, slightly eerie, quite inexplicable.
In early 2013, I must’ve been left unsupervised for too long since I made The Quite Rubbish Clock:
Written in (Owen Wilson voice) kind of an obsolete vernacular and running on hardware that’s now best described as “quaint”, it was still absurdly popular at the time. Raspberry Pis were still pretty new, and people were looking for different things to do with them.
I happened across the JASchilz/uQR: QR Code Generator for MicroPython the other day, and remembered I had some tiny OLED screens that were about the same resolution as the old Nokia I’d used in 2013. I wondered: could I …?
The board is a LOLIN S2 Mini with a OLED 0.66 Inch Shield on top, all running MicroPython. One limitation I found in the MicroPython QR library was that it was very picky about input formats, so it only displays the time as HHMMSS with no separators.
Source, of course:
# -*- coding: utf-8 -*-
# yes, the Quite Rubbish Clock rides again ...
# scruss, 2022-06-30
# MicroPython on Lolin S2 Mini with 64 x 48 OLED display
# uses uQR from https://github.com/JASchilz/uQR
# - which has problems detecting times with colons
from machine import Pin, I2C, RTC
import s2mini # on Lolin ESP32-S2 Mini
import ssd1306
from uQR import QRCode
WIDTH = 64 # screen size
HEIGHT = 48
SIZE = 8 # text size
r = RTC()
# set up and clear screen
i2c = I2C(0, scl=Pin(s2mini.I2C_SCL), sda=Pin(s2mini.I2C_SDA))
oled = ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c)
oled.fill(0)
def snazz():
marquee = [
" **",
" **",
" **",
" **",
" **",
"********",
" ******",
" ****",
" **",
" quite",
"rubbish",
" clock",
" mk.2",
"<scruss>",
" >2022<"
]
for s in marquee:
oled.scroll(0, -SIZE) # scroll up one text line
oled.fill_rect(0, HEIGHT-SIZE, WIDTH,
SIZE, 0) # blank last line
oled.text("%-8s" % s, 0, HEIGHT-SIZE) # write text
oled.show()
time.sleep(0.25)
time.sleep(5)
oled.fill(1)
oled.show()
snazz() # tedious crowd-pleasing intro
qr = QRCode()
while True:
qr.add_data("%02d%02d%02d" % r.datetime()[4:7])
qr.border = 1 # default border too big to fit small screen
m = qr.get_matrix()
oled.fill(1)
for y in range(len(m)):
for x in range(len(m[0])):
# plot a double-sized QR code, centred, inverted
oled.fill_rect(9 + 2*x, 1 + 2*y, 2, 2, not m[y][x])
oled.show()
time.sleep(0.05)
qr.clear()
If your output is glitchy, you might need to put the following in boot.py:
import machine
machine.freq(240000000)
This increases the ESP32-S2’s frequency from 160 to 240 MHz.
Update: there’s a fork of uQR that provides better character support, particularly those required for sending Wi-Fi Network config.
The Internet Archive has Threlkeld Granite Co Ltd’s Album of ornamental granitic tiles online, and I’m really digging the patterns of the hydraulic tiles they made. I’ve recreated some of their patterns in InkScape, and made this small demo by tiling bitmapped renderings.
Naturally, I had to verify this. So I tuned to the WWV 10 MHz time signal on my amateur rig, tuned a portable radio to CBC Radio 1 FM, which broadcasts on 99.1 MHz in Toronto and recorded them together:
Yup: Rob’s right – CBC is broadcasting the NRC 13:00:00 signal at 13:00:10, which for time nerds might as well be the change from Julian to the Gregorian calendar.
This recording was made directly from the airwaves. There should be effectively no difference between the signal broadcast times, but here we are with the “National Research Council official time signal” going out at a very wrong time indeed.
Following on from a customer query at Elmwood Electronics, I can confirm that one can install install addressable RGB LEDs/NeoPixels inside one of these large buttons. It’s not the easiest build, so whether one should attempt this is another matter entirely.
Thin (and I mean thin: I used 28 AWG) Silicone Cover Stranded-Core Wire in several colours. You’ll want to cut this quite long at first, as you have to ease it through some tiny holes in the button assembly. If you solder connectors on the end, you won’t be able to disassemble or install the button without cutting them off. Do I speak from experience here? You betcha!
The usual soldering/hot gluing/bending/prying/grabbing/cutting tools you already know and love. In addition, you might consider a non-marring spudger and a pair of small(ish) arterial forceps (aka hemostats, aka Kelly forceps, aka fishing hook removal pliers)
I’m not going to cover soldering the wires to the LED PCB in any depth here. You’ll need three wires: 5 V power, Ground and Data. Even though the LEDs I used need 5 V power, they are quite happy with 3.3 V logic on the data line. They need more than 3.3 V power to light, though.
Simulated (and not quite right yet) output from a “HOOT-NANNY” or Magic Designer, a proto-Spirograph toy that drew six-sided curves on round paper sheets. It was made by Howard B. Jones and Co. of Chicago, IL and first sold in 1929. The company’s better known for producing Jones Plugs and Sockets, sometimes known as Cinch-Jones connectors. The “HOOT-NANNY” name was dropped when production moved to the Northern Signal Company of Saukville, WI.
My eBay-acquired Magic Designer is quite beaten up, and doesn’t always produce accurate results. Here’s how one should look, from the instruction pamphlet:
As far as I’ve been able to work out, the parameters of the machine are:
central turntable is 6″ in diameter, with 192 gear teeth around the edge;
each crank disc is 1″ diameter (32 teeth), with the crank pin at 3/8″ radius;
the shift lever has a 10-70 degree scale, which corresponds to moving the upper crank disc between 30-90 degrees of arc from the lower (fixed) crank disc;
the pencil arms have 18 holes labelled A to R, at 1/4″ spacing from 5.75 to 1.5″. The perpendicular distance from the pivot holes to the pencil is 5/16″. This small offset makes very little difference to the overall arm length.
If we model the toy with a fixed turntable:
the crank pins describe epitrochoids around the edge of the paper;
the pencil point traces the intersection of two circles of radius the lengths of the pencil arms, each centred on a crank pin.
Original “WW1 Fighter Pilot” Snoopy ASCII art from “SNOOPY.BA” for the DEC PDP-8, written by Mr Kay R. Fisher of DEC some time before July 1973. It’s referred to in the first printing of the “101 Basic Computer Games” book, which was published in 1973.
ncal, banner: their respective authors
pstext an ascii to PS filter by Dan Judd, usenet comp.lang.postscript, December 1989. I had to really mess around with the output of this program to use a custom font and add the music ruling, but it produces cleaner PostScript than the giant messes that enscript and a2ps have become
Font: mnicmp, by me. Based on the DecWriter II font.