Ever since I became a father in 2023, there's been less time, even more so than I anticipated, for hobby projects such as this blog.
But I am still here.
My name is Mikael Silvén.
I was born in '89,
I work in IT and this is my website where I occasionally write about stuff.
Any blogopinions expressed here are mine alone and do not (necessarily) represent those of my employer or co-workers.
Ever since I became a father in 2023, there's been less time, even more so than I anticipated, for hobby projects such as this blog.
But I am still here.
After a few meetups about embedded systems I got very interested in getting an ESP32-C3, due to the combination of Rust and RISC-V.
Before my vacation, I got the wifi working but I did not have time to write a post about it.
Next I'm going to splice some cables to connect a sensor to the i2c bus so I can show some interesting stuff on the display. Another idea would be to use Bluetooth and publish the sensor's data to my larger weather station. Let's see where I end up with this.
Sidenote: Source for the larger station is up on github.
For a long time, I've kept a co2 meter on my desk to monitor the air quality (or lack thereof) in my office. I have now upgraded this a bit and this is a short post about that project.
It started a few weeks ago, when I was inspired by a video on youtube. Creator Allen made a beautiful display based on a rgb led matrix display and I immediately felt that I needed (to build) one as well. This Ascension Day long weekend, I finished it.
Except for the fact that my soldering is terrible, I'm very happy with the finished result. I designed the case using tinkercad and printed it on my Prusa MK3S+. Initially, I has placed an order on the 5mm pitch version, but thankfully I realized that that would have been too large for me to able to cover the screen the with a single piece, so I changed the order to the 3mm pitch version.
Electronics-wise, I opted for a Pi Zero 2 W, with the Adafruit Hat as that does a lot of the job for me. I added a power button with an LED and a rotary encoder I can use to provide some input to the software.
I also chose to put in some cables to expose the USB and HDMI ports of the Pi Zero to make it easier to access.
The most important piece though, is the co2 meter. Initially I had thought of using the one I already had, but I found a smaller one that would make a better fit. In my first version of the case, I placed it behind the display - but it turned out that the screen gets so hot, it skews the temperature measurements quite a lot. After moving the sensor to the base of the case, it's only about 2 degrees over ambient, thanks to the heat of the Pi.
I have learned more about 3D printing, a lot more about soldering, electronics, i2c and the Raspberry Pi GPIO ecosystem. I haven't really done anything like this since high school. Software-wise I'm still in the process of writing and cleaning up code, but I'll push it up on github in the near future.
For the past few weeks, the topic of my book club has been Unit Testing by Vladimir Khorikov.
It's an intermediate+ level book on automated tests, mostly focusing on unit tests. Fairly opinionated, but I found myself agreeing to the core of pretty much every claim, so I mean, that's good because I fairly opinionated myself.
In the book, the author presents four core characteristics of a good unit test.
Arguably the main point of writing tests at all. We want to know when something doesn't work. But it's a sliding scale of course, some tests are better in this regard, and some are worse. Tests with many Asserts generally score better, because it nails down the behavior more, and tests that Assert on nothing, are worthless. (At least in compiled languages. Tests that assert on nothing in interpreted languages still protect against typos.)
In my opinion, the most important facet. You want your code, and this includes your tests, to work on a high level of abstraction, so that you can easily change the details of things without needing to rewrite everything.
Unit Tests can cover a lot of the code base really quickly. In a good testable system, you can run hundreds of tests in less than a second. This is is very important for the Developer Experience and workflow. You need your tests to not move out of the process boundary, not touch the network or hard drive, be parallelizable etc.
Lastly maintainability touches upon a lot of the other things, like if there's a separate program you need to install or run, be it a database or such. How hard the code is to read.
Vladimir does a nice job explaining mocking, and how to push the mocking to the very edge of the system in order to gain maximum rewards from it. He then completely invalidates mocking as a tool by introducing spies, and show how they can give you a better experience. I too have championed for interfaces at the edges, but thanks to this book it was taken to the next level. And this touches on the bigger picture idea that you should design your code to be testable like this, and refactor/rewrite it to enable these sorts of things.
When the author talks about testing the database, he says quite categorically, "Do not use in-memory databases". I too have had that come back and bit me. But the lesson I learned was that relying on an in-memory database, and assuming that it matches the production one is a flaw. So I think a test suite that works with both an in-memory one, and a "real" one is not inherently evil. This is a particular pinch of the salt you might want to apply to more of the book. Be aware of your assumptions.
All in all, a great book. Do read it.
So I recently had a look through my old blog posts and one in particular caught special interest.
It's been a long time since I solved these kinds of problems and I wanted to freshen up, at least a teeny tiny bit, so I rewrote the solution in Rust.
use std::collections::BTreeSet as Set; use std::collections::HashMap as Map; fn solve_subset_sum(input: &[i32], target: i32) -> Option<Set<i32>> { let mut possible_sums = Map::<i32, Set<i32>>::new(); for elem in input.iter() { let mut new_sums = possible_sums.clone(); for (prev_sum, mut components) in possible_sums.drain() { let new_sum = prev_sum + elem; components.insert(*elem); new_sums.insert(new_sum, components); } possible_sums.extend(new_sums); possible_sums.insert(*elem, { let mut s = Set::new(); s.insert(*elem); s }); if possible_sums.contains_key(&target) { return possible_sums.remove(&target); } } return None; } fn main() { let input = vec![-6, -9, -10, -11, -15, 1, 2, 3, 4, -3]; println!("Solution: {:?}", solve_subset_sum(&input, 0)); }
I realized I can use essentially a list of maps instead of a matrix of booleans. It makes the calculation of the solution trivial and is probably just a side-grade when it comes to compute and memory requirements, depending the input.