Skip to content

September 2024 Updates

Published On:
Sep 30, 2024
Last Updated:
Sep 30, 2024

Split Battery Page

The Lithium Thionyl Chloride and Zinc-Air info have been split from the main Batteries page into their own pages. This was done to match the other battery chemistries.

Embedded Template Library (ETL)

Added info on the C++ Embedded Template Library (ETL).

nRF Connect for VS Code

Added info on nRF Connect for VS Code.

I2S Communication Protocol

Added info on the I2S Communication Protocol.

Cover Images

Added cover images to many pages, especially those in the Communication Protocols section.

Added Ability To Add Page Aliases

I added the ability for document pages to specify aliases. Aliases are great when you are moving pages around and the existing page is linked (and has a significant amount of traffic) from external sites. These aliases are added to each .mdx documents front matter like so:

src/content/pages/my-page.mdx
---
aliases: [/my-old-page/]
authors: [gbmhunter]
date: 2024-09-11
lastUpdated: 2024-09-11
tags: [my tag]
title: My Page
type: page
---
This is my page!

This field is specified in the content collection so it becomes available in the .data object when inside the getStaticPaths() function. To specify it in the collection’s schema, add the following to the src/content/config.ts file:

src/content/config.ts
const pagesCollection = defineCollection({
type: 'content',
schema: ({image}) => z.object({
aliases: z.array(z.string()).optional(), // Used to setup dynamic redirects. This is done in [...slug].astro
authors: z.array(z.string()),
date: z.date(),
description: z.string().optional(),
draft: z.boolean().default(false),
image: image().optional(),
lastUpdated: z.date(),
tags: z.array(z.string()).optional(),
title: z.string(),
}),
});

Then, in the [...slug].astro file which contains the getStaticPaths() function which determines the routes for the site, I check if the page has aliases and if so, add them to the static paths array. Alias paths are passed a aliasTo prop which is used to redirect to the correct page:

src/pages/[...slug].astro
---
export async function getStaticPaths() {
const combinedCollection = await getAllCollections();
const routablePages = getRoutablePages(combinedCollection);
const sidebarData = getSidebarData(combinedCollection);
let staticPaths = [];
for (let pageRoute of routablePages) {
//...
// Need to create additional paths if the page contains aliases in it's front matter
const aliases = pageRoute.data.aliases;
if (aliases) {
for (let alias of aliases) {
staticPaths.push({
params: { slug: alias },
props: {
aliasTo: pageRoute.slug,
// Nothing more is needed for the alias page
frontmatter: undefined,
render: undefined,
sidebarData: undefined,
},
});
}
}
staticPaths.push({
params: { slug: pageRoute.slug },
props: {
aliasTo: undefined,
frontmatter: pageRoute.data,
render: pageRoute.render,
sidebarData: sidebarData,
},
});
}
return staticPaths;
}
const { aliasTo, frontmatter, render, sidebarData } = Astro.props;
// Check if this page is an alias, and if so, redirect before attempting to render page
if (aliasTo) {
return Astro.redirect(`/${aliasTo}/`);
}
---

Created a Programming > Design Section

I decided that the Programming > General section was somewhat mis-labeled (what is “General” mean anyway?). Some of the pages under this section seemed to fit a “Design” section, so I created a new section called Programming > Design. As a start, the pages on state machines have been moved across.

Added Info on GNU Linker Scripts

Added info on GNU Linker Scripts.

Added New Optocoupler Schematic Symbols

Added new schematic symbols for bidirectional phototransistor optocouplers.

Moved the EEPROM Info to it’s Own Page

The EEPROM info has been moved from the Memory page to it’s own EEPROM page.

Added Code To Convert From Year/Month/Day To Day Of The Week

Added code to calculate the day of the week from the year, month and day in the Real Time Clock (RTC) ICs page.

Google Analytics vs. Umami

Umami has now been running long enough to do a useful comparison with Google Analytics. Below are the results for this blog, NinjaCalc and NinjaTerm. The time periods are slightly different for each site (due to different dates on which the data was valid).

Blog

Time range: 1st April 2024 - 30th June 2024 (3 months)

MetricGoogle AnalyticsUmami
Views92k116k
Visitors (Users)45k66.9k

NinjaCalc

Time range: 1st May 2024 - 31st August 2024 (4 months)

MetricGoogle AnalyticsUmami
Views13k4.32k
Visitors (Users)1.1k1.57k

NinjaTerm

Time range: 1st April 2024 - 31st August 2024 (5 months)

MetricGoogle AnalyticsUmami
Views1.5k3.21k
Visitors (Users)5901.18k

Added Info on the Memfault CLI

Added info on the Memfault CLI.

Updated Menu Styling

The default Starlight menu styling provided different font size and weights to sections and links. Given the arbitrarily nested nature of this site’s menu hierarchy, this did not look that great. I decided to make all the menu items look the same. To this I add to add some more selective CSS that would override the default Starlight styling.

A comparison of the old and new menu styling.

Info on Phototransistors

Added info on phototransistors, including the schematic symbol and a basic circuit.

Added Figure/Table Numbering and References

I wanted to be able to refer to figures and tables within page text using syntax like “Figure 1”, “Table 2” e.t.c, rather than “see the figure below”. Unfortunately this feature is not supported by .mdx out-of-the-box.

I figured I had two options to implement this:

  1. Write a remark/rehype plugin that runs during the build process.
  2. Write a Typescript script that runs on the client’s browser.

Even though the remark/rehype plug-in would of have been the best for load speeds, I decided to go with the Typescript route as it would be easier to implement (I have no experience with remark/rehype).

I decided to call this feature “Item References” to distinguish them from the references already used to cite sources. I made a <IRef /> Astro component that can be used to reference items. This allows page authors to write this:

src/content/pages/my-page.mdx
<IRef iref="my-figure" /> shows my picture I made. Yay!
<Image iref="my-figure" src={import('./_assets/my-figure.webp')}>A picture!</Image>

The user will see <IRef ... /> replaced with the text Figure 1, and the figure will have Figure 1 prefixed to the caption text.

So far only images and tables can be referenced, but I plan on adding support for equations and code snippets soon. The entire client-side script can be shown below.

The client-side script source code
src/js/IRefClientScript.ts
/**
* This script is run in the user's browser and adds "Item Reference" (IRef) functionality.
* Item references are used to link to specific elements in the page, such as figures, tables, equations, e.t.c. They
* are different from "references" which are used to cite sources.
* It prefixes figure captions with "Figure 1", "Figure 2", etc.
* It also looks for <IRef /> components and replaces the text and link to the corresponding item.
*/
/**
* Represents a reference destination in the page.
*/
class RefDestination {
type: string;
index: number;
ref_name: string;
constructor(type: string, index: number, ref_name: string) {
this.type = type;
this.index = index;
this.ref_name = ref_name;
}
}
function create_ref_links() {
let found_iref_destinations: { [key: string]: RefDestination } = {};
const markdownContentDiv = document.querySelector('.sl-markdown-content');
if (!markdownContentDiv) {
console.error('markdownContentDiv not found');
return;
}
// Find all figures.
// 1) Prefix figcaptions with "Figure X: "
// 2) Add figure to found_ref_destinations if ref present
const figures = markdownContentDiv.querySelectorAll('.figure');
console.log('figures', figures);
figures.forEach((figure, index) => {
const figcaption = figure.querySelector('figcaption');
if (figcaption) {
figcaption.textContent = `Figure ${index + 1}: ` + figcaption.textContent;
}
// If figure has an id, add it to the found_ref_destinations array
const iref = figure.getAttribute('data-iref');
if (iref) {
found_iref_destinations[iref] = new RefDestination('figure', index, `Figure ${index + 1}`);
}
});
// Find all tables under the markdown content div
// 1) Prefix table captions with "Table X: "
// 2) Add table to found_ref_destinations if iref present
const tables = markdownContentDiv.querySelectorAll('table');
console.log('tables', tables);
tables.forEach((table, index) => {
console.log('table', table);
const tableCaption = table.querySelector('caption');
if (tableCaption) {
tableCaption.textContent = `Table ${index + 1}: ` + tableCaption.textContent;
}
// If table has an iref, add it to the found_ref_destinations array
const iref = table.getAttribute('data-iref');
if (iref) {
found_iref_destinations[iref] = new RefDestination('table', index, `Table ${index + 1}`);
// Add id to table so we can link to it
table.id = iref;
}
});
console.log('found_ref_destinations', found_iref_destinations);
// Find all ref-source elements and link them to the corresponding item ref in the page
const refSources = document.querySelectorAll('.ref-source');
console.log('refSources', refSources);
refSources.forEach((refSource) => {
const ref = refSource.getAttribute('data-iref');
console.log('ref', ref);
// Check if ref is in found_ref_destinations
if (!ref) {
console.error('ref not found in refSource', refSource);
return;
}
if (found_iref_destinations[ref]) {
refSource.textContent = `${found_iref_destinations[ref].ref_name}`;
} else {
// This is an error, it means there is an <IRef /> component in the markdown
// that does not have a corresponding figure, table, etc.
console.error('ref not found in found_ref_destinations', ref);
}
});
}
// Create ref links on page load (e.g. navigating to the site from a different site)
create_ref_links()
// Re-link figures after swapping pages (i.e. navigating between pages on this site)
// We need to do this because page transitions do not reload the entire page and
// trigger this script file to be reparsed.
// Using after-swap is better than page-load because it is before the page is rendered
document.addEventListener("astro:after-swap", create_ref_links)