Generating ball tickets/programmes using LaTeX

My father does a lot of Scottish Country dancing, he was a treasurer for the Clan MacKenzie association for quite a while, and a president there for about 10 years too. He was given a task for making some ball tickets, but each one being uniquely numbered.

After hearing him swear at LibreOffice for a bit, then at Avery’s label making software, I decided to take matters into my own hands.

First step was to come up with a template. The programs were to be A6-size booklets; made up of A5 pages folded in half. For ease of manufacture, they would be printed two to a page on A4 pages.

The first step was to come up with the template that would serve as the outer and inner pages. The outer page would have a placeholder that we’d substitute.

The outer pages of the programme/ticket booklet… yes there is a typo in the last line of the “back” page.
\documentclass[a5paper,landscape,16pt]{minimal}
\usepackage{multicol}
\setlength{\columnsep}{0cm}
\usepackage[top=1cm, left=0cm, right=0cm, bottom=1cm]{geometry}
\linespread{2}
\begin{document}
\begin{multicols}{2}[]

\vspace*{1cm}

\begin{center}
\begin{em}
We thank you for your company today\linebreak
and helping to celebrate 50 years of friendship\linebreak
fun and learning in the Redlands.
\end{em}
\end{center}

\begin{center}
\begin{em}
May the road rise to greet you,\linebreak
may the wind always be at your back,\linebreak
may the sun shine warm upon your face,\linebreak
the rains fall soft upon your fields\linebreak
and until we meet again,\linebreak
may God gold you in the palm of his hand.
\end{em}
\end{center}

\vspace*{1cm}

\columnbreak
\begin{center}
\begin{em}
\textbf{CLEVELAND SCOTTISH COUNTRY DANCERS\linebreak
50th GOLD n' TARTAN ANNIVERSARY TEA DANCE}\linebreak
\linebreak
1973 - 2023\linebreak
Saturday 20th May 2023\linebreak
1.00pm for 1.30pm - 5pm\linebreak
Redlands Memorial Hall\linebreak
South Street\linebreak
Cleveland\linebreak
\end{em}
\end{center}

\begin{center}
\begin{em}
Live Music by Emma Nixon \& Iain Mckenzie\linebreak
Black Bear Duo
\end{em}
\end{center}

\vspace{1cm}

\begin{center}
\begin{em}
Cost \$25 per person, non-dancer \$15\linebreak
\textbf{Ticket No \${NUM}}
\end{em}
\end{center}
\end{multicols}
\end{document}

The inner pages were the same for all booklets, so we just came up with one file that was used for all. I won’t put the code here, but suffice to say, it was similar to the above.

The inner pages, no placeholders needed here.

So we had two files; ticket-outer.tex and ticket-inner.tex. What next? Well, we needed to make 100 versions of ticket-outer.tex, each with a different number substituted for $NUM, and rendered as PDF. Similarly, we needed the inner pages rendered as a PDF (which we can do just once, since they’re all the same).

#!/bin/bash
NUM_TICKETS=100

set -ex

pdflatex ticket-inner.tex
for n in $( seq 1 ${NUM_TICKETS} ); do
	sed -e 's:\\\${NUM}:'${n}':' \
            < ticket-outer.tex \
            > ticket-outer-${n}.tex
	pdflatex ticket-outer-${n}.tex
done

This gives us a single ticket-outer.pdf, and 100 different ticket-inner-NN.pdf files that look like this:

A ticket outer pages document with substituted placeholder

Now, we just need to put everything together. The final document should have no margins, and should just import the relevant PDF files in-place. So naturally, we just script it; this time stepping every 2 tickets, so we can assemble the A4 PDF document with our A5 tickets: outer pages of the odd-numbered ticket, outer pages of the even-numbered ticket, followed by two copies of the inner pages. Repeat for all tickets. We also need to ensure that initial paragraph lines are not indented, so setting \parindent solves that.

This is the rest of my quick-and-dirty shell script:

cat > tickets.tex <<EOF
\documentclass[a4paper]{minimal}
\usepackage[top=0cm, left=0cm, right=0cm, bottom=0cm]{geometry}
\usepackage{pdfpages}
\setlength{\parindent}{0pt}
\begin{document}
EOF
for n in $( seq 1 2 ${NUM_TICKETS} ); do
	m=$(( ${n} + 1 ))
	cat >> tickets.tex <<EOF
\includegraphics[width=21cm]{ticket-outer-${n}.pdf}
\includegraphics[width=21cm]{ticket-outer-${m}.pdf}
\includegraphics[width=21cm]{ticket-inner.pdf}
\includegraphics[width=21cm]{ticket-inner.pdf}
EOF
done
cat >> tickets.tex <<EOF
\end{document}
EOF
pdflatex tickets.tex

The result is a 100-page PDF, which when printed double-sided, will yield a stack of tickets that are uniquely numbered and serve as programmes.

Mastodon experiment: a few months in

A little while back I decided to try out Mastodon, deploying my own instance running as a VM on my own hardware. This was primarily done to act as a testing ground for experimenting with integrating with it, but also as a means of keeping up with the news.

The latter is particularly important, as I no longer have the radio on all the time. I might hear a news item in the morning, but after the radio switches off, I’m unlikely to turn it back on until the next working day. A lot of news outlets moved to Twitter over the past decade, but with that site in its death throws, the ActivityPub ecosystem is looking like a safer bet.

Not many outlets are officially using this newer system yet. There are a few outlets that do publish directly to Mastodon/ActivityPub, examples being Rolling Stone, The Markup (who run their own instance), STAT, The Conversation AU/NZ and OSNews. Some outlets aren’t officially posting on ActivityPub, but are nonetheless visible via bridges from RSS (e.g. Ars Technica) and others are proxies of these outlets’ Twitter accounts (e.g. Reuters, Al Jazeera, The Guardian, Slashdot). Others are there, but it’s not clear how the material is being mirrored or if they’re official.

There’s also a decent dose of satire if you want it, including satirical news outlets The Chaser and The Shovel, and cartoonists such as Christopher Downes, David Rowe, Fiona Katauskas, Jon Kudelka, David Pope, Cathy Wilcox and Glen Le Lievre.

As you can gather, a big chunk of who I follow is actually news outlets, or humour. There are a few people on my “follow” list whom are known for posting various humour pieces from elsewhere, and I often “boost” (re-post) their content.

Meta (who run Facebook) have made noises they might join in with their own Twitter-clone in the ActivityPub fediverse. I wouldn’t mind this so much — the alternatives to them doing this is: (1) the rest of us needing dozens of social media accounts to keep in touch with everybody, (2) relying on the good will of some mega-site to connect us all, or (3) forgoing being in touch altogether.

I tried option (1) in the early part of this century, and frankly I’m over it. Elon Musk dreams of Twitter becoming option (2) but I think the chances of this happening are buckleys and none. (3) is not realistic, we’re social beings.

Some of these instances will be ad supported, and I guess that’s a compromise we may have to live with. Servers need electricity and Internet to run, and these are not free. A bigger cost to running big social networks is actually managing the meat-ware side of the network — moderation, legal teams to decide how moderation should be applied, handling take-down notices… etc.

ActivityPub actually supports flagging the content so the post is not “listed” (indexed by instances’ search engines), private posts (cannot be boosted, visible to followers only), even restricting to just those mentioned specifically. I guess there’s room for one more: “non-commercial use only” — commercial instances could then decide to they forgo the advertising on that post, or do they filter the post.

ActivityPub posting privacy settings on Mastodon

I did hear rumblings that the EU was likely to pass some laws requiring a certain level of interoperability between social networks, which ActivityPub could in fact be the basis of.

Some worry about another Eternal September moment — a repeat of the time when AOL disgorged its gaggle of novice Internet users on an unsuspecting Usenet system. Usenet users prior to AOL opening up in 1993 only had to deal with similar shenanigans once a year around September when each new batch of first year uni students would receive their Internet access accounts.

I’m not sure linking of a site like Facebook or Tumblr (who have also mentioned joining the Fediverse) is all that big a deal — Mastodon lets you block whole domains if you so choose, and who says everybody on a certain site is going to cause trouble?

Email is a federated system, always has been, and while participation as a small player is more complicated than it used to be, it is still doable. Big players like Microsoft and Google haven’t killed off email (even with the former doing their best to do so with sub-par email clients and servers). Yes, we have a bigger spam problem than we had back in the 90s, but keeping the signal-to-noise ratio up to useful levels is not impossible, even for mere mortals.

We do have to be mindful of the embrace-extend-break game that big business like to play with open protocols, I think Mastodon gGmbH’s status as a not-for-profit and a reference implementation should help here.

I’d rather throw my support behind a system that can allow us to all interoperate, and managing the misbehaviour that may arise on a case-by-case basis, is a better solution than us developing our own little private islands. The info-sec press seem to have been quick to jump ship from Twitter to Mastodon. IT press is taking a little longer, but there’s a small but growing group. I think the journalism world is going to be key to making this work and ensuring there’s good-quality content to drown out the low-quality noise. If big players like Meta joining in help push this along, I think this is worth encouraging.

A crude attempt at memory management

The other day I had a bit of a challenge to deal with. My workplace makes embedded data collection devices which are built around the Texas Instruments CC2538 SoC (internal photos visible here) and run OpenThread. To date, everything we’ve made has been an externally-powered device, running off either DC power (9-30V) or mains (120/240V 50/60Hz AC). CC2592 range extender support was added to OpenThread for this device.

The CC2538, although very light on RAM (32KiB), gets the job done with some constraints. Necessity threw us a curve-ball the other day, we wanted a device that ran off a battery. That meant going into sleep mode periodically, deep sleep! The CC2538 has a number of operating modes:

  1. running mode (pretty much everything turned on)
  2. light sleep mode (clocks, CPU and power stays on, but we pause a few peripherals)
  3. deep sleep mode — this comes in four flavours
    • PM0: Much like light-sleep, but we’ve got the option to pause clocks to more peripherals
    • PM1: PM0, plus we halt the main system clock (32MHz crystal or 16MHz RC), halting the CPU
    • PM2: PM1 plus we power down the bottom 16KiB of RAM and some other internal peripherals
    • PM3: PM2 plus we turn off the 32kHz crystal used by the sleep timer and watchdog.

We wanted PM2, which meant while we could use the bottom 16KiB of RAM during run-time, the moment we went to sleep, we had to forget about whatever was kept in that bottom 16KiB RAM — since without power it would lose its state anyway.

The challenge

Managing RAM in a device like this is always a challenge. malloc() is generally frowned upon, however in some cases it’s a necessary evil. OpenThread internally uses mbedTLS and that, relies on having a heap. It can use one implemented by OpenThread, or one provided by you. Our code also uses malloc for some things, notably short-term tasks like downloading a new configuration file or for buffering serial traffic.

The big challenge is that OpenThread itself uses a little over 9KiB RAM. We have a 4KiB stack. We’ve got under 3KiB left. That’s bare-bones OpenThread. If you want JOINER support, for joining a mesh network, that pulls in DTLS, which by default, will tell OpenThread to static-allocate a 6KiB buffer.

9KiB becomes about 15KiB; plus the stack, that’s 19KiB. This is bigger than 16KiB — the linker gives up.

Using heap memory

There is a work-around that gets things linking; you can build OpenThread with the option OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE — if you set this to 1, OpenThread forgoes its own heap and just uses malloc / free instead, implemented by your toolchain.

OpenThread builds and links in 16KiB RAM, hooray… but then you try joining, and; NoBufs is the response. We’re out of RAM. Moving things to the heap just kicked the can down the road, we still need that 6KiB, but we only have under 3KiB to give it. Not enough.

We have a problem in that, the toolchain we use, is built on newlib, and while it implements malloc / free / realloc; it does so with a primitive called _sbrk(). We define a pointer initialised up the top of our .bss, and whenever malloc needs more memory for the heap, it calls _sbrk(N); we grab the value of our pointer, add N to it, and return the old value. Easy.

Except… we don’t just have one memory pool now, we have two. One of which, we cannot use all the time. OpenThread, via mbedTLS also winds up calling on malloc() very early in the initialisation (as early as the otInstanceInitSingle() call to initialise OpenThread). We need that block of RAM to wind up in the upper 16KiB that stays powered on — so we can’t start at address 0x2000:0000 and just skip over .data/.bss when we run out.

malloc() will also get mighty confused if we suddenly hand it an address that’s lower than the one we handed out previously. We can’t go backwards.

I looked at replacing malloc() with a dual-pool-aware version, but newlib is hard-coded in a few places to use its own malloc() and not a third-party one. picolibc might let us swap it out, but getting that integrated looked like a lot of work.

So we’re stuck with newlib‘s malloc() for better or worse.

The hybrid approach

One option, we can’t control what malloc the newlib functions use. So use newlib‘s malloc with _sbrk() to manage the upper heap. Wrap that malloc with our own creation that we pass to OpenThread: we implement otPlatCAlloc and otPlatFree — which are essentially, calloc and free wrappers.

The strategy is simple; first try the normal calloc, if that returns NULL, then use our own.

Re-purposing an existing allocator

The first rule of software engineering, don’t write code you don’t have to. So naturally I went looking for options.

Page upon page of “No man don’t do it!!!”

jemalloc looked promising at first, it is the FreeBSD malloc(), but that there, lies a problem — it’s a pretty complicated piece of code aimed at x86 computers with megabytes of RAM minimum. It used uint64_ts in a lot of places and seemed like it would have a pretty high overhead on a little CC2538.

I tried avr-libc‘s malloc — it’s far simpler, and actually is a free-list implementation like newlib‘s version, but there is a snag. See, AVR microcontrollers are 8-bit beasts, they don’t care about memory alignment. But the Cortex M3 does! avrlibc_malloc did its job, handed back a pointer, but then I wound up in a HARDFAULT condition because mbedTLS tried to access a 32-bit word that was offset by a few bytes.

A simple memory allocator

The approach I took was a crude one. I would allocate memory in fixed-sized “blocks”. I first ran the OpenThread code under a debugger and set a break-point on malloc to see what sizes it was asking for — mostly blocks around the 128 byte mark, sometimes bigger, sometimes smaller. 64-byte blocks would work pretty well, although for initial testing, I went the lazy route and used 8-byte blocks: uint64_ts.

In my .bss, I made an array of uint8_ts; size equal to the number of 8-byte blocks in the lower heap divided by 4. This would be my usage bitmap — each block was allocated two bits, which I accessed using bit-banding: one bit I called used, and that simply reported the block was being used. The second was called chained, and that indicated that the data stored in this block spilled over to the next block.

To malloc some memory, I’d simply look for a string of free blocks big enough. When it came to freeing memory, I simply started at the block referenced, and cleared bits until I got to a block whose chained bit was already cleared. Because I was using 8-byte blocks, everything was guaranteed to be aligned.

8-byte blocks in 16KiB (2048 blocks) wound up with 512 bytes of usage data. As I say, using 64-byte blocks would be better (only 256 blocks, which fits in 64 bytes), but this was a quick test. The other trick would be to use the very first few blocks to store that bitmap (for 64-byte blocks, we only need to reserve the first block).

The scheme is somewhat inspired by the buddy allocator scheme, but simpler.

Bit banding was simple enough; I defined my struct for accessing the bits:

struct lowheap_usage_t {
        uint32_t used;
        uint32_t chained;
};

and in my code, I used a C macro to do the arithmetic:

#define LOWHEAP_USAGE                                                   \
        ((struct lowheap_usage_t*)(((((uint32_t)&lowheap_usage_bytes)   \
                                     - 0x20000000)                      \
                                    * 32)                               \
                                   + 0x22000000))

The magic numbers here are:

  • 0x20000000: the start of SRAM on the CC2538
  • 0x22000000: the start of the SRAM bit-band region
  • 32: the width of each word in the CC2538

Then, in my malloc, I could simply call…

struct lowheap_usage_t* usage = LOWHEAP_USAGE;

…and treat usage like an array; where element 0 was the usage data for the very first block down the bottom of SRAM.

To implement a memory allocator, I needed five routines:

  • one that scanned through, and told me where the first free block was after a given block number (returning the block number) — static uint16_t lowheap_first_free(uint16_t block)
  • one that, given the start of a run of free blocks, told me how many blocks following it were free — static uint16_t lowheap_chunk_free_length(uint16_t block, uint16_t required)
  • one that, given the start of a run of chained used blocks, told me how many blocks were chained together — static uint16_t lowheap_chunk_used_length(uint16_t block)
  • one that, given a block number and count, would claim that number of blocks starting at the given starting point — static void lowheap_chunk_claim(uint16_t block, uint16_t length)
  • one that, given a starting block, would clear the used bit for that block, and if chained was set; clear it and repeat the step on the following block (and keep going until all blocks were freed) — static void lowheap_chunk_release(uint16_t block)

From here, implementing calloc was simple:

  1. first, try the newlib calloc and see if that succeeded. Return the pointer we’re given if it’s not NULL.
  2. if we’re still looking for memory, round up the memory requirement to the block size.
  3. initialise our starting block number (start_nr) by calling lowheap_first_free(0) to find the first block; then in a loop:
    • find the size of the free block (chunk_len) by calling lowheap_chunk_free_length(start_nr, required_blocks).
    • If the returned size is big enough, break out of the loop.
    • If not big enough, increment start_nr by the return value from lowheap_chunk_used_length(start_nr + chunk_len) to advance it past the too-small free block and the following used chunk.
    • Stop iterating of start_nr is equal to or greater than the total number of blocks in the heap.
  4. If start_nr winds up being past the end of the heap, fail with errno = ENOMEM and return NULL.
  5. Otherwise, we’re safe, call lowheap_chunk_claim(start_nr, required_blocks); to reserve our space, zero out the actual blocks allocated, then return the address of the first block cast to void*.

Implementing free was not a challenge either: either the pointer was above our heap, in which case we simply passed the pointer to newlib‘s free — or if it was in our heap space, we did some arithmetic to figure out which block that address was in, and passed that to lowheap_chunk_release().

I won’t publish the code because I didn’t get it working properly in the end, but I figured I’d put the notes here on how I put it together to re-visit in the future. Maybe the thoughts might inspire someone else. 🙂

One year on in the Russia-Ukraine conflict

It was about this time, news was a-buzz with the talk of tanks lining up on Russia’s western border, then crossing over into Ukraine in a conflict that was meant to be over in a little over a fortnight.

Time must move slow for Vladimir Putin — 12 months in real time and they’re still at it! So that’s approximately one “Putin Day” equates to approximately 26 real days for the rest of us. (Some would argue the conflict actually began in 2014 — I guess there’s some merit in that opinion, but things really began heating up 12 months ago.)

I’ve actually learned a lot about the place. Okay, “a lot” is a relative measure, what I actually know could be scribbled onto the back of a postage stamp with a thick permanent marker, however I am picking up tidbits here and there.

12 months ago, I actually had to frequently correct my spelling — I kept missing the “i” (i.e. “Ukrane”). I wasn’t aware Chernobyl was actually on Ukraine’s northern border with Belarus (or that Belarus was even their northern neighbour). I might’ve heard of Moldova, but wasn’t sure where it was, I had not heard of the disputed territory of Transnistria. Nor did I realise they shared their western border with Poland.

Over the last 12 months I’ve slowly become a bit more familiar with where some of their more major cities are: Lviv in the west, the port cities of Odesa, Mykolaiv (and the general Kherson area — watermelon territory) and we heard lots about Mariupol, particularly the steelworks there. Dnipro and Luhansk in the east, Kharkiv and Sumy in the north-east… Kyiv up in the north.

Point me to a blank map 12 months ago and I wouldn’t have had much idea where those places were, but I have a vague idea now.

I could spot Cyrillic writing before this conflict but couldn’t read any of it. Today while I can’t identify the language, I’m starting to be able to pick out individual letters and recognise the odd word. Various news articles have covered various aspects of the Ukrainian culture. Of course, the before-and-after photos that pop up from time to time showing what was, and what’s just been pulverised by Russian shelling reveal a lot of ornate buildings that are now little more than rubble.

Okay, so little things… very basic facts. The depressing thing is it’s taken a bloody war to even gain a modest familiarity with these things. I have a fear of flying and have no passport, so there’s practically zero chance of me visiting that part of the world.

I guess there was no real necessity for me to really understand the geography of the area pre-conflict, it would have been a personal interest thing if I had done so. Whatever happens though, I think the rest of the world will have to be there ready to help pick up the pieces and help Ukraine re-build.

I wouldn’t be doing business with any businesses based in Belarus, Democratic People’s Republic of Korea (aka North Korea), Eritrea, Mali, Nicaragua, Russia or Syria… and I’d think twice about “no-limits” Russia supporter China.

If the governments in those places change their tune on this conflict, then we re-evaluate, but it’s a fact that supporting business there helps support that country’s government, which only positively-reinforces their current behaviour.

Sadly, with North Korea firing test missile after test missile into the sea, and China eyeing off Taiwan (and its proximity to the Mariana Trench — it’s just about chip fabrication) with jealous eyes — one can only wonder what the next few decades have in store for us.

The real scary thing, I don’t think we in Australia can really count on our allies. The United Kingdom is an utter basket-case post-Brexit and the United States is actually looking very much less united with every passing day as the society there slowly edges towards a race-fuelled civil war.

Methinks we need to start looking at doing things on our own soil, “global economy” looks like it’ll be taking a back seat for a little while!

Demise of classic hardware: the final act

So today I finally got around to the SGI kit in my possession. Not quite sure where all of it went, there’s a SGI PS/2 keyboard, Indy Presenter LCD and a SGI O2 R5000 180MHz CPU module that have gone AWOL, but this morning I took advantage of the Brisbane City Council kerb-side clean-up.

Screenshot of the post — yes I need to get Mastodon post embedding working

I rounded up some old Plextor 12× CD-ROM drives (SCSI interface) that took CD caddies (remember those?) as well to go onto the pile, and some SCSI HDDs I found laying around — since there’s a good chance the disks in the machines are duds. I did once boot the Indy off one of those CD-ROM drives, so I know they work with the SGI kit.

The machines themselves had gotten to the point where they no longer powered on. The O2 at first did, and I tried saving it, but I found:

  1. it was unreliable, frequently freezing up — until one day it stopped powering on
  2. the case had become ridiculously brittle

The Indy exploded last time I popped the cover, and fragments of the Indigo2 were falling off. The Octane is the only machine whose case seemed largely intact. I had gathered up what IRIX kit I had too, just in case the new owners wanted to experiment. archive.org actually has the images, and I had a crack at patching irixboot to be able to use them. Never got to test that though.

Today I made the final step of putting the machines out on the street to find a new home. It looks like exactly that has happened, someone grabbed the homebrew DB15 to 13W3 cable I used for interfacing to the Indy and Indigo2, then later in the day I found the lot had disappeared.

With more room, I went and lugged the old SGI monitor down, it’s still there, but suspect it’ll possibly go too. The Indy and Indigo2 looked to be pretty much maxxed-out on RAM, so should be handy for bits for restoring other similar-era SGI kit. I do wish the new owners well with their restoration journey, if that’s what they choose to do.

For me though, it’s the end of an era. Time to move on.

Earmuff headset reverse engineering

So, previously I’ve tried looking for a suitable earmuff headset, and drew blanks: they wanted $400+ for them and I wasn’t willing to spend that much. That said, there are times when such a headset is useful: if it’s raining heavily on the roof of the annex, hearing the radio is bloody difficult!

Now, my tactical headset should fulfil that, but really it was intended for less noisy environments. What if that’s not enough? Years ago, Brisbane WICEN used to help out with the car rallies, and there you really do need something that will muffle outside noise. I had tried to make my own, with limited success, but one wondered how the commercial options compared.

The other day, I spotted this earmuff headset. One thing I note, the seller seems unaware of how these things are worn — that’s a neck band fellas, not a head band. (Or maybe their ears are wider than they are tall? Mine are taller than they are wide.)

This is up the upper limit of what I was willing to spend, but what the hell… we’ll try it. They arrived today (about 6 days ahead of schedule). They’re comfortable enough — sound dampening is reasonable. I like the fact the microphone is on the right side. There’s a PTT button on the left ear-cup and a volume control on the right.

Being a behind-the-head design, it plays nice with my hard hat too. The head strap is a little tricky in my case, since my hard hat has a chin strap — I figured out I had to undo the head strap, thread the headset through the chin strap, then put hard hat and headset on together. I should be able to wear it with the coolie hat as well.

A downside is basically when I do this, I can’t take the headset off without taking the hard hat off too, but the intent of this was something that I was going to leave on anyway, and the same criticism is true of the tactical headset too.

The headset, with its packaging and cables.

One wrinkle is that this headset you can see, uses a Kenwood-style pin-out — my Kenwood TH-D72A died recently, so great timing there. Luckily for us though, it’s just a small pigtail: we can make our own up to suit this pin-out. The pig-tail actually is a 6-pin Mini-DIN: Jaycar part number PP0366. I was thinking of chopping cables, but this is even simpler, we’ll just make our own pigtail.

At the other end, it appears to be a 5-pin mini-XLR, but I haven’t tried buzzing that out at this stage. As is typical with comms headsets, this one is mono. There’s a 3.5mm jack (left earcup) for plugging in a comms receiver or media player, and it works — in mono. The microphone appears to be an electret.

For now, I’ve decided to have a look at reverse-engineering the pin-out to use the headset as-is. I rummaged around and found the aforementioned connector in my junk box, still sealed in its packet, so I opened that up, and used the little insert inside to plug into the socket, put the headset on, and went buzzing with the multimeter. Using the Kenwood pigtail for clues (since I know the Kenwood pin-out), I located the PTT, speaker and microphone connections on the headset cable.

One pin appears to be a no-connect; the PTT and microphone are commoned.

Reverse-engineered Mini-DIN 6 pin-out

Set up a Mastodon instance

So yep, following my recent post about Twitter, I bit the bullet after some thought… I’m now reachable over Mastodon. I decided I’d start small, deploy my own instance where I could play around and get to know it.

In particular, as I’ve started doing a few small websites for people based on WordPress — social media integration has come up in these discussions — I figure it’s better to test against my own instance rather than me throwing pre-alpha grade code at someone else’s production server. I can’t do this with Facebook, I can’t do this with Twitter, but I can do this with Mastodon. If either of those two decide to implement ActivityPub protocols, sure, I’ll let them connect.

There’s also the practice I’ve been engaging in for some years, of just emailing links to sites I stumble across to people who I think may be interested — some of those could be posted on Mastodon since this is a format that would suit that use case well. Maybe cartoons, images — although I need to watch copyright!

The fact that I was able to deploy it on my own hardware, means I can be 100% responsible for what I post there, and post there 100% on my own terms. While I’m unlikely to intentionally post something inflammatory, when someone else owns the house, they make the rules, and they can change the rules when they like. Some journalists found this out the hard way, when Twitter banned them for discussing the ElonJet Twitter account. Then scores of people got hit by the ban-hammer for simply mentioning rival social networks. A month ago, there was nothing wrong with doing this, now it’s taboo on that social network. Seems ironic that a “social” network must be so anti-social.

Individual server admins might block my server, and that’s fine, that’s their right. Big deal. As stated, this is more for me to learn in a controlled environment. By me posting here, I cost them nothing unless one of their users decides my content is interesting, and me subscribing to a few posts can be done with minimal impact. I think the risk of this is low.

Right now, I’ve just “followed” a few IT and general journalists… to get a rough “feel” for how this stuff works. Following someone “cross servers” like this is a little clunky, but I can understand why it is that way: when you visit the remote server’s page, to them you’re an anonymous user — I don’t think there is a way yet to safely “identify” yourself to a Mastodon server without identifying yourself to every Internet web page. Maybe that’ll be worked on as time progresses, who knows? Time will tell.

I don’t expect to “toot my own horn” on Mastodon much. Those who are interested in what I’m doing can look up what I’m up to directly. I think most of the Mastodon content I post will be articles others have written. Then again, this whole blog started in 2006 as a way of publicising my activities in Gentoo, and look where it is today? It has evolved, and so might this. Once again, only time will tell.

Yaesu FTM-350AR audio interface: part two

Today I rummaged through my junk box and pulled out:

  • a screw-terminal type RJ-45 jack
  • a 3.5mm stereo panel-mount socket

Sadly, the socket is too big to fit inside the RJ-45 jack housing, but I was able to cobble together a test using the jack as a break-out.

As it happens, there’s some repeater traffic that I’m hearing through the tablet beautifully. I set up Mumble to just transmit on VOX activity — this seems good enough. The signal out of the radio is quiet, so VOX works well enough for detecting the incoming signal with no false detection.

There’s a fraction-second delay, which is to be expected for a cobbled together Radio-over-IP solution such as this. It’ll be annoying if you happen to be next to the radio, but probably fine if you’re away from the radio (or you use a headset so you hear the delayed signal through the headset louder than the real-time signal out of the radio).

Things I need to look into from here:

  • Computer transmit audio interface
  • Computer PTT interface
  • Mumble integration into the above

Yaesu FTM-350AR audio interface: part one

I do a lot of support for horse endurance rides up at Imbil, and that means running a base for about 3 or 4 check-points. My go-to radio for this has been the Yaesu FTM-350AR, which is a good rig, but the head unit has woefully pathetic audio output.

The mono speaker is better, and you can get pretty good results plugging an un-amplified pair of speakers into the back — I use some old “SoundBLASTER” speakers from a SoundBLASTER 16 sound card. Works great if you’re in earshot of the radio, and much better than the built-in speaker in the radio body or the pathetic excuse for speakers in the head unit.

The radio can do cross-band, and this works pretty well, assuming you can find a pair of frequencies that don’t have interference. However, cross-band means you can’t use the radio directly as a standard radio, you must use a second radio to communicate on the cross-band link. In addition, it also means you can’t monitor more than one frequency at a time in base, or run packet from the same radio.

I’ve considered whether I make an audio interface that would link the radio to Mumble. Mumble is a VoIP solution for online gaming, and amongst other things, behaves much like a PTT-style radio in operation. The thinking was to interface the audio and PTT signals between the radio and the Mumble client, so that any transmit audio on the Mumble channel is fed to the radio, and vice versa.

Computer to radio is easy enough. Some years ago when I was doing the Brisbane to Gold Coast bicycle ride (net controller, in the back seat of the control vehicle), I had made an adaptor that let me plug in my DIN-5 headsets into the microphone port. I basically ignored most of the contacts, just paying attention to 0V (GND), EXTMIC and PTT.

Microphone pin-out for the FTM-350AR

An annoyance at the time is there was nowhere to plug headphones except the external speaker port, which is a little over-powered for headphones, so still had to rely on the radio’s speaker with my headset.

For a two-way radio interface, I’ll need to tap into that speaker audio. Now, an attenuator pad for the external speaker connector is a viable option, however it has the downside that it disables the internal speakers, meaning you need either a double-adaptor break-out, or you rely on the computer forwarding the audio somehow.

I contemplated opening up the head unit and adding a headphone jack, but then came to the conclusion that the audio is unlikely to be a digital signal being sent to the head unit — it must exist in analogue form on the 8-wire link between the head-unit and main-unit. Sure enough, I found some schematics, and there it is:

FTM-350AR head unit connector, showing left and right speaker connections

This goes off to a (rather anaemic) audio amplifier, so likely are line-level signals. Looks like I should be able to make a cable that taps off pins 2, 3 and 4 bringing those out to a 3.5mm jack which I can plug into an audio interface of my choosing. I have a Behringer U202 USB audio interface, seems like a good candidate for this experiment.

I lack the PTT signal on this connector, it is likely multiplexed in the TXD line, so unless I feel like reverse-engineering Yaesu’s protocols, the easiest bet is that I only tap received audio from here, and use the microphone socket for transmit audio (which I already know how to drive).

As for the software side, talKKonnect is one such option I could employ here.

Time to rummage through the junk box and see what I have on hand.

A stereo/binaural tactical headset: part three

So, last time I 90% finished the headset I’m likely to use at horse endurance ride and other “quiet” emergency comms events in the near future. The audio quality (at least on receive) sounds great. From what I can tell between hand-helds, the transmit audio sounds good. It’s quite comfortable to wear for extended periods, and while my modifications do muffle sound slightly, it’s perfectly workable.

There are just a couple of niggles:

  1. the headset uses a dynamic microphone, thus is not compatible (microphone-wise) with the other radio interfaces I have
  2. I used solid-core CAT5 which is sure to develop a fault at some inconvenient moment
  3. the cable to the connector is way too short

CAT5 was fine for a proof-of-concept, but really, I want a stranded cable for this. Being a dynamic microphone, it’s not necessary for it to be screened, and in fact, we should not be using unbalanced coaxial-type cable like we’d use on an electret microphone. That brings up another problem: interfaces designed for an electret will not work with this microphone — the impedance is too low and they’ll supply a bias current which needs to be blocked for dynamic microphones.

Right now I use a DIN-5 connector, but this is misleading — it implies it’ll connect to any radio interface with a DIN-5, and that my electret headsets will plug into its interfaces. At most I can listen with such a set-up, but not talk. The real answer is to use a completely different connector to avoid getting them mixed up. I decided whatever I used, it should be relatively common: exotic connectors are a pain to replace when they break. My criteria is as follows:

  • As discussed, common, readily available.
  • Cheap
  • Able to carry both speaker and microphone audio in a single connector so we don’t get speaker and microphone mixed up
  • Polarised, so we can’t get a connector around the wrong way
  • Ruggedised
  • Panel and cable mount versions available

Contenders I was considering were the 240° DIN-5 (I bought some by mistake once), 5-pin XLR and mini-XLRs, and the humble “CB microphone” connector. Other options I’ve used in the past include the DIN-7/DIN-8 and DE15HD (aka “VGA” connectors). DIN-7/DIN-8s can be fiddly to solder, and are overkill for the number of contacts. Same with DE15HDs — and the DE15HDs do not like moisture!

In the end, I decided the CB microphone connector seemed like my best bet. Altronics and Jaycar both sell these. I don’t know what the official name of these things is. They were common on radio equipment made between the mid-70s through to the late 80s — my Yaesu FT-290R-II uses an 8-pin connector, my Kenwood TS-120S uses a 4-pin. They’re pretty rugged, feature a screwing locking ring, and have beefy contacts for passing current. Usually the socket is available as a panel-mount only, but I found Altronics sell a cable-mount version (and today I notice Jaycar do too). If someone knows a RS/Mouser/Element14/Digikey link for these, I’ll put it here.

The big decision was to also consider how to wire the connector up. As this is “my own” standard, I can use whatever I like, but for the sake of future-me, I’ll document what I decided as I’ve forgotten how I wired up DIN-5’s before. I did have it written down, but misplaced that scrap of paper. I ended up quickly opening up a connector and taking this photo to refresh my memory.

A photo of an actual headset connector, showing the connections.

To wit, I therefore shall commit to public record, exactly how I wired this thing, and propose a standard for dynamic microphone headsets.

The current (left) DIN-5 pin-out, and my proposed “CB microphone” pin-out — both looking into socket

Some will point out that yes, I’m creating yet another standard. In my defence, mine is aimed at stereo headsets, which traditionally have been two separate 3.5mm phone jacks. Very easy to mix up. Some might argue that there exists a new standard in the form of the 4-pole TRRS connector, however not all interfaces are compatible — at the time when I devised the DIN-5 connector, I was using a Nokia 3310 which did not like having the microphone and speaker connected to a common pin.

Keeping them separate also allows me to do balanced audio tricks for interfacing electret microphones with radios like the Yaesu FT-857D which expect a dynamic microphone. For this; I need 5 contacts — left/right speaker, speaker common, and two for the microphone. There are 5-pole TRRRS connectors, the TP-105 is one such example — but they’re not common outside of the aviation industry where they are used.

For the cabling, I’ve cut the CAT5 cabling shorter, and spliced onto the end some 4-wire telephone ribbon onto each side. That makes the headset cable a comfortable length. I began by first soldering the “CB microphone” connector, choosing colours for the speaker and microphone connections and wiring it up in a “loop”, before cutting the far end of the loop, stripping back insulation and tinning the wires. I used a multimeter to decide which was the “left” and “right” connections — then these were spliced with some heat shrink.

After a quick test on the radio, I sealed it up using some hot-melt glue. This should prevent the solder joints from flexing and thus prolong the life of the connection.

I might look at a small J-FET or BJT adaptor cable that will allow me to use this headset in place of an electret microphone headset — as it’d be nice to be able to just plug this into the tablet to listen to music or use with VoIP. I’ve got extra line-mounted sockets for that. Not sure if it’s viable to go the other direction — I’d need a small battery to power the electret I think, that or a bypass switch on the PTT cable to allow me to power an electret microphone.

That though, is a project for another day.