The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Category: Software

General-purpose computers doing something specific

  • Wyze Cam vs. Xiamoi-Dafang Hacks

    The Wyze Cam is a surprisingly inexpensive camera firmly lashed to the Wyze app, with no provision for ordinary IP camera streaming. It seems to be a generic camera with custom firmware and, unsurprisingly, one can commandeer the bootloader with different firmware from a MicroSD card, thereby adding missing functions and suppressing undesired actions.

    Oddly, buying a genuine Wyze Cam directly from Wyze isn’t significantly more expensive than a generic from the usual eBay / Amazon sellers. Bonus: the legit camera arrives next week rather than in a month or two.

    I found one of my few remaining 2 GB MicroSD cards, formatted it with a 512 MB (!) FAT32 partition (per the suggestions), set up the “custom firmware” bootloader, and installed it with no issues.

    Installing the new firmware requires copying a directory tree, configuring the WiFi SSID and password in the usual wpa_supplicant, and rebooting. Works fine and, yeah, the camera now runs Linux.

    I told the router to assign a known IP address to the camera’s MAC address, set up port forwarding for port 8554 to that IP address, put the camera against the storm window in the kitchen, and rebooted everything to get it working:

    Wyze Cam in kitchen window
    Wyze Cam in kitchen window

    Unfortunately, while it works more-or-less well with browsers on the local network, it’s apparently inaccessible from outside. The router manages a DDNS name-to-IP mapping to make itself findable, the port is open, the forwarding seems correct, no image data arrives to browsers outside, and they eventually time out.

    Changing to port 8080 doesn’t help, nor does using MJPEG instead of H264 encoding.

    Even more unfortunately, the router doesn’t do hairpin connections (inside to outside to inside), so I can’t debug this mess from the Comfy Chair.

    This is a placeholder for what I’ve done while I accumulate more knowledge …

  • Troubles in PC Land

    So this happened:

    Dell Optiplex - SSD failure
    Dell Optiplex – SSD failure

    As far as I can tell, the Crucial M5500 SSD in that PC (an off-lease Dell Optiplex 760) stopped being a SATA hard drive, although it seems to work OK when jammed in a USB adapter.

    So I picked up a new-to-me Optiplex 9020 with Windows 8.1 on an SSD, shrank the partition, tried to install Xubuntu 18.04, fat-fingered the UEFI password dance, reinstalled Windows from the SSD’s recovery partition, and got this display after a while (clicky for more dots):

    Dell Optiplex - recovery disk stall
    Dell Optiplex – recovery disk stall

    After letting it stew in its own juices during supper, I forced it off (pushed the power button until it died), restarted, got through the UEFI dance, and it now seems All Good. I made recovery DVDs (remember DVDs?), both before and after the fumbled Xubuntu installation, but didn’t need them.

    I expect we’ll never boot Windows 8.1 again, but it’s there Just In Case.

  • Streamripper Setup

    The Intertubes occasionally clog up while streaming low-bit-rate audio, for no reason I can fathom, leading to discontent in the User Community when it affects quiet classical music played in the dead of night. Given that the stream from far-off Switzerland consists entirely of public-domain performances, I set up Raspbian Lite on a headless Raspberry Pi 1 Model B+ in a spot where it won’t be disturbed:

    Raspberry Pi 1 for streamripper
    Raspberry Pi 1 for streamripper

    Connecting to the Pi with a screen session, so as to allow disconnection & reconnection without anguish, I fired streamripper thusly:

    streamripper http://relay.publicdomainproject.org/classical.mp3.m3u -xs2 -o larger -u "MPlayer2" -m 30 -r -R 6 --with-id3v1
    

    The -r -R 6 options set up a relay stream on http://ripper.local:8000 for the benefit of my local streamers, so only one trickle of bits crosses the Atlantic.

    The total CPU load amounts to a percent or two, tops, so a single-core 700 MHz Pi has no trouble keeping up.

    Because streamripper slings bits in more-or-less real time, the local servers don’t get any track title data when they start up, so the OLED display doesn’t update immediately. This is less of a problem than you might think, as we’re generally not hanging on the display to find out exactly which Vivaldi bassoon concerto is playing.

    Given a suitable collection of tracks, I’ll set up an icecast server as the classical music “station” for the streamers, but that’s an adventure for another day. I also want to splice separate movements back into continuous symphonies, the way they’re suppose to be heard.

  • GIMP Menu Verbiage

    GIMP seems to set up its menu structure during installation, with sensible, if lengthy, hardcoded addresses and menu names for your remote (network) scanner based on its host and USB port location:

    (proc-def "xsane-net-3a-plex760-2e-local-3a-genesys-3a-libusb-3a-002-3a-002" 2
    … snippage …
    (menu-path "<Image>/File/Create/Acquire/XSane/net:plex760.local:genesys:libusb:002:002")
    

    Should you happen to plug the scanner into a different PC or USB port, perhaps while replacing a failed system, then you must change those hideous strings all by yourself.

    So, for example, plugging the aforementioned scanner into a randomly chosen USB port on a new-to-me Dell Optiplex 9020 showing up as plex9020.local on the network produces this identification string:

    [ed@shiitake ~]$ scanimage -L
    device `net:plex9020.local:genesys:libusb:003:003' is a Canon LiDE 120 flatbed scanner
    

    GIMP’s ~/.gimp-2.8/pluginrc file defines the device address:

    (proc-def "xscanimage-net-3a-plex9020-2e-local-3a-genesys-3a-libusb-3a-003-3a-003" 2
    

    The -3a- string seem to be an escape sequence for the colon symbol separating parts of the address. Why we need so many different escape sequence standards mmm escapes me at the moment.

    The menu-path string defines the text appearing in the GIMP UI, so you can use a somewhat more readable generic name:

    (menu-path "<Image>/File/Create/Acquire/xscanimage/Plex9020-scanner")
    

    The ~/.gimp-2.8/menurc file contains GIMP’s keyboard accelerators, which (apparently) must match the revised proc-def strings:

    ; (gtk_accel_path "<Actions>/plug-in/xscanimage-net-3a-plex9020-2e-local-3a-genesys-3a-libusb-3a-003-3a-003" "")
    

    A keyboard accelerator for the scanner wouldn’t save any appreciable amount of time or effort, so (I think) the semicolon marks it as Disabled in the UI.

    It is remarkably easy to make a one-character typo while doing this, particularly if you’re using sed to change All. The. Strings. at once.

    There is, AFAICT, no documentation, which almost certainly means I don’t know where to look.

  • LibreOffice 5.3+ vs. Adobe Type 1 Fonts

    LibreOffice from 5.3 onward (Xubuntu 18.04 uses LO 6.0) no longer supports Adobe Type 1 fonts, which comes as a surprise to those of us who actually bought fonts, back in the day, and have been using them ever since. Apparently, Windows dropped Type 1 font support some time ago.

    Based on some hints, I set up the Adobe Font Development Kit for OpenType. It’s a Python thing, preferably running in a virtual environment to avoid screwing up the rest of one’s system with bizarre dependencies. It seems one (“I”) must not update pip using pip after installing python-pip using apt-get; recovering from that mess was good for another hour of flailing.

    The default AFDKO installation spat out an error message about ufolib (I am not making this up) being at 2.1.1, instead of the required 2.3.1. In for a penny, in for a pound, I updated ADFKO with the “prerelease” option:

    pip install -U afdko --pre
    

    Which fetched ufolib 2.3.1, apparently from wherever Python keeps its prerelease stash. I have NFC what’s going on with any of this.

    An Adobe blog post on the AFDKO tx tool suggested it can convert Type 1 fonts to CFF (a.k.a. Adobe Type 2) fonts and some poking around suggested CFF also figures in OTF fonts.

    tx -cff -n -N -A awb_____.pfb
    --- Filename: awb_____.pfb
    --- FontName: ACaslon-Bold
    tx: --- awb_____.pfb
    tx: (cfw) unhinted
    tx: (cfw) unhinted
    tx: (cfw) unhinted
    tx: (cfw) unhinted
    tx: (cfw) unhinted
    tx: (cfw) There are 222 additional reports of 'unhinted'.
    

    The -A option replaces the bizarre Adobe 8.3 file names with actual font information:

    awrg____.pfb ⇒ ACaslon-Bold.cff
    awbi____.pfb ⇒ ACaslon-BoldItalic.cff
    awi_____.pfb ⇒ ACaslon-Italic.cff
    awrg____.pfb ⇒ ACaslon-Regular.cff
    awsb____.pfb ⇒ ACaslon-Semibold.cff
    awsbi___.pfb ⇒ ACaslon-SemiboldItalic.cff
    

    Regrettably, CFF files don’t actually work as fonts, at least as far as LibreOffice 6.0 (or whatever it uses as a font engine) is concerned.

    Although it’s possible to convert fonts locally with fontforge, doing it one-by-one is tedious and the learning curve for its Python scripting feature seems rather steep. I fired the most vital fonts at Convertio, an online converter running fontforge in the background, got a matching pile of OTF fonts, and installed them in /usr/share/fonts/custom/type1 to indicate their heritage.

    Whereupon LO rammed into a problem I’d had before. The solution this time required sorting the various Caslon and American Typewriter fonts into different “font families” and forcing the TTF names to match their new families. The difference between Medium and Regular seems to have Gone Away.

    I should just use Comic Sans and be done with it …

  • Fireball Island Figures

    A cousin asked if my 3D printer could replace some figures gone missing from their old Fireball Island game board, a classic apparently coming out in a new & improved version.

    Fortunately, solid models exist on Thingiverse:

    Fireball Island figure - Thingiverse 536867
    Fireball Island figure – Thingiverse 536867

    Unfortunately, the left arm requires support, which Slic3r supplies with great exuberance:

    Fireball Island figure - Slic3r support
    Fireball Island figure – Slic3r support

    The vast tower on the figure’s right side (our left) seemed completely unnecessary, not to mention I have no enthusiasm for the peril inherent in chopping away so much plastic, so I replaced it with a simple in-model pillar:

    Figure Support Mods
    Figure Support Mods

    The pillar leans from an adhesion-enhancing lily pad and ends one layer below the left hand, with all dimensions and angles chosen on the fly to make the answer come out right.

    Works like a champ:

    Fireball Island Figures - orange - on platform
    Fireball Island Figures – orange – on platform

    The dark band down the middle comes from the Pixel’s shutter.

    They emerged with some PETG hair, the removal of which I left as an end-user experience.

    I mailed a small box containing figures printed in my (limited!) palette of four colors, some spares Just In Case™, and a few QC rejects showing the necessity of lily pads.

    Game on!

    The OpenSCAD source code as a GitHub Gist:

    // Adding support under Fireball Island figure arm
    import("/mnt/bulkdata/Project Files/Thing-O-Matic/Fireball Island/Fireball Island figure – 100k.stl", convexity=5);
    translate([6.5,-4.0,0]) {
    intersection(){
    translate([-10/2,-10/2,0])
    cube([10,10,11.6],center=false);
    rotate([0,-5.0,0])
    rotate(180/6)
    cylinder(d=4.0,h=30,$fn=6,center=true);
    }
    translate([8/4,0,0])
    rotate(180/6)
    cylinder(d=8,h=0.2,$fn=6);
    }
  • Streaming Radio Player: I2C Display

    Although I2C on the Raspberry Pi fails with devices using clock stretching, cheap I2C OLED displays seem to work well enough to not generate any problems search-able with the obvious keywords:

    RPi I2C OLED
    RPi I2C OLED

    Given a picture of the header pinout, the wiring is trivially easy:

    RPi I2C OLED - RPi header detail
    RPi I2C OLED – RPi header detail

    Using yellow for the ground hurts a bit, but that’s what I get for peeling the SPI cable down to four wires. The pin directly adjacent to the green wire is also ground, should that be easier to reach.

    Tweaking the Luma driver to use I2C doesn’t require much:

    #from luma.core.interface.serial import spi
    from luma.core.interface.serial import i2c
    
    ... snippage ...
    
    # reduce SPI bus from default 8 MHz to (maybe) avoid OLED failure-to-start
    #serial = spi(device=0,port=0,bus_speed_hz=1000000)
    
    # use I2C bus to avoid SPI timing spec failure
    serial = i2c(port=1,address=(0x78 >> 1))     # PCB label = 0x78, low bit = R/W
    

    The OLED PCB lists the I2C address with the R/W bit

    And then It Just Works, with one gotcha. Although the Python program shuts itself and the system down, the wall wart continues to supply power and, because the I2C bus doesn’t include a Reset line, the OLED display doesn’t know the RPi has gone away. So you must issue a command to turn it off before shutting down:

    device.cleanup()        # ideally, switches to low-power mode
    rc = subp.call(['sudo','shutdown','-P','now'])
    

    Now, to discover what works … oddly … with these displays.