Tag Archives: hardware

ESPHome HA notification helper

A while ago I made a little notify thingy using an LCD display, LED, button and a buzzer.

Some friends of mine made one also, and today I was talking to a new guy.
I could not find this project on my site .. again .. so I’ll post it now.

Some things it does right now

  • Front door opens
  • Door bell rings (because my lab is in the garden)
  • Girlfriend is 10Km away, so let’s start cooking
  • Garage door opens (friend of mine uses this to know when kids arrive)
  • More .. because it’s very easy to customize in Home Assistant

Doorbell pressed: Led starts blinking, backlight LCD enabled, text displayed on LCD, Buzzer sounds (or plays a RTTTL ringtone)
LCD backlight on and buzzer beep until acknowledge button pressed.

Heating for brewing: temperature on display, led on when temperature reached. Press acknowledge to start timer.

….. etc

CODE:

esphome:
  name: lcdalarm
  friendly_name: LCDAlarm
  on_boot:
    priority: -100.0
    then:
      - lambda: 'id(lcddisplay).no_backlight();'

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "OOpDzMu0VvfWAnJXXXXXXXXXXXXXXXXXXXXXXXXKNDa74OJ1Cc="

ota:
   - platform: esphome
     password: "627992017XXXXXXXXXXXXXXX738c2a3"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Lcdalarm Fallback Hotspot"
    password: "z9dZXXXXXXXX7a"

captive_portal:
web_server:
  port: 80

i2c:
  sda: D2
  scl: D1
  scan: true
  frequency: 400kHz

light:
  - platform: binary
    name: "Status LED"
    output: light_output
    icon: "mdi:led-on"
    id: led


binary_sensor:
  - platform: gpio
    pin:
      inverted: true
      number: D5
      mode:
        input: true
        pullup: true
    name: "LCD Button"

text_sensor:
  # Create text input helper in homeassistant and enter id below
  - platform: homeassistant
    entity_id: input_text.lcd_display_text
    id: lcd_display_text

  - platform: wifi_info
    ip_address:
      id: ip_address
      name: IP address
    mac_address:
      id: mac_address
      name: Mac address
    ssid:
      id: connected_ssid
      name: Network SSID
    bssid:
      id: connected_
      name: Network Mac

sensor:
  - platform: wifi_signal
    id: wifisignal
    name: wifi-signal
    update_interval: 300s

globals:
  - id: backlight_on
    type: bool
    initial_value: 'false'
  - id: flash_beep_on
    type: bool
    initial_value: 'false'

switch:
  - platform: template
    name: "LCD Backlight"
    id: backlight
    restore_mode: RESTORE_DEFAULT_OFF
    turn_on_action:
      - globals.set:
          id: backlight_on
          value: 'true'
      - globals.set:
          id: flash_beep_on
          value: 'false'
      - lambda: |-
          id(lcddisplay).backlight();
      - delay: 5s
    turn_off_action:
      - globals.set:
          id: backlight_on
          value: 'false'
      - lambda: |-
            id(lcddisplay).no_backlight();
    lambda: |-
      return id(backlight_on);

  - platform: template
    name: "Flash and Beep"
    id: flash_beep
    restore_mode: RESTORE_DEFAULT_OFF
    turn_on_action:
      - globals.set:
          id: flash_beep_on
          value: 'true'
      - globals.set:
          id: backlight_on
          value: 'false'
      - lambda: |-
          id(lcddisplay).backlight();
      - lambda: |-
          id(buzzer).turn_on();
      - delay: 0.75s
      - lambda: |-
          id(lcddisplay).no_backlight();
      - lambda: |-
          id(buzzer).turn_off();
      - delay: 0.75s
      - lambda: |-
          id(lcddisplay).backlight();
      - lambda: |-
          id(buzzer).turn_on();
      - delay: 0.75s
      - lambda: |-
          id(lcddisplay).no_backlight();
      - lambda: |-
          id(buzzer).turn_off();
      - delay: 0.75s
      - lambda: |-
          id(lcddisplay).backlight();
      - lambda: |-
          id(buzzer).turn_on();
      - delay: 0.75s
      - lambda: |-
          id(lcddisplay).no_backlight();
      - lambda: |-
          id(buzzer).turn_off();
      - delay: 0.75s
      - lambda: |-
          id(lcddisplay).backlight();
    turn_off_action:
      - globals.set:
          id: flash_beep_on
          value: 'false'
      - lambda: |-
            id(lcddisplay).no_backlight();
      - lambda: |-
          id(buzzer).turn_off();
    lambda: |-
      return id(flash_beep_on);


  - platform: restart
    name: "Restart ESPhome"

output:
  - platform: gpio
    pin: D3
    id: buzzer
  - platform: gpio
    pin: D6
    id: light_output

display:
  - platform: lcd_pcf8574
    id: lcddisplay
    dimensions: 16x2
    address: 0x27
    lambda: |-
      if(id(wifisignal).has_state()) {
        if (id(lcd_display_text).state != "") {
          it.print(0, 0, id(lcd_display_text).state.c_str());
        }
      } else {
        it.print("    ESPhome        booting...   ");
      }

CODE: RTTTL (Use passive buzzer!!!!)

esphome:
  name: lablcd
  friendly_name: LabLCD

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "ga17mNCtxdiXXXXXXXXXXXXXXXXXXXXX70a5W0VPZR51Q="
  actions:
    - action: rtttl_play
      variables:
        song_str: string
      then:
        - rtttl.play:
            rtttl: !lambda 'return song_str;'

ota:
  - platform: esphome
    password: "84013b9XXXXXXXXXXXXXXX5401039f7c"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Lablcd Fallback Hotspot"
    password: "nnzXXXXXXXXXXXXXXXXXXXCb"

rtttl:
  output: rtttl_out
  on_finished_playback:
    - logger.log: 'Song ended!'


captive_portal:

i2c:
  sda: D2
  scl: D1
  scan: true
  frequency: 400kHz

light:
  - platform: status_led
    name: "Status LED"
    icon: "mdi:led-on"
    pin:
      number: D5
      inverted: true

binary_sensor:
  - platform: gpio
    name: "Push Button"
    pin: 
      number: D4
      inverted: true
      mode: 
        input: true
        pullup: true
    on_press:
      - homeassistant.service:
          service: homeassistant.toggle
          data:
            entity_id: input_boolean.esphome_notification_lcdlab_push_button

text_sensor:
  # Create text input helper in homeassistant and enter id below
  - platform: homeassistant
    entity_id: input_text.esphome_notification_lcdlcd_display_text
    id: lcd_display_text

  - platform: wifi_info
    ip_address:
      id: ip_address
      name: IP address
    mac_address:
      id: mac_address
      name: Mac address
    ssid:
      id: connected_ssid
      name: Network SSID
    bssid:
      id: connected_
      name: Network Mac

sensor:
  - platform: wifi_signal
    id: wifisignal
    name: wifi-signal
    update_interval: 300s

globals:
  - id: backlight_on
    type: bool
    initial_value: 'false'
  - id: flash_beep_on
    type: bool
    initial_value: 'false'

switch:
  - platform: template
    name: "LCD Backlight Lab"
    id: backlight
    restore_mode: RESTORE_DEFAULT_OFF
    turn_on_action:
      - globals.set:
          id: backlight_on
          value: 'true'
      - globals.set:
          id: flash_beep_on
          value: 'false'
      - lambda: |-
          id(lcddisplay).backlight();
    turn_off_action:
      - globals.set:
          id: backlight_on
          value: 'false'
      - lambda: |-
            id(lcddisplay).no_backlight();
    lambda: |-
      return id(backlight_on);

 
  - platform: restart
    name: "Restart ESPhome"

output:
  - platform: esp8266_pwm
    pin: D8
    id: rtttl_out

display:
  - platform: lcd_pcf8574
    id: lcddisplay
    dimensions: 16x2
    address: 0x27
    lambda: |-
      if(id(wifisignal).has_state()) {
        if (id(lcd_display_text).state != "") {
          it.print(0, 0, id(lcd_display_text).state.c_str());
        }
      } else {
        it.print("    ESPhome        booting...   ");
      }


WHY2025

In case of doubt .. MORE LEDS!

We went to WHY2025 a hackers camp in the Netherlands.

The first time I went was in 1997, with Bigred.
Many followed after that.
Tyrone, Bigred were also there from our old Crew.
Coline joined me several times since 2005.

I joined the Badge team, and was making spacers for the Badges in bulk using my 3D printer.
Also made some fancy cases.

In case of doubt .. more leds!

Nice weather, good friends. New friends. Booze. Food and Hacking.
We visited a lot of talks and enjoyed the music. (And fire)

I worked on: RSS feed on a epaper display, Midi monitor and the MQTT Pong website.

RSS Feed display

While waiting in line for the Badge:

A stone was passed from behind!
It was a ping request. We passed it forward, and 15 minutes later a TTL time exceeded stone came from the front of the line.
You gotta love those nerds!

The Badge:
This should have got much potential ..
Many misses, much to learn.

Sadly broken:

Our 7M Led string attached to Bigred’s Antenna.

BirdNet installation

I bought Peterson’s Vogelgids, just for fun.
It’s an old version, but that’s on purpose.

Then I saw a little project named BirdNet Pi.
(I used the Android app already)

This is a Raspberry installation which recognises bird sounds. And gives you statistics about the detected birds.
Cool for identifying birds in my garden.

Next to do : Integration in Home Assistant

Bert Visscher has the same book.

Cynthcart (c64) midi control

I bought a teensyrom a while ago.

UPDATE 20250712 : Display
UPDATE 20250904 : Added PitchBend and stabilized potmeters.

Tyrone and I wanted to control the settings using potmeters.
So I grabbed a Teensy 4.1 controller and some 10K potmeters, and worte some code

Code for 12 pots, pitch bend and display, don’t forget to set your USB mode to midi!
Schematic soon, also all tweaks and note sending.

Todo: Nice 3D printed Pitch Bend wheel, rest of display code and extra buttons!

Let’s use an old box to hold the pots!

3D printed wooden knobs (Yes wood filament)
Unstable release pot due too bad wires. Cynthcart has no decay and hold (hold is how long you are pressing key)

Knob 1 = volume now, and left of box has centering Pitch Bend.


CODE

#include <MIDIUSB.h>
#include <ResponsiveAnalogRead.h>

const int pitchbend = A14;
const int filterresonance = A0;
const int filtercutoff = A1;
const int voicemode = A2;
const int modulationmode = A3;
const int attack = A4;
const int release = A5;
const int pulsewidth = A6;
const int tremolodepth = A7;
const int tremolospeed = A8;
const int oscwave = A9;
const int oscvoice2 = A10;
const int oscvoice3 = A11;
const int volume = A12;

ResponsiveAnalogRead analogpitchbend(pitchbend, true);

ResponsiveAnalogRead analogfilterresonance(filterresonance, true);
ResponsiveAnalogRead analogfiltercutoff(filtercutoff, true);
ResponsiveAnalogRead analogvoicemode(voicemode, true);
ResponsiveAnalogRead analogmodulationmode(modulationmode, true);
ResponsiveAnalogRead analogattack(attack, true);
ResponsiveAnalogRead analogrelease(release, true);
ResponsiveAnalogRead analogpulsewidth(pulsewidth, true);
ResponsiveAnalogRead analogtremolodepth(tremolodepth, true);
ResponsiveAnalogRead analogtremolospeed(tremolospeed, true);
ResponsiveAnalogRead analogoscwave(oscwave, true);
ResponsiveAnalogRead analogoscvoice2(oscvoice2, true);
ResponsiveAnalogRead analogoscvoice3(oscvoice3, true);
ResponsiveAnalogRead analogvolume(volume, true);


// A0 -> A3, A6 -> A13
const int numPots = 12;


int lastfilterresonance = -1;
int lastfiltercutoff = -1;
int lastvoicemode = -1;
int lastmodulationmode = -1;
int lastattack = -1;
int lastrelease = -1;
int lastpulsewidth = -1;
int lasttremolodepth = -1;
int lasttremolospeed = -1;
int lastoscwave = -1;
int lastoscvoice2 = -1;
int lastoscvoice3 = -1;
int lastvolume = -1;


// CC 7 ff niet

// Custom mappings:
const int potPins[numPots]     = {A0, A1, A2, A3, A6, A7, A8, A9, A10, A11, A12, A13};  // Analog pins
const int ccNumbers[numPots]   = {0,1,2,3,4,5,6,8,9,13,14,15};             // CC numbers
const int midiChannels[numPots]= {1,1,1,1,1,1,1,1,1,1,1,1};                // MIDI channels (1–16)

int lastValues[numPots];  // Store last values to reduce redundant MIDI messages


void setup() {
  Serial.begin(9600);
  for (int i = 0; i < numPots; i++) {
    pinMode(potPins[i], INPUT);
    lastValues[i] = -1;
  }
  pinMode(pitchbend, INPUT);
}

void loop() {
  analogfilterresonance.update();
  if(analogfilterresonance.hasChanged()) {
    int analogValue = analogfilterresonance.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastfilterresonance) > 0){
    usbMIDI.sendControlChange(7, midiValue, 1);
    lastfilterresonance = midiValue;
    }
  }

  analogfiltercutoff.update();
  if(analogfiltercutoff.hasChanged()) {
    int analogValue = analogfiltercutoff.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastvoicemode) > 0){
    usbMIDI.sendControlChange(1, midiValue, 1);
    lastvoicemode = midiValue;
    }  
  }

  analogvoicemode.update();
  if(analogvoicemode.hasChanged()) {
    int analogValue = analogvoicemode.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastvoicemode) > 0){
    usbMIDI.sendControlChange(2, midiValue, 1);
    lastvoicemode = midiValue;
    }
  }

  analogmodulationmode.update();
  if(analogmodulationmode.hasChanged()) {
    int analogValue = analogmodulationmode.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastmodulationmode) > 0){
    usbMIDI.sendControlChange(3, midiValue, 1);
    lastmodulationmode = midiValue;
    }
  }


  analogattack.update();
  if(analogattack.hasChanged()) {
    int analogValue = analogattack.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastattack) > 0){
    usbMIDI.sendControlChange(4, midiValue, 1);
    lastattack = midiValue;
    }
  }


  analogrelease.update();
  if(analogrelease.hasChanged()) {
    int analogValue = analogrelease.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastrelease) > 0){
    usbMIDI.sendControlChange(5, midiValue, 1);
    lastrelease = midiValue;
    }
  }


  analogpulsewidth.update();
  if(analogpulsewidth.hasChanged()) {
    int analogValue = analogpulsewidth.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastpulsewidth) > 0){
    usbMIDI.sendControlChange(6, midiValue, 1);
    lastpulsewidth = midiValue;
    }
  }


  analogtremolodepth.update();
  if(analogtremolodepth.hasChanged()) {
    int analogValue = analogtremolodepth.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lasttremolodepth) > 0){
    usbMIDI.sendControlChange(8, midiValue, 1);
    lasttremolodepth = midiValue;
    }
  }


  analogtremolospeed.update();
  if(analogtremolospeed.hasChanged()) {
    int analogValue = analogtremolospeed.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lasttremolospeed) > 0){
    usbMIDI.sendControlChange(9, midiValue, 1);
    lasttremolospeed = midiValue;
    }
  }


  analogoscwave.update();
  if(analogoscwave.hasChanged()) {
    int analogValue = analogoscwave.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastoscwave) > 0){
    usbMIDI.sendControlChange(13, midiValue, 1);
    lastoscwave = midiValue;
    }
  }


  analogoscvoice2.update();
  if(analogoscvoice2.hasChanged()) {
    int analogValue = analogoscvoice2.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastoscvoice2) > 0){
    usbMIDI.sendControlChange(14, midiValue, 1);
    lastoscvoice2 = midiValue;
    }
  }


  analogoscvoice3.update();
  if(analogoscvoice3.hasChanged()) {
    int analogValue = analogoscvoice3.getValue();
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127
    if (abs(midiValue - lastoscvoice3) > 0){
    usbMIDI.sendControlChange(15, midiValue, 1);
    lastoscvoice3 = midiValue;
    }
  }







// PITCHBEND!
  analogpitchbend.update();
  if(analogpitchbend.hasChanged()) {
    int pitchBend = map(analogpitchbend.getValue(), 0, 1023, 0, 16383); // Map to MIDI Pitch Bend range
    sendPitchBend(pitchBend, 0);
  }
  /*
  int potValue = analogRead(A14);         // Read pot (0–1023)
  int pitchBend = map(potValue, 0, 1023, 0, 16383); // Map to MIDI Pitch Bend range
  //pitchBend = pitchBend + 8192;


  if (abs(pitchBend - lastPitch) > 250) { // Send only on significant change
    sendPitchBend(pitchBend, 0); 
        Serial.println(pitchBend);

    lastPitch = pitchBend;
  }
  */


  delay(5);  // CPU-friendly update rate
}

void sendPitchBend(int value, byte channel) {
  byte lsb = value & 0x7F;
  byte msb = (value >> 7) & 0x7F;

  midiEventPacket_t pitchBendPacket = {0x0E, 0xE0 | (channel & 0x0F), lsb, msb};
  MidiUSB.sendMIDI(pitchBendPacket);
  // Needed? 
  MidiUSB.flush();
}

CODE for display

#include <MIDIUSB.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// A0 -> A3, A6 -> A13
const int numPots = 12;

// A14 - Pitch bend!
int lastPitch = -1;

//A4 A5 I2C display


// Custom mappings:
const int potPins[numPots]     = {A0, A1, A2, A3, A6, A7, A8, A9, A10, A11, A12, A13};  // Analog pins
const int ccNumbers[numPots]   = {0,1,2,3,4,5,19,7,8,9,13,14};             // CC numbers
const int midiChannels[numPots]= {1,1,1,1,1,1,1,1,1,1,1,1};                // MIDI channels (1–16)

int lastValues[numPots];  // Store last values to reduce redundant MIDI messages

void setup() {
  for (int i = 0; i < numPots; i++) {
    pinMode(potPins[i], INPUT);
    lastValues[i] = -1;
  }
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.clearDisplay();
  display.display();
}

void loop() {
  for (int i = 0; i < numPots; i++) {
    int analogValue = analogRead(potPins[i]);
    int midiValue = analogValue / 8;  // Scale 0–1023 to 0–127

    if (abs(midiValue - lastValues[i]) > 1) {
      usbMIDI.sendControlChange(ccNumbers[i], midiValue, midiChannels[i]);
      lastValues[i] = midiValue;
    }
  }
  int potValue = analogRead(A12);         // Read pot (0–1023)
  int pitchBend = map(potValue, 0, 1023, 0, 16383); // Map to MIDI Pitch Bend range

  if (abs(pitchBend - lastPitch) > 5) { // Send only on significant change
    sendPitchBend(pitchBend, 0); // Channel 1
    lastPitch = pitchBend;
  }

  displayInfo();


  delay(5);  // CPU-friendly update rate
}

void sendPitchBend(int value, byte channel) {
  byte lsb = value & 0x7F;
  byte msb = (value >> 7) & 0x7F;

  midiEventPacket_t pitchBendPacket = {0x0E, 0xE0 | (channel & 0x0F), lsb, msb};
  MidiUSB.sendMIDI(pitchBendPacket);
  // Needed? 
  MidiUSB.flush();
}

void displayInfo(){
 byte x0, y0, x1, y1;     // start/end coordinates for drawing lines on OLED
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("Attack / Release");

  // draw attack line
  x0 = 0;
  y0 = 63;
  x1 = map(attackParam, 0, 127, 0, ((SCREEN_WIDTH / 4) - 1));
  y1 = 20;
  display.drawLine(x0, y0,  x1,  y1, SH110X_WHITE);

   // draw release line
  x0 = x1;  // start line from previous line's final x,y location
  y0 = y1;
  x1 = x0 + map(releaseParam, 0, 127, 0, ((SCREEN_WIDTH / 4) - 1));
  y1 = 63;
  display.drawLine(x0, y0,  x1,  y1, SH110X_WHITE);

  display.display();

}

Weather display using LilyGO T5-4.7 inch S3 E-paper E-ink ESP32-S3

I was planning to make a RSS reader using this display, but I came across a weather display project I wanted to check out.
(So I probably end up buying another one)

There are many questions and issues around this project using the S3.

So here is my solution.

Install vscode

Goto plugins and install platformio

git clone https://github.com/Xinyuan-LilyGO/LilyGo-EPD-4-7-OWM-Weather-Display.git

Warning this is NOT the branch you want to use

git checkout web

(git pull)

open directory in code

open platformio.ini and change line 13
default_envs = T5_4_7Inc_Plus_V2
(If needed add upload_port = /dev/tty**** at the end)

Change INO file lines 144-146.
Comment serial out, else update won’t work using regular power or battery.



When saving this platformio.ini file, some downloading and installing should be popping up.
When issues occur about libraries see below.

Fill out
data>config.json
and owm_credentials.h to be sure.
(use owm_credentials information to fill config.json)

Next press the platformio icon

Fix for uploading:

Press and hold STR_IO0 button
Press REST button
Release STR_IO0 button

Libraries:

Press platformio icon, libaries and install ArduinoJson, Button2 and LilyGo-EPD47 (select your project while doing so!)

Note: Per default once per hour update, change if you want to.
Line 70 in the INO file

Build/Upload errors? .. Press clean to recompile using a clean state !

Found a rogue AP in my house

Where is it, what device could it be?

Its SSID started with ESP. So I probably am the one responsible for its existence.
I’ve got a sh*tload of ESPs/NodeMCUs/8266 turned on 24-7.

Using a Wifi analizer I could narrow it down to my livingroom.
Checked all devices, and they are all connected to my AccessPoint.
(So no fallback AP mode)

The problem with this method is that you can’t figure out a direction.

So I used this on my Laptop.

See graphs on the left

This is a directional antenna.

Using Wireshark and wavemon, I could find the direction.

There were only two devices in the direction with the strongest signal.
My photo viewer remote, and my mini turntable controller with RFID.

But these devices are working just fine! .. So lets disconnect the power.
So it IS the mini recordplayer!

Lets look at the code. (part of)

WiFi.mode(WIFI_AP_STA); 

I should have used

WiFi.mode(WIFI_STA); 

Now it was a client AND an Access Point!

Mystery solved!

Ben Eater Clock Module on steroids

My previous build of the clock module is as Ben designed. Wellll . Not really, I added some components to change the clock range.

Okay, Ben’s design is awesome. Not because of its technical design. No, you will learn to use the NE555 chip in three ways!
Variable freq, debounce with delay and a flip-flop like switch
I added another function to it. While making my version.
I added a 555 power-on reset pulse part.

I used a perm board (which is shorter than a regular breadboard)
And I moved some components over and added some LEDs/pin headers.
While doing so, I only used 3/4 of the board.

So I added a power-on reset part with a manual push-button. (Partly like the C64 power-on)
I also added the 1Mhz crystal.

Rest of the boards will use JST connectors for the bus-connections.

I am working on big motor controllers and 3D print modelling for clients.

Hard disk click of death

DISCLAIMER: Do this at own risk, I’m not responsible for loss of data!

Today is the second time I fixed a dead hard disk.

A few years ago, I fixed one containing a university assignment.
It took a while, but I managed to get it running long enough to make a backup of the data.

Today, a friend asked me to get the data from a drive containing family pictures.

Below the way I did this.

First thing to try: See if PCB has contact problems.
Unscrew pcb and clean contacts with ISOPropanol.
See red arrows.

If this fails, the head can be stuck.
Do below, but be carefull.

Use a Sata Adaptor so you can move the harddisk around.

Try to figure out which way the actuator arm is pointing. (Thingy with the read sensor)

Do below while powered up.
Point the hard disk with this arm upward. And let it fall vertically on the desk. (Perfectly vertical! Else you may risk head clashing against platters)

When you have a smaller hard disk you can tap the side.

In both cases I managed to get most data from these drives!