Fixing my desk

This is probably a unique problem.

I have a VertDesk, and a lot of USB devices. Rather than running a bundle of USB extension cables through the cable channel from my computer on the floor to the cable management box on the desk, I run a single USB 3.0 cable to a USB 3.0 hub in the cable management box.

I also have a Stream Deck. I do have a Twitch account, but I do not stream regularly. Instead, the Stream Deck has buttons for opening and closing the shades, turning the lights on and off, switching between headphones and speakers, moving the desk up and down, controlling Spotify, etc.

This USB hub scheme has caused me problems, and I think this is the third USB hub that I have purchased for this purpose. This hub has enough ports, does not cause problems booting the computer, and does not disconnect from the computer when slightly jostled, but somehow the Stream Deck does not like it: when my computer goes to sleep, the Stream Deck goes into standby, but when my computer wakes up, the Stream Deck does not wake up with it, and I need to disconnect and reconnect the USB cable to get it back.

I had been dealing with this for months, but since working from home I have been needing to do this every morning, and this weekend I finally got around to fixing it by making a USB device which sits in the middle of a USB cable and interrupts the power line of that cable.

This is the first project like this which I actually bought a case to put it in, and the case is quite small, so I downloaded a copy of KiCad and made this compact design.

Simple circuit using a Trinket M0 and a transistor to control a relay between two USB VBUS lines

Protoboard PCB layout for using a Trinket M0 and a transistor to control a relay between two USB VBUS lines

Unfortunately, there wasn't space to use the USB header pins shown in the layout, so I just soldered the connected wires directly to each other and the VBUS wires directly to the PCB. All the USB pins not shown connected in the layout should be connected to the corresponding pin on the other USB header.

I don't have any pictures of the terrible soldering or the huge globs of hot glue used to protect the USB wires soldered to the board, but assembled the circuit board looks something like this.

Protoboard with Trinket M0, relay, transistor, resistor placed, sitting on top of the box it needs to fit in

(I took this picture before adjusting the resistor position as seen in the KiCad layout to reduce the amount of contacts I would need to bridge on the PCB)

The software side of this may have taken more time than the hardware side, despite me having much more experience in software.

The Trinket M0 is designed to work with CircuitPython, which is great for quick stuff like this, but this is something that needs to be connected to the computer to work and I don't want the CircuitPython programming drive always visible on my computer, and I don't think CircuitPython exposes the kind of USB interfaces I wanted to use.

The plan was to use Rust using the trinket_m0 and usb-device crates to make a HID device I could control from a companion program on the PC, but when I put my code together and loaded it onto the Trinket M0, Windows told me that the device had malfunctioned. I could get the device to detect, but only when directly connecting to a motherboard USB port, and only sometimes.

I switched to using Arduino IDE. Receiving simple commands over USB without using a USB serial port does not seem common, and it was very difficult to find documentation on the correct calls to make for configuring the USB. By searching through the source code for TinyUSB and adafruit/ArduinoCore-samd I stumbled upon some interesting callbacks, including tud_resume_cb. It turns out I can detect when the Stream Deck should be resuming without any program on the PC side.

#define RELAY 2

void setup()
{
  pinMode(RELAY, OUTPUT);
  digitalWrite(RELAY, LOW);
  
  USBDevice.setManufacturerDescriptor("mdonoughe");
  USBDevice.setProductDescriptor("usb usb");
}

void loop()
{
  __WFI();
}

void tud_resume_cb(void)
{
  digitalWrite(RELAY, HIGH);
  delay(1000);
  digitalWrite(RELAY, LOW);
}

If you use this code, make sure to set USB Stack to TinyUSB in the tools menu.

USB USB device connected under a desk

This works perfectly for me. As the computer wakes from sleep, the relay clicks twice, and then the Stream Deck wakes up. I can close the panel on the wire management box so wires stop falling out and dangling down.