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…

Categories
Video

Custom sounds in macOS

Just add them to ~/Library/Sounds!

Categories
Arduino Projects

Bluetooth LE Page Turner

Added to the labs, a proof of concept page turning button: https://github.com/twstokes/labs/tree/master/pageturn

Categories
Projects Swift SwiftUI

Retro Menu Bar calculator

I’ve always wanted to play around with Menu Bar apps, so I decided to make an easy to access retro calculator in SwiftUI.

Categories
Technology

APC UPS data in Grafana

Grafana dashboard with APC UPS information

I don’t know why I didn’t do this long ago. With the Telegraf apcupsd plugin it’s trivial to add your APC UPS data to Grafana.

I did have to manually install the latest binary from Telegraf’s site instead of the version that comes with Ubuntu, but that wasn’t a problem at all.

Categories
Arduino Coding Golang Raspberry Pi

A Tree Divided

See the GitHub Repo!

The tree reacting to Clemson scoring

Idea

Every year Clemson plays USC for their state rivalry game. I pull for Clemson and my wife pulls for USC, so we’re what you call a “House Divided”. Since this game takes place on or after Thanksgiving, it’s a great time to incorporate the LED Christmas tree and troll my spouse!

The tree works like this:

  • When a team scores, it plays their fight song and lights up with their primary and secondary colors.
  • The lights on the tree are distributed by the ratio of points. When it’s tied, they each get 50% of the lights. If Team A has 2/3 more points, they get 2/3 more lights in their color.
  • The ring under the star at the top of the tree is the color of winning team. If they’re tied, it’s split.
  • When the game is finished, the tree is the color of the team that won.

Software

I used Golang for the software since I primarily write code in another language and want to get better at it. It makes use of various interfaces to aid in testing and abstration:

  • The Fetcher interface gets the latest game state from a data source
  • The Player interface plays audio at the given path
  • The Illuminator interface controls a light source (in this case the LED tree)

The source code for a local data fetcher is included in this repo only. The remote fetcher I built may or may not have used an API meant for this sort of consumption. It simply fetched from a remote data source, unmarshalled a JSON data structure, and supplied what the Fetcher interface needed.

The code runs on a Raspberry Pi and communicates with the MCU via serial. An iHome IBT63 speaker is used to play audio from the Raspberry Pi. I didn’t use the Bluetooth connection and instead used the shared power and audio connector, plugging one end into the RPi’s stereo jack and the other into the USB port.

I cross-compiled from my Mac using the rpi.sh script in the executable’s directory.

Firmware

  • Uploaded using PlatformIO
  • Runs on a NodeMCU ESP8266
Categories
Swift

Diving into SwiftUI

Categories
Coding Nerd Sniped Processing

Nerd Sniped

When I saw https://cacheflowe.com/art/digital/deepflat I was “nerd sniped”. I had to figure out how to do it. A bunch of coffees later…

What’s better? I have source code!

https://github.com/twstokes/labs/tree/master/viz/depth_grid

Categories
Clemson Projects

Bringing Clemson’s TigerOne card to the iPhone and Watch

Over the past few months our team at Clemson worked with TigerOne, our card services department, and Apple to successfully bring mobile ID provisioning to campus. We were the first school to integrate this functionality into our own app, my.Clemson, and that integration correlated with the success we saw on launch day – around 4500 students, faculty, and staff were able to add their TigerOne Mobile ID to their phone and Watch. These are record numbers to date. 🎉

Adobe XD Process

There were a lot of unknowns when we started the project, one being that we’d never integrated Duo (the University’s two factor system) with a native client. It made sense to reuse the embedded web version so we didn’t have to reinvent the wheel, but we weren’t sure of how to combine this with our current method of authentication via SwiftECP, whose goal is to avoid the browser!

We ended up with a pretty slick solution. After configuring the IdP to allow a Duo flow from a client that authenticated ECP, it worked like this:

  1. Authenticate via ECP
  2. Try to load the resource that’s protected by Shibboleth + MFA
  3. If the resource needs MFA authentication, show it in a WKWebView and use the navigationDelegate methods to determine when the user has responded
  4. Finally, inject the cookies provided by the Duo flow back into the native client so that it could be used for subsequent requests with a URLSession
Duo flow in action. Please ignore my super old ID picture.