September 2024 Updates
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:
---aliases: [/my-old-page/]authors: [gbmhunter]date: 2024-09-11lastUpdated: 2024-09-11tags: [my tag]title: My Pagetype: 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:
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:
---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 pageif (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)
Metric | Google Analytics | Umami |
---|---|---|
Views | 92k | 116k |
Visitors (Users) | 45k | 66.9k |
NinjaCalc
Time range: 1st May 2024 - 31st August 2024 (4 months)
Metric | Google Analytics | Umami |
---|---|---|
Views | 13k | 4.32k |
Visitors (Users) | 1.1k | 1.57k |
NinjaTerm
Time range: 1st April 2024 - 31st August 2024 (5 months)
Metric | Google Analytics | Umami |
---|---|---|
Views | 1.5k | 3.21k |
Visitors (Users) | 590 | 1.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.
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:
- Write a remark/rehype plugin that runs during the build process.
- 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:
<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
/** * 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 rendereddocument.addEventListener("astro:after-swap", create_ref_links)