Categories
Projects Swift SwiftUI

Playground support for SwiftGFXWrapper

For even easier prototyping, I’ve added Swift Playground support to my SwiftGFXWrapper project!

Categories
Coding Projects Scripts

Scraping an Arris cable modem status page

Screenshot of the modem status page with arrows pointing to a screenshot of the data's final form in Grafana.
This project’s purpose is to start with a status page and end with Grafana graphs and alerts.

It felt good to complete this project that’s been on my list for quite some time. The main goal was to scrape the values from my modem’s status page and pipe them into InfluxDB, which feeds Grafana. Not only could I look at data trends, but I could receive alerts if certain values exceeded an acceptable threshold.

Overall this is a straightforward process:

  1. Pull in the HTML from the status page (which happens to not need any authentication, making it even easier)
  2. Parse the tables we care about (Downstream and Upstream) using XPaths
  3. Munge the data into something suitable for InfluxDB
  4. Insert the data into InfluxDB
  5. Query the InfluxDB data from Grafana

I knew I wanted to use Python for the project, so I first looked into Scrapy. After wrapping my mind around it (somewhat) I gave it a go and actually had a working solution… but it felt way over engineered and at times inflexible for what I wanted. I threw 90% of that solution away and went with a simpler script.

What I landed on was something that’s custom and lightweight, but extendable in case someone has a different status page or wants to use an alternative to InfluxDB.

Grafana screenshot showing that a fluctuation in downstream power around 10:00 a.m. caused the "Correcteds" values to spike.
I’ve had it running for a day and I’m already seeing interesting data!

See the repo on GitHub!

Categories
Projects Swift SwiftUI

Adafruit GFX -> Swift

Another fun project from a few weekends ago: Wrapping the Adafruit GFX in Objective C and bridging to Swift for some neat 8-bit effects!

The most interesting component of this project so far has been building a Swift closure that is executed in C as a const void *. This was new territory for me and a lot of fun to figure out!

This (work in progress) repo has been open sourced!

Categories
Arduino Projects

nRF24L01+ and SLIP

Currently a work in progress, I’ve nerd sniped myself to get TCP/IP working over SLIP with a pair of Arduinos equipped with nRF24L01+ radios.

Categories
SwiftUI

SwiftUI for rapid prototypes

SwiftUI is turning into my go-to for rapid prototyping. Here’s an example of a light / dark mode switcher that I think would work well on web…

See the code here!

Categories
Raspberry Pi

Booting the Raspberry Pi from an SSD

It’s been possible to boot a Raspberry Pi from an SSD for a while now, but the process didn’t seem to have the full blessing from the Raspberry Pi Foundation. This changed recently when they announced their beta firmware to officially support it.

It was pretty painless to get it working, so here’s how I did it. Some notes up front:

  1. Based on research of past success stories, I went with this adapter – the ELUTENG SATA to USB 3.
    ⚠️ Beware! I’m seeing in Amazon reviews that this adapter may have moved to a non-compatible chip! Here’s a great resource for compatible adapters.
  2. I used a Raspberry Pi 4 4GB
  3. The SSD I used was a spare Samsung EVO 250GB I had from a retired machine
  4. I connected the SSD directly to the USB 3 port
  5. I went with the headless version of Raspberry Pi OS (formerly known as Raspbian), but this should work with any of them
  6. Since my RPi was already running off an SD card I wanted to be able to plug in the USB drive and do this all from my couch. I only had to get up once, and that was to take out the SD card on the final reboot!

Steps

Most of these instructions all came from here.

Don’t blindly follow these instructions unless this post is only a few days old! Things are bound to change – this is beta software. Do it at your own risk. If a step has something in [brackets], you need to figure out your particular value.

  1. sudo apt update
  2. sudo apt full-upgrade
  3. sudo vim /etc/default/rpi-eeprom-update
    1. Change to: FIRMWARE_RELEASE_STATUS="beta"
  4. sudo rpi-eeprom-update -d -f /lib/firmware/raspberrypi/bootloader/beta/pieeprom-2020-05-15.bin
  5. On the RPi, download the Raspberry Pi OS image and copy it to the SSD. Make sure the SSD isn’t mounted!
    1. wget https://downloads.raspberrypi.org/raspios_lite_armhf_latest
    2. shasum -a 256 raspios_lite_armhf_latest (verify the SHA)
    3. unzip raspios_lite_armhf_latest
    4. sudo dd bs=4M if=2020-05-27-raspios-buster-lite-armhf.img of=/dev/[SSD DEV NAME] conv=fsync
  6. sudo rpi-update
  7. Reboot, and hopefully the SSD will automount. The RPi isn’t expected to boot from it yet, but should treat it like an external drive.
  8. sudo cp /boot/*.elf /media/[SSD mount]/boot
  9. sudo cp /boot/*.dat /media/[SSD mount]/boot
  10. Because I went with the headless install, I wanted to be able to SSH into it
    1. sudo touch /media/[SSD mount]/boot/ssh
  11. Power off the RPi
  12. Remove the SD card
  13. Power up
  14. Hopefully you’re booting off the SSD!

Performance

SD Reads: 42MB/sec
SD Writes: 22 MB/sec

SSD Reads: 314 MB/sec (7.4x)
SSD Writes: 212 MB/sec (9.6x)

Categories
Tutorials

macOS CPU, GPU, and fans in Grafana

Graph all the things!

I know I talk about Grafana way too much, but it’s such a cool tool. Installing Telegraf on Linux gets me about 80% of what I want, but on macOS (via Homebrew) it’s lacking even more.

Note: I use InfluxDB as my Telegraf output.

The Telegraf part

To get values for my Mac’s CPU temperature, GPU temperature, and fan speed, here’s what I did:

  1. Install iStats: https://github.com/Chris911/iStats
  2. Add the following executable inputs to Telegraf’s config in /usr/local/etc/telegraf.conf
# CPU temperature from iStats - https://github.com/Chris911/iStats
# Note - this would need to be adjusted for multiple CPUs
# Key was discovered as TC0P
[[inputs.exec]]
commands = ["/usr/local/bin/istats cpu --value-only"]
timeout = "5s"
name_override = "cpu_temperature_mac"
data_format = "value"
data_type = "float"

# GPU temperature from iStats - https://github.com/Chris911/iStats
# Note - this would need to be adjusted for multiple GPUs
# Key was discovered as TCGc
[[inputs.exec]]
commands = ["/usr/local/bin/istats scan TCGc --value-only"]
timeout = "5s"
name_override = "gpu_temperature_mac"
data_format = "value"
data_type = "float"

# Fan speed from iStats - https://github.com/Chris911/iStats
# Note - this would need to be adjusted for multiple fans
[[inputs.exec]]
commands = ["/usr/local/bin/istats fan speed --value-only"]
timeout = "5s"
name_override = "fan_speed"
data_format = "value"
data_type = "integer"

The Grafana part

CPU and GPU temperature queries in Grafana
Fan query in Grafana

Some caveats to this approach:

  • This was on a 2019 i9 iMac with a single processor, GPU, and fan and won’t automatically work for all machines. You’d need to run iStats to see what all keys and values appear for your particular Mac.
  • It would be better if the CPU and GPU stats all played nice with the same measurement names Linux uses so we wouldn’t have to have multiple Grafana queries. I may do this in the future, but right now this was easy to implement with my RPi, PC, Mac, and Linux boxes.
Categories
Commodore 64 Projects Raspberry Pi

Pi 1541 – it works!

After some initial struggles, we’re good to go!

Categories
Arduino Gadgets iOS Projects

iPad Zoom mute button

At work we’re primarily using Zoom for meetings while we’re in remote mode. Due to the recent problems found in their desktop software, I run it only on my iPad to provide a little more security (thanks to iPadOS’ sandboxed environment), plus the front facing camera on my iPad Pro is superior to my iMac and MacBook Pro’s.

The first issue I found with this setup was that I wanted to get the iPad into a position more perpendicular like a web cam, rather than the angled up shot below my face. I don’t think anyone wants to look up my nose unless I’m on a telehealth call, so I ordered this flexible stand for about $25 from Amazon and got it mounted:

Trying to bend this thing will give you a workout.

So far so good, until my first meeting. I wanted to follow conference call etiquette by muting myself when I wasn’t speaking, but it was a pain to reach and manually tap the mute button every time. Plus, although the flexible arm is super strong, it’s still going to wobble wildly if you touch the iPad and your video is going to show that.

Was there a way I could toggle muting myself without touching the iPad? After a quick Google search, the answer was YES!

The attached keyboard (Smart Keyboard Folio, Magic Keyboard) didn’t make any sense in this case, but a Bluetooth keyboard would be perfect!

Logical answer

The logical answer is to connect up a Bluetooth keyboard and hit Command + Shift + A when you want to toggle muting your mic, and you’re done. That’s it.

I’m not totally logical

Of course, the route I chose was different. I have enough keyboards on my desk, I really just want one button to do one thing.

Recently, I rigged a button to turn pages in the Books app over Bluetooth. We should be able to do the same thing here, right?

Figuring out the codes

The objective is clear: When we press a button, we send “command+shift+a” to the iPad.

Using Adafruit’s HID codes, we find:

  • Left shift == 0x02
  • a == 0x04

But what’s the “Command” button? Is that the “meta” or “GUI” key? If so, is it a modifier or just a regular key?

Lots o’ troubleshooting

I spent a lot of time troubleshooting over and over:

Compile a new sequence of commands, upload new firmware to the microcontroller, re-pair with the iPad, open Zoom and try to mute: No go.

From what I could tell, “shift + a” were working, but “shift + command + a” wasn’t. It wasn’t until I almost gave up that I had a breakthrough:

Let’s try remapping another key to “command” and see if we can press that key. How about Caps Lock?

It worked.

In my code I wrote:

// gold
ble.println("AT+BleKeyboardCode=02-00-39-00-00-00-00"); // shift + caps lock
ble.println("AT+BleKeyboardCode=02-00-04-00-00-00-00"); // shift + a
ble.println("AT+BleKeyboardCode=00-00");

This is as if the user pressed “shift + caps lock”, and then “shift + a” at the same time, then released. Now the Zoom app was getting the proper command from a Bluetooth “keyboard”.

Hardware

This setup is virtually identical to what I have here, it’s just a different button.

The USB cable coming in is only for power, and if I had the Adafruit Feather version on hand I would’ve used that with a battery so the box would be totally wireless.

The button is no more complicated than this tutorial.

GitHub: https://github.com/twstokes/labs/tree/master/zoommuter

Improvements

A better version of this would be:

  • A button to also toggle video
  • Using the Adafruit Feather BLE version with a battery so the box would be entirely wireless
Categories
Commodore 64 Projects Raspberry Pi

Pi1541

The Commodore 64 in all its glory!

After the fun and nostalgic experience of pulling our C64s out of storage, I wondered how someone could add new software in this day and age. It didn’t take me long to find Pi1541, which is:

a real-time, cycle exact, Commodore 1541 disk drive emulator that can run on a Raspberry Pi 3B, 3B+ or 3A+

https://cbm-pi1541.firebaseapp.com/

All I needed to do was provide an RPi 3, load some stuff to an SD card, make a cable, and I would magically have an emulated 1541!

Making a cable

I bought a high quality DIN 6 pin cable, some Dupont headers, and a crimping tool from Amazon to get it built. This guide (also linked from the Pi1541 page) set me on the right track.

Crimped and ready to go. This was only my THIRD time cutting them all off and starting over.
Header attached. I chose to order the cables by pin order – 1 through 6.
Assembled, wired up, and ready to go… so I thought.

The process to construct the cable was straightforward after teaching myself how in the world one crimps these tiny Dupont connectors. The trick is to line up and fasten the wire with some needle-nose pliers first, then carefully insert the connector in your crimp tool so you don’t destroy the “box end” where the male connector is received.

Connecting things up

I followed the “Option A” on the Pi1541 site for wiring since I wouldn’t be including any other peripherals. An important piece of this schematic is the bi-directional logic level converter. The RPi speaks 3.3V logic while the C64 speaks 5V.

I loaded up the SD card, connected everything, fired up the C64, and plugged in the RPi. Everything worked perfectly, right? Nope.

Trouble

At first the RPi didn’t boot at all – I had just a solid red light, so it was time to troubleshoot:

  • Did I fry something on the Pi?
  • Was the SD card bad?
  • Did I load the software incorrectly to the card?

Good news: The Pi wasn’t dead. I confirmed this by booting from a spare SD card that had Raspbian loaded to it. Whew.

I then found the Pi would boot with the Pi1541 firmware, but only if the cables on the header pins were disconnected.

The culprit: The cheapo “bi-directional logic converter” I had lying around didn’t work at all like I assumed it did. Instead of supplying 3.3V and 5V to it (like it properly should), it only wants 5V. Turns out I was sending 3.3V to an output. Ouch.

Now we had a booting Pi with everything connected up, but still no disk drive:

The emulated device can’t be found by the C64.

Now we wait

After much troubleshooting I’ve come to the conclusion that I’ll just need to use a proper logic level shifter before proceeding. I’ve ordered some of these and will have to wait patiently as their expected arrival is the end of the month…