Introduction

This is James' Laboratory Notebook, which aims to be a collection of all of my thoughts on the things that I am working on, researching, thinking about, etc.

It is meant to be primarily useful for me to organize my thoughts, and potentially as reference material for sharing my notes with other people.

If you would like a more "stream of consciousness" style view of what I am working on, consider following me on twitter.

Pull requests that add relevant details are welcome, pull requests that restructure, rearrange, or add details that aren't arbitrarily considered useful to me will not be accepted.

This document is tracked on GitHub in this repository.

This document was inspired by whitequark's Lab Notebook, though is structured on a topic basis, rather than on a chronological basis. Chronological notes can be found in the Notes section.

License

This document is licensed under a CC BY-SA 4.0 license.

Projects

These are projects of mine that I am working on or have worked on. They may be software based, hardware based, or a combination of both.

Personal projects are rarely "done", but they occasionally reach new levels of usefulness.

Unless otherwise specified, any software for these projects is written in Rust.

This section serves as a combination of "showcase" and "current status", depending on the state of the project.

All items in this section are either "work in progress" or "complete for now". For project ideas that are still in the concept phase, see the Ideas Section.

Embedded - The Missing Parts

"Embedded - The Missing Parts" is a work-in-progress book that aims to track all of the concepts I believe are important in "real world" embedded engineering projects, but are not necessarily taught as part of a university education, or could be easy to miss when self-taught.

It started as a Twitter Thread, and is tracked online at https://emp.jamesmunns.com. The project uses mdBook, which also powers this lab notebook. It spawned from a table of contents written with other engineers from the embedded industry.

LiPo Stamp

LiPo Stamp Render

The LiPo Stamp is a small sized, general purpose LiPo charge controller with power path and a 3v3 regulator. Currently the targeted size is 0.7" by 0.7", or just under 18mm square. It is intended to be used with single cell 18650, 21700, pack, or similar batteries.

I intend to use this in projects such as Kuma's Collar.

Features

  • TP4056 Charge Controller
    • 500mA charging current
    • Low voltage trickle charging
  • Over/Under-voltage Protection
    • AP9101CK6 + FS8205
    • Disables battery on over/under charge
  • Power Path
    • Provides power via USB when connected
    • 500mA max output (USB or Battery)
  • Switchable 3v3 Regulator
    • Default off
    • 100mA+ output
  • Resettable Polyfuse
    • For high current, direct battery applications (e.g. LEDs)
    • Max 2A continuous output

The main project page for the LiPo Stamp is hosted on GitHub.

Testing

Test Setup drawing

Implementation of the automated tester is currently going on in this pull request

I plan to build a hardware in the loop test rig for the LiPo stamp. It will probably be a proto-version of the KTA, though probably not strictly at the start.

Unsorted test brainstorming

I think I will need the following equipment/setup items for the test cases.

  • A LiPo Stamp
    • Mounted to a breakout board for easy wiring
    • Maybe with headers or screw terminals
  • A KTA
    • This will "Run" the test
    • Also use some ADCs to measure voltage
    • Also look control GPIO input/outputs for LiPo stamp
  • A 5v supply
    • This only needs to provide 500mA for charging, so USB might be suitable if it can power everything
    • Otherwise use an external AC/DC supply
  • INA219 breakouts
    • Measure battery input/output
    • Measure 5v charging line
  • Relays to connect/disconnect:
    • 5v charging source
    • Battery connection (high side)
    • LED connection (high side)
  • WS2812B strip/panel for programmable load
  • Some kind of display for monitoring output?
  • Protected 18650 cell
    • Probably NCR-18650B, rated to 5A discharge, 2.5v cutout
    • 3.6v nominal
    • PDF Datasheet
  • Unprotected 18650/21700 cell
  • I might need some kind of custom holder for protected battery cells (if they don't fit the holders I have)

I think I will need at least the following test cases for the board:

  1. No battery, 5v present
    1. Check vmax output
    2. Check 3v3 output (disabled)
    3. Check 3v3 output (enabled)
  2. Protected cell
    1. 5v connected
      1. Check vmax output
      2. Check 3v3 output (disabled)
      3. Check 3v3 output (enabled)
      4. Verify charging
      5. Wait for charge complete
      6. Verify charging complete (no charging)
      7. Verify LEDs (manual)
    2. 5v disconnected
      1. Check vmax output
      2. Check 3v3 output (disabled)
      3. Check 3v3 output (enabled)
      4. Begin discharge pattern
      5. Verify cutout at low voltage
      6. Verify LEDs (manual)
  3. Polyfuse test
    1. Charge all the way
    2. Discharge at steps
      • 500mA
      • 1000mA
      • 1500mA
      • 1900mA
    3. Verify discharge okay, no cutout
    4. Push discharge above levels
      • 2000mA
      • 2100mA
    5. Ensure cutout occurs after XX seconds
    6. Disconnect load
    7. Ensure board restores after YY seconds
  4. Unprotected Cell
    • Probably repeat steps above to verify cutout happens because of LiPo Stamp protections, not protected battery
    • How to generate load at voltages below the cut-out voltage? We probably need to go as low as 2.5v to verify cut-out.

Test board pinout

  • I2C for the INA boards and Display?
    • SCL
      • PORTB-06
    • SDA
      • PORTB-07
  • Analog Inputs
    • Battery+ (might be available on INA219?)
      • ???
    • 5v Source
      • Also VBUS for the test system?
        • Maybe not? Going through a regulator?
      • ???
    • 3v3 Reg Output
      • ???
    • VMax Output (May be greater than 5v!)
      • Voltage Divider?
      • ???
  • Digital Outputs
    • 3v3-EN
      • ???
    • RELAY: 5v Input NO
      • ???
    • RELAY: Battery Input NO
      • ???
    • RELAY: WS2812 Panel Connection NO
      • ???
  • SPI output
    • WS2812b Panel
      • PORTB-08

M60 Keyboard

part image

TODO: Picture of my keyboard with caps and a case

I am building a mechanical keyboard, based on the makerdiary m60. This PCB is based on an nRF52840 m.2 SOM. It is a 60% keyboard, e.g. no numpad, arrow pad, PgUp, PgDn, etc.

Right now the code for this only exists as part of the Anachro project, and only outputs using the Anachro protocol. USB and BT support has not been written.

Parts

Usage

I intend to use this in multiple ways:

  • As a USB-C keyboard for my PC
  • As a Bluetooth keyboard for my PC or Tablet
  • As a wired keyboard for Anachro-PC
    • This will use the debugging header on the PCB

Knurling Test Adapter

Note: Add picture or render of the breakout board

This is intended to be used as part of Hardware in the Loop testing. The idea is the ability to write host-based "integration tests" that exercise the behavior of a Unit Under Test (UUT) by mocking the outside world, or serving as a data Sink or Source for instrumentation commands or logging.

See HIL Testing Theory for more design and concept details.

It is currently a breakout board for the [Black Pill], based on the STM32F411. The breakout board adds:

  • Dual RS-485 channels
  • RJ-45 connectors for daisy-chaining power and RS-485
  • A WS2812B LED for status
  • PMOD breakouts for:
    • GPIO
    • I2C
    • SPI
    • UARTs

In the future, the hope is to donate this project to the Knurling-rs project.

Kuma Collar

TODO: Picture or render of the collar

TODO: Picture of Kuma

Kuma is my dog. He is a corgi. For walks at night, I would like to have an LED collar for him.

To make this happen, this will involve:

  • A WS2812b strip for LEDs, capable of up to 1A of output
  • A 18650 or 21700 battery cell, providing 3Ah-5Ah of power
  • An nRF52840 MCU for control and communications, probably an MS88SF2 module
  • A LiPo stamp for battery management and charging
  • A custom PCB to hold all the parts
  • A waterproof case of some kind, possibly cut acrylic
  • Aviation connectors for:
    • USB data + power (4 pins)
    • WS2812b strip connection (3 pins)

Home Fleet

TODO: More details here

Home fleet is my project to build a home sensor network entirely from scratch.

See the Home Fleet Repo for more information.

Home fleet uses the Anachro Protocol over the ESB wireless protocol.

Anachro

Anachro is an umbrella projects that consists of two main things:

  • A flexible and lightweight communication protocol designed to work over a variety of transports (e.g. wireless, UART, RS-485, SPI)
  • A microcontroller based PC architecture

See the Anachro Docs and the Anachro Repo for more details.

You can also watch a talk I gave about Anachro at RustLab 2020.

BBQueue

BBQueue is a queue that aims to be a lock-free, performant, and misuse-resistant library for use with DMA on embedded systems. It is loosely based on BipBuffers, as written by Simon Cooke.

In the near future, it will be rewritten to use the const-generic features that are stabilizing in Rust 1.50. This will mark the 1.0.0 release of bbqueue.

Useful links:

bitpool

bitpool is intended to be a lightweight and lock-free allocator for embedded systems.

It aims to support all of the following use cases:

  • As a global static memory pool (without language support)
  • As an implementor of the global_alloc, allowing for use with the alloc crate
  • As an implementor of whatever future "local allocator" traits are provided, allowing it to be used as a custom allocator for collections

bitpool uses a 32-bit word as a tree of booleans which express the ability to break a page into blocks ranging up to 5 power-of-two allocations.

For example, when using a 1024 byte "page", you could have any combination of the following blocks allocatable:

  • 1x 1024 byte block
  • 2x 512 byte blocks
  • 4x 256 byte blocks
  • 8x 128 byte blocks
  • 16x 64 byte blocks

Because a bit is used for each "tier" of blocks, this uses 31 bits of metadata per block (1 + 2 + 4 + 8 + 16) = 31. This allows for atomic CAS operations on each block metadata word.

In the future, it should be possible for users to choose any power-of-two as the page size, meaning you could have two (or more) pools that cover a wider range, e.g.:

  • Pool 1 (small): 1024-64 byte allocations
  • Pool 2 (medium): 16KiB-2KiB allocations
  • Pool 3 (large): 512KiB-32KiB allocations

See the github repo for more information.

Brick Mount

"Brick Mount" is a project to use LEGO bricks (or other similar building blocks) as a fundamental basis for laying out circuit boards and modules while prototyping.

This is achieved by designing boards that have holes compatible with the "pegs" of a LEGO brick, e.g. 4.8mm pegs with a spacing of 8.0mm. With cheap board design services like OSHPark or JLCPCB, it is relatively straightforward to design these holes onto breakout boards or other assemblies.

This allows for arrangement of components into pretty much any 3d structure, and even prototyping (or shipping!) enclosures using LEGOs directly.

This idea is not original (and has likely been done many times before), but I was inspired by LOOKMUMNOCOMPUTER's Kosmo Minis parts, which use exactly this scheme.

I have collected KiCAD footprints as well as the boards I have designed so far in this GitHub repo. At the moment, I have not yet been able to verify the footprints or boards (I'm waiting for boards to be delivered).

Background

When working on projects consisting of multiple boards, it is useful to group them onto a single physical medium, so they don't turn into a web of connectors, to relieve cable strain when moving or storing them, and to make it easy for them to "travel together".

It could be possible to use a breadboard (or three) for this purpose, but I haven't gotten into the habit of having a stack of breadboards around, and they always seem finnicky to me. Perma-proto boards are also an option, but then I still need to mount them to something, and it can be difficult to rework.

Over the past months, I've used a variety of methods, including cardboard or foam core board as a backboard, with spacers to mount the PCBs. This is always a little tedious, and I didn't love it. It also meant having the right set of spacers around, and I really should have ordered some kind of punch tool for M3 or similar screws.

For lighter boards, I've even duct-taped PCBs to cardboard, usually by their cables. This works, but was inelegant.

Current Footprint Design

brick mount footprints

Currently, I have seven footprints that I have designed:

  • 1x1 peg
  • 1x2 pegs
  • 1x3 pegs
  • 1x4 pegs
  • 1x6 pegs
  • 1x8 pegs
  • 2x4 pegs

Each part has the following features:

  • A courtyard that wraps around each peg.
    • A 1x1 peg would have an 8x8mm courtyard
    • A 1x2 peg would have an 8x16mm courtyard
  • A hole with a drill diameter of 4.9mm, to allow some clearance of the peg diameter of 4.8mm.
    • This +0.1mm tolerance is influenced by the capabilities of my usual PCB fabricator
    • I don't know yet if these will be tight enough tolerance to "snap" to the pegs of the brick, or if they will require glue or a cap.
  • An annular ring with a diameter of 6.0mm
    • These could be electrically connected, or used with some kind of connection wire or alligator clip.
    • So far, I've left these unconnected or grounded in all of my designs

These features were based on this page's CAD analysis of LEGO bricks.

That's pretty much it. In my boards, I've left 0.25-0.50mm edge-cut clearance (inside the courtyard) to ensure that my boards will fit "inside" of the footprint of an actual lego brick.

Current Board Designs

I've designed a couple of boards to test out working with this concept. These boards have been designed and ordered (the KiCAD projects are in the repo), though I have not received or tested them yet.

Feather Carrier - Small

Feather Small

This board is meant to mount to a single 2x4 brick. Because it mounts internally, it is probably best suited for boards that don't require a lot of movement, or for boards with a lot of top mounted buttons.

This carrier can be used for mounting an Adafruit Feather compatible board, or Featherwing expansion board.

It features:

  • Two horizontal QWIIC connector footprints (daisy-chainable)
  • A single set of breakout headers for each feather pin
  • Two additional breakout pins for each of GND, VBUS, VBAT, and +3v3 power rails
  • Feather mounting holes

Feather Carrier - Medium

Feather Medium

This board is top and bottom mounted. It features additional cutouts for running wires within the 4x8 peg footprint, and has additional prototyping space.

This carrier can be used for mounting an Adafruit Feather compatible board, or Featherwing expansion board.

It features:

  • Two horizontal QWIIC connector footprints (daisy-chainable)
  • Three breakout pins for each feather pin
  • Three additional breakout pins for each of GND, VBUS, VBAT, and +3v3 power rails
  • A mix of vertical bus connections and connectionless prototyping pins
  • Feather mounting holes

Feather Carrier - Large

Feather Large

This board is side mounted. It features additional cutouts for running wires within the 6x8 peg footprint, and has additional prototyping space.

This carrier can be used for mounting an Adafruit Feather compatible board, or Featherwing expansion board.

It features:

  • Two horizontal QWIIC connector footprints (daisy-chainable)
  • Three breakout pins for each feather pin
  • Three additional breakout pins for each of GND, VBUS, VBAT, and +3v3 power rails
  • A mix of vertical bus connections and connectionless prototyping pins
  • Feather mounting holes

Proto Board - Quarter

Proto Quarter

This board is a general purpose prototyping board, inspired by the Adafruit Perma Proto board.

It features:

  • Two vertical QWIIC connector footprints (daisy-chainable)
  • A row of 6 pins for each of SDA and SCL that are common with both QWIIC connectors
  • A column of 10 pins for each of GND and +3v3 that are common with both QWIIC connectors
  • 20 5-pin rows, arranged as a 10x10 pin prototyping space
  • Fits a 4x6 peg footprint

NOTE: In the future, I will likely revise the boards to use the same horizontal QWIIC connector as the feather boards. This will likely reduce the number of power rail pins by one for each rail, and the top prototyping row to three pins instead of five to allow for connector room. It will also swap the rows used for SDA and SCL breakout.

Proto Board - Half

Proto Half

This board is a general purpose prototyping board, inspired by the Adafruit Perma Proto board.

It features:

  • Two vertical QWIIC connector footprints
    • These require a connection on the board to daisy-chain
  • Two 30-pin power rails
  • Two 24-pin power rails, that can be connected to the QWIIC connectors via Solder Bridges
  • A row of pins for each of SDA, SCL, +3v3, and GND, for each of the two QWIIC connectors
  • 60 5-pin rows, and 12 5-pin center columns arranged as a 30x12 pin prototyping space.
  • Fits a 12x6 peg footprint

NOTE: In the future, I will likely revise the boards to use the same horizontal QWIIC connector as the feather boards. This will not affect the number of prototyping pins, though will change the ordering of the breakout rows. I may also provide some way to short the SDA and SCL pins without running a discrete wire, e.g. using another pair of solder jumpers.

Calculator

I realized that I will reach for my phone or for a python REPL window for most basic math operations.

Additionally, there are a couple things that I could probably stand to offload from my phone, in order to look at it less often. This includes:

  • Calculator
  • Unit Conversion
  • Alarm Clock
  • Timer

I figured I'd try my hand at making a replacement for my old-school TI-83, and maybe make a REPL with CircuitPython or even a Rust based scripting language like RHAI.

The Keypad

Behavior

See Notes from 2021-01-22 for the original KeyPad investigation and brainstorming.

So, I have a limited number of keys to use, so I started looking at what kind of keys I typically used in my Python REPL. It turns out, they were surprisingly low.

  • Numbers
  • Parenthesis
  • Operators
  • Period
  • Equals
  • Letters (for variables - T9?)
  • Comment (#)
  • Arrows
  • Enter

I came up with a 4x6 arrangement that I thought would be pretty reasonable:

+   -   *   /
7   8   9   (
4   5   6   )
1   2   3   #
=   0   .   FUNC
LET ARR SPC ENT

For the "Special" keys:

  • "FUNC": Some kind of menu for python keywords (for, min, max, sin, cos)
  • "LET": Switch the keypad into T9 mode for entering letters
  • "ARR": Switch the keypad into arrow mode for navigation
  • "SPC": Space Bar
  • "ENT": Enter/Return

Hardware

choc keypad v1 render

Inspired by the MNT Reform, I wanted to use low profile mechanical keys. Luckily, the MNT Reform hardware is all open source, which means I could see the footprints, circuit, and spacing used.

I put together a 4x6 keyboard matrix PCB, with LEDs under each key, and a row of 19 LEDs at the bottom, maybe to use for palm lighting or notifications.

It has one connector for the Row/Column IO, and one connector for the WS2812-alike smart LEDs.

The hardware project is here.

I plan to use:

See my parts pages for more details about the switches and keycaps, and for the smart leds.

I might use black keycaps for the numbers, and clear keycaps for the "meta" keys. TBD.

So far, I haven't chosen an MCU, but I'll use a feather board or something, and likely prototype the UI and behavior from my PC before digging into the embedded side of things.

The Screen

I want to have some kind of screen that fits above the keypad. I've thought about keeping the total size of the calculator device roughly to the size and shape of my current cellphone, which is 150.0mm x 75.0mm.

Right now, the Keyboard (minus LEGO mounting rails) takes up about 112.0mm x 80mm.

Ideally, I'd find some sort of 4:3 ratio LCD or epaper display that has dimensions of rougly 60.0mm x 80.0mm. This would give a total device size of 172.0mm x 80mm, which is close enough to the target.

With a 16:9 ratio, I would have a display of 45.0mm x 80.0mm, which would give a total size of 157.0mm x 80mm, which would be almost exactly the size of my phone.

I've also thought about mixing LCD and e-paper, think like an Accountant's calculator with the paper feed, but with e-paper instead of the paper reel (a single row of LCD/LED characters for quick updates, e-paper for history).

Next Steps

Now I need to wait for the keypad to arrive, and likely make a pretty "dumb" USB peripheral out of it. Then I can start working on a plan for the UI, likely simulated on-PC, until I find a design I like.

Parts

This section keeps track of parts or components that I am using, have purchased, or have interest in using.

It includes usage notes, important datasheet sections, and links to other projects that use this part (mine or others) that can be used as a reference.

When possible, I'll try to link to a supplier of these parts, including where I bought them (if applicable).

LEDs

LEDs are a specific kind of Actuator. I work with LEDs more often than many other parts, so they get their own section.

Often this section will be used to track a variety of "Smart LEDs", or digitally controlled LEDs that contain their own Logic ICs.

Smart LEDs

TODO: Link the following datasheets:

  • WS2812b
  • SK6812w
  • APA102

TODO: Explain smart leds and their protocol

TODO: Explain techniques for using SPI, I2S, PWM, etc. for controlling smart leds

TCWIN TC2020RGB

TCWIN TC2020RGB front image

I found this small (2.1mm x 2.1mm) LED that seems to use the same Smart LED protocol as the WS2812/SK6812/AP102 families. At the time, it was available in a small-ish quantity (600 stock or so) on JLCPCB parts list, which was enough for 5 of the original Calculator Keypad prototypes.

I haven't yet tested them to see if they require different control schemes or modifications from standard techniques.

HUB75 Panels

part image

I have ordered a pair of HUB75 panels from AliExpress.

These use a different protocol to the WS2812B or other smart-leds, and I haven't figured out how I will drive them yet.

WS2812 Panels

Part Image

I currently have a couple of flexible WS2812b panels ordered from Aliexpress. They come in the following configurations:

  • 8x8 pixels, 80x80mm
  • 16x16 pixels, 16x16mm
  • 8x32 pixels, 80x320mm

I plan to use these as a programmable load when testing the LiPo Stamp.

Sensors

Sensors are the "input" from the "real world". They provide a specific view on something measurable.

This section may contain individual parts, or modules that are used prototyping.

INA219

The INA219 is a "High Side" DC current sensor. It communicates over I2C.

Usage

I plan to use this part for:

Modules

Adafruit INA219 Breakout

Part Image

  • Default config, 26V, +/- 3.2A, 800uA resolution
  • Selectable I2C addresses (7-bit):
    • 0x40
    • 0x41
    • 0x44
    • 0x45
  • Docs and How-To

Actuators

Actuators are any part that act as "output" to the "real world".

Quad Relay

part image

I bought a couple of opto-isolated quad relays off of Amazon. They seem to be similar to parts available on Aliexpress.

I use these as part of Home Fleet to control fixed-color DC LED strips.

FS90R Micro Servo

part image

I have ordered a couple of FS90R continuous rotation micro servos from Adafruit.

I haven't decided what to do with these yet, or how to control them.

Modules

Modules are assemblies that contain multiple parts necessary for operation. These are often meant to be a single complex part of a larger assembly.

MS88SF2 - nRF52840 SOM

MS46SF11 - nRF52805 SOM

Communication Parts

These are parts that are used to (assist with) device-to-device communications. These are often transcievers or other similar components.

RS-485

MAX3490ESA+T

Part Picture

  • LCSC Datasheet
  • LCSC Part Number: C68934
  • Bidirectional
  • No RX/TX enable

MAX3485EESA+T

Part Picture

Logic ICs

Buffers, Drivers, Tranceivers

SN74LVC125ADR

Part Picture

FPGAs

ECP5 i5 Module

Upduino iCE40

MCU Dev Boards

STM32F411 Black Pill

STM32H743 Core

Connectors

PMOD

Qwiic/Stemma QT

Aviation Plug

USB-C

Sockets

USB4110-GF-A

part image

USB4105-GF-A

part image

Battery Clips

These are clips for a LiPo Battery Cells

18650

Keystone 54

  • THT
  • Digikey: 36-54-ND

Keystone 254

  • SMT
  • Digikey: 36-254-ND

20700/21700

Keystone 247

  • 20700/21700
  • THT
  • Digikey: 36-247-ND
  • Clip Spacing
    • (2:1 pins): 9.6mm +/- 0.13
    • (clip:clip): 2x700 - 47.03mm +/- 0.13
  • Clip Size
    • Length: 14.99mm
    • Height (exl pins): 17.87mm

Keystone 246

  • 20700/21700
  • SMT
  • Digikey: 36-246-ND

Memory ICs

ESP-PSRAM64H

Switches

Buttons, switches, etc.

Things for mechanical input to a circuit.

Kailh Choc Low Profile 1350s

kailh switch image

These low profile switches have a size of 15mm x 15mm x 8mm. These are the same switches used on the MNT Reform. I also use this for my Calculator project.

The switches have a 5.00mm x 3.15mm window for LEDs.

The MNT Reform uses the following center-to-center spacing for the keys:

  • X spacing of keys: 18.60mm
  • Y spacing of keys: 17.60mm

This spacing seems to give an approximately 1.1mm gap between the keycaps of each key.

They are not compatible with Cherry-MX switches. The MNT Reform has a variety of footprints and symbols available for use.

These switches seem to come in three varieties:

  • Red - "Linear"
  • Brown - "Tactile"
  • White - "Clicky"

I haven't been able to find a good source of bulk switches, with the product pages above offering them in 10 packs or 20 packs, with limited stock, and expensive from-the-US shipping.

Keycaps

I haven't been able to find an official source of Keycaps, or a good bulk source.

I have ordered these two different "artisan" sets of 1u caps (expensive, especially with shipping from the US):

The Ergo Alphas state they have a size of 17.5mm x 16.5mm, while the Natural keys don't list a size.

In this blog post, Lukas from MNT Research states "Blank keycaps are sourced from Kailh", though I don't know how he ordered them (probably in bulk), maybe I could buy a batch of keycaps and keys from him.

Standards

This section tracks relevant standards for my project. This may include:

  • Physical standards
  • Electrical standards
  • Communication standards

This aims to capture important or relevant aspects of these standards, as they relate to my projects.

Ideas

This section captures concepts that may someday become a project, but for now are just a loosely captured idea in my head.

These ideas are likely to be an area of research, and may at some point "graduate" to the Projects section.

These ideas may be incomplete, or even incorrect, based on my current understanding.

Standard PCB Sizes

Problem Statement

As a PCB designer, it is difficult to pick a "standard" size for designing hardware, where it will be possible to find off the shelf cases or accessories that will fit that board.

Solution

Invent a new standard!

Reference Material/Background

  • IEC-602970-3 - Eurocard standard
    • I'm stealing the base card size
    • Based around a 100x160mm card
    • Typically used for test equipment
    • Also influnced Eurorack hardware
  • ISO 216 - Standard Paper Sizes
    • I'm stealing the idea of "half sizes"
    • Every card is 1/2 the larger dimension of the next size up
    • ISO 216 keeps the same ratio - I don't.
  • Sick of Beige v1 - OSHW Board Sizes
    • Really similar approach
    • Based on the "golden ratio"
    • Also has an "alt" square format
    • Steal same spacing for mounting screws/edge keepout

Standard rules

  1. Start with a "base size" of 200x160mm
  2. For each step, reduce the longest dimension by 1/2
  3. For each size, there are four variants:
    1. AR: Full rectangle, corner mounted holes
    2. BR: Full rectangle, "plus" mounted holes
    3. AS: "Slim" rectangle, corner mounted holes
    4. BS: "Slim" rectangle, "plus" mounted holes
  4. For all boards, mounting holes are M3 screws
    • 4x4mm from each corner
    • 3.2mm holes
    • TODO: What about small boards?
    • 6mm keepout around holes (diameter?)
    • 1.7mm edge keepout for case clearance

Notes

  • "Plus" mounted holes are for cases that have physical posts in the corners where the PCB must "keep out"

Full case size listing

FamilyVariantFull NameHole Location?Board ShapeWidthLengthcm^2
P0ARP0ARCorner HolesRegular200160320
P0ASP0ASCorner HolesSlim20080160
P0BRP0BRPlus HolesRegular200160320
P0BSP0BSPlus HolesSlim20080160
P1ARP1ARCorner HolesRegular160100160
P1ASP1ASCorner HolesSlim1605080
P1BRP1BRPlus HolesRegular160100160
P1BSP1BSPlus HolesSlim1605080
P2ARP2ARCorner HolesRegular1008080
P2ASP2ASCorner HolesSlim1004040
P2BRP2BRPlus HolesRegular1008080
P2BSP2BSPlus HolesSlim1004040
P3ARP3ARCorner HolesRegular805040
P3ASP3ASCorner HolesSlim802520
P3BRP3BRPlus HolesRegular805040
P3BSP3BSPlus HolesSlim802520
P4ARP4ARCorner HolesRegular504020
P4ASP4ASCorner HolesSlim502010
P4BRP4BRPlus HolesRegular504020
P4BSP4BSPlus HolesSlim502010
P5ARP5ARCorner HolesRegular402510
P5ASP5ASCorner HolesSlim4012.55
P5BRP5BRPlus HolesRegular402510
P5BSP5BSPlus HolesSlim4012.55
P6ARP6ARCorner HolesRegular25205
P6ASP6ASCorner HolesSlim25102.5
P6BRP6BRPlus HolesRegular25205
P6BSP6BSPlus HolesSlim25102.5
P7ARP7ARCorner HolesRegular2012.52.5
P7ASP7ASCorner HolesSlim206.251.25
P7BRP7BRPlus HolesRegular2012.52.5
P7BSP7BSPlus HolesSlim206.251.25

Unsorted

  • https://twitter.com/bitshiftmask/status/1337100886066294786

HIL Testing Theory

I think that Hardware in the Loop testing breaks down into three "dimensions", or aspects that represents some aspect of an individual test or test case. These include:

  • Interfaces - or how you interact with your unit under test
  • Behaviors - or how your test adapter behaves over one or more Interfaces
  • Paradigms - or how your test is structured between the testing host and the testing adapter, usually guided by a balance of max acceptable latency and necessary automation required to reach lower latency numbers

IMO, every test will be some combination of all three of these dimensions.

This list discusses hosts, or the PC orchestrating the test cases, the adapter, which manages real time communications and simulation, and the unit under test, the device that is being tested.

Dimensions

  • Interfaces
    • GPIOs
    • SPI
    • I2C
    • UART
    • ADCs
    • DACs/PWM
    • I2S
    • CANBus
    • RS-485
    • Long tail of domain specific interfaces/protocols
  • Behaviors
    • Immediate Read (one value)
    • Immediate Write (one value)
    • Stream In (sniffing)
    • Stream Out (dumping)
    • React/Response (potentially chained)
      • e.g. GPIO goes low, send UART message
    • Table/Register Based
      • e.g. SPI/I2C
    • Sequencing/Timing?
    • Controlling External Tools, e.g.
      • Power Supply
      • Relay
      • Current sensor
      • Expansion Boards/"Hats"/"Shields"
  • Paradigms
    • Adapter: 0% automated; Host: 100% automated - Raspberry Pi use cases
      • Immediate "host side" controls
      • Assume 10-100ms Round Trip Latency
    • Adapter: 33% automated; Host: 66% automated (IFTTT-lite) - Arduino use cases
      • Host: Preloads specific response/trigger behavior
      • Adapter: Limited number of trigger responses
      • 0ms latency for "automated" tasks, 10-100ms for Roundtrip
    • Adapter: 66% automated; Host: 33% automated (Full sequencer)
      • Host: Set up specific sequences
      • Adapter: Can hold up to N actions, triggered by M conditions
    • Adapter: 100% automated; Host: 0% automated
      • Adapter has a full scripting environment (WASM, Python, 'pre-compiled')
      • Host: Just loads scripts and executes test cases

Robot Face

Idea: Make a dev board that looks like a "robot face". Optionally put it on some kind of Servo arm to give it pitch/roll/yaw, and potentially a neck to rotate or tilt up/down.

To Be Researched

  • Which micro servos should I get?
    • FS-90R not good, continuous
    • Find something quiet AND torque'y?
  • 3d layout
    • Probably want to learn a 3d CAD to make sure I have clearance
  • How to make brackets?
    • Pre-fab?
    • Acrylic?
    • Cut metal?

Original brainstorming

Development Board Notes

Arm Logistics and Servo Control

Robot Arm Sketch

emu-pac

Summary: The goal is to create an emulated "Peripheral Access Crate" for the purposes of learning and teaching embedded Rust. This would mean we would not require physical hardware to teach embedded Rust, and could also provide visualization and debugging tools that could help users diagnose issues they have.

Rather than using a more accurate tool for simulating an existing architecture, such as Arm or RISC-V, this would be a "native" target, capable of running on the host PC.

Ideally, this would allow people to create "fake" peripherals such as UART, SPI, and I2C, that could be exposed via simulated PAC interactions.

The idea would be to provide learning material, like the Discovery Book, based around this, so EVERYONE can learn a little embedded Rust (or at least the "higher in the stack" parts) without needing to buy specific hardware.

Once an emu-hal has been built on top of the emu-pac, the usual embedded-hal portability techniques could be used to integrate "off the shelf" drivers.

Prior Art

This isn't a totally novel idea, RTOS' like RIOT-OS have a "native" target that does something similar, though they simulate at the OS level. This is a similar technique, but moves the emulation layer down to the PAC, which I think could still be reasonable enough for Rust, where we still have the register interfaces to draw a boundary at, also sort of like disasm's avatar-rs does for passing CPU actions through to probe-rs.

Other projects to investigate:

  • litex_sim:
    • From disasm: "have you seen litex_sim? It emulates a full SoC with cycle accuracy. Some of the peripherals have "real world" connections, you can even talk via Ethernet. With verilator (or maybe even cxxrtl) it's possible to emulate this with a decent frequency. Also litex generates SVD for a SoC if you ask it"
  • renode
  • TinyGo Preview

Architecture Diagram

architecture diagram

Moving pieces

This is a brainstorm of the pieces needed to make this happen.

  • Multiple threads for simulating each component
    • One "main" thread that acts as the entry point
    • One thread for each interrupt
    • One thread for "hardware simulation", running concurrently to the "main" thread
  • An AtomicU32 based primitive for each hardware register
    • This allows for multi-threaded sharing of "registers" between interrupts, "main", and the simulation context
    • We may want to have some sort of shared global mutex to simulate non-concurrency between interrupts, e.g. a critical section prevents all interrupts from touching any registers
    • We may want some kind of "rate limiting" of register access (read or write) to make the operational speed somewhat more reasonable, e.g. only allow one access per (1/64MHz) time scale, at least from the main/interrupt threads
  • Simulators of external devices, such as a WS2812b
    • This will be necessary, as we won't have physical hardware
  • We need to "invent" our own peripherals, e.g. define the register layout, capabilities, and behaviors of the provided UART, SPI, I2C, and Timer peripherals
    • We could also write an external connector, allowing for the simulated CPU to talk "real" SPI using a USB FT2232H (or similar) adapter
    • We probably want a "fake" RTT peripheral, which can be used for rprintln or defmt capabilities
  • A "board simulator", which would be a 2d or 3d rendering of the board and any connected components, allowing users to "see" things like LEDs blinking, motors spinning, etc. based on the operation of the emulated CPU
  • We probably need some kind of "notification system" where writes to certain registers can trigger behaviors, such as a peripheral starting operation

Unknowns

There are a couple things I don't know how to handle, including:

  • Use of svd2rust
    • Should we fake an svd2rust style interface?
    • Or should we make an actual SVD to stay coupled?
  • How to build an emu-rt crate
  • How to add rtic support

Other people's projects

This section tracks projects by other people that I am interested in following. It primarily aims to be a "bookmarks folder" of sorts, and contain notes on why I am interested in these projects.

Lore

Funny snippets of history

Rust

Load bearing build system nop

This is code from the pre-1.0 version of Rust's build system.

Code available here.

# Copy a dylib or rlib
# $(1) is the filename/libname-glob
#
# XXX: Don't remove the $(nop) command below!
# Yeah, that's right, it's voodoo. Something in the way this macro is being expanded
# causes it to parse incorrectly. Throwing in that empty command seems to fix the
# problem. I'm sorry, just don't remove the $(nop), alright?
define PREPARE_LIB
    $(nop)
    @$(call E, prepare: $(PREPARE_WORKING_DEST_LIB_DIR)/$(1))
    $(Q)LIB_NAME="$(notdir $(lastword $(wildcard $(PREPARE_WORKING_SOURCE_LIB_DIR)/$(1))))"; \
    MATCHES="$(filter-out %$(notdir $(lastword $(wildcard $(PREPARE_WORKING_SOURCE_LIB_DIR)/$(1)))),\
                        $(wildcard $(PREPARE_WORKING_DEST_LIB_DIR)/$(1)))"; \
    if [ -n "$$MATCHES" ]; then                                              \
      echo "warning: one or libraries matching Rust library '$(1)'" &&       \
      echo "  (other than '$$LIB_NAME' itself) already present"     &&       \
      echo "  at destination $(PREPARE_WORKING_DEST_LIB_DIR):"                    &&       \
      echo $$MATCHES ;                                                       \
    fi
    $(Q)$(PREPARE_LIB_CMD) `ls -drt1 $(PREPARE_WORKING_SOURCE_LIB_DIR)/$(1) | tail -1` $(PREPARE_WORKING_DEST_LIB_DIR)/
endef

Bastion of the TurboFish

Code available here.

// Bastion of the Turbofish
// ------------------------
// Beware travellers, lest you venture into waters callous and unforgiving,
// where hope must be abandoned, ere it is cruelly torn from you. For here
// stands the bastion of the Turbofish: an impenetrable fortress holding
// unshaking against those who would dare suggest the supererogation of the
// Turbofish.
//
// Once I was young and foolish and had the impudence to imagine that I could
// shake free from the coils by which that creature had us tightly bound. I
// dared to suggest that there was a better way: a brighter future, in which
// Rustaceans both new and old could be rid of that vile beast. But alas! In
// my foolhardiness my ignorance was unveiled and my dreams were dashed
// unforgivingly against the rock of syntactic ambiguity.
//
// This humble program, small and insignificant though it might seem,
// demonstrates that to which we had previously cast a blind eye: an ambiguity
// in permitting generic arguments to be provided without the consent of the
// Great Turbofish. Should you be so naïve as to try to revolt against its
// mighty clutches, here shall its wrath be indomitably displayed. This
// program must pass for all eternity, fundamentally at odds with an impetuous
// rebellion against the Turbofish.
//
// My heart aches in sorrow, for I know I am defeated. Let this be a warning
// to all those who come after. Here stands the bastion of the Turbofish.

// See https://github.com/rust-lang/rust/pull/53562
// and https://github.com/rust-lang/rfcs/pull/2527
// for context.

fn main() {
    let (oh, woe, is, me) = ("the", "Turbofish", "remains", "undefeated");
    let _: (bool, bool) = (oh<woe, is>(me));
}

Notes

This section contains unsorted chronological notes. These might include research items that will be used to fill in concepts in the more topic-ordered sections.

This section is strongly inspired by whitequark's Lab Notebook.

All notes are in an RFC8601 compatible date specification, e.g. YYYY-MM-DD.md, and may include a topic, e.g. YYYY-MM-DD-topic.md.

Transcribed notes from iPad

Want to do items

  • Design 50x80mm nrf52840 board
    • Use a power fet for LED cutout?
    • Add PMOD and QWIIC connectors?
    • Include a non-pop USB-C footprint?
  • Write an Anachro Bootloader
    • A/B side image
    • Update-able bootloader image (maybe)
  • Clean up and publish anachro crates
    • Split out Stargazer to its own repo
  • Design LiPo Stamp test stand
  • Something with the SCD30 CO2 sensor?
  • const-fn bbqueue implementation
  • Design Acrylic cases
    • Water resistant one (Kuma Collar)
    • General purpose "sandwich" case
  • Document "Eye of Sauron" idea
    • Grab sketch from iPad
  • Think about Anachro protocol versioning
  • Document "Tweet to Lab Notebook Importer"
    • Bot to open PR with my tweets and replies on a daily basis
    • Include text for search (not just links)
  • Mail out LiPo stamps to testers

Thoughts for me

I probably want to stream starting out the LiPo stamp tester, because I'll need that for the Kuma Collar/80x50 board.

2020-12-26 Thoughts

Board Stackup

Video about board stackup

Dumb Terminal for Smart Devices

Rough idea: Some kind of simple USB device that talks to "smart devices" and provides a basic USB/Serial interface that exposes data in some kind of table format.

But, problems:

  • No one has standard schemas
  • Doesn't solve this problem
  • Even if we have a schema, everyone does the BT thing
    • "data over serial/proprietary protocol"

Podcasting notes

I need to set up a couple things:

  • Recording
    • Currently done with Zoom, audio quality isn't great
    • I might want to switch to something else with higher quality audio
  • Leveling
    • Currently using Auphonic, I like it
  • Editing
    • I'll probably use Audacity
  • Theme Music
    • I'll probably use Louie Zong - Polaroid
  • Album Art
    • ...?
  • Hosting
    • I'll use Digital Ocean Spaces
  • RSS Feed
    • I'll probably just host on my website
    • https://castfeedvalidator.com/ for validation

Task Manager

  • Pocket based device for time tracking
  • Only shows you one task at a time

Using eMMC chips

See this twitter thread

Videos to watch

  • https://www.youtube.com/watch?v=LO1mTELoj6o

Using eMMC chips

See this twitter thread

Videos to watch

  • https://www.youtube.com/watch?v=LO1mTELoj6o

Plan for 2021-01-02 Stream

  • Document my Standard case size/PCB size standard ideas
  • Look up some IDC cable stuff
  • Do some board design for my nrf52/kuma collar board

BBQueue abstract storage

I'd like for bbqueue to be abstract over the storage medium of the BBBuffer.

There are two main components here:

  • The tracking header (fixed size)
  • The data buffer (variable-ish size)

Right now both of these items are stored in the current-day BBBuffer, and they always travel together.

Ideally, we'd like to be able to support any of the following kinds of data buffers:

  • [u8; N] - a const generic "owned" buffer - likely in static storage. This is the embedded use case, but could also be useful for local usage? Though probably not because lifetimes will get complicated if they are not 'static, but this is still supported today, even if not very useful
  • &'a mut [u8] - A "user provided" buffer. Again, this is probably not very useful when 'a is not 'static, which would require unsafe to acquire in most cases. However I think that unsafe-ty can be deferred to the caller?
  • Arc<[u8]>, or something like that

I think the Arc case is really just a subset of the &'a mut [u8] case (where 'a is 'static if handled correctly, but this exposes the necessity to think of the tracking header and the data buffer in the same context.

Really, they need to have the same lifetime. It feels like I should have a struct like this:


#![allow(unused)]
fn main() {
struct BBBuffer {
    header: BBHeader,
    storage: BBStorage,
}
}

And then when handing out something that holds both, they need to have the same lifetime.

I guess not the SAME lifetime, but rather any time we create a reference to BBHeader, it must have a lifetime less than or equal to BBStorage. e.g. 'storage: 'header.

However, BBStorage feels like it should be generic over something. At the very least, I want three potential situations:

  • A statically allocated "owned" buffer. This needs inner mutability to ensure that the buffer can only be taken once, e.g. a Singleton.
  • A "borrowed" buffer provided by the user, we just rely on lifetimes and the borrow checker to make sure this works out.
  • A reference counted buffer, which de-allocates itself when no handles (e.g. Buffer, Producer, Consumer, Grants) are held.

Brainstorming with fake code

What if we make BBStorage a trait? This way I could provide some convenience types for the three above situations, and a user could also construct their own if they had some other use case, like shared memory between CPUs or IPC.

I worry a little bit about the generic types making the API look pretty garbage or overly complicated. Especially with fully qualified types when using as a static. I wonder if I can "paper over" this with type aliases or dependant traits.


#![allow(unused)]
fn main() {
// I don't know if this lifetime syntax is right
trait BBStorage<'header, 'storage: 'header> {
    // We need some kind of "associated lifetimes"
    type StorageLifetime = PhantomData<'storage>;
    type HeaderLifetime = PhantomData<'header>;

    // I think `self` needs to have `'storage` lifetime, as it is longer.
    fn get_storage(&'storage self) -> &'storage impl Deref<Target=Storage>;
    fn get_header(&'storage self) -> &'header impl Deref<Target=Header>;
}
}

I think with this approach, I could:

  • Create a struct that owns both Storage and Header (with a const constructor), which each would have the same lifetime as the storage structure
  • Create a struct that borrows both Storage and Header
  • A wrapper type that holds an Arc of the owned version, and returns a reference

But wait, if these two lifetimes are always the same, can't I just have a parent structure that references both children?

The main problem is I need some kind of "hook" to increment refcounts in the Arc case. I wonder if I can simplify it to just this:


#![allow(unused)]
fn main() {
struct BBBorrow<'header, 'storage: 'header> {
    header: &'header Header,
    storage: &'storage Storage,
}

trait BBGetter<'bbqueue> {
    fn get_borrow(&'bbqueue self) -> impl Deref<Target = BBBorrow<'bbqueue, 'bbqueue>;
}
}

Hmm, maybe? Not sure. I think the issue here is that all the types need to be generic over the BBGetter. This would mean that I need:


#![allow(unused)]
fn main() {
struct Producer<B: BBGetter> {
    bbq: B,
    // ...
}

struct GrantW<B: BBGetter> {
    bbq: B,
    // ...
}
}

I'm dancing around the concept of BBBorrows being Clone-able, but those clones would be bound to the lifetime of the BBBorrow itself, which is what I want, and in the case of 'static, this is no problem. I just need the borrow checker to understand that BBBorrow can't outlive 'storage, which I think the borrow checker can understand already?

Okay, let's go write some playground code to prove this out.

Attempt 1

use core::sync::atomic::AtomicUsize;
use std::sync::Arc;

struct Header {
    fake: AtomicUsize,
}

struct Storage {
    fake: AtomicUsize,
}

struct OwnedBuffer {
    hdr: Header,
    sto: Storage,
}

#[derive(Clone)]
struct BBBorrow<'header, 'storage: 'header> {
    header: &'header Header,
    storage: &'storage Storage,
}

struct BBArc {
    barc: Arc<OwnedBuffer>,
}

trait BBGetter<'hdr, 'sto: 'hdr> {
    fn get_borrow(&self) -> BBBorrow<'hdr, 'sto>;
}

impl<'hdr, 'sto: 'hdr> BBGetter<'hdr, 'sto> for BBBorrow<'hdr, 'sto> {
    fn get_borrow(&self) -> BBBorrow<'hdr, 'sto> {
        return self.clone()
    }
}

// impl BBGetter<'static, 'static> for BBArc {
//     fn get_borrow(&self) -> &BBBorrow<'static, 'static> {
//         return self.barc.get_borrow()
//     }
// }


fn main() {
    println!("Hello, world!");
}

Attempt 2

use core::sync::atomic::AtomicUsize;
use std::sync::Arc;

struct Header {
    fake: AtomicUsize,
}

struct Storage {
    fake: AtomicUsize,
}

struct OwnedBuffer {
    header: Header,
    storage: Storage,
}

impl OwnedBuffer {
    fn borrow(&self) -> BBBorrow {
        BBBorrow {
            header: &self.header,
            storage: &self.storage,
        }
    }
}

#[derive(Clone)]
struct BBBorrow<'header, 'storage: 'header> {
    header: &'header Header,
    storage: &'storage Storage,
}

#[derive(Clone)]
struct BBArc {
    barc: Arc<OwnedBuffer>,
}

trait BBGetter<'hdr, 'sto: 'hdr>: Clone {
    fn get_header(&self) -> &'hdr Header;
    fn get_storage(&self) -> &'sto Storage;
}

impl<'hdr, 'sto: 'hdr> BBGetter<'hdr, 'sto> for BBBorrow<'hdr, 'sto> {
    fn get_header(&self) -> &'hdr Header {
        &self.header
    }
    fn get_storage(&self) -> &'sto Storage {
        &self.storage
    }
}

impl<'hdr, 'sto: 'hdr> BBGetter<'hdr, 'sto> for BBArc {
    fn get_header(&self) -> &'hdr Header {
        // Can't borrowck
        self.barc.borrow().get_header()
    }
    fn get_storage(&self) -> &'sto Storage {
        todo!()
    }
}


fn main() {
    println!("Hello, world!");
}

Attempt 3

The problem with this attempt is that the Producer and Consumer need to OWN something that can reference the data, but NOT for longer than the reference to the Arc (in this case) lives. How can I introduce this?

use core::sync::atomic::AtomicUsize;
use std::sync::Arc;
use core::marker::PhantomData;

struct Header {
    fake: AtomicUsize,
}

struct Storage {
    fake: AtomicUsize,
}

// TODO: Wrap this in a "Singleton"?
// Should this be a private type? This probably
// wont play nice with being `'static`...
// Probably requires `MaybeUninit` and such
struct OwnedBuffer {
    header: Header,
    storage: Storage,
}

impl OwnedBuffer {
    // Should this be a private or unsafe method?
    fn borrow(&self) -> BBBorrow {
        BBBorrow {
            header: &self.header,
            storage: &self.storage,
        }
    }

    fn split(&self) -> Result<(Producer<BBBorrow>, Consumer<BBBorrow>), ()> {
        Ok((
            Producer {
                bbq: self.borrow(),
                pds: PhantomData,
                pdh: PhantomData,
            },
            Consumer {
                bbq: self.borrow(),
                pds: PhantomData,
                pdh: PhantomData,
            }
        ))
    }
}

#[derive(Clone)]
struct BBBorrow<'header, 'storage: 'header> {
    header: &'header Header,
    storage: &'storage Storage,
}

#[derive(Clone)]
struct BBArc {
    barc: Arc<OwnedBuffer>,
}

trait HeaderGetter<'hdr>: Clone {
    fn get_header(&self) -> &'hdr Header;
}

trait StorageGetter<'sto>: Clone {
    fn get_storage(&self) -> &'sto Storage;
}

impl<'hdr, 'sto: 'hdr> HeaderGetter<'hdr> for BBBorrow<'hdr, 'sto> {
    fn get_header(&self) -> &'hdr Header {
        &self.header
    }
}

impl<'hdr, 'sto: 'hdr> StorageGetter<'hdr> for BBBorrow<'hdr, 'sto> {
    fn get_storage(&self) -> &'sto Storage {
        &self.storage
    }
}

impl<'hdr, 'sto: 'hdr> HeaderGetter<'hdr> for BBArc {
    fn get_header(&self) -> &'hdr Header {
        &self.barc.header
    }
}

impl<'hdr, 'sto: 'hdr> StorageGetter<'hdr> for BBArc {
    fn get_storage(&self) -> &'sto Storage {
        &self.barc.storage
    }
}

struct Producer<'hdr, 'sto: 'hdr, G>
    where G: StorageGetter<'sto> + HeaderGetter<'hdr>,
{
    bbq: G,
    pds: PhantomData<&'sto ()>,
    pdh: PhantomData<&'hdr ()>,
}

struct Consumer<'hdr, 'sto: 'hdr, G>
    where G: StorageGetter<'sto> + HeaderGetter<'hdr>,
{
    bbq: G,
    pds: PhantomData<&'sto ()>,
    pdh: PhantomData<&'hdr ()>,
}



fn main() {
    println!("Hello, world!");
}

Attempt 4

Hmm, this feels better, though it feels like .borrow() and .split() could be part of a combined Getter trait that doesn't split the header and storage borrows...

use core::sync::atomic::AtomicUsize;
use std::sync::Arc;
use core::marker::PhantomData;

struct Header {
    fake: AtomicUsize,
}

struct Storage {
    // TODO: This should probably be something
    // like `MaybeUninit<[u8; N]>`
    fake: AtomicUsize,
}

// TODO: Wrap this in a "Singleton"?
// Should this be a private type? This probably
// wont play nice with being `'static`...
// Probably requires `MaybeUninit` and such
struct OwnedBuffer {
    header: Header,
    storage: Storage,
}

impl OwnedBuffer {
    // Should this be a private or unsafe method?
    fn borrow(&self) -> BBBorrow {
        BBBorrow {
            header: &self.header,
            storage: &self.storage,
        }
    }

    fn split(&self) -> Result<(Producer<BBBorrow>, Consumer<BBBorrow>), ()> {
        Ok((
            Producer {
                bbq: self.borrow(),
            },
            Consumer {
                bbq: self.borrow(),
            }
        ))
    }
}

impl BBArc {
    // Should this be a private or unsafe method?
    fn borrow(&self) -> BBBorrow {
        self.barc.borrow()
    }

    fn split(&self) -> Result<(Producer<BBArc>, Consumer<BBArc>), ()> {
        Ok((
            Producer {
                // bbq: self.borrow(),
                bbq: self.clone()
            },
            Consumer {
                // bbq: self.borrow(),
                bbq: self.clone()
            }
        ))
    }
}

#[derive(Clone)]
struct BBBorrow<'header, 'storage: 'header> {
    header: &'header Header,
    storage: &'storage Storage,
}

#[derive(Clone)]
struct BBArc {
    barc: Arc<OwnedBuffer>,
}

trait HeaderGetter: Clone {
    fn get_header(&self) -> &Header;
}

trait StorageGetter: Clone {
    fn get_storage(&self) -> &Storage;
}

impl<'hdr, 'sto: 'hdr> HeaderGetter for BBBorrow<'hdr, 'sto> {
    fn get_header(&self) -> &Header {
        &self.header
    }
}

impl<'hdr, 'sto: 'hdr> StorageGetter for BBBorrow<'hdr, 'sto> {
    fn get_storage(&self) -> &Storage {
        &self.storage
    }
}

impl HeaderGetter for BBArc {
    fn get_header(&self) -> &Header {
        &self.barc.header
    }
}

impl StorageGetter for BBArc {
    fn get_storage(&self) -> &Storage {
        &self.barc.storage
    }
}

struct Producer<G>
    where G: StorageGetter + HeaderGetter,
{
    bbq: G,
}

struct Consumer<G>
    where G: StorageGetter + HeaderGetter,
{
    bbq: G,
}



fn main() {
    println!("Hello, world!");
}

Let's try combining the traits and see what that does to the lifetimes everywhere...

Attempt 5

Okay, combining the traits works, but I don't think I can move split to the trait, because that NEEDS to be a singleton.

The Arc option could never give you the original BBArc back for convenience, leaving split to be a static singleton artifact.

use core::sync::atomic::AtomicUsize;
use std::sync::Arc;
use core::marker::PhantomData;

struct Header {
    fake: AtomicUsize,
}

struct Storage {
    // TODO: This should probably be something
    // like `MaybeUninit<[u8; N]>`
    fake: AtomicUsize,
}

// TODO: Wrap this in a "Singleton"?
// Should this be a private type? This probably
// wont play nice with being `'static`...
// Probably requires `MaybeUninit` and such
struct OwnedBuffer {
    header: Header,
    storage: Storage,
}

impl OwnedBuffer {
    // Should this be a private or unsafe method?
    fn borrow(&self) -> BBBorrow {
        BBBorrow {
            header: &self.header,
            storage: &self.storage,
        }
    }

    fn split(&self) -> Result<(Producer<BBBorrow>, Consumer<BBBorrow>), ()> {
        Ok((
            Producer {
                bbq: self.borrow(),
            },
            Consumer {
                bbq: self.borrow(),
            }
        ))
    }
}

impl BBArc {
    // Should this be a private or unsafe method?
    fn borrow(&self) -> BBBorrow {
        self.barc.borrow()
    }

    fn split(&self) -> Result<(Producer<BBArc>, Consumer<BBArc>), ()> {
        Ok((
            Producer {
                // bbq: self.borrow(),
                bbq: self.clone()
            },
            Consumer {
                // bbq: self.borrow(),
                bbq: self.clone()
            }
        ))
    }
}

#[derive(Clone)]
struct BBBorrow<'header, 'storage: 'header> {
    header: &'header Header,
    storage: &'storage Storage,
}

#[derive(Clone)]
struct BBArc {
    barc: Arc<OwnedBuffer>,
}

trait BBGetter: Clone {
    fn get_header(&self) -> &Header;
    fn get_storage(&self) -> &Storage;
}

impl<'hdr, 'sto: 'hdr> BBGetter for BBBorrow<'hdr, 'sto> {
    fn get_header(&self) -> &Header {
        &self.header
    }

    fn get_storage(&self) -> &Storage {
        &self.storage
    }
}

impl BBGetter for BBArc {
    fn get_header(&self) -> &Header {
        &self.barc.header
    }

    fn get_storage(&self) -> &Storage {
        &self.barc.storage
    }
}

struct Producer<G>
    where G: BBGetter,
{
    bbq: G,
}

struct Consumer<G>
    where G: BBGetter,
{
    bbq: G,
}



fn main() {
    println!("Hello, world!");
}

Thoughts on 6

  • I should have three types exposed:
    • ArcBBQueue
      • Arc
      • Only have an interface create prod/cons
        • Takes ownership
    • OnceBBQueue
      • Singleton
      • Interface to create prod/const
        • Takes &self
    • BorrowBBQueue // Never instantiatable
      • Wait, how can we "take ownership" of an immutable borrow?
      • I guess we just have something like:
        • unsafe fn with_storage(&'sto mut [u8]) -> Borrow<'sto>
      • Can this ever be sound?

bullet notes

Books Recommendations

Using Lego blocks as circuit rails

Video Screenshot

In this youtube video, Look Mum No Computer has circuit boards that use Lego pegs as a mounting tool.

According to this page, Lego bricks have an 8mm center-to-center spacing, with a 4.8mm outer diameter for the brick pegs. This means that the PCB hole would need to be just larger than 4.8mm, depending on the fab's tolerance.

It also seems that he sells the modules used in the video on his store. I haven't been able to find any online design files though.

Idea for message serialization

keev - a Key-Value Serialization format with a concept of optionality on each side.

tl;dr:

  • All messages exist in a "top level" namespace
  • There is no versioning of messages. New message, new number.
  • Protocol speakers should expect to "negotiate" which versions they CAN speak, and which they will use.
    • Input/Output capabilities?
    • This should probably only happen once, on connection
  • For anachro: Remove the concept of paths
  • Use an enum, make it non-exhaustive
  • Have a method for generating the RX and TX capabilities

Lego Shopping

So, I want to use Legos as a physical design medium as described in my prior note.

Adafruit Feather mounting board

I think as a test run, I'm going to make a demo for mounting an Adafruit Feather. Brainstorming:

  • Have THT holes for the edge pins
  • Have mounting holes for the main corner mounts
  • Have an optional pair of SMT QWIIC connectors at the front and back
  • Maybe break out some other pins too?
  • Maybe make a "side mount" and a "top/bottom" mount

Lego parts shopping

I want some base plates and parts to use for basic mounting (and maybe a little for playing with).

Here's my notes on shopping. These are all amazon.de links, because I'm in Germany.

Other resources

I had these two online stores recommended to me, which look like a good choice if I want specific parts/colors. Not sure on the cost of shipping or anything.

  • https://www.brickowl.com/
  • https://www.bricklink.com
  • https://ebay.de
    • There seem to be a lot of people selling batches here
    • The going price seems to be about 20-25EUR/kg, which claims ca. 700 pieces

JLC Tolerances

  • Drill Hole Size Tolerance:
    • +0.13/-0.08mm

PCB design

2.0" / 8mm => 6.350 0.9" / 8mm => 2.858

X: 89.84 + Y: 119.05

23 + 39.50 + 32 + 23.50

30x12pin 2x30pin 2x22pin 4xsolder bridge 2x 1x6brick 2x QWIIC

Interesting approach to serial comms

https://www.alex-spataru.com/blog/introducing-serial-studio

I wonder if I could use something like this, with pre-sending the schema, then sending binary packed messages?

Connector review

proto-half * Connector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_Vertical proto-quarter * Connector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_Vertical feather-small * Connector_JST:JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal feather-medium * Connector_JST:JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal feather-large * Connector_JST:JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal

Interesting approach to serial comms

https://www.alex-spataru.com/blog/introducing-serial-studio

I wonder if I could use something like this, with pre-sending the schema, then sending binary packed messages?

Connector review

proto-half * Connector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_Vertical proto-quarter * Connector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_Vertical feather-small * Connector_JST:JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal feather-medium * Connector_JST:JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal feather-large * Connector_JST:JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal

Slim Key Switches

These are footprints for the keyswitches for the MNT reform.

https://source.mntmn.com/MNT/reform/src/branch/master/reform2-keyboard-pcb/keyswitches.pretty

And the switches themselves:

https://kbdfans.com/products/kailh-low-profile-1350-choc-rgb-switch-10-pcs?variant=34418543034507

But what would I want an external keyboard to do?

It would be sort of nice to have a standalone device that I can run a python-ish style calculator app on, but that would require a significant number of keys.

Maybe something that can do some basic functionality as a standalone device, but when plugged in, could be some kind of macro hotkey style device?

Should I just prototype this with the blackberry featherwing?

Brainstorm use cases

  • Timer
  • Calculator
  • Alarm Clock

What keys would I use for a python calculator?

  • Parenthesis
  • Operators
  • Period
  • Equals
  • Letters (for variables - T9?)
  • Comment (#)
  • Arrows

What if:

Keys: 15x15mm

Note: Xiaomi Mi Mix 2 Dimensions: 151.8x75.5mm

4x6 keypad: 90.0x60.0mm w/ 5mm space: 120x80mm

+   -   *   /
7   8   9   (
4   5   6   )
1   2   3   #
=   0   .   FUNC
LET ARR SPC ENT

Do a prototype with a break-away fifth column?

320x240 display: 1.333 ratio 2.6" screen: 66.04mm 40x53mm

https://www.waveshare.com/product/displays/e-paper/epaper-2/4.2inch-e-paper-module-c.htm

400x300px 103x78.5mm outline 84.8x63.6mm display

https://www.sparkfun.com/products/710

MNT Reform Notes

X spacing of keys: 18.60mm Y spacing of keys: 17.60mm

LED Notes

TC2020RGB-3CJH-TX1812Z5 - 2.1x2.1mm WS2812 knockoff?