Keyboard Electronics

    Part two in an on-going series describing the process of turning a Psion Series 5 keyboard into a practical, day-to-day, Bluetooth keyboard.

    Having got the basics of the keyboard working, the next challenge is to start the process of tidying up the circuit, designing a custom printed circuit board (PCB), and moving away from the prototype breadboard electronics.

    Adding a Power Switch

    Since the iPhone on-screen keyboard is only shown when no keyboard is connected, you quickly learn that it’s imperative to be able to easily power off any Bluetooth keyboard. Until now, I’d been relying on manually disconnecting the battery, but that won’t cut it long term.

    The Adafruit Feather nRF52 includes support for this, with the documentation including the following note about the enable pin:

    If you’d like to turn off the 3.3V regulator, you can do that with the EN(able) pin. Simply tie this pin to Ground and it will disable the 3V regulator. The BAT and USB pins will still be powered.

    I tested that I’d understood this correctly by simply shorting this enable pin to ground on my breadboard, and was happy to see it powered down the board but still left the battery charging. Soon after, I added a slide switch to the breadboard:

    Slide switch

    Designing the Circuit

    Autodesk EagleCAD seems to be the go-to software for PCB design and, like Fusion 360, is free for non-commercial use. It’s somewhat inscrutable, but there’s a good introduction on The Ben Heck Show, which helped me get started.

    In EagleCAD, you typically design your circuit first, using the schematic file, and then switch over to a partially generated board file where you perform layout and routing. I started my schematic, and manually drawing connections between each and every one of the pins:

    EagleCAD

    This quickly leads to a very complex schematic and, as I soon learned, is unnecessary: EagleCAD will automatically connect up wires with matching names. This allows for something much, much simpler:

    EagleCAD

    Separate parts and named connections

    As you can see, I’ve also added the slide switch, and made use of the wonderful Adafruit component library1 (adafruit.lbr) which is included in EagleCAD by default. This includes a FeatherWing component, making it extremely easy to design a board to extend the Feather nRF52.

    During the process, I learned the hard way (missing undo history, and out-of-sync board and schematic files) that it’s good to store EagleCAD files in git. I’d certainly recommend this to anyone getting started with EagleCAD.

    Laying Out the Board

    With the schematic nailed down, I went about the process of laying out the board. The board file presents a plan view of the PCB where you simply drag-and-drop components, adjust the shape of the board, place drill holes, and add silkscreen printing.

    The grid is a crucial part of this process. Unfortunately, it’s not enabled by default, so the first thing I do is turn it on, and configure it with a ‘Size’ of 25 mil2, and an ‘Alt’ of 5 mil. I’ve also found that the default board shape isn’t aligned to the grid, so the easiest thing is to simply delete this and re-draw the outline.

    Since this was my first PCB, I left routing to the auto-router and, while this gave me a few more vias than I suspect I need, I’m pretty pleased with the outcome:

    EagleCAD Board

    Ordering the Board

    I turned to OSHPark for manufacture. Ordering is just a matter of uploading the EagleCAD board file, and they generate previews immediately, helping to give me confidence in the design.

    All told, the minimum order of three boards cost me $30 for a rush job. Had I been more patient, I could have saved myself $15. They turned up a week later, ready for soldering:

    Custom PCB

    I already see a few things I’d like to improve for the next version:

    • the silkscreen component names (X2, and S2) have been auto-assigned by EagleCAD and aren’t terribly helpful
    • the orientation of the Feather headers means I need to sit the control board on the top, leading to unnecessary height
    • I forgot to add a name and version to the board

    Assembly

    Much to my surprise, placing the components on the board went surprisingly smoothly. Even my surface mount soldering worked first-time around–I’m starting to learn the benefits of a hot soldering iron, lashings of flux, and quick, deliberate action.

    Finishing Up

    The breadboard is already earmarked for another project, and I very much enjoyed stripping it down now the keyboard has found a new home:


    1. I took a passing look at the ‘con-lsta’, ‘con-lstb’, and ‘pinhead’ libraries which provide default 100 mil header components, but ultimately these were less convenient than using Adafruit’s own components. 

    2. From Wikipedia: “A thousandth of an inch is a derived unit of length in an inch-based system of units. Equal to ​1⁄1000 of an inch, it is normally referred to as a thou /ˈθaʊ/, a thousandth, or (particularly in the United States) a mil.” 

    Psion Bluetooth Keyboard

    Series 5 keyboard and Adafruit Feather

    The Psion Series 5 keyboard is aguably one of the best mobile device keyboards ever made. Heck, there’s even an Indiegogo campaign–the Gemini from Planet Computers–to bring it back in the form of an Android PDA.

    Given the dearth of high quality small bluetooth keyboards, I’ve been wondering for quite some time if it would be possible to instead use the keyboard from a broken Series 5. A few weeks ago, I purchased such a Series 5 from eBay and set about giving it a shot.

    Hardware

    Upon receipt of delivery, I set about disassembling the Series 5, extracting the keyboard, and figuring out how it connects to the mainboard:

    Cable

    As you can see, that’s a 20 way FFC (Flexible Flat Cable). What you can’t see from the photo, is that it’s actually connected to a 22 way ZIF connector. Forutnately, the Psion Series 5 Service Manual helps clear up this discrepancy:

    The Series 5 keyboard contains a total of 53 keys, organised in a conventional QWERTY arrangement. The implementation is a departure from the S3a family of products, and features a separate keyboard switch matrix assembly which slides out as the LCD screen is opened. The electrical connection between the keyboard and the main Series 5 PCB is achieved by means of a 22 way Flat Flexi Cable (only 20 ways are used for keys), fitted to a 22 way ZIF connector. The outside pins on the flexi are the grounded to allow a for a ground ring on the keyboard membrane to improve ESD protection.

    This means that, while I should use a 22 way connector, it’s sufficient to connect up only 20 of those pins. Searching Amazon, I managed to find a suitable connector, and breakout board. Fortunately–given my lack of surface mount soldering experience–these both came in large quantities:

    Ribbon Cable Breakout Boards

    For debugging purposes, I used a breadboard to connect this to the many GPIO pins of a Raspberry Pi:

    Raspberry Pi

    Protocol

    To check that the keyboard was functional (and help figure out how it works), I reassembled the internals, and James and I spent an enjoyable few hours probing the keyboard connector with a multimeter:

    Ad hoc magnification

    We identified ground (as described in the service manual), discovered some pins were consistently high, and that the others were fluttering around low:

    Pin Behaviour
    0 Ground
    1 ~ Low
    2 ~ Low
    3 ~ Low
    4 ~ Low
    5 ~ Low
    6 ~ Low
    7 ~ Low
    8 ~ Low
    9 ~ Low
    10 ~ Low
    11 ~ Low
    12 High
    13 High
    14 High
    15 ~ Low
    16 High
    17 High
    18 High
    19 High
    20 High
    21 Ground

    Having been involved in the OpenPsion project to port Linux to various Psion devices, I had been hopeful that this, coupled with the observations above, would suggest how to read the hardware. Sadly, while Tony Lindgren’s Series 5mx and Revo kernel patches are (wonderfully) still online, and the keyboard driver does indeed confirm the scanning matrix implied above, it’s too far removed from the hardware layout to be clear on the details.

    Instead, the breakthrough came when James discovered Rasmus Backman’s Psioπ project on Hackaday. In it, he details the process of building a USB adapter for the Series 5 keyboard as part of an ambitious project to build a Raspberry Pi powered Psion. It even includes the following diagram showing the layout of the keyboard matrix, and explanation of how to scan:

    Pin-out

    Reverse Engineering the Keyboard, Part I

    Scanning the keyboard:

    1. First of all, all pins are set to Inputs. This makes them high-impedance.
    2. The internal pull-up resistors are enabled on the column pins. This turns them logic HIGH.
    3. Then, one row at a time is turned to an output and driven low.
    4. Check the status on the column pins. A logic LOW signal means that column is connected to the active row because that key is pressed.
    5. When the matrix is scanned, it is compared to the last known state. Then we send ‘pressed’ scancodes for the newly pressed keys, and ‘released’ scancodes for the keys that has been released.
    6. Repeat from step 3.

    Reverse Engineering the Keyboard, Part IV

    Coupled with our earlier pin mapping, this got us off to a great start. What proved a little confusing however was that–for my Series 5 keyboard at least–the key mapping was subtly incorrect: Fn, Menu, Esc and Ctrl did not appear to be working. After some experimentation, I realised that rows 9, 10, and 11 were, in fact, columns, leading to the following revised layout:

    Col 01 (15) Col 02 (11) Col 03 (10) Col 04 (9) Col 05 (8) Col 06 (7) Col 07 (6) Col 08 (5) Col 09 (4) Col 10 (3) Col 11 (2) Col 12 (1)
    Row 01 (20) Space Up . / Left Right Left Shift
    Row 02 (19) Z X C V B N Right Shift
    Row 03 (18) H J K M . ? Down Fn
    Row 04 (17) Tab A S D F G Left Control
    Row 05 (16) 1 2 3 4 5 6
    Row 06 (14) U I O P L Enter Menu
    Row 07 (13) Q W E R T Y Esc
    Row 08 (12) 7 8 9 0 Del ‘ -

    Looking back at the observations made when inspecting the powered-up Series 5 with a multimeter, this allocation of pins corresponds reassuringly with the behviour we were seeing from the Psion itself.

    Bluetooth

    Since it’s clearly is overkill to use a Raspberry Pi, I chose the Adafruit Feather nRF52 Bluefruit LE - nRF52832 for the Bluetooth controller. It supports Bluetooth LE, has 19 GPIO pins, and an Arduino-friendly microcontroller. There’s even an Adafruit HID library and an introductory guide.

    All told, the nRF52 is a great solution with only one minor limitation: the keyboard has 20 connections, but the board only 19 GPIOs pins. Fortunately the solution is a simple one: combine two of the columns into one. I chose to short pins 1 and 4, moving Ctrl onto column 9, alongside Fn.

    The firmware itself is fairly simple, using a tight loop to iterate over the columns and rows as described above. These are then converted to standard Bluetooth HID events, with Psion specific modifiers being handled locally to ensure the behaviour matches the keycaps. To give as much flexibility as possible, the Fn and Menu keys are treated as Alt and Command respectively when not used for these local key combinations.

    for (int c = 0; c < MAX_COLUMNS; c++) {
        int column = COLUMNS[c];
    
        // Pull the column low.
        pinMode(column, OUTPUT);
        digitalWrite(column, LOW);
        delay(5);
    
        // Iterate over the rows, reading their state.
        for (int r = 0; r < MAX_ROWS; r++) {
                
                int keyDown = (digitalRead(ROWS[r]) == LOW) ? 1 : 0;
                int currentKeyDown = keyboardState[c][r];
                
                if (keyDown != currentKeyDown) {
                        char character = CHARACTER_MAP[c][r];
                        if (character != HID_KEY_NONE) {
                                sendKey(character, keyDown);
                                keyboardState[c][r] = keyDown;
                        }
                }
        }
    
        // Restore the column.
        pinMode(column, INPUT_PULLUP);
    }
    

    With this, I now have a fully functional–albeit not terribly portable–keyboard. I’ve even managed to get the Psion specific key presses working, including adjusting the display brightness using the LCD contrast keys:

    Raspberry Pi Setup

    Setting up a Raspbery Pi can be a little troublesome when you have limited connectivity options: without a monitor, keyboard, and mouse, network cable, or serial cable, it can be hard to see how to bootstrap the process.

    It’s actually possible to configure many aspects of the Pi by modifying files on the FAT32 ‘boot’ partition of a standard Raspbian install. I tend to go through the following steps when setting up a new Raspberry Pi:

    1. Install Raspbian
    2. Configure WiFi
    3. Enable SSH
    4. Discover the IP address
    5. Connect using SSH
    6. Enable VNC

    Install Raspbian

    Download and install the latest Raspbian distribution from the Raspberry Pi downloads page, and follow the installation guide. The following instructions will work with both the ‘Desktop’ and ‘Lite’ distributions, so pick whichever is best suited to your needs.

    Configure WiFi

    Create a file named wpa_supplicant.conf on the newly created ‘boot’ partition of your microSD card, entering your own network name, pre-shared key, and changing the country and security type if necessary:

    ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
    update_config=1
    country=GB
    
    network={
        ssid="<Network Name>"
        psk="<Pre-Shared Key>"
        key_mgmt=WPA-PSK
    }
    

    This will be used to update /etc/wpa_supplicant/wpa_supplicant.conf on the next boot.

    There’s a pretty good discussion on Stack Exchange, and you can find more details of the wpa_supplicant.conf file and general command-line WiFi configuration in the Raspberry Pi documentation.

    Enable SSH

    In order to be able to connect to the Pi, you’ll need to enable SSH. This can be done by creating a empty file named ssh on the ‘boot’ partition of your microSD card; the presence of this file will enable SSH access on subsequent boots.

    For example, on a Mac:

    touch /Volumes/boot/ssh
    

    Discover the IP address

    In order to be able to connect to the Rapsberry Pi, you’ll need to find its IP address.

    Return your microSD card to the Raspberry Pi and power it on. Once it has booted, you can discover the device on the network using the nmap command, substituting 10.0.0.1 with your own IP address:

    sudo nmap -sPn 10.0.0.1/24
    

    This will scan your current subnet for other devices. By default, your Raspberry Pi will have a hostname of raspberrypi, making it fairly easy to find. (You may wish to change this using raspi-config for ease of identification in the future.)

    Connect using SSH

    Since we enabled SSH access in an earlier step, connecting to the Raspberry Pi should simply be a matter of using SSH (remember to substitute the IP address below for the one you discovered using nmap):

    ssh pi@10.0.0.154
    

    The default password is raspberry. You’ll be prompted to change this on first log in.

    Enable VNC

    If you’re running a Desktop distribution, you might find it useful to use VNC. There are great step-by-step instructions in the Raspberry Pi documentation.

    In short, you will need to install the RealVNC VNC Server as follows:

    sudo apt-get update
    sudo apt-get install realvnc-vnc-server realvnc-vnc-viewer
    

    After this, you can enable the VNC Server by running the interactive raspi-config utility:

    sudo raspi-config
    

    From here, you will need to select 5 Interfacing Options, P3 VNC, then <Yes>. I also find it useful to increase the display resolution, in 7 Advanced Options1.

    If you want to use VNC Connect–which allows you to connect back to you Pi, even if you’re on a different network–you will first need to connect over the local network, then sign in to your VNC Connect account using UI as there’s no command-line mechanism to do so.


    1. If you require a non-standard resolution, you can specify this by editing config.txt on the ‘boot’ volume and setting framebuffer_width and framebuffer_height. These will override the raspi-config settings. 

    Upside

    Upside

    I have recently been looking for a simple design with which to start learning Autodesk Fusion 360, and to try out ShapewaysPorcelain Ceramic material.

    One project I’ve been contemplating for some time is something to help balance shampoo bottles upside-down when they’re near empty. The solution–a simple doughnut-like hollow cylinder–seemed like a good candidate:

    First model in Fusion 360

    First model in Fusion 360

    The idea is that you rest the bottle atop the ceramic base most of the time and, when you eventually need to stand it upside-down, you invert it and place the lid in the central hole: I’m tentatively calling it 'Upside’.

    The base modelling is fairly simple: drawing and extruding two concentric circles, then filleting the resulting edges. I parameterised the dimensions, so it’s easier to adapt it to different bottles.

    What makes things a little interesting is that tolerances for porcelain are large: Shapeways states, ‘± 3% shrinkage + 0.2-1.0 mm of glaze’. Since this particular design works if things are a little too big, but fails entirely if it’s too small (and the lid of the bottle doesn’t fit inside the central hole), I simply scaled the whole model by a factor of 1.03.

    Drawing

    Original dimensions

    Overall things shrank a little more than I expected as I forgot to account for the glaze. Including this, it does indeed look like roughly 3% shrinkage:

    • inner diameter, from 30.90 mm to 29.25 mm
    • outer diameter, from 82.40 mm to 80.76 mm
    • height, from 30.90 mm to 30.35 mm

    Given the 3D printed nature of the design, I had expected it to be fairly precise. Instead, the print has a somewhat handmade, almost wabi-sabi look about it. That was disappointing at first, but I’m coming to like the result. The base is un-glazed, and has a fairly rough, abrasive texture, and might perhaps benefit from a piece of cork.

    You can download the original model here.

    Scripting Photos

    Sometimes, when editing photos, it’s useful to be able to copy the GPS location of one photo, and assign it to another. This was supported in iPhoto using the ‘Paste Location’ menu option, but not in Photos.

    Using JavaScript–which is now a first-class citizen in macOS Automation–it’s easy to use JSON to transfer this data via the clipboard.

    First, copy the details of the selected photo to the clipboard (date and location in this example):

    photos = Application('Photos');
    photos.includeStandardAdditions = true;
    photos.activate();
    item = photos.selection()[0];
    details = {"location": item.location(), "date": item.date()};
    photos.setTheClipboardTo(JSON.stringify(details));
    

    Next, retrieve these same details from the clipboard, and update the newly-selected photo:

    photos = Application('Photos');
    photos.includeStandardAdditions = true;
    photos.activate();
    item = photos.selection()[0];
    details = JSON.parse(photos.theClipboard());
    item.location = details["location"];
    item.date = new Date(details["date"]);
    

    While JavaScript makes it wonderfully easy to implement this functionality, it may not be immediately apparent how to create a git-friendly plain text script file which macOS automation knows to treat as JavaScript. I’ve found the easiest solution is to create an executable script and specify the interpreter as  osascript, passing the language flag to explicitly specify the language as follows:

    #!/usr/bin/osascript -l JavaScript
    
    photos = Application('Photos');
    ...