tomwardill - Apr 15Building a Balloon Tracking Cyberdeck
As with a lot of my projects, this one started with a message:
David: “Sooo I think I want to launch a high altitude balloon…”
Me: “Sounds like fun! Why? Got plans?”
David: “I have this dream of sending a camera up really really high. From first attempts at research, looks to be plausible”
There was some scope creep.
The story of the balloon aspect of the
4 days ago
As with a lot of my projects, this one started with a message:
David: “Sooo I think I want to launch a high altitude balloon…”
Me: “Sounds like fun! Why? Got plans?”
David: “I have this dream of sending a camera up really really high. From first attempts at research, looks to be plausible”
There was some scope creep.
The story of the balloon aspect of the project will be saved for another day, but this is about the tracking.
4 days ago
caolan - Jan 03Destructuring Infinity
-/-
4 months ago
-/-
4 months ago
caolan - Aug 02Smaller JavaScript Using Module Exports
-/-
9 months ago
-/-
9 months ago
kitation - Jul 24Disabled or not?Unlike my neurodivergence and mental illnesses, I’ve always been reluctant to classify my physical health conditions as disabilities. That has changed a little in recent years as these conditions require me to take drugs that suppress my immune system, but then I would say that being immunocompromised is my disability. I want to talk in this post about being on that edge of disabled and not, and 9 months ago Unlike my neurodivergence and mental illnesses, I’ve always been reluctant to classify my physical health conditions as disabilities. That has changed a little in recent years as these conditions require me to take drugs that suppress my immune system, but then I would say that being immunocompromised is my disability. I want to talk in this post about being on that edge of disabled and not, and the experiences of conditions that can be fine most of the time until they’re not. 9 months agocaolan - Aug 19A Very Simple Markup Language
-/-
over a year ago
-/-
over a year ago
benofbrown - Jan 24Pure Data Objects Written in C
I’ve been using Pure Data for a month or two now to spice up the visuals when I stream live, and I’ve found it pretty fun but occasionally frustrating. Most of my frustration has been around finding objects (or combinations of objects) that do what I want to do. For whatever reason I’ve strugged to find specific things online so there’s a good chance that what I’ve done in the rest of this blog has
over a year ago
I’ve been using Pure Data for a month or two now to spice up the visuals when I stream live, and I’ve found it pretty fun but occasionally frustrating. Most of my frustration has been around finding objects (or combinations of objects) that do what I want to do. For whatever reason I’ve strugged to find specific things online so there’s a good chance that what I’ve done in the rest of this blog has been for naught, other than a learning experience for me. The main thing I wanted was something I could use to switch between scenes I have set up. Each one has one or more ♦ Yesterday I had a look at this excellent git repo, HOWTO write an External for Pure Data, and saw that it would be pretty simple to write an object in C, which I then did. And then I wrote a few more. The object I created to help me with this problem is one I’ve called ♦ Now that I’d got bitten by the bug, I wrote some more objects. The next one was I’ve also written That’s all I written for now, I’m sure I’ll write more in future. I’ve made them all freely available under the Clear BSD License over on gitlab: gitlab.com/benofbrown/pd-objects/ over a year agocaolan - Nov 18Bramley: Splash Screen and Shutdown
-/-
over a year ago
-/-
over a year ago
caolan - Oct 25Bramley: Buttons
-/-
over a year ago
-/-
over a year ago
caolan - Oct 14Bramley: Driving a Sharp Memory Display from Rust
-/-
over a year ago
-/-
over a year ago
caolan - Oct 07Bramley: Display
-/-
over a year ago
-/-
over a year ago
caolan - Oct 06Bramley: Human Input
-/-
over a year ago
-/-
over a year ago
caolan - Oct 05Bramley: Build Log (Introduction)
-/-
over a year ago
-/-
over a year ago
|
caolan - Apr 14Revisiting the Bramley
-/-
5 days ago
-/-
5 days ago
tomwardill - Aug 02Adventure Racing Tips for Beginners
Recently, I had the pleasure of taking part in ITERA-lite 2023 as a pair with my good friend David. We crossed the start line (our main ambition) and made it to Transition 3 after 26 hours and 150km before my fitness gave up and we withdrew.
However, in the process of preparing for the race, we were lucky enough to be supported by a bunch of people who have a lot of experience with this event and a
9 months ago
Recently, I had the pleasure of taking part in ITERA-lite 2023 as a pair with my good friend David. We crossed the start line (our main ambition) and made it to Transition 3 after 26 hours and 150km before my fitness gave up and we withdrew.
However, in the process of preparing for the race, we were lucky enough to be supported by a bunch of people who have a lot of experience with this event and adventure racing in general.
9 months ago
caolan - Jul 28Smaller JavaScript Using Encapsulation
-/-
9 months ago
-/-
9 months ago
caolan - Nov 24The Merits of Visible Alt-Text
-/-
over a year ago
-/-
over a year ago
caolan - May 13Serving Markdown Direct from Apache
-/-
over a year ago
-/-
over a year ago
benofbrown - Dec 28Streaming Pure Data GEM Video Via OBS
Recently I’ve been doing a bit of streaming with my modular synth and wanted something to make the video side a bit more interesting for anyone watching it. After a bit of searching I discovered Pure Data (aka Pd), and the GEM plugin for it, which adds graphical elements you can create and manipulate in Pd, so I thought I’d give it a go.
over a year ago
Recently I’ve been doing a bit of streaming with my modular synth and wanted something to make the video side a bit more interesting for anyone watching it. After a bit of searching I discovered Pure Data (aka Pd), and the GEM plugin for it, which adds graphical elements you can create and manipulate in Pd, so I thought I’d give it a go. Getting audio in was pretty simple, I already use JACK for my audio routing, so all I had to do was configure Pd to use JACK as well, and connect everything up in After getting the This took quite a long time to figure out, and in fact I never did figure it out. I ended up having trouble getting the vanilla Pd distributed with Debian to run with the plugins you need to be equivalent to Pd-extended and ended up installing something else entirely, Purr Data. Not only did it give me access to the objects I needed, it also fixed the annoying window problem, so I’d say Step 1: Install Purr Data. Now I had a few cubes spinning around in the GEM window, I wanted to get those to show in OBS Studio, which is a really excellent program you can use to combine various audio and video sources in to a stream which you can push to YouTube, Twitch etc etc etc. My first attempt was to use the XWindow capture source in OBS. This worked, but not very well. I don’t know if it’s my window manager or just X11 but it was very flickery, which is no good at all. The next part of this blog is the whole reason I’m writing it, because it took me a stupidly long time to figure out and get working. So far I’ve been enjoying Pd, but I’ve found resources online a bit lacking. It turns out the help in Pd (or at least Purr Data) itself is pretty good, though looking for things online didn’t really give me much joy, though I did find the v4l2loopback Linux kernel module. If you’re using Debian like me, then you can get this easily by installing the
Now, the label bit was copy and pasted from the To add it as a source in OBS it’s really very simple, you just add a Again I didn’t find that much info about doing this online, though a thread on the Pure Data Mailing List pointed me in the right direction. The first thing I learnt is that the way to send the video to a If anyone wants to use this method, I’ve attached an example patch gem-record-example.pd that should get you up and running, here’s the gist of it:
caolan - Nov 18Bramley: 6. Splash Screen and Shutdown
Your browser does not support the video tag.
Communicating with the Bramley via serial cable is fine, but to use the device in my hands, I need to launch a program automatically when it's turned on and - using only the buttons - be able to shut it down cleanly again. In this post, I hook into various parts of the startup/shutdown process to handle input and display messages to over a year ago
Your browser does not support the video tag.
Communicating with the Bramley via serial cable is fine, but to use the device in my hands, I need to launch a program automatically when it's turned on and - using only the buttons - be able to shut it down cleanly again. In this post, I hook into various parts of the startup/shutdown process to handle input and display messages to the user. StartupThe first message you see in the video above is the 'Bramley' name bouncing impatiently. The animation is based on this GIF: ♦Decoding the GIF at runtime added around 2 seconds of latency. So in a moment of yak-shaving madness I created a Rust macro to turn it directly into code (I'll probably delete this later as it slows down compile times). Then, to display the animation as early as I could, I created a systemd service file with as few dependencies as possible.
Using ♦ There's a hard floor of around 6 seconds before systemd starts launching services. The splash screen itself is launched at around 6.7 seconds. I could tweak the order a little, but it's unlikely to get significantly faster. The problem with starting a program this early is that the kernel's SPI module hasn't been loaded yet, and at first I couldn't draw to the screen. My solution was to compile a custom kernel that included the SPI module statically. Startup completeOnce the Pi is booted, I run another program to read input and shut down the Pi if I press all the buttons at once.
Unfortunately, the full startup sequence is really slow. To reach the graphical target takes over 35 seconds as measured by systemd (wall clock time from power on is even longer).
My menu program runs at I'm now wondering what an acceptable time-to-menu would be. As a child, I remember the GameBoy being slow, but acceptable, and that takes around 6-7 seconds from power on to menu. Your browser does not support the video tag.The Bramley currently takes around 12 seconds to display the splash screen and 34 seconds to display the menu. If I'm to approach that GameBoy benchmark of 6-7 seconds there's still a long way to go - I'm not even sure if it's possible with a Pi. Of course, most modern devices cheat and don't power off at all. A standby mode may be worth investigating, but I'm going to leave that problem for another day. For now, I can shutdown the Pi while prototyping and that's enough. ShutdownTo display the 'Shutting down...' message, I created another systemd service using the
Shutdown complete
At the end of shutdown, once the filesystem has been re-mounted as read-only, and just before halting the system, I display "Bye" - which due to how the screen works, fades out poignantly after the power is cut. There's no systemd service file for this. Instead, the program is simply symlinked into the I now have basic control over startup and shutdown. Next time, I'll make my first attempt at chorded input. over a year agocaolan - Oct 25Bramley: 5. ButtonsUsing the rppal library and the Raspberry Pi's GPIO pins, I've added support for the six Cherry ML switches on the back of the Bramley. ♦
Reading from the pins is easy enou over a year ago Using the rppal library and the Raspberry Pi's GPIO pins, I've added support for the six Cherry ML switches on the back of the Bramley. ♦
Reading from the pins is easy enough, but to correctly detect a key press, I've got some more work to do. InterferenceWithout a pull-up or pull-down resistor, the value read from a GPIO pin will be floating. As a digital input, it will hover between 0 and 1, depending on background interference, and, if I'm not careful, produce unwanted key presses. Normally, I rely on the Raspberry Pi's internal pull-up resistors to deal with this because they're easy to enable in software, and that's my comfort zone.
But, this time, despite enabling the internal resistor I was still experiencing 'phantom' key presses. Determined to exorcise them using hardware, I wired another 10K pull-up resistor to each switch:
That seemed to block out the noise. I'm now ready to try and make sense of the signal. DebouncingAn actuated switch doesn't produce a signal that's immedately high or low. Before stabalising, it will bounce between each state. ♦ If I watch only for transitions from high to low (1 to 0), I would incorrectly count three keypresses. This could be avoided in hardware by using a capacitor to smooth the transition, but I'm going to do it in software where the solution is to take several readings and wait for the signal to stablise. Normally, I make use of interrupts and timeouts to wait for a stable signal, but the code is always more complicated than I'd like. So this time I've decided to treat the Pi more like a microcontroller and just poll the pins at a regular iterval. Every 3 milliseconds, I read a digital high/low from the six GPIO pins and emit new button events according to these rules:
If both of those are true, I can emit a new event. The code works something like this:
For the full context see the source code. By waiting for 8 stable reads at 3ms intervals, I give the signal 24ms to stablise.
And by storing the high/low bits in an unsigned 8-bit integer, I can easily compare it to ♦ Compared to some other debouncing techniques (like using a capacitor), my approach has the drawback that it won't protect against brief background inteference during an otherwise stable signal - the hope being that this has been safely avoided by those 10K resistors. This approach does, however, have a side-effect I like: provided the signal was stable beforehand, button presses are detected immediately. My aim is to reduce input latency wherever possible, because I know some screen updates might be slow. What I'm unsure about is whether polling every 3ms is significantly more CPU hungry than using interrupts. Using interrupts I don't see any CPU usage because it happens inside the kernel, but for all I know it might be doing the same polling I'm doing. If anyone knows how Linux handles this I'd be curious to learn more. Anyway, I think the CPU use will be negligable either way. Next time, I'll get a Rust program to automatically launch when the Pi is booted. Next entry: Bramley: 6. Splash Screen and Shutdown over a year agocaolan - Oct 14Bramley: 4. Driving a Sharp Memory Display from Rust♦ Having convinced myself to use a Sharp Memory Display for the Bramley, I put an order in at Pimoroni for the new 400x240 module. A few years ago, I'd bought a 96x96 version (photo above), and I decided to dig that out too so I could make an early start on the code. Unlike the new display, the 'black' pixels on the older board actually have a mirror-like finish. It's striking, but over a year ago ♦ Having convinced myself to use a Sharp Memory Display for the Bramley, I put an order in at Pimoroni for the new 400x240 module. A few years ago, I'd bought a 96x96 version (photo above), and I decided to dig that out too so I could make an early start on the code. Unlike the new display, the 'black' pixels on the older board actually have a mirror-like finish. It's striking, but depending on what it reflects it can be hard to read. Both displays use the same interface, though, so it will make good target practice until my new screen arrives. I gathered my resources and prepared to write a Rust library.
The Memory Display is driven using SPI, but unlike most SPI devices, it expects the chip select pin to be high when writing to the display, rather than low. And, unfortunately, I couldn't persuade the Raspberry Pi to use active high. Eventually, I gave up and wired a generic GPIO pin as my chip select that I manually set high or low as required. Other libraries do the same, so perhaps it's a known limitation of the Pi. Anyway, with my workaround in place, I was ready to write some pixels. ♦ Oh, the new screen arrived by the way! And good news - it works on both screens. There's just one problem: the refresh rate is too slow - especially on the large display - it can take 160ms to redraw the screen. Even if I did nothing but draw, that's only 6 frames per second! Going fasterIf you want to know how painful 160ms of latency is, try it for yourself (alternative). If I'm going to use this screen, it will need to update faster. So I checked the datasheet to see how fast I could push the SPI clock. I had started with the 'typical' value of 1MHz. By switching to the 'maximum' 2MHz I could refresh the screen in 80ms. Much better. I also spent some time tweaking the SPI buffer size and using chunked writes, but the gains were small. After that, it seemed like a good point to benchmark against Adafruit's own Python library - just to check I wasn't doing anything stupid. The Python performance wasn't great, but in the process of inspecting their code, I noticed they ran the clock at 8MHz - four times the maximum! With a little apprehension, I set my code to 8MHz too and ran it. The screen updated in 37ms. But was it safe? That was a few weeks ago now, and since then - after 11 months at 8MHz - Adafruit have slowed their library to the recommended 2MHz. Perhaps they'd run into problems? I asked Sharp for their opinion. To my surprise, someone responded. The memory display has a mean time between failures of 50KHrs at 25°C, and they explained that, at a higher speed, I'd increase the internal temperatures and lower its functional life. It might be OK - depending on product variation and my control over the operating temperature - but they understandably wouldn't warrant values outside the spec. So, at least to begin with, I'm going to stick to 2MHz and hope 80ms is fast enough for a full screen refresh. I wouldn't want to damage anyone elses screen if they ran my code - even if it works OK on mine. But, before I finish, this screen has one more trick up its sleeve. Doing lessEach screen update looks something like this:
Basically, a byte to tell the display we're writing to it, followed by each line's address and pixel data. This is where the 'Memory' in 'Memory Display' comes in. Once updated, those lines will display the same pixels continually while powered. So, in theory, I could skip any lines that remain the same between updates and send only those that changed. ♦ I modified my library to remember the pixels from each screen update and diff them against the next. Now, if a row hasn't changed, I skip it. It's a big improvement, and the update times now vary between 0ms - 80ms, depending on how many rows were updated. Updating horizontal text, for example, would only modify a few rows, which is great because it gives low latency for typing. The varying update times are a quirk, for sure - but I think this might be good enough. My Rust library for the Sharp Memory Display (source code) Next time, I'll look at reading input from the six buttons on the back of the device. Next entry: Bramley: 5. Buttons over a year agocaolan - Oct 07Bramley: 3. DisplayAsk someone to point to their computer and they usually point to their monitor. The display is a huge part of a machine's personality. It is also likely to be its largest power drain. So in a portable device, it's doubly important to get right. My first thought was e-ink - it sips minuscule power and looks beautiful at rest. But, when stirred, will flash desperately to shed its soiled pix over a year ago Ask someone to point to their computer and they usually point to their monitor. The display is a huge part of a machine's personality. It is also likely to be its largest power drain. So in a portable device, it's doubly important to get right. My first thought was e-ink - it sips minuscule power and looks beautiful at rest. But, when stirred, will flash desperately to shed its soiled pixels. Even partial updates feel too slow. I need something more immediate. That's when I remembered the Sharp Memory Display. It sits somewhere between e-ink and an LCD: daylight readable and low power but with a faster refresh rate. And luckily, Adafruit have now released a breakout board for the larger 2.7" version. ♦ It's a little limited in pixels, at only 400x240 resolution, but consider all the great VGA games that were released in only 320x200: Doom, Worms, Prince of Persia, Indiana Jones and the Fate of Atlantis… Ok, I'm cheating a bit. The Memory display is monochrome - 1-bit per pixel - and those games all use colour. But I think 1-bit graphics might develop some of the personality I'm searching for. ♦ The Macintosh Plus had a 512x342 pixel 1-bit display and buckets of personality. Susan Kare's fonts and icons looked excellent on it. ♦ Want a more modern example of 1-bit artwork? Just look at this beautiful screenshot from Return of the Obra Dinn: ♦ Or these Creative Commons pixel fonts by fontenddev: ♦ So I'm not worried about the 1-bit colour. And I'm not worried about the 400x240 pixels either. If the PalmPilot managed with a 160x160 monochrome display, then I'm sure I can write some useful programs with 400x240 pixels. Now I just need to write some code to drive it. Next entry: Bramley: 4. Driving a Sharp Memory Display from Rust over a year agocaolan - Oct 06Bramley: 2. Human InputTock, tock - bouncing like a bee at a windowpane. A buzz echos on the other side, but alas, you can only watch. I don't like touchscreens. That's why my desk is adorned with beautiful keyboards: the affirmation of a mechanical switch. Unfortunately, it's difficult to squeeze a full keyboard into your pocket, and the tiny membrane keys that do fit compromise tactility too far - if the Bram over a year ago Tock, tock - bouncing like a bee at a windowpane. A buzz echos on the other side, but alas, you can only watch. I don't like touchscreens. That's why my desk is adorned with beautiful keyboards: the affirmation of a mechanical switch. Unfortunately, it's difficult to squeeze a full keyboard into your pocket, and the tiny membrane keys that do fit compromise tactility too far - if the Bramley is going to have nice switches, it'll have to have fewer of them. I'm inspired by an old telegraph code invented by Émile Baudot in the 1870s. It contains 5 bits that, before teletypewriters, operators would transmit using five piano-like keys. ♦ By pressing several keys at once in a chord, the operator had enough combinations to cover the whole alphabet. Baudot clearly had the operators in mind, too, because his code used only one hand or a single key-press for the most common characters. Letter Finger IV V I II III A * E * I * * O * * * U * * Y * more…I'm going to use these chords for the Bramley's keyboard. But, since not all keys are covered by the Baudot Code, I also want to expand its repertoire. On smaller keyboards, the key map is often split and layered like a deck of cards. Android does this too with separate layers for numbers, symbols, and emoji. By adding more (virtual) layers, I vastly increase the available input. I think six buttons is the minimum: one key to swap layers, five more to populate them. ♦ I know it's odd, but I decided to place the buttons on the back - three down either side. To push a button, I need an opposing force: a desk, my lap, the palm of my hand. But if I hold the device like a smartphone, I only have one hand free to play chords - or two thumbs, if I hold it like a joypad. With buttons on the back, however, I can rest it on my fingertips and press all the keys comfortably. Of course, I still need to write software for chorded input before I can really test the design - but that can wait until I have a working screen. Next entry: Bramley: 3. Display over a year agocaolan - Oct 05Bramley: 1. Build LogI want to create a small handheld device for taking notes and playing games. Something with personality - like a Palm Pilot, Gameboy or Walkman. It's not to replace a phone - hell, it may not even be networked - I just want something that's fun to use. This is my build log: a series of posts documenting each step and my thoughts along the way. Here's a sneak preview of what I've built so over a year ago I want to create a small handheld device for taking notes and playing games. Something with personality - like a Palm Pilot, Gameboy or Walkman. It's not to replace a phone - hell, it may not even be networked - I just want something that's fun to use. This is my build log: a series of posts documenting each step and my thoughts along the way. Here's a sneak preview of what I've built so far: ♦ It's called the Bramley after the apple used in crumbles and pies. In the next post I'll discuss its design. Next entry: Bramley: 2. Human Input over a year ago |