open sesame


The Idea

I have a spare ESP32C6 laying around and a garage door opener with two spare pins for opening and closing the garage door. In my last post I wrote:

As im hosting my homeassistant on my Raspberry Pi 3, I cant flash the ZigBee firmware, because my RP3 will OOM kill the process. Somewhere in the future, I´ll change my hosting over to my small EliteDesk G3.

Luckily I didnt have to change my hosting over to my EliteDesk G3 because I found another way to circumvent the RAM limitation during the flashing process.

Prerequisite

Im using a Windows maschine as my daily driver. Some of the edits/changes are easier on linux, thats why I have a WSL2 Ubuntu installation. You can skip the next step when you are already on a UNIX system.

WSL2

  1. open Powershell as an admin
  2. run wsl --install (maybe windows prompts you to activate some features)
  3. when prompted -> restart you PC
  4. get you WSL2 installation updated: sudo apt update && sudo apt upgrade -y

ESPHome install

  1. install Python and dependecies: sudo apt install python3-pip python3-venv libvenv-dev -y
  2. create a temporary virtual env.: python3 -m venv esphome_venv
  3. and activate the workspace: source esphome_venv/bin/activate
  4. install ESPHome: pip install esphome

USB passthrough

When pluggin in you ESP32 into your host maschine (e.g my windows maschine) WSL2 can´t see new hardware by default. So we have to bridge the connection

On your Windows PC:

  1. open Powershell as an admin
  2. install usbipd-win: winget install --interactive usbip-win
  3. connect your ESP32 via USB
  4. list devices to find its ID: usbipd list

example output:

PS C:\Windows\system32> usbipd list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
1-3    0c45:652f  USB-Eingabegerät                                              Not shared
1-4    9886:002c  Astro A50 Voice, Astro A50 Game, USB-Eingabegerät             Not shared
1-5    046d:c07d  USB-Eingabegerät                                              Not shared
1-8    256f:c635  USB-Eingabegerät                                              Not shared
1-11   0000:0002  Unbekanntes USB-Gerät (Fehler beim Anfordern einer Geräte...  Not shared
1-12   2341:8037  Arduino Micro (COM3), USB-Eingabegerät                        Not shared
1-13   1e71:2017  USB-Eingabegerät                                              Not shared
1-14   8087:0032  Intel(R) Wireless Bluetooth(R)                                Not shared
3-1    303a:1001  Serielles USB-Gerät (COM6), USB JTAG/serial debug unit        Not shared

for me its BUSID 3-1. note the STATE: "Not shared". To share the USB device with our WSL install we have to bind to it, like so: usbipd bind --busid BUS_ID where BUS_ID is 3-1 for me. usbipd bind --busid 3-1

example output:

PS C:\Windows\system32> usbipd list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
1-3    0c45:652f  USB-Eingabegerät                                              Not shared
1-4    9886:002c  Astro A50 Voice, Astro A50 Game, USB-Eingabegerät             Not shared
1-5    046d:c07d  USB-Eingabegerät                                              Not shared
1-8    256f:c635  USB-Eingabegerät                                              Not shared
1-11   0000:0002  Unbekanntes USB-Gerät (Fehler beim Anfordern einer Geräte...  Not shared
1-12   2341:8037  Arduino Micro (COM3), USB-Eingabegerät                        Not shared
1-13   1e71:2017  USB-Eingabegerät                                              Not shared
1-14   8087:0032  Intel(R) Wireless Bluetooth(R)                                Not shared
3-1    303a:1001  Serielles USB-Gerät (COM6), USB JTAG/serial debug unit        Shared

And attach it to WSL: usbipd attach --busid BUS_ID --distribution Ubuntu

WSL2 Linux Terminal

Check if your bridged hardware appears with the following command: ls /dev/tty*

example output:

ikameo@Win11:~$ ls /dev/tty*
/dev/tty    /dev/tty16  /dev/tty24  /dev/tty32  /dev/tty40  /dev/tty49  /dev/tty57  /dev/tty8     /dev/ttyS6
/dev/tty0   /dev/tty17  /dev/tty25  /dev/tty33  /dev/tty41  /dev/tty5   /dev/tty58  /dev/tty9     /dev/ttyS7
/dev/tty1   /dev/tty18  /dev/tty26  /dev/tty34  /dev/tty42  /dev/tty50  /dev/tty59  /dev/ttyACM0
/dev/tty10  /dev/tty19  /dev/tty27  /dev/tty35  /dev/tty43  /dev/tty51  /dev/tty6   /dev/ttyS0
/dev/tty11  /dev/tty2   /dev/tty28  /dev/tty36  /dev/tty44  /dev/tty52  /dev/tty60  /dev/ttyS1
/dev/tty12  /dev/tty20  /dev/tty29  /dev/tty37  /dev/tty45  /dev/tty53  /dev/tty61  /dev/ttyS2
/dev/tty13  /dev/tty21  /dev/tty3   /dev/tty38  /dev/tty46  /dev/tty54  /dev/tty62  /dev/ttyS3
/dev/tty14  /dev/tty22  /dev/tty30  /dev/tty39  /dev/tty47  /dev/tty55  /dev/tty63  /dev/ttyS4
/dev/tty15  /dev/tty23  /dev/tty31  /dev/tty4   /dev/tty48  /dev/tty56  /dev/tty7   /dev/ttyS5

my ESP32 appeard as /dev/ttyACM0

YAML file

Now you have to make a choice: create the YAML file in your WSL Linux installation, or create it under Windows and move it to WSL. I explain both.

Linux:

touch garage_door.yaml you can choose any file name
nano garage_door.yaml
Add your YAML code. You can find my a bit further down.

Windows:

  • create a text file, add the YAML code, and change the extension of the file to .yaml
  • move the file to you WSL directory. To do so: type this in your explorer address bar and drag and drop your file \\wsl$\Ubuntu\home\yourusername\

example YAML file:

esphome:
  name: esp32_c6_button

esp32:
  board: esp32-c6-devkitc-1
  variant: esp32c6
  framework:
    type: esp-idf

wifi:
  ssid: "WIFI NAME"
  password: "PASSWORD"

# Enable logging
logger:

api:
  encryption:
    key: "GENERATE ONE WITH THE CODE BELOW"

ota:
  platform: esphome
  password: "YOUR_OTA_PASSWORD"

button:
  - platform: template
    name: "Garagentor"
    icon: "mdi:garage"
    on_press:
      - switch.turn_on: relay_1
      - delay: 2s
      - switch.turn_off: relay_1

switch:
  - platform: gpio
    id: relay_1
    pin: GPIO18
    inverted: false
    internal: true

Encryption key:

openssl rand -base64 32

Compile and Flashing

With your ESP32 attached and the YAML ready, run this in your WSL2 terminal:

esphome config garage_door.yaml

esphome run garage_door.yaml

When done you should be able to see and use your programmed ESP32 in homeassistant ESP_1 ESP_2

ESP32C6 as a remote


The final product

MQTT_16

MQTT_12 MQTT_10 MQTT_9 MQTT_8 MQTT_11

The programming

For now the remote works over WIFI. As im hosting my homeassistant on my Raspberry Pi 3, I cant flash the ZigBee firmware, because my RP3 will OOM kill the process. Somewhere in the future, I´ll change my hosting over to my small EliteDesk G3.

#include <WiFi.h>
#include <PubSubClient.h>

// WiFi settings
const char* ssid = "NETWORKNAME";
const char* password = "WIFIPASSWORD";

// MQTT settings
const char* mqtt_server = "192.168.178.220";
const int mqtt_port = 1883;
const char* mqtt_user = "ikameo";
const char* mqtt_password = "SECRETMQTTPASSWORD";
const char* mqtt_topic = "home/esp32/button";

// Button settings
const int buttonPins[] = {1, 2, 3, 4, 6, 7, 8, 14, 15}; // Updated GPIO pins
const int numButtons = 9;
int buttonStates[9] = {0};
int lastButtonStates[9] = {0};

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(9600);
  Serial.println("ESP32 started!");

  // Initialize all button pins
  for (int i = 0; i < numButtons; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
  }

  // Connect to WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");

  // Set MQTT server and port
  client.setServer(mqtt_server, mqtt_port);
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" Retrying in 5 seconds...");
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // Check each button
  for (int i = 0; i < numButtons; i++) {
    buttonStates[i] = digitalRead(buttonPins[i]);

    // If the button is pressed (LOW because of pull-up)
    if (buttonStates[i] == LOW && lastButtonStates[i] == HIGH) {
      char message[50];
      snprintf(message, sizeof(message), "Button %d pressed", i + 1);
      client.publish(mqtt_topic, message);
      Serial.print("Button ");
      Serial.print(i + 1);
      Serial.println(" pressed — message sent!");
      delay(50); // Debounce delay
    }
    lastButtonStates[i] = buttonStates[i];
  }
}

The CAD

MQTT_6

The MQTT configuration

MQTT_1 MQTT_2 MQTT_3 MQTT_4

Fig.1: Change the payload to the correct button. Button 1 pressed, Button 2 pressed, Button 3 pressed etc.
Fig. 2 & 3: Via the configuration cog you are able to see which is pressed on your remote.
Fig. 4: Broker and Port and Username is the default. The Password is the same as your admin login.

The function

Parts used:

  • Female USB-C port
  • PW Switch
  • ESP32C6
  • TPS63802 buck converter
  • TP4056 lithium charger
  • 18650 Battery

    MQTT_15

    MQTT_13

security? yes, please!


The Problem

Since last year, I´m hosting a FoundryVTT instance for our regular DnD sessions. In FoundryVTT you have the option to setup usernames and passwords for each member individual.

foundry_login

As I know my way around the IT world relatively well, I knew the passwords would be realtiv easy to guess, if the player have to choose them. So I set a website wide password with nginx and a .htpasswd file.

The Setup

Locate your nginx configuration file under /etc/nginx/sites-available/domain.com and add a new block for location /join

location /join {
    proxy_pass http://127.0.0.1:30000;

        #Defines the HTTP protocol version for proxying
        #by default it it set to 1.0.
        #For Websockets and keepalive connections you need to use the version 1.1
        proxy_http_version  1.1;

        #Sets conditions under which the response will not be taken from a cache.
        proxy_cache_bypass  $http_upgrade;

        #These header fields are required if your application is using Websockets
        proxy_set_header Upgrade $http_upgrade;

        #These header fields are required if your application is using Websockets
        proxy_set_header Connection "upgrade";

        #The $host variable in the following order of precedence contains:
        #hostname from the request line, or hostname from the Host request header field
        #or the server name matching a request.
        proxy_set_header Host $host;

        #Forwards the real visitor remote IP address to the proxied server
        proxy_set_header X-Real-IP $remote_addr;

        #A list containing the IP addresses of every server the client has been proxied through
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        #When used inside an HTTPS server block, each HTTP response from the proxied server is rewritten to HTTPS.
        proxy_set_header X-Forwarded-Proto $scheme;

        #Defines the original host requested by the client.
        proxy_set_header X-Forwarded-Host $host;

        #Defines the original port requested by the client.
        proxy_set_header X-Forwarded-Port $server_port;

        ### CHANGE ME ###

        auth_basic           "Member Area";
        auth_basic_user_file /home/YOUR_USERNAME/foundryvtt_server/htpasswd/.htpasswd;

set the path under auth_basic_user_file /home/YOUR_USERNAME/foundryvtt_server/htpasswd/.htpasswd; to your own path. And generate a .htpasswd file with sudo htpasswd -c $HOME/foundryvtt_server/htpasswd/.htpasswd USERNAME where USERNAME can be anything you want. Make sure you generate the file in the exact location which is set in the nginx config file.

Restart nginx $ sudo systemctl restart nginx and open your website.

The Result
The website prompts you to authenticate

foundry_login2

The Troubleshooting
If you are getting an Error 500, or Error 403 you should check your nginx error.log
$ sudo tail -f /var/log/nginx/error.log

The most common errors are either permission denied, or nginx is unable to find the .htpasswd file. There are sereval option in fixing your errors:

$ sudo chmod 644 /home/USERNAME/foundryvtt_server/htpasswd/.htpasswd
$ sudo chmod 755 /home/USERNAME
$ sudo chmod 755 /home/USERNAME/foundryvtt_server
$ sudo chmod 755 /home/USERNAME/foundryvtt_server/htpasswd
$ sudo chown -R www-data:www-data /home/USERNAME/foundryvtt_server/htpasswd/.htpasswd

As a server admin you should know what those commands do, how to use them and which danger could come with type those into you CLI.

Enjoy your newly protected FoundyVTT instance.

Designing in photoshop is aweful.


Bill of Materials

  1. 3D Printer
  2. Black & White PLA/PETG
  3. Hairdryer
  4. Heat-shrinking clear pouches

Finished product

Icognito_7


Planning phase

There wasn't much planning. As you can see from the finished product. The print area could have been a bit larger. But anyway, I'm happy with the end result, and so is my brother - because it was his birthday present.


CAD

Originally I wanted to create a few selected Pokemon, but I realized very quickly that organic shapes are not my strength and I only saw one way to successfully complete my project - Unown.

CAD CAD2


Packaging

I printed a total of 120 Unown. 60 models and 60 TGC-ish cards. This meant that I had to print, cut out, glue, heatshrink and pack 60 packages.

Icognito_1 Icognito_2 Icognito_3 Icognito_4 Icognito_6

Im pretty happy with the end result, despite the small blemishes.

Finally replacing the removable bikelight and mounting a real light. + Update from "Bike Carrier v2"


Bill of Materials

  1. 1,5mm² Speakercable
  2. USB 2.0 port for soldering on breadboard
  3. breadboard
  4. shrinking tube
  5. male and female wire connectors
  6. soldering iron

Finished product

As it´s the unwritten law of the internet to show the finished product first, here it is:

Bikelight_15


Planning phase

The whole idea about this project is to use my bike battery to power the lights. I wrote the FISCHER support a long time ago with the inquiry if the sell lights that are compatible with my bike. Unfortunately they don´t.
As I have an USB port on my bikes controller, my plan was to buy some lights which intended use was receiving current from a dynamo. The problem with that? Bike dynamos generate 6V @ 3 watt. And my USB port generates 5V @ 1 amp. I got my bench powersupply and hooked up the lights with 5V @ 1 amp - and they worked!


Manufacturing

With this new knowledge, my plan was to chop up an old USB cable, clip the data cables and use the two remaining wires.

Bikelight_4

But the wires were way too thin to solder them, so I built my own USB cable with breadboard, 1.5mm² speaker cable an USB port and a soldering iron. Which worked on the first try.

Bikelight_1 Bikelight_2 Bikelight_3 Bikelight_6 Bikelight_7 Bikelight_8

As I have a 3D printer, a housing for the connector had to be made.
Not shown here: I filled in all the cavities with hot glue to protect the electronics from the weather.

USB_Shell_1 USB_Shell_2 Bikelight_10 Bikelight_11 Bikelight_14

Now that the difficult part was done, all that had to be done was printing the remaining mounts and wire everything to the bike.

FrontlightClamp BacklightClamp Bikelight_9 Bikelight_12 Bikelight_13


Update from Bike Carrier v2

After almost 2000 km there a no visible damages or deformation on the 3D printed PETG parts.