[openBF511] Reverse Engineering a Omron BF511 scale

I actually own a Omron BF511 human scale, a nice bit of japaense kit. Its a bit old and certainly does not want to interact with the world expect for its LCD display. I wanted to change that and integrate the scale into my health apps/do not keep on tracking stats via paper and pencil. For that reason I did a small deep dive on how to best access its data and send it back via LoRaWAN. All infos can be found on this little Github Repo: https://github.com/nmaas87/openBF511

[Paper] apex Mk.2/Mk.3, apex MCC, apex MRMSS

Finally the series of studies of the apex Mk.2/Mk.3 experiments flown on MAPHEUS 10 (2021-12-06) are peer-reviewed and published. You will find them as follows:

  • apex Mk.2/Mk.3: Secure Live Transmission of the First Flight of Trichoplax adhaerens in Space Based on Components Off-the-Shelf: https://doi.org/10.3390/eng6090241
  • The apex MCC: Blueprint of an Open-Source, Secure, CCSDS-Compatible Ground Segment for Sounding Rockets, CubeSats, and Small Lander Missions: https://doi.org/10.3390/eng6090246
  • The apex MRMSS: A Multi-Role Mission Support System and Service Module Simulator for Payloads of Sounding Rockets and Other Space Applications: https://doi.org/10.3390/eng6090247

Some additional materials:

Reworking a Dell Latitude XT in 2024

Intro

I had an old Dell Latitude XT (first generation) in my posession since... quite a long time. One of the first tablet/convertible laptops by Dell, released in 2007.

It was quite a nice machine, but it suffered from multiple issues. On the one hand, the ULV Core2Duo and internal 1 GB of DDR2 memory are nothing to write home about - but the biggest problem in my opinion - is the used mass memory, a 1.8' 5400 rpm harddrive. And no, you read correctly - 1.8'. What's that? Thats basically the size and model of HDDs used in Apples iPods: They are awfully slow and even more prone to break.

Actually, I used this laptop quite a lot during my masters studies - and I had it breaking 2 drives in a short amount of time. I used it during a space campaign later, but after that it started to develop Read/Write Errors again and I shelved it - until now.

1.) RAM

The easiest thing to "solve" is the memory issue, solve - however - is a big word. You can extend the memory to a total of 3 GB by plugging in another SO-DIMM DDR2 667 MHz module, but sadly thats it. The CPU can address a maximum of 4 GB of RAM, but as 1 GB of RAM is already soldered to the mainboard - well, it ends with that upgrade, sadly. I had really hoped someone would have tinkered around with the BIOS and found out how to extend the memory by accepting 4 GB modules or such, but I have not found anything on the web - if you got something, please let me know :).

2.) CMOS battery

Being that old, (2008-03-26) the CMOS battery has long been dead. Sadly, the usual issue arrives - the laptop quickly turns on - then shuts off and does not boot. The first thing to do is to remove the keyboard bezel and keyboard - to be able to remove the battery altogether to allow the system to boot again. Removing the battery reveals that it has three connector pins: 2 GND and 1 VCC pin. Removing the shrink wrap shows its just an ordinary Panasonic CR2025 cell:

So, creating a new battery is very easy. As I did not have another CR2025 at hand with solder tabs, I just took a new CR2032, put the soldering iron to 420 Degrees Celsius and quickly soldered the connectors directly to the battery. But be careful, you need to be very quick to not damage the coin cell.

In the end I put it in shrink wrap...

... but I found out that the battery got too thick to be inserted beneath the keyboard. So I ended up in soldering ground directly to the GND terminal of the battery and the positive terminal to the side of the battery case - and keeping the solder as low profile as possible. As the shrink wrap also added too much height I put it into kapton tape.

Good thing - it worked out in the end :).

3.) Harddrive replacement

Getting a new harddrive for the ZIF PATA socket would be quite expensive - and the 1.8' drives will be slow and fail anyway after some time. So I looked for alternatives. There are ZIF Sdcard connectors, but they are also expensive and while Sdcards can be faster, they are still not a good memory media for hosting the OS of an Desktop computer. There are also ZIF SSD drives, but - again - very expensive. But the last thing I found was interesting: An ZIF to mSATA adapter for some bucks on one big online shop.

The most important parts of this adapter was that the ZIF socket did not correctly fit the original adapter cable from the Latitude XT and my mSATA SSDs passive components collided a bit with the adapter board, so the first thing I did was to use some kapton tape to make sure we will not create any short-circuit:

Then I lined up the original connector cable with the board, as shown, metal contacts up:

As written already, the original connector cable was "too thin" to be correctly grabbed by the ZIF socket, so I applied two strips of kapton tape to the backside of the connector to make it a bit thicker - this worked like a charm:

The last issue was that the drive was floating around the case - only dangling by the delicate ribbon cable. So I printed a little baseplate which would fit into the slot neatly:

Upon this baseplate I alligned the overall construct and made some notes so I could apply some doublesided tape...

... after I enclosed the new construct in the "rubber frame" I took off the hard disk drive and screwed it all into position.

Luckily it fit and even the old battery still had enough clearance to slide into position.

After that, I could use the SSD as any other internal harddrive and install Debian 12 on the system, which run quite well (the only exception were the lower touchpad mouse buttons and the very old Broadcom Wireless Card). The best part was the impressive speed the system now boasted - in contrast to the old clunky harddrive. Really nice!

4.) System Battery

Well, the main battery does still charge - but only to a certain degree until the battery icon starts blinking red and it becomes a bit... toasty. So, I took the battery out. Sadly the laptop is quite top heavy and tends to tipover if its used without the battery. There are still spare batteries around, but I wonder what the life expectancy of those would be, probably nothing fancy. I am thinking about dissassembling the battery and removing the cells, inserting some lead or other ballast to just have it in its original look (the main battery is an important part of the case which looks a bit... funny without it) and useability. But, thats something for later.

RAK3172T in Arduino

Updated on 2024-12-01 with latest fixes of STM32LoRaWAN v0.3.0

As the RAK11300 did not really match my requirements (it uses way too much energy), I looked further and found the RAK3172 which is based on an STM32WLE5CCU6 - an STM32 chip which usually include good powersaving modes. As with the RAK11300, I did not want to go down the Wisblock way and designed my own breakout board:

This was actually not complicated as the RAK3172 has all needed components on board. I just added an H7333 LDO for powering it from 18650 cells and two additional features: First, the i2c port was aligned with the pinout the the usual BME280 breakouts from your typical websources - so that I could directly plug those in. Secondly, I made a solderbridge that allowed this i2c port to be 3v3 powered either directly by the LDO or by an port of the STM32. The later option I actually now use in normal operations, as this allows the system to really cut the BME280 from power so that it does not drain energy while the setup is in sleep mode / not actively measuring.

As core I wanted to go with the stm32duino Core as it already had the STM32WLE5CC supported. The only thing missing was the fact that I actually bought the TCXO enabled 3172T variant and this was not yet supported - and so I got my first PR on the stm32duino repo.

For the radio library I was thinking about chosing the RadioLib again and made a lot of research already on how to get it implemented, but at that time, RadioLib was working around their LoRaWAN implementation quite a lot - and shooting for a moving target was getting me nowhere, so I tried out STMs own implementation.

Update 2024-12-01
The following two paragraphs detail workarounds for issues with STM32LoRaWAN v0.2.0 which have been resolved for good three days ago with release of v0.3.0 and are thus only interesting in the historical context of me setting up those sensors initially in ~June 2024.

(Fixed with PR #47):
This did work out quite well, however, there is to this day a bug with the timers which prevents the chip from ever waking up again - a workaround for it is given in the issue in case you want to try it too ( calling rtc.begin(true) after modem.begin() makes it work! ).

(Fixed with PR #44 / #39):
I also had an additional issue, I left the RAK3172T sleep usually for 10 minutes, wake up, do a measurement and transmitting it back - then go to sleep again. This worked in my testing until 28 "wake-ups" - then on the next sleep, the chip would never wake up again. I am not sure what is causing this, but I went down the road of just counting the wake-ups and restarting the chip after the 24 or so, just to have a safe margin.

Again, this was not a project I spend too much time with, I just wanted something to replace my breadboard wired mess of two CubeCell HTCC-AB01 - which absolutly did not stay unchanged in place since a short test back in 2021 and were working like this ever since (and suffering from disconnect whenever looked at from a wrong direction ;))

So - yeah, if I were to have time I would probably look and test RadioLib support.

Turing Pi 2 - Retrofitting an EMC2301 Fancontroller

The Turing Pi 2 (v.2.4) has actually some secrets that just start to get uncovered. Looking closely at the board, you will find the red marked area with what seems to be an unpopulated 4-pin fan header (J16) and an unpopulated IC spot (U109).

These spots were, as found out by Sam Edwards (CFSworks) actually a place to install Molex 47053-1000 4-pin fan connector and a Microchip EMC2301 fan controller. It is not clear why they were left out of the production model, probably for cost savings, but without that the Turing Pi 2 lost a nice smooth fan controller. However, thanks to Sam Edwards work, the chip is now supported by the Turing Pi 2 BMC, so that after retrofitting, this will just work out of the box!

To do this little hack you just need to order an Microchip EMC2301 in its MSOP-8 / standard packaging. If you don't find the specific molex conenctor its also fine if you "just" use an male 2.54mm header pin. Yes, you will not have the locking feature of that connector and you will need to watch out for the correct orientation when plugging in fans - but it still works in a pinch.

When soldering I would advise to solder the EMC2301 first onto its spot, the orientation is as shown in the second picture. Afterwards you can solder the fan header.

Make sure the circle matches up as shown so that you got the chip in the correct orientation.

After the installation, update your Turing Pi 2 BMC to the latest firmware 2.0.5. Please make sure to read the comments on the repo, if you're updating from a BMC version < 2.0.0 you will need to make a first flash via a Micro SD card to get to version 2.0.0 and update further afterwards.

When everything is done and you log into your Turing Pi 2 BMC, you will see this nice slider to set the speed of your EMC2301 connected fan.

Currently there are still some things up for debate and configuration with the latest kernel release which changed naming of components but overall its already working and a nice and easy mod to get a better fan controller installed - instead of just the "on/off" as soon as one Turing node gets activated - or all deactivated.

Again, thanks a lot to Sam Edwards who had the idea and came towards with the PRs and implementation - thank you! 🙂

GeeekPi RPi 4G Hat for RAKwireless RAK5146 USB

In 2021 I started playing around with the SX1303 based RAKwirless RAK5146 USB LoRaWAN concentrator in mini-PCIe form factor ( 1, 2 ). Back at the time I used the RAK2287 Pi Hat which - even though the website states otherwise ("Note: RAK2287/RAK5146 Pi HAT is compatible only with the SPI version of the RAK2287 and RAK5146 LPWAN concentrators.") is actually compatible with the USB version of RAK5146. In the second post I even want so far to hack the RAK2287 and bridge the PPS (Pulse Per Second) output of the included GPS onto the RPi GPIOs so that I could turn the RPi into a precise GPS NTP server - and I even added an I2C/"poor-mans-QWIIC connector" to it.

While all these hacks were successful and the balenaOS backed RPi 3 LoRaWAN concentrator was still working (with regular updates) in 2024, I finally wanted to streamline my overall IT setup: I had an WD My Cloud Mirror Gen2 NAS I updated to Debian 11 in 2022, the LoRaWAN concentrator/NTP server/Room sensor RPi 3 and an very old Medion Laptop was I was abusing as the main "server" - it was time to unify those systems and move on.

Luckily, I had gotten a TuringPi 2 and Turing RK1 SBCs which were a powerful combination: My plan was to use one RK1 in Node 3 position on the TuringPi 2 which did give the SBC one NVMe slot, a PCIe attached Dual Port SATA bridge and the additional USB 2.0 port which could be switched to any Node on the board. With the RK1 as a way more powerful CPU (in contrast to the old AMD 1 GHz Dual Core on the Medion Laptop...) and the Dual Port SATA bridge I already got Server and NAS functionality solved. But what about LoRaWAN and the other functionalities?

Well, as I bought the RAK5146 USB (with LBT and GPS), I could just use the RAK2287 to plug a cable from the Micro USB cable into the additional USB 2.0 port of the TuringPi 2 - and this was done. However, what about the GPS and PPS input to allow for an NTP server? And did I really wanted to install the RAK2287 into an ugly box - or leave it open for further projects?

Thats when I realized I still had the GeeekPi RPi 4G Hat at hand, an RPi "Hat" I bought back then when I thought that might be the cheaper alternative, but went out of my way to also buy the RAK2287 and never really tested it.

The GeeekPi is just an ordinary hat, well - lets say it does not even has any connection to the RPi, just an USB C port - not even some GPIO connection - its basically just an "USB LTE Modem in mini-PCIe to USB breakout" in the shape of an RPi Hat.

Looking at the SIM Card Slot I had some mixed feelings and hoped that it would not interfere with the RAK5146 - so I rechecked the pinout:

Luckily it turned out that the RAK engineers thought about this and marked the SIM Card Slot connectors as NC/not connect. With this, this card could be inserted into any Laptops WWAN slot or an SBC like the balenaFin. Neat!

After the pinout situation was clear, there was just one thing left: Soldering the needed connections directly to the mini-PCIe connector:

The needed pins were GND, PPS, PI_UART_TX and PI_UART_RX which I broke out onto a 2.54mm female header and fixed them into place with a bit of hotglue as strain relief:

Then I just needed to add an FTDI USB UART to the mix and connect these pins:

  • GND to GND
  • PPS to DCD
  • PI_UART_TX to TX
  • PI_UART_RX to RX

Afterwards I just needed to install the RAK5146 and the LoRaWAN and GPS antennas before powering up the unit via the USB C port of the Hat and connect the FTDI UART USB as well.

And the end I also broke out the RESET_GPS and STANDBY_GPS pins just for good measure, but as they are active low I did not need to pull them to any potential. But then again, it could come in handy in the future.

With those changes I can now use the RAK5146 directly over an USB connection and still get the GPS data and PPS signal for use in gpsd. I ended up printing an enclosure for the overall construction, added an 4 port USB hub and connected one of my RAK11300 breakouts as a Meshtastic node and another Waveshare RP2040 Zero as an environmental sensor which terminates a BME280 and an Amphenol Telair T6713 CO2 sensor to measure the room climate. I also rewired the USB 2.0 Hub to an USB C connector and now everything has its place - and I still have my empty RAK2287 board lying around in case I where to get another RAK5146 for other jobs ;).

Install a bootloader onto Turing RK1 eMMC memory

The Turing RK1 is a new Compute Module, made for the Turing Pi 2 board and with compability to the Nvidia Jetson cards in mind. While the board has some impressive specs ( https://docs.turingpi.com/docs/turing-rk1-specs-and-io-ports ) it had one small issue: The used Rockchip RK3588 needed a bootloader on its eMMC to be able to boot from an attached NVMe drive. As no isolated bootloader was available, most people flashed a whole OS onto the eMMC - just to be able to use the attched NVMe drive. This process can take quite a while via the Turing Pi 2 board ( e.g. 60 - 90 minutes: https://docs.turingpi.com/docs/turing-rk1-flashing-os ) it was never a good option.

Now there is finally a solution brought up by Mister gregordinary from the DietPi Forums - who deserves all the credit ( https://dietpi.com/forum/t/new-turingpi-rk1/19142/9 ):

On your local machine:

  • Make yourself a working directory and open a terminal in that location.
  • Download the u-boot apk: https://alpine-rk1.cfs.works/packages/main/aarch64/u-boot-turing-2024.04-r0.apk
  • Open it with an archive utility and extract the following files into your working directory:
    a. idbloader.img
    b. u-boot.itb
  • Create an .img file we’ll use later to flash the RK1: touch turing-rk1-uboot-only.img
  • You should now have the idbloader.img, u-boot.itb, and turing-rk1-uboot-only.img in your working directory. From there, use dd to create our combined image:
    a. dd if=idbloader.img of=turing-rk1-uboot-only.img bs=512 seek=64
    b. dd if=u-boot.itb of=turing-rk1-uboot-only.img bs=512 seek=16384
  • The resulting .img file can be used to flash the RK1 device through the Web UI or tpi utility.
  • Once generated, this .img can be used "as is" to flash the other nodes after moving them to NVME.

With that generated file you can directly flash the eMMC and use any NVMe to boot from.

... and here is already a prepared file if you want to use it directly: turing-rk1-uboot-only.zip

After that, the RK1 will be able to directly boot from an NVMe, given the fact a suitable ARM64 image has been installed on it.

And to install the operating system image onto the NVMe, you can use balenaEtcher with e.g. your Linux, Mac or Windows computer and an USB to NVMe adapter. But please be careful not accidently overwrite one of your interal disks and ruin your computer - I don't take any responsibilty, just in case.

Repairing a Turing Pi 2

Intro

In April 2023 I finally got my Turing Pi 2 - happy to finally got it into my hands I plugged it in, headed over to the Turing Pi 2 Github ( https://github.com/turing-machines/BMC-Firmware ) got myself the latest BMC firmware and started to flash it. Thats when things got wrong: The power supply coils started to screech - and seconds later I was greeted with blue smoke and a dead Turing Pi 2. The firmware update actually flashed the board into a paper weight.

The one green led was still on, the BMC led was off and the board did not react - also the networking LEDs were off. To be honest, this was the first time a firmware update actually physically bricked hardware / got it up into smoke.

Luckily, Turing Machines, the company behind Turing Pi 2 did send me another board, thinking that the (also ordered at Turing Machines) small form factor power supply could have been the culprit - thanks a lot guys for that tremendous help!

The new board was working without an issue and did wait for quite some time until I updated it - horrified the same thing could happen again. I think I updated it to 1.1.0 and things were ok back then.

However, with half a year passed and still the dead board lying around I thought, maybe I could fix it?

The incident

What happened was a short circuit - but how? The only thing I could think of was an error in the firmware upgrade - and the BMC processor - a dual core Allwinner T113-S3 - shortcircuiting something as it did not have its initialization correctly. While this should never be possible (e.g. that a board is layouted in such a way that a processor would short-circuit if it has no program available), I had no other explanation. But how to find out what was broken? The incident actually burned the 5A SMD fuse for the 3v3 line - meaning the board was now powering up ok-ish, but without any of the 3v3 components (e.g. the BMC). Short-circuiting the fuse directly lead to warmth and a bad stench - something was still short-circuiting. So I build myself a Thermal Camera. I was quite sure that the T113-S3 was toast by now, so I removed it.

After that, I tried bridging the blown fuse again - this time with an ampere meter. About 6 Watts were going somewhere I did not know. The Thermal Camera showcased an SMD capacitor glowing red - that poor thing got mangled so badly that it decided to become conductive for DC - not good. I removed that capacitor as well - no excess power was drawn anymore after that.

The repair

I ordered two new Allwinner T113-S3 ( T113-S3 ELQFP-128, position 2 on the picture below ) as well as some spare 5A SMD fuses ( LF5A - 10PCS/LOT 1808 SMD fuse 125V fast 5A, position 1 on the picture below ) from the web. The capacitor with 570uF was replaced by a 12.2uF for testing ( position 3 on the picture below).

As soon as I had everything in place, I got the latest firmware image, 2.0.5 from the Turing Pi Website ( https://firmware.turingpi.com/turing-pi2/v2.0.5/tp2-firmware-sdcard-v2.0.5.img ). I flashed it onto a MicroSD Card with balenaEtcher and inserted the MicroSD Card into the appropriate slot on the backside of the Turing Pi 2. With everything in place, I hooked everything up, got myself a flathead screwdriver and shortend the MOSI and SCLK/Clock lines of the flash module on-board of the Turing Pi 2 as mentioned in this issue ticket: https://github.com/turing-machines/BMC-Firmware/issues/134 ( see position 4 in green, the left bottom two legs of the chip ). You can also see the both legs better on the picture below:

With my screwdriver still shorting both legs of the flash module, I applied power to the Turing Pi 2. After some seconds, the LEDs came on and the 4 LEDs of the ethernet ports started blinking slowly. I removed the short-circuit on the flash module, pressed KEY1 3 times rapidly and the ethernet ports started turning on one LED at a time. After about 30 seconds, the LEDs stopped the animation and all LEDs blinked twice (again and again) to show the flash was a success. I powered down the Turing Pi 2, removed the MicroSD card and powered it up again - and it was fixed! 🙂
( Usual procedure for update from a MicroSD Card can be found here: https://docs.turingpi.com/docs/turing-pi2-bmc-v1x-to-v2x )

Good ending

With that, the Turing Pi 2 was fixed - BMC is answering my calls, all four nodes are working. It was not an easy fix by all means, but using a microscope, loads of flux and patience made it work again. And the Thermal Camera! That less than 40 Euro thing really helped a lot, otherwise I would have not found the issue without turning the whole board into a burning mess.

Building a MLX90640 Thermal Camera

Intro

Thermal Cameras are neat tools, especially in electronics: You can quickly diagnose short circuits, identify misbehaving components or quantify if subsystems get too hot. However, thermal cameras are still a luxury item which can quickly costs hunderds of euros. Sometimes - however - you do not need the accurarcy of a FLIR or similar system, somtimes its just enough to see which part of a PCB is heating up quicker than others. Enter MLX90640: A 32x24 pixel sensor in two different FoVs, exposing its data via i2c. These sensors can be had on different websites for as low as 25 euros during sale.

Components/BoM

  • 1x MLX90640 Thermal Sensor
  • 1x Waveshare RP2040 Zero
  • 1x HT7333 LDO
  • 2x SMD Capacitor 10 uF
  • 2x SMD Resistor 2.2k Ohm

All in all about 30 Euros

Schematic

Nothing really surprising - a HT7333 with some filter caps to allow for a clean power source, two pull-up resistors to allow for the high-speed, 1 MHz communication via the i2c bus. Keep the i2c wires between RP2040 Zero and the sensor as short as possible and pull-up the i2c SDA/SCL lines directly at the sensor.

Operation modes

There are two operation modes here, you can either choose to use the sensor with the Adafruit and Sparkfun example, which will just write a (very long) ASCII line consiting of 768 comma seperated float values per frame, delimited by \n. This will also work with a Processing example to showcase the picture (about 4 FPS).
The alternative to that, would be using another approach, packing the 768 temperature values not in a data intensive ASCII line, but send them via SLIP protocol to the computer. The processing example is not available for that, but I got some other Python code in the making (about 7.5 FPS).

Serial

Firmware (Serial)

Programmed via Arduino using the Arduino-Pico framework, using the Adafruit_MLX90640 library ( https://github.com/adafruit/Adafruit_MLX90640 ) - please be aware that 1 MHz I2C operations and mlx.setRefreshRate(MLX90640_32_HZ) will only work with extremely short wires!

#include 

Adafruit_MLX90640 mlx;
float frame[32*24]; // buffer for full frame of temperatures

// uncomment *one* of the below
#define PRINT_TEMPERATURES
//#define PRINT_ASCIIART

void setup() {
  while (!Serial) delay(10);
  Serial.begin(115200);
  delay(100);

  //Serial.println("Adafruit MLX90640 Simple Test");
  if (! mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire)) {
    Serial.println("MLX90640 not found!");
    while (1) delay(10);
  }
  /*
  Serial.println("Found Adafruit MLX90640");

  Serial.print("Serial number: ");
  Serial.print(mlx.serialNumber[0], HEX);
  Serial.print(mlx.serialNumber[1], HEX);
  Serial.println(mlx.serialNumber[2], HEX);
  */

  //mlx.setMode(MLX90640_INTERLEAVED);
  mlx.setMode(MLX90640_CHESS);
/*
  Serial.print("Current mode: ");
  if (mlx.getMode() == MLX90640_CHESS) {
    Serial.println("Chess");
  } else {
    Serial.println("Interleave");    
  }
*/

//  mlx.setResolution(MLX90640_ADC_18BIT); //default
  mlx.setResolution(MLX90640_ADC_19BIT);
  /*Serial.print("Current resolution: ");
  mlx90640_resolution_t res = mlx.getResolution();
  switch (res) {
    case MLX90640_ADC_16BIT: Serial.println("16 bit"); break;
    case MLX90640_ADC_17BIT: Serial.println("17 bit"); break;
    case MLX90640_ADC_18BIT: Serial.println("18 bit"); break;
    case MLX90640_ADC_19BIT: Serial.println("19 bit"); break;
  }
*/

  //mlx.setRefreshRate(MLX90640_2_HZ);
  //mlx.setRefreshRate(MLX90640_8_HZ);
  mlx.setRefreshRate(MLX90640_32_HZ);

  /*Serial.print("Current frame rate: ");
  mlx90640_refreshrate_t rate = mlx.getRefreshRate();
  switch (rate) {
    case MLX90640_0_5_HZ: Serial.println("0.5 Hz"); break;
    case MLX90640_1_HZ: Serial.println("1 Hz"); break; 
    case MLX90640_2_HZ: Serial.println("2 Hz"); break;
    case MLX90640_4_HZ: Serial.println("4 Hz"); break;
    case MLX90640_8_HZ: Serial.println("8 Hz"); break;
    case MLX90640_16_HZ: Serial.println("16 Hz"); break;
    case MLX90640_32_HZ: Serial.println("32 Hz"); break;
    case MLX90640_64_HZ: Serial.println("64 Hz"); break;
  }
  */

  //Wire.setClock(400000); // max 1 MHz
Wire.setClock(1000000); // max 1 MHz
//  Wire.setClock(3400000); // max 1 MHz

}

void loop() {
  //delay(500);

  if (mlx.getFrame(frame) != 0) {
    //Serial.println("Failed");
    return;
  }

  //Serial.println();
  //Serial.println();
  for (uint8_t h=0; h<24; h++) {
    for (uint8_t w=0; w<32; w++) {
      float t = frame[h*32 + w];
#ifdef PRINT_TEMPERATURES
      Serial.print(t, 1);
      Serial.print(",");
#endif
#ifdef PRINT_ASCIIART
      char c = '&';
      if (t < 20) c = ' ';
      else if (t < 23) c = '.';
      else if (t < 25) c = '-';
      else if (t < 27) c = '*';
      else if (t < 29) c = '+';
      else if (t < 31) c = 'x';
      else if (t < 33) c = '%';
      else if (t < 35) c = '#';
      else if (t < 37) c = 'X';
      Serial.print(c);
#endif
    }
    //Serial.println();
  }
  Serial.println();
  //Serial.write('\n'); //10
}

Software (Serial)

To read the data from the sensor, the SparkFun Processing example ( https://learn.sparkfun.com/tutorials/qwiic-ir-array-mlx90640-hookup-guide/all#example-code ) can be used.
Alternatively, you can use Python 3.x and pyserial, giving back a list of thermal readings which can then be drawn into pictures.

import serial
import re
ser = serial.Serial('COM12', 115200, timeout=2)
while True:
    line = ser.readline().decode('ascii')   # read a '\n' terminated line
    thermalData = re.split('\,', line[:-3]) # last ,\n needs to go!
    print(thermalData)

SLIP

Firmware (SLIP)

Programmed via Arduino using the Arduino-Pico framework, using the Adafruit_MLX90640 library ( https://github.com/adafruit/Adafruit_MLX90640 ) as well as PacketSerial ( https://github.com/bakercp/PacketSerial ) - please be aware that 1 MHz I2C operations and mlx.setRefreshRate(MLX90640_32_HZ) will only work with extremely short wires!

#include 
SLIPPacketSerial packetSerial;

#include 
Adafruit_MLX90640 mlx;
float frame[32*24]; // buffer for full frame of temperatures

void setup() {
  while (!Serial) delay(10);
  Serial.begin(460800);
  Serial.setTimeout(100);
  packetSerial.setStream(&Serial); 
  delay(100);

  if (! mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire)) {
    //Serial.println("MLX90640 not found!");
    while (1) delay(10);
  }
  /*
  Serial.println("Found Adafruit MLX90640");
  Serial.print("Serial number: ");
  Serial.print(mlx.serialNumber[0], HEX);
  Serial.print(mlx.serialNumber[1], HEX);
  Serial.println(mlx.serialNumber[2], HEX);
  */

  //mlx.setMode(MLX90640_INTERLEAVED);
  mlx.setMode(MLX90640_CHESS);

//  mlx.setResolution(MLX90640_ADC_18BIT); //default
  mlx.setResolution(MLX90640_ADC_19BIT);

  //mlx.setRefreshRate(MLX90640_2_HZ);
  //mlx.setRefreshRate(MLX90640_8_HZ);
  mlx.setRefreshRate(MLX90640_32_HZ);

  //Wire.setClock(400000);
  Wire.setClock(1000000); // max 1 MHz
}

void loop() {
  packetSerial.update();
  if (mlx.getFrame(frame) != 0) {
    //Serial.println("Failed");
    return;
  } else {
    packetSerial.send((uint8_t*)&frame, sizeof(frame));   
  }
}

Software (SLIP)

Using Python 3.x, pyserial and sliplib, giving back a list of thermal readings which can then be drawn into pictures

import serial
import sliplib
import time
import struct
ser = serial.Serial('COM12', 115200, timeout=2)
while True:
    bytesToRead = ser.inWaiting()
    if (bytesToRead!=0):
        readData = ser.read(bytesToRead)
        decodedData = sliplib.decode(readData)
        if decodedData != b'':
            thermalData = []
            for i in range(0, len(decodedData), 4):
                thermalData.append(struct.unpack('f', decodedData[i:i+4])[0])
            print(thermalData)
            # ca. 7.41 Frames raw
    else:
        time.sleep(0.07)

Finishing up

After connecting everything, I finished the build up by printing a small PLA housing and even printed a TPU cap for the lense itself, so that (via friction fit) the sensor could be just thrown into the toolbox and still would be protected from dust and other issues. Could the finish be gotten more pretty? Yes. Did this hinder its function? Not at all, it already helped me fix up a defective turingPi2 🙂 (maybe something for the next post).

Optimisation

As usual in engineering, some problems can be solved by throwing money at them. In this case, a Teensy 4.x as a processor could significantly increase the data throughput. However, I did not want to pair a 40 Euro MCU with a 25 Euro Sensor - be it for costs and footprints sake - and opted in for the 2.5 Euro RP2040 Zero. Still, performance can be improved over my first optimisation using SLIP - by just using the second core of the RP2040. Ideally you would multi-thread the application, having the first core always retriving new data, while the second core packs it into SLIP packets and sends them on their way. This should be fairly easy and might result in more fps :).

Photos