danieljon.es

index posts opinions portfolio

Posts

My posts about programming and things.
Date format is day/month/year because I'm sane.

prev 1 2 3 4 5 6 7 8 9 10 11 12 13 next


Pinned posts:
CHIP-8 interpreter #2 - interpreter
Logic gate simulator
Librebooted my x200



IBM Model F XT

27/09/2024

A little bit ago I acquired an IBM Model F XT in quite good condition. It has no damage internal or external, both feet and handles are present. I just had to replace the feet with some of my own cork.

I also had to create an adapter to convert the PS/2 XT format to something a modern system can understand. This cannot be done with most active or passive converters. So I made one using a Pro Micro microcontroller running Soarer's Converter firmware and a female PS/2 connector.

I use this as my main keyboard for everyday tasks and games. As this keyboard is not made using a matrix there is no key rollover issues, so unlike the Model M, it is great for gaming (in my opinion). The layout is a bit hard to get used to though.







Inside a Powerpal

7/09/2024

I have a spare Powerpal sitting around. Powerpal is a simple energy monitoring device you install on your meter box, it works by counting the flashing rate of an LED. Simple and effective.

I thought it'd be cool to see what is inside.

This casing is welded shut and has non-replacable batteries. So opening it is destructive to the casing, but it still slides back together.





The device is powered by 2 double A batteries that have spot welded metal strips on them, and are glued together. These are easy to replace for someone comfortable with a soldering iron. I question why they didn't make them user replacable. My two guesses: they want you to buy more (they're over $100 each) and/or reduce tech support calls when the units batteries are replaced, as it seems to not have any non-volatile memory for user configurations.

The SOC is a nRF52840 with a 64MHz Cortex M4, 1MB flash and 256KB RAM, BLE and a ton of other peripherals that are not used. This seems very powerful for what it is.

I noticed the soldering of the sensor was very poor from the factory, so I fixed it while I was inside.





I'm integrating this into Home Assistant as the data is free to access from any bluetooth device.


Service monitoring on Googles Cloud Platform

21/08/2024

I had a reason to run an instance of uptime-kuma, which is a service monitor, so I put together a little guide to setting this up on Googles Cloud Platform free tier.

Initial Setup

1.       Create Google Cloud account

2.       We need to upload a .raw image of Alpine to our Google Cloud account. To do this, create a new bucket at https://console.cloud.google.com/storage/browser

3.       Download the Google Cloud Platform build of Alpine at https://alpinelinux.org/cloud/ ensure you select the latest release, x86_64, BIOS, Virtual. Then Download the tiny image

4.       GCP requires the image be in .tar.gz format and called disk.raw, so rename the .raw image, then archive it.

5.       Upload this .tar.gz to the Google Cloud bucket you made.

6.       We need to create an image that this VM will be based on. Go to https://console.cloud.google.com/compute/images and select Create Image

7.       Name your image, select Cloud Storage file as the source, and select the .tar.gz file you uploaded to your bucket. Then click create.

8.       Create a new VM instance with the following:

-       1 non-preemptible e2-micro VM instance per month in one of the following US regions:  

o          Oregon: us-west1 o       Iowa: us-central1 o         South Carolina: us-east1

-       30 GB-months standard persistent disk

-       1 GB of outbound data transfer from North America to all region destinations (excluding China and Australia) per month  

image 

Be sure to select Standard as the Network Service Tier, it is premium by default

image 

Change the Boot disk, under image select the image we made.

Ensure you select Standard persistent disk as the Disk type Set the size to 30GB

image 

Under security, add an ssh public key which will be used to login. At the end of the public key, change the username to alpine.

 

Virtual Machine

1.       Connect to your instance using the ssh key you uploaded and the username alpine: ssh

-i <keyfile> alpine@<ip>

a. You can find your IP in the VM instances tab of GCP

2.       Set password, update, install packages

a.       echo "alpine:passwordhere" | doas chpasswd

b.      doas apk update

c.       doas apk upgrade

d.      doas apk add docker

e.       doas apk add caddy

3.       Setup environment

a.       doas rc-update add docker default

b.      doas rc-update add caddy default

c.       printf "<yourdomain.tld>\n{\nreverse_proxy :3001\n}\n" | doas tee -a

/etc/caddy/Caddyfile

d.      printf "ChallengeResponseAuthentication no\nPasswordAuthentication no\nPermitRootLogin no\nPermitRootLogin prohibit-password\n" | doas tee

-a /etc/ssh/sshd_config

e.       doas reboot

f.        reconnect to your vm

4.       create and run docker image

a.       mkdir /home/alpine/kuma

b.      cd /home/alpine/kuma

c.       doas docker run -d --dns 1.1.1.1 --restart=always -p 3001:3001 -v /home/alpine/kuma:/app/data --name uptime-kuma louislam/uptimekuma:1-alpine

DDNS

1. The free tier GCP vm has a dynamic IP and I have experienced it changing between reboots. So we need to setup dynamic DNS and a hostname that points to our domain.

a.       Add an A record to your DNS records pointing to the vm’s IP

b.       Each registrar has their own API for doing DDNS updates, find a script for your service, place it at /home/alpine/scripts/ddns.sh and add it as a cronjob at boot and hourly on your vm

i.          chmod +x /home/alpine/scripts/ddns.sh

ii.         crontab -e

iii.       add: 0 * * * * /home/alpine/scripts/ddns.sh

iv.       add: @reboot /home/alpine/scripts/ddns.sh

c.       Note, GoDaddy changed their API requirements, unless you have 50 domains with GoDaddy, you may no longer use their DNS API, you’re out of luck.

You can now access uptime-kuma at your domain.



Program/script launching keypad

1/08/2022

I have various cameras around inside/outside my house. Originally to view them I was displaying them on a 10 second rotation, however this gave me no control and often i'd find myself looking at something happening on a camera only for it to switch.

I have a keypad and arduino laying around so I thought it'd be neat to add in controls.
I have 5 cameras in this view (I have more inside my house in areas I don't want to always look at), so the keypad buttons 1-A will swap whichever camera is "minimised" to the main view. I use motion to manage and record my cameras, the minimised cameras are substreams, so use less bandwidth than the main view. Each camera is also capable of IR mode and switch when it gets dark. I also have IR floodlights on the outside of the house for bright night viewing (the cameras have IR LEDs as well, however outside it is nice to have more).

To listen via serial and actually execute programs/scripts, I wrote a C program. It connects over serial, receives the key pressed and takes action according to what is assigned to each key. The assignments are controlled via a .ini file, the key value can either be the word "cam" which means a camera command, or a program name/script that is to be executed. The program then forks off and exectutes whatever you desire. Camera commands are different - I use wServer as a very simple websocket server that accepts connections from a websocket client (the viewer), the server then sends the client whatever key I press if it is assigned as a camera command.
To view/control the cameras and receive commands I use a basic html/javascript pairing.


I also recently made a laptop vesa mount tray which I have my laptop sitting on now to save desk space. Commercial laptop tray offerings were all too thin to support my girthy x200 and ultrabase. Also I'm using my x200 tablet temporarily while I figure out issues with my plain x200's.




Serial console via rpi zero

14/6/2022

The other day I was doing some networking changes on a server upstairs. Without thinking of the consequences I took the ethernet interface down with the intention of bringing it back up - however I was connected via ssh on that interface so I lost connection. I had to do a shameful walk upstairs to recover.

I have a spare rpi zero and a USB to rs232 cable and thought it'd be neat to get a recovery console using these (accessible over ssh to the pi). However I wasn't sure about the voltages and if i'd need extra hardware to deal with this, but while researching I found it can be done via the data USB port, called "USB Gadget Serial".

Setting this up was easy - connect the rpi zero to the server via USB (I also am powering the pi from a UPS), have it run an ssh server and do some simple config changes:
- on the pi add dtoverlay=dwc2 to /boot/config.txt
- on the pi add modprobe g_serial above exit 0 in /etc/rc.local
- on the server enable getty on ttyACM0 systemctl enable --now serial-getty@ttyACM0.service

Now you can open a connection to the server using screen /dev/ttyGS0, even when the networking is broken.


IBM Model M restoration

17/2/2022

I first purchased an IBM Model M a few months ago, recently I bought another one from ebay. The condition seemed ok visually - some missing key caps and a scratch or two. It is a 1390120 from 1986:



However when I got it, the keyboard felt like typing on a sponge, it obviously needed to have a bolt modification performed on it. This modification involves dismantling the keyboard and replacing plastic rivets holding the membrane together with bolts.

to perform this modification you need a few tools and bolts. Places on the internet will claim you need ~80 M2 2mm machine screws/bolts and ~80 matching M2 nuts and matching washers. This isn't true. I used maybe 20 bolts and nuts, and no washers (even though I ordered them), there just isn't room to put them on.

Another tool you will need is a 5.5mm driver. This is quite a unique driver size, however I happened to have one. Unfortunately though it was too fat to fit into the recessed holes the case bolts are fitted into. So I ended up grinding my 5.5mm driver bit down on a bench grinder.



With the case removed, the damage and dirt can be inspected:





It took me awhile to notice (and for it to snap in half) for me to notice the barrel plate had cracks, and was prone to snapping. (see the arrows) These cracks extend along the whole length of the plate:



I found epoxy resin to be incredibly good for fixing this problem, you can see i glued it back together and filled in the crack on both sides:



I proceeded to clean all the keys, caps, plate, case, sand away the rust etc - that isn't interesting.

Pictured below the back of the metal plate that holds the key membrane sandwich. The Model M has a major design flaw in that the rivets (the black circles below) used to hold this sandwich together is made of plastic that over time degrades and breaks. It only takes a handful of these to break for the membrane to no longer be held together tight enough for keys to register and feel correct, this is why we replace them with bolts. Mine doesn't look that bad, but it felt bad and did not register keys properly.



I cut off the rest of the rivets using a small craft knife. This let the membrane sandwich come apart into 3 pieces: the barrel plate, a cloth protector and the back metal plate. My model M seems unique as only a few others online have their membrane physically glued to the backplate. It is usually 5 pieces. The posts that were left over by the rivets needed to be cut flat, I did that using nail clippers.



The next step is to drill holes through the barrel plate where the old rivets were located. To do this you use a 1/16" drill bit. To help with the drilling I first marked each post I would have to drill through using some nail polish:



Then, using a soldering iron I don't mind destroying the tip on, I slightly melted through the top of the posts as a guide for the drill bit:



Now I started drilling. This was scary because I thought accuracy was important. It is not, I was sloppy and inaccurate but only a couple holes didn't line up in the end, and like I said, you do not need all rivets replaced. It also makes a mess:





I then reassembled the membrane sandwich, installed the screws and nuts, put it all back together and I had a finished Model M.




Userspace driver for Microsoft serial mouse

9/1/2022

To learn the basics of serial programming in UNIX I created a simple userspace driver for a Micrsoft serial mouse.

I use uinput to emulate an input device.

/*
 * Microsoft mouse
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <ctype.h>
#include <stdint.h>
#include <linux/uinput.h>

#define DOWN 		1
#define UP 		0

#define PORT 		"/dev/ttyUSB0"
#define BAUD 		B1200
#define LBIT 		1 << 5
#define RBIT		1 << 4
#define SYNCBIT 	1 << 7
#define XBYTE0MASK	0x03
#define XBYTE1MASK 	0x7F
#define YBYTE0MASK	0x0C
#define YBYTE2MASK 	0x7F

void process(void);
void emit(int fd, int type, int code, int val);

uint8_t packet[3];

struct 
{
	uint8_t left;
	uint8_t right;
} mouse;

struct uinput_setup usetup;
int ud;

int
main(void)
{
	int fd, flags, onbyte;
	ssize_t byte;
	struct termios options;
	fd = open(PORT, O_RDWR | O_NOCTTY);
	if (fd == -1)
	{
		perror("cannot open serial device");
		exit(EXIT_FAILURE);
	}
	flags = fcntl(fd, F_GETFL);			// get current flags
	fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);	// mask out O_NONBLOCK = blocking

	// get port options and set flags
	tcgetattr(fd, &options);
	cfsetispeed(&options, BAUD);
	cfsetospeed(&options, BAUD);
	options.c_cflag |= (CLOCAL | CREAD);	// local line, enable read
	// set character size
	options.c_cflag &= ~CSIZE;		// mask out the character size bits
	options.c_cflag |= CS7;			// 7 bits
	// no parity
	options.c_cflag &= ~PARENB;		// mask out parity bit
	options.c_cflag &= ~CSTOPB;		// mask out stop bit (unset = 1 bit)
	// raw input
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);	
	options.c_iflag &= ~INPCK; 		// mask out parity checking
	options.c_oflag &= ~OPOST;		// mask out postprocessing - raw output

	options.c_cc[VMIN] = 1;			// minimum of 1 byte read
	options.c_cc[VTIME] = 0;		// wait indefinitely
	// apply options
	tcsetattr(fd, TCSANOW, &options);

	// setup uevent device

	ud = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
	if (ud == -1)
	{
		perror("cannot open /dev/uinput");
		exit(EXIT_FAILURE);
	}


	// enable mouse left/right and relative events
	ioctl(ud, UI_SET_EVBIT, EV_KEY);
	ioctl(ud, UI_SET_KEYBIT, BTN_LEFT);
	ioctl(ud, UI_SET_KEYBIT, BTN_RIGHT);

	ioctl(ud, UI_SET_EVBIT, EV_REL);
	ioctl(ud, UI_SET_RELBIT, REL_X);
	ioctl(ud, UI_SET_RELBIT, REL_Y);

	memset(&usetup, 0, sizeof(usetup));
	usetup.id.bustype = BUS_USB;
	usetup.id.vendor = 0x1234; 
	usetup.id.product = 0x5678;
	strcpy(usetup.name, "Userspace Microsoft serial mouse");

	ioctl(ud, UI_DEV_SETUP, &usetup);
	ioctl(ud, UI_DEV_CREATE);

	mouse.left = 0x00;
	mouse.right = 0x00;
	onbyte = 0;
	byte = 0x00;

	while(1)
	{
		read(fd, &byte, 1);
		if (byte == 0xcd) 		// some kind of init?
			continue;
		// is byte first of packet?
		if (byte & SYNCBIT && onbyte == 0)
		{
			packet[0] = byte;
			onbyte = 1;
		}
		else if (onbyte == 1)
		{
			packet[1] = byte;
			onbyte = 2;
		}
		else if (onbyte == 2)
		{
			packet[2] = byte;
			onbyte = 0;
			process();
		}
		fflush(stdout);
	}
	close(fd);
	ioctl(ud, UI_DEV_DESTROY);
	close(ud);
	return 1;
}

void
process(void)
{
	int8_t relx, rely;
	printf("0x%02x\t", packet[0]);
	printf("0x%02x\t", packet[1]);
	printf("0x%02x\t\n\n", packet[2]);
	if (packet[0] & LBIT && !mouse.left)
	{
		mouse.left = DOWN;
		puts("left down");
		emit(ud, EV_KEY, BTN_LEFT, 1);
		emit(ud, EV_SYN, SYN_REPORT, 0);
	}
	else if (mouse.left && !(packet[0] & LBIT))
	{
		mouse.left = UP;
		puts("left up");
		emit(ud, EV_KEY, BTN_LEFT, 0);
		emit(ud, EV_SYN, SYN_REPORT, 0);
	}
	if (packet[0] & RBIT && !mouse.right)
	{
		mouse.right = DOWN;
		puts("right down");
		emit(ud, EV_KEY, BTN_RIGHT, 1);
		emit(ud, EV_SYN, SYN_REPORT, 0);
	}
	else if (mouse.right && !(packet[0] & RBIT))
	{
		mouse.right = UP;
		emit(ud, EV_KEY, BTN_RIGHT, 0);
		emit(ud, EV_SYN, SYN_REPORT, 0);
	}

	relx |= ((packet[0] & XBYTE0MASK)<<6) | (packet[1] & XBYTE1MASK);
	rely |= ((packet[0] & YBYTE0MASK)<<4) | (packet[2] & YBYTE2MASK);
	emit(ud, EV_REL, REL_X, relx);
	emit(ud, EV_REL, REL_Y, rely);
	emit(ud, EV_SYN, SYN_REPORT, 0);


}

void
emit(int fd, int type, int code, int val)
{
	struct input_event ie;
	ie.type = type;
	ie.code = code;
	ie.value = val;
	/* timestamp values below are ignored */
	ie.time.tv_sec = 0;
	ie.time.tv_usec = 0;

	write(fd, &ie, sizeof(ie));
}


Various updates

1/1/2022

I haven't made a post in over 6 months because my website generator has had a bug with pagination and I haven't bothered to do anything about it.

I've become very interested in the hobby of soldering and have spent a lot of time practicing. I acquired a Pinecil iron, a holder, hotair station, DC power supply, cheap microscope, extra ts100 soldering tips, flux/solder/alcohol dispenser/good precision tweezers etc. I've been taking things apart, removing components and soldering them back on, mostly laptop motherboards. I've repaired a few broken items of mine so far (a wire breaking in a servo motor, ch341a voltage mod, a "smart" light bulb for a family member with an internal trace ripped), so these skills have come into use.

I purchased another Thinkpad x200, this time a Japanese import with a JIS keyboard, I think it's pretty neat.

I purchased two IBM Model M keyboards from ebay (1391401 from 1988 and a 1390120 from 1986). One keyboard was in great mechanical and electrical condition, I just disassembled the shell and cleaned each key and the shell, it works perfectly and I am using it now. The other, the 1390120 with a square medal badge from 1986 felt like typing on a sponge. So I have completely cleaned the shell/keys and am waiting on hardware to arrive to bolt mod it.

I'm going to start putting out more posts.

Here is a couple of photos of my soldering/desk setup and the x200 and keyboards.


Not much going on, but some pictures

18/06/2021

Not much has been going on, project wise I've basically stalled. I'm slowly working on a gameboy emulator in C, we'll see how that goes. I have a bunch of new posters and figures though.

Most notably I got 2 fumo, Tewi and Cirno - they're very soft.



I also replaced the thermal paste on my main x200, the temperatures use to go into the 80's, now stay below 60 all the time, usually 40's.

Cirno helped of course:


Brainfuck interpreter

19/11/2020

I wrote a simple brainfuck interpreter in C.



The source code can be found in my git repository.


prev 1 2 3 4 5 6 7 8 9 10 11 12 13 next


RSS feed
FSF member

page generated 27/9/2024 using websitegenerator in C