Without further ado here is my implementation:

```
(ns pepijndevos.lifeclj
(:refer-clojure :exclude [==])
(:use clojure.core.logic)
(:gen-class))
;; A helper to get the neighbouring cells.
;; Clips to zero.
(defn get-neighbours [rows x y]
(for [dx (range -1 2)
dy (range -1 2)
:when (not (= dx dy 0))]
(get-in rows [(+ x dx) (+ y dy)] 0)))
;; Produces binary vectors of a certain number of bits.
;; This is used to generate all neighbour combinations.
(defn bitrange [n]
(sort-by #(apply + %)
(for [i (range (bit-shift-left 1 n))]
(vec (map #(bit-and 1 (bit-shift-right i %)) (range n))))))
;; Encode the game of life rules as a 256 element conde.
;; Depending on the number of ones in a vector,
;; the corresponding rule is generated
;; that equates the pattern to the neigbours
;; and the appropriate next state.
;;
;; This can be asked simply what the next state is for
;; given neighbours and current state.
;; OR you could drive it backwards any way you like.
(defn lifegoals [neigh self next]
(or*
(for [adj (bitrange 8)
:let [n (apply + adj)]]
(cond
(or (< n 2) (> n 3)) (all (== next 0) (== neigh adj))
(= n 3) (all (== next 1) (== neigh adj))
:else (all (== next self) (== neigh adj))))))
;; Relate two grids to each other according to the above rules.
;; Applies lifegoals to every cell and its neighbours.
;; in the forwards direction executes one life step,
;; in the backwards direction generates grids
;; that would produce the next step.
(defn stepo [size vars next]
(let [rows (->> vars (partition size) (map vec) (into []))
neig (for [x (range size)
y (range size)]
(get-neighbours rows x y))]
(everyg #(apply lifegoals %) (map vector neig vars next))))
;; Make a grid of unbound variables.
(defn grid [size] (repeatedly (* size size) lvar))
;; Simply execute a life step on the state.
(defn fwdlife [size state]
(let [vars (grid size)
next (grid size)]
(run 1 [q]
(== q next)
(== vars state)
(stepo size vars next))))
;; Produce three backward steps on state.
(defn revlife [size state]
(let [start (grid size)
s1 (grid size)
s2 (grid size)
end (grid size)]
(run 1 [q]
(== q [start s1 s2 end])
(== end state)
(stepo size s2 end)
(stepo size s1 s2)
(stepo size start s1)
)))
;; Nicely print the board.
(defn printlife [size grids]
(doseq [g grids]
(doseq [row (->> g (partition size) (map vec) (into []))]
(doseq [t row]
(print t ""))
(print "\n"))
(print "\n")))
;; Test with a glider.
(defn -main [& args]
(->> [0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 1 0
0 0 1 1 0 0
0 0 0 0 1 0
0 0 0 0 0 0]
(revlife 6)
first
(printlife 6)))
```

output:

```
$ clj -Mrun
1 0 1 0 1 1
1 0 0 0 0 1
0 0 1 0 0 0
0 0 0 0 0 1
1 0 1 1 0 0
1 0 1 1 1 1
0 1 0 0 1 1
0 0 0 1 1 1
0 0 0 0 0 0
0 1 1 1 0 0
0 0 1 0 0 1
0 0 1 0 1 0
0 0 0 1 0 1
0 0 0 1 0 1
0 0 0 0 0 0
0 1 1 1 0 0
0 0 0 0 1 0
0 0 0 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 1 0
0 0 1 1 0 0
0 0 0 0 1 0
0 0 0 0 0 0
```

Sadly, this is nowhere near fast enough to solve the play button problem.

]]>A raised guillotine blade has a certain potential energy which is transferred to the contact point between the blade and the neck. So let’s assume a frictionless spherical cow, I mean neck, and calculate the contact point, force, and energy as a function of the angle of the blade.

The equation of a circle is given by \(x^2-y^2=r^2\), so a horizontal blade intersecting the circle of radius \(r\) at height \(y\) intersects the circle at \(x=\pm\sqrt{r^2-y^2}\). The length of the cut is therefore \(L(y)=2\sqrt{r^2-y^2}\).

For a diagonal blade, we can rotate the reference frame to be aligned to the blade, such that the knife has a horizontal component of \(y\sin(\theta)\) and a vertical component of \(y\cos(\theta)\), creating a contact patch of \(L(y\cos(\theta))=2\sqrt{r^2-y^2\cos^2(\theta)}\).

So now we can express the force from the contact area in terms of some constant \(k\) as \(F(y)=kL(y)\) perpendicular to the blade edge, resulting in a vertial and horizontal component:

\[\begin{aligned} F_x(y)&=\sin(\theta)kL(y)\\ F_x(y)&=\sin(\theta)k2\sqrt{r^2-y^2\cos^2(\theta)}\\ F_y(y)&=\cos(\theta)kL(y)\\ F_y(y)&=\cos(\theta)k2\sqrt{r^2-y^2\cos^2(\theta)} \end{aligned}\]This means that an angled blade creates “leverage” where less force but a longer travel is required. To be precise, the travel is given by \(\frac{2r}{\cos(\theta)}\).

To compute the total energy required for a cut, assuming the dominant force scales with the length of the contact patch, is the integral of the force over the vertical travel component.

\[W_y = k \cos(\theta) \int_{-\frac{r}{\cos(\theta)}}^{\frac{r}{\cos(\theta)}} L(y\cos(\theta)) dy\]Now we can simplify by substituting \(u=y\cos(\theta)\), \(dy=\frac{du}{\cos(\theta)}\) and adjust the integration bounds accordingly, cancelling all \(\theta\) terms!

\[W_y = k \int_{-r}^{r} L(u) du\]So just as much vertical energy is required to cut through the circle no matter the angle, but we’re also excerting a horizontal force. If the guillotine blade is running on math bearings this is of no concern, but for a wooden sled there is a (Coulomb) friction of \(F_c=\mu F_x\) giving

\[\begin{aligned} W_x &= \mu k \sin(\theta) \int_{-\frac{r}{\cos(\theta)}}^{\frac{r}{\cos(\theta)}} L(y\cos(\theta)) dy\\ W_x &= \mu k \tan(\theta)\int_{-r}^{r} L(u) du \end{aligned}\]But what about the pointy blade? I’m not glad you asked because the math is more messy. We can consider one half of the pointy blade at a rotated reference frame. So we get the same \(L\) as before, but now the blade stops at the tangent line of the blade angle, as follows. But then once the point of the blade exits the circle it becomes two flat sections, forming a piecewise function.

For the first part we have half of a flat blade length \(L\), the uncut section \(M\), and the pointy blade section \(N\)

\[\begin{aligned} L&=\sqrt{r^2-(\cos(\theta)h)^2} \\ M&=\tan(\theta)\cos(\theta)h\\ &=\sin(\theta)h \\ N&=L-M \\ &=\sqrt{r^2-(\cos(\theta)h)^2}-\sin(\theta)h \end{aligned}\]Giving us

\[\begin{aligned} L_{tot} &=\begin{cases} 2\left(\sqrt{r^2-\cos^2(\theta)h^2}-\sin(\theta)h\right) & -r < h \leq r \\ 4\sqrt{r^2-\cos^2(\theta)h^2} & r < h \leq \frac{h}{\cos(\theta)} \\ \end{cases} \\ F_y &= \cos(\theta)k L_{tot} \end{aligned}\]For the total work, we already know the square root terms are independent of the angle. And since the sum of the integral is the same as the integral of the sum, we only need to concern ourselves with the \(\sin(\theta)h\) term.

\[\begin{aligned} W_p &= k\cos(\theta)\sin(\theta)\int_{-r}^r h dh \\ W_p &= k\cos(\theta)\sin(\theta)\left(\frac{1}{2}(-r)^2-\frac{1}{2}(r)^2\right) \\ W_p &= 0 \end{aligned}\]So all our blades take the same amount of work to cut through an uniform saussage, with the diagonal blades being worse due to friction. The brachistochrone blade is left as an exercise to the reader.

But there is more to cutting than uniform math saussages. KnowArt indeed found that the diagonal blade seemed to perform worse than the flat blade due to friction. But the pointy blade performed better. The most likely explanation for this is that a horizontal slicing motion is beneficial for cutting. This makes intuitive sense to anyone who’s done some cooking, and was also explained to me by a medical doctor relative as the difference between a sword and a sabre.

But the story goes that the real reason the blade is diagonal is that the king suggested it might help with people with fat necks. Ironically his own fat neck ended on the block some time later.

]]>This is very similar to a geocentric model of the universe, it’s not fundamentally wrong to choose earth as your reference frame, just very inconvenient for describing orbits around the sun. In the same way it’s not fundamentally wrong to assume the earth is flat, but it warps the rest of the universe in strange ways.

For example, here is a round earth. We observe ships disappearing over the horizon, and we observe day and night.

Most flat earth models don’t tend to have satisfactory explanations for sunset, things disappearing behind the horizon, and eclipses. The solution is simple: light curves away from the earth. At some point sunlight curves back into space.

In this model the sun is still further away than the moon, so a solar eclipse is simply the moon moving in front of the sun as usual. In a lunar eclipse, the light of the sun has to bend so deep that it would touch the earth before it could bend back up to the moon.

A lovely result from this flat earth model is that it clearly answers the questions what is below the earth: the singularity. Even better is that the density of the earth goes down the closer you get to the singularity, meaning the earth is in a sense hollow. Finally a grand unifying theory of hollow earth and flat earth models!

The downside of this model that physics isn’t independent of location and direction. For example the atmosphere is denser in the middle of the disk. A simple equation like \(F=ma\) becomes hellishly complicated if you want it to work everywhere. There is also a magic Pacman teleportation point on the south pole.

This model is internally consistent and impossible to falsify since it is simply a coordinate transform of conventional physics. You can’t make any observations that would disagree with the model and agree with a spherical model since they are the same universe. It is not possible to measure which way of looking at things is “real” because all your observations and tools are curved in the same way. Therefore, you could interpret the earth to be flat.

With the combined skills in Blender and mathematics of me and my brother, we managed to implement the flat earth coordinate transformation in Blender geometry nodes so that you can make a 3D model and see what it looks like on a flat earth.

We struggled to get lighting to work since Blender would render linear light after the transform, so instead we drew physical light cones with luminescent hemispheres that pass through the transform correctly. Unfortunately this means we can’t render eclipses, but the upside is you can really see the wild curves light makes on a flat earth.

We’ve theorized that it might be possible to bake lighting into the texture as is commonly done for video games, but it’s nice weather outside so we pressed render and left eclipses as an exercise to the reader.

]]>A lot has happened since the first part:

- I renamed the project after learning about lithopedion
- I had a private lesson from ceramic artist Ilse Scholten
- I rewrote my text to gcode scripts
- I made a bunch of tablets and had them fired
- I started working towards a website for the project

Let’s start from the result and work back through the details. I made a bunch of clay tablets, had them fired by Ilse, and put them up **for sale on my Etsy store**. Here is a photo album to check out:

The reason it took so long to get the second update out is that I wanted to build a proper website with the concept, motivation, tutorials, a gallery, and a webshop. But as a suprpise to no one that’s quite a big project on its own, so I finally decided to write another update on my blog meanwhile.

The first key element to the new proccess is my newfound gcode swiss army knife: vpype. With vpype-gcode turning an SVG or text file into gcode is a one-liner, you just need a config file for your CNC and a few command-line flags.

```
vpype --config vpype.toml pagesize a5 text --position 1cm 1cm --wrap 12.5cm --size 22pt --hyphenate en --justify "$RANDOM_PAGE_TEXT" linemerge show gwrite --profile cnc random.gcode
```

But the biggest change was in the preparation of the clay slab itself. I abandoned my failing attempts at making a mold, and followed Ilse’s advice to roll the clay between strips of wood.

Materials needed:

- clay (0.2mm chamotte)
- clay wire cutter
- clay rib
- dough roller
- plasterboard
- hardwood strips (5mm to 10mm thick)
- some old bedsheet
- knife

To make a clay tablet you obviously need clay. It is important that the clay contains chamotte, which makes it less likely to explode in the oven. It’s also nice if the chamotte is small to get smooth writing. Clay with 0.2mm chamotte has worked the best for me.

We need a very flat surface, we’re aiming for sub milimeter precision to get consistent writing. Plasterboard is the surface that Ilse recommended, which has worked well for me. (unlike wood which will warp) I cut off roughly square sheets and taped the edges so we can safely move the drying tablets around.

It is very important to work on top of a smooth non-strechy cloth. I use a cut up cotton bedsheet. This allows us to flip the tablet over, inspect it for air bubbles, and prevents it from sticking to the plasterboard and warping while drying.

Now we can get to work. Cut off a piece of clay with a wire cutter, place it on the cloth betweent the wood strips, and use the dough roller to flatten it out. If you’re reusing scraps from a pervious tablet, first knead the clay into a ball, while making sure not to knead any air bubbles.

After rolling it flat we can cut off the excess clay with a knife. I use a folded piece of paper as a reference. Then take the rib and smooth out the surface, making it nice and shiny.

An important step at this point is to slightly lift the fabric, bending the tablet. If there are any air bubbles they will show up as little blisters. Poke them with a knife and flatten the tablet again.

Now cover the tablet with a cloth and flip it over, taking care not to leave fingerprints. Then take the rib and smooth the other side. Then flip it to whichever side looks nicer to become the front.

With my current process I put the entire board with the wet clay tablet right under the CNC. I give it a final roll with the dough roller because clay has a little bit of memory. Then I zero the CNC on the wood strip to get the correct height without leaving a mark. For the CNC bit I actually use a thick needle in a 2mm chuck at 0 RPM, I found that the stationary sharp point produces the finest writing in the wet clay.

A sneak peak at my current experiments is that I’m trying out what happens if you let the clay dry a little bit. Maybe there is a sweet spot where you can CNC it without accumulating or chipping, but I’ve not yet found it. I’m also experimenting with V-carving to be able to engrave more complicated graphics such as mathematical equations. My current goal is to engrave Maxwell equations on a tablet, so that’s a whole new can of worms.

]]>A theremin is an electronic instrument that you play by moving your hands in the air. It uses an analog circuit with two tuned oscillators, one of which is modulated by the antenna impedance which is perturbed by the presence of your body in the near field. The slight frequency differences are fed into a mixer to produce an audible frequency.

A singing bowl is a very old insturment that is usually played by rotating a suede covered mallet around its outside rim. The stick-slip motion of the mallet excites vibration modes of which the nodes are pushed around by the mallet. Some more experimental musicians also play singing bowls by drawing a violin bow across the rim.

A hurdy-gurdy is a string instrument, which uses a hand-cranked wheel with rosin on it rather than a bow. It also has other unique features like drone strings and a keyboard on the neck.

So here is the idea: what if you used a theremin to drive a hurdy-gurdy wheel and use that to play a singing bowl? Magic theremin singing bowl!

For the theremin I built myself a copy of the Thierrymin (using J113 jfets), which I fed into a comparator, stepper motor driver, and stepper motor. From there the only things I had to tweak were a few resistors to add a little hysteresis, and I played around with the microstepping pins to change the speed of the stepper motor. Here is a rough schematic:

That was the easy part. Then I did a bunch of experiments with different types of wheels. I tried rosin, suede, wood, plastic. I tried to spin the wheel on a static bowl, or to spin the bowl against a static baton. It kind of worked, but nothing worked quite the way I wanted it. And then life got rather busy and the project sat dormant for a while. So that’s when I decided to just write a blogpost with my progress and shelve the project. Maybe I’ll get back to it one day, or inspire someone else.

]]>For this page we wanted a simple gauge, so I figured a solution would be an internet search away. But all the examples I found seemed really complicated, with verbose markup, opaque CSS, and sometimes an entire JS library. So I decidede to make my own.

My goals were

- Very minimal HTML without any extra dummy elements.
- CSS that is easy to understand and modify.
- Easy to update the value from JS.
- A gauge of 270° rather than a semi-circle.
- (browser compat was not a goal)

The HTML is as simple as it gets, just a `div`

with custom properties and the textual value.

```
<div class="gauge" style="--value:0.3; font-size:2rem;">30%</div>
```

Conceptually, the CSS isn’t very complicated either.

- set the size of the gauge
`div`

- set a
`border-radius`

to make a circle - draw a
`conic-gradiant`

to make a pie-chart - draw a
`radial-gradient`

to cut out the center - center the text with
`text-align`

and`line-height`

The code CSS makes use of `calc`

and `var`

, primarily to adjust the `conic-gradient`

angle based on a custom property, but also to parameterize the dimensions of the gauge. This means you can override `--size`

and friends to style the gauge without changing hardcoded values.

```
.gauge {
--size: 200px;
--cutout: 50%;
--color: red;
--background: white;
width:var(--size);
height:var(--size);
border-radius:calc(var(--size) / 2);
background:
radial-gradient(
var(--background) 0 var(--cutout),
transparent var(--cutout) 100%),
conic-gradient(from -135deg,
var(--color) calc(270deg*var(--value)),
grey calc(270deg*var(--value)) 270deg,
transparent 270deg);
text-align: center;
line-height: var(--size);
}
```

The JavaScript for changing the gauge value is pretty simple too, given some gauge DOM element `el`

you can change the gauge value and text content by simply doing

```
el.style.setProperty("--value", 0.8)
el.innerHTML = "80%"
```

Below is a codepen to play with the code. I hope it’s useful to someone.

See the Pen Pure CSS gauge by Pepijn de Vos (@pepijndevos) on CodePen.

After a bit of chatting on the Recurse Center Zulip, I came up with the following alternative gradients that provide a 3D effect or that goes from red to orange to green. The 3D one works by adding a transparent black gradient to the radial part. The colourful one works by making a fixed backdrop and a transparent-grey gradient on top that reveals the underlying one.

```
background:
conic-gradient(from -135deg,
transparent 270deg,
white 270deg),
radial-gradient(
var(--background) 0 var(--cutout),
#0002 calc(var(--cutout)),
#0000 calc(var(--cutout) + 15px),
#0002 calc(var(--cutout) + 30px),
#0000 calc(var(--cutout) + 30px) 100%),
conic-gradient(from -135deg,
var(--color) calc(270deg*var(--value)),
grey calc(270deg*var(--value)) 270deg,
transparent 270deg);
background:
radial-gradient(
var(--background) 0 var(--cutout),
transparent var(--cutout) 100%),
conic-gradient(from -135deg,
transparent calc(270deg*var(--value)),
grey calc(270deg*var(--value)) 270deg,
transparent 270deg),
conic-gradient(from -135deg,
red 0,
orange 135deg,
lime 270deg,
transparent 270deg);
```

So, how do you build ChatGPT with data compression? What if you compress a large corpus of text to build up the encoding table, then you compress your prompt and append some random data and decompress the random data, and hope it decompresses to something sensible.

It feels vaguely similar to diffusion, but what do I know. Look, this is just a dumb idea, let’s just see what happens ok? Well, here is my progress so far. It’s kind of whack but it’s hilarious to me that it produces something resembling words.

```
import nltk
import lzma
import random
my_filters = [
{"id": lzma.FILTER_LZMA2, "preset": 9 | lzma.PRESET_EXTREME},
]
lzc = lzma.LZMACompressor(lzma.FORMAT_RAW, filters=my_filters)
corp = nltk.corpus.reuters.raw().encode()
out1 = lzc.compress(corp)
corp = ' '.join(nltk.corpus.brown.words()).encode()
out2 = lzc.compress(corp)
corp = nltk.corpus.gutenberg.raw().encode()
out3 = lzc.compress(corp)
out_end = lzc.flush()
lzd = lzma.LZMADecompressor(lzma.FORMAT_RAW, filters=my_filters)
lzd.decompress(out1)
lzd.decompress(out2)
lzd.decompress(out3)
# mess around to avoid LZMAError: Corrupt input data
lzd.decompress(out_end[:-344])
# insert prompt????
print(lzd.decompress(random.randbytes(50)).decode(errors="ignore"))
```

Here are a few runs. Note how the start is always `, and tri`

, usually completing it into some word. Are we doing some primitive accidental “prompting” or just flushing the buffer? Either way, not bad for mere seconds of “training”!

```
$ python train.py
, and triof billioerse,
But
ht and see th,
Thy smile, in to be happy,
Wmson,
Over tout as aThy smile;t as aThyrged in
ent, foldehe snoion since how long,
my roomr? Is ic books
$ python train.py
, and triompact, sca,
Take deepcky fouy vitaliz bodiehow there i,
Nor drummiwisibly wile of the-ations, dutway?
Yet ld woman'okesmanall whoy slow bekesmanalle me
$ python train.py
, and tri billions of the boftier, faie no acqutory's dazzd haOr that thpages:
(Sometimeseathe ihern, Sounte, fld Turkey n one,
Worlseathe Border Minstrelsy,ine, New-Ene Queen.
'Thelicate l
$ python train.py
, and tri, sleepinlke babes bent,
Abird;
Forhis fair n!
By thea mystic strangehe gifts ofhe body aering
t: I haue a lugs whipageantuperb-fnz
$ python train.py
, and triions of b--n the gra, with
e open the countless buAnd bid theng;
Billi toward you.i ally undya Songs
To f Death, istas of was mar to be UY 9,30
```

That’s it. That’s the plan. “set in stone” Wikipedia, literally. I know it sounds crazy and I don’t have any illusions about completing the project, but it seems a fun mix of art project and potentially my biggest contribution to the persistence of human kind haha

But how do I go about doing this? I want to automate it as much as possible, but it also has to be super durable and some amount of authentic. Like, if you’re going for pure efficiency and inforation density you’d maybe engrave binary into acrylic, but what’s the fun in that. There are so many remaining options from laser engraving slate to CNC routing clay tablets. Let’s just research and try some?

I started watching a bunch of youtube videos about low fire backyard pottery, where people make pottery on their own improvised coal furnaces and such. Some things I learned:

- get clay and mix in sand to make it less prone to exploding
- dry really really really well
- stick it in a bunch of coal and hope for the best

So I started making my clay tablet by mixing sand into the clay and adding water. Then I used (wet) planks to make the tablet as flat as possible.

Then I copied a wikipedia page into a text file and used hf2gcode to make gcode:

```
text=$(python - $1 <<END
import unidecode, sys
with open(sys.argv[1]) as f:
print(unidecode.unidecode(f.read()))
END
)
echo "$text" | fold -w 20 | ./src/hf2gcode -s 0.3 -n 12
```

After letting the clay dry and with my gcode in hand I went to Tkkrlab to try to CNC it. I used bCNC for this. The first try chipped off some bits, so I sprayed some water to make it more soft. But the text was way too small, so I redid the gcode with a bigger font and only the first paragraph of the wikipedia page.

With a bigger font size and regular water spraying I was making good progress, untill the bit started cutting deeper and deeper. Despite my best efforts the tablet wasn’t flat enough, so I had to stop, readjust the CNC, and do some hacky maneuvers to start the job from that point. Success?! Some parts were barely touching, but it’s something!

So I went back home and put it in the oven to dry further. First at below 100, then at 150, and then cranking it all the way up. I guess I was too impatient because it exploded.

- I need to be way more patient with drying (and possibly avoid making bubbles?)
- It needs to be way flatter (or I need to measure and compensate)
- It looks amazing
- Information density is super low

So I’m a bit torn, the first two items are probably solvable, and I really love how it looks, but at this font size it can barely fit a paragraph. With a beter process I can maybe reduce it a little bit, in particular the line height is quite excessive, but it’s just not going to fit an entire wikipedia page.

Do I continue pushing this, or do I try something else? I think I might try what laser engraving slate looks like once the laser cutter at Tkkrlab is back in operation.

]]>You see, the sensible thing would be to just run Julia on the EV3 which runs a full Linux, but the *modern* approach would be to run Julia on the Robot Inventor/Spike Prime Hub, which is an STM32 running a MicroPython firmware.

Here is the game plan. Lacking a MicroJulia implementation and the resources to write a completely custom firmware, we’re going to compile Julia functions into MicroPython extension modules and run them on PyBricks.

This way you can do all your fancy ControlSystems stuff in Julia, compile it to machine code, and then write some Python glue for the sensors and motors.

We’ll basically need to do 3 things

- Compile Julia into ARM machine code
- Link the machine code into an .mpy library
- Run said library on PyBricks

MicroPython has this whole page about how to write native modules in C and compile them into .mpy files. This should be easy!

So I just copied their factorial example, changed `ARCH = armv7emsp`

, and ran `make`

to end up with a sweet native .mpy module. Cool now just upload it. Oh. Anyway after some hacks I could use `pybricksdev run ble test.py`

to upload a tiny test program:

```
import factorial
print("hello", factorial.factorial(4))
```

Except it threw `ValueError: incompatible .mpy arch`

at me no matter what I tried. A little digging later, I found that I needed to enable `MICROPY_EMIT_THUMB`

. But then I got a weird error in MicroPython, which I hacked around as well.

Then, a small victory: running C code! Now onto Julia.

For the first part I used the amazing AVRCompiler.jl which ties into GPUCompiler.jl which ties into LLVM. Long story short, we can abuse the machinery Julia has for running a static subset of Julia on the GPU for generating machine code for other architectures.

All I did was compile Julia from source^{1} while adding `ARM`

to the supported architectures, and changed the target triple in AVRCompiler to the one I found by by copying the CFLAGS from PyBricks and asking LLVM:

```
clang -print-effective-triple -mthumb -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard --target=arm-none-eabi
thumbv7em-none-unknown-eabihf
```

Once that is in place you can just do

```
using AVRCompiler
function factorial(x)
if x == 0
return 1
end
return x * factorial(x - 1)
end
obj = AVRCompiler.build_obj(factorial, (Int32,))
write("factorial.o", obj)
```

So we have a way to run native .mpy modules, and we have a way to compile Julia functions to .o files. Now we just need some glue to link the object file into .mpy and make it accessible from MicroPython.

I basically took the factorial example, ripped out the `factorial_helper`

and replaced it with `extern int julia_factorial(int);`

and modified the Makefile to call my Julia script to generate the object file.

```
# Location of top-level MicroPython directory
MPY_DIR = ../pybricks-micropython/micropython
# Name of module
MOD = factorial
# Source files (.c or .py)
SRC = wrapper.c
SRC_O = factorial.o
# Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin)
ARCH = armv7emsp
# Include to get the rules for compiling and linking the module
include $(MPY_DIR)/py/dynruntime.mk
# Build .o from .jl source files
%.o: %.jl $(CONFIG_H) Makefile
$(ECHO) "JL $<"
julia --project=. $<
```

This was all looking really promising until I ran `make`

and was greeted by

```
LinkError: factorial.o: undefined symbol: __aeabi_unwind_cpp_pr0
```

But, yolo, lemme just comment that out in `mpy_ld.py`

and press on. This symbol appears to be related to exceptions and I don’t really care.

```
$ pybricksdev run ble test.py
Searching for any hub with Pybricks service...
100%|████████████████████████████| 280/280 [00:00<00:00, 2.15kB/s]
hello 24
```

At last! Julia code running on the Lego hub! TODO: make cool demo

]]>I started designing the part that clamps the phone, and the tilt mechanism. Everything from building the Lego set to making the tripod was basically an excuse to use these linear screw cylinders in my builds. The clamp is made of these rounded pieces to hold the phone, with a 5.5 axle with stop sandwiched between cross blocks to provide a stiff adjustable size. From there I just tried to find the most compact and straightforward way to attach the linear screw cylinder.

Next I wanted to be able to raise and lower the phone. At first I wanted to use scisor lift mechanism, but quickly realised it’d be too complicated. Instead I went for a car jack mechanism. The set comes with just the right amount of gears and liftarms. The only downside is that the gears have to be offset half a tooth so it’s not exactly straight. It also wasn’t very stable at first, but after adding the center bar, it’s acceptable. Just give it a bit of help when tryig to raise the jack while a phone is inserted.

Then it was just a matter of connecting them together with the turntable. Here it is in the lowest and highest position.

To use my phone as a camera, I use DroidCam OBS

]]>