Crutch Guitar

That’s right, crutch guitar. I got swept into the world of cigar box guitars trying to make Robin’s broken leg somewhat fun.

Downloads

crutch_guitar.stl

crutch_guitar.skp

Pics

The bridge

On the back I added some metal nuts to act as washers so the string ends wouldn’t eat into the plastic.

The head could be adjusted a little but it’s good enough to play with the concept.

It sounds and works a lot better than I thought it would. And Robin’s way into it. Although I underestimated how hard it would be to play it without frets. It’s very much a different cat, people online make it look easy :).

And Handwriting for All

I wrote something pretty neat for Plottybot, and for the longest time I thought I should make it available on its own, and detached from the project. Then the most excellent Stuff Made Here guy made a writing machine, and ran into all the issues I ran into which drove me to write my own algorithms for capturing and replaying handwriting. My stuff wasn’t online then and that’s a shame, it was only available by building a Plottybot, or at least using its Pi image. Oh well.

As is tradition, I captured my kids’ handwritings as I do every year some time in the Winter. But this time I made sure to have the new site ready before Thanksgiving so that people could use it as they went and met with loved ones.

So here, this site serves the purpose of capturing one’s handwriting. It supports cursive, character variations, saving, and finally exporting to SVG & GCode. Hopefully this means you can use it with your favorite craft machine for the coolest of personalized projects.

https://ben.akrin.com/plottytools/

Pi Zero W to Pi Pico W

We’re finally able to get our hands on some sweet Pis after a long shortage. I got a Pi Pico W because it’s just that cheap, why not? It’s the W that really makes the difference; years ago I went from full Pis to Pi Zeros because the Wifi capabilities meant I didn’t have to worry about USB dongles. Everything I do tends to be Wifi connected so not having it makes a board irrelevant. And so with a W next to the Pico’s name, I’m definitely interested. It’s pretty smart of the Raspberry Pi foundation to put in this functionality into a microcontroller, it’s just enough to get me curious in what I could actually do with it.

Now I have to get over the barrier of not having a fully fledged OS (and the sweet package managers with all the Debian packages that comes with it), but with Wifi I can definitely do some tasks with a Pico I’m usually doing with a Zero. So I figured I’d try replacing the house temperature sensor and see how that goes. I initially tried to use the Pico’s built in temperature sensor, but it’s terribly inaccurate so I did rewire the DS18B20 from the Zero to the Pico.

Brain transfer, The size gain is nice but not particularly significant

Coding was a breeze, there is isn’t much to say about that, but if you’re interested, here the code I’m running for a web server I can ask for current temperature. One thing I learned is that you need to make sure you have a “lib” directory with libraries you might need for your code.

import machine
import time
import network
import socket
from machine import ADC
import machine, onewire, ds18x20

html = """{\"house_temperature\":<TEMPERATURE>}"""

led = machine.Pin( "LED", machine.Pin.OUT )

ds_pin = machine.Pin( 17 )
ds_sensor = ds18x20.DS18X20( onewire.OneWire(ds_pin) )
roms = ds_sensor.scan()
print( "Found DS devices: ", roms )

ssid = "<redacted>"
password = "<redacted>"
wlan = network.WLAN( network.STA_IF )
wlan.active( True )
wlan.connect( ssid, password )

# wait for connect or fail
max_wait = 20
while max_wait>0:
    if wlan.status() < 0 or wlan.status() >= 3:
        break
    max_wait -= 1
    print( "waiting for connection..." )
    time.sleep( 1 )

# handle connection error
if wlan.status()!=3:
    raise RuntimeError( "network connection failed" )
else:
    print( "connected" )
    status = wlan.ifconfig()
    print( "ip = " + status[0] )

# open socket
addr = socket.getaddrinfo( "0.0.0.0", 80)[0][-1]
s = socket.socket()
s.bind( addr )
s.listen( 1 )
print( "web server listening on", addr )

# listen for connections
while True:
    try:
        cl, addr = s.accept()
        print( "client connected from", addr)

        request = cl.recv( 1024 )
        print( request )
        request = str( request )
        
        ds_sensor.convert_temp()
        time.sleep_ms(750)
        response = html.replace( "<TEMPERATURE>", str(ds_sensor.read_temp(roms[0])) ) 
        
        led.value( 1 )
        
        cl.send('HTTP/1.0 200 OK\r\nContent-type: application/json\r\n\r\n')
        led.value( 0 )
        cl.send(response)
        cl.close()
 
    except OSError as e:
        cl.close()
        print('connection closed')

I got the libraries online, but here they are in case it’s useful.

# 1-Wire driver for MicroPython
# MIT license; Copyright (c) 2016 Damien P. George

import _onewire as _ow


class OneWireError(Exception):
    pass


class OneWire:
    SEARCH_ROM = 0xF0
    MATCH_ROM = 0x55
    SKIP_ROM = 0xCC

    def __init__(self, pin):
        self.pin = pin
        self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP)

    def reset(self, required=False):
        reset = _ow.reset(self.pin)
        if required and not reset:
            raise OneWireError
        return reset

    def readbit(self):
        return _ow.readbit(self.pin)

    def readbyte(self):
        return _ow.readbyte(self.pin)

    def readinto(self, buf):
        for i in range(len(buf)):
            buf[i] = _ow.readbyte(self.pin)

    def writebit(self, value):
        return _ow.writebit(self.pin, value)

    def writebyte(self, value):
        return _ow.writebyte(self.pin, value)

    def write(self, buf):
        for b in buf:
            _ow.writebyte(self.pin, b)

    def select_rom(self, rom):
        self.reset()
        self.writebyte(self.MATCH_ROM)
        self.write(rom)

    def scan(self):
        devices = []
        diff = 65
        rom = False
        for i in range(0xFF):
            rom, diff = self._search_rom(rom, diff)
            if rom:
                devices += [rom]
            if diff == 0:
                break
        return devices

    def _search_rom(self, l_rom, diff):
        if not self.reset():
            return None, 0
        self.writebyte(self.SEARCH_ROM)
        if not l_rom:
            l_rom = bytearray(8)
        rom = bytearray(8)
        next_diff = 0
        i = 64
        for byte in range(8):
            r_b = 0
            for bit in range(8):
                b = self.readbit()
                if self.readbit():
                    if b:  # there are no devices or there is an error on the bus
                        return None, 0
                else:
                    if not b:  # collision, two devices with different bit meaning
                        if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i):
                            b = 1
                            next_diff = i
                self.writebit(b)
                if b:
                    r_b |= 1 << bit
                i -= 1
            rom[byte] = r_b
        return rom, next_diff

    def crc8(self, data):
        return _ow.crc8(data)
# DS18x20 temperature sensor driver for MicroPython.
# MIT license; Copyright (c) 2016 Damien P. George

from micropython import const

_CONVERT = const(0x44)
_RD_SCRATCH = const(0xBE)
_WR_SCRATCH = const(0x4E)


class DS18X20:
    def __init__(self, onewire):
        self.ow = onewire
        self.buf = bytearray(9)

    def scan(self):
        return [rom for rom in self.ow.scan() if rom[0] in (0x10, 0x22, 0x28)]

    def convert_temp(self):
        self.ow.reset(True)
        self.ow.writebyte(self.ow.SKIP_ROM)
        self.ow.writebyte(_CONVERT)

    def read_scratch(self, rom):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_RD_SCRATCH)
        self.ow.readinto(self.buf)
        if self.ow.crc8(self.buf):
            raise Exception("CRC error")
        return self.buf

    def write_scratch(self, rom, buf):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_WR_SCRATCH)
        self.ow.write(buf)

    def read_temp(self, rom):
        buf = self.read_scratch(rom)
        if rom[0] == 0x10:
            if buf[1]:
                t = buf[0] >> 1 | 0x80
                t = -((~t + 1) & 0xFF)
            else:
                t = buf[0] >> 1
            return t - 0.25 + (buf[7] - buf[6]) / buf[7]
        else:
            t = buf[1] << 8 | buf[0]
            if t & 0x8000:  # sign bit set
                t = -((t ^ 0xFFFF) + 1)
            return t / 16

Half the amp draw from a Zero

That’s it!