Repairing a TuringPi 2

Intro

In April 2023 I finally got my TuringPi 2 - happy to finally got it into my hands I plugged it in, headed over to the TuringPi 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 TuringPi 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 TuringPi 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 TuringPi 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 TuringPi 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 TuringPi 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 TuringPi 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

RT-Thread IoT-Contest 2023 with NXP i.MX RT1060 - Getting started

The folks over at RT-Thread decided to make a IOT-Contest using hardware from different vendors and their RT-Thread OS. I was luckily enough to get chosen for a networking project with the NXP i.MX RT1060 EVKB, so I decided to document my journey first getting started with RT-Thread. Here we go! 🙂

Getting started

First of all, its important to know that there are two version of the MIMXRT1060 - the EVK and the EVKB. The EVK seems to be an older version which also includes a Camera sensor module, the EVKB version the recent one. RT-Threads example on Github is for the EVK version, which means things like User LED blinking does not work out of the box, but we will take care of this later.

NXP i.MX RT1060 EVKB files and documentation

You'll need to create a free user account on NXPs webpage to download the User manual and Schematic, otherwise the last two links will not work.

RT-Thread Setup

  • Main RT-Thread Github Page for the NXP i.MX RT1060: https://github.com/RT-Thread/rt-thread/tree/master/bsp/imxrt/imxrt1060-nxp-evk
  • If not yet done, download and install Git
  • RT-Thread Studio: https://www.rt-thread.io/studio.html
    • Just download and install (e.g. D:\RT-ThreadStudio)
  • envTool: https://github.com/RT-Thread/env-windows/releases
    • Pick the latest env-windows-v*.7z and download
    • Unzip the included folder (e.g. D:\RT-ThreadStudioEnv\env-windows-v1.3.5)
    • Start env.exe
    • Click on the Hamburger Menu, Settings, Integration
    • Click on "Register" and "Save settings", close the settings menu and the envTool
    • More detailed info for the envTool can be found: https://github.com/RT-Thread/rt-thread/blob/master/documentation/env/env.md
  • RT-Thread Github Repo: https://github.com/RT-Thread/rt-thread
    • Clone the repo ( git clone https://github.com/RT-Thread/rt-thread.git ) to your system, (e.g. to D:\RT-ThreadGithub)
  • First project
    • Start RT-Thread Studio
    • File -> Import -> RT-Thread Bsp Project into Workspace
    • Bsp Location (within the Github Repo): D:\RT-ThreadGithub\rt-thread\bsp\imxrt\imxrt1060-nxp-evk
    • Project Name: What you want, I choose blinky
    • Chip Name: MIMXRT1060
    • Debugger: DAP-LINK
    • click finish
    • This will lead to an error, pointing you to the workspace folder ( e.g. mine is D:\RT-ThreadStudio\workspace.metadata ) where a .log file resides. Open it up and scroll to the end. At this time in the development, there seems to be an error with the initial compilation shown with this error: "!MESSAGE D:\RT-ThreadGithub\rt-thread\bsp\imxrt\imxrt1060-nxp-evk>scons --dist-ide --project-path=D:\RT-ThreadStudio\workspace/blinky --project-name=blinky"``. To fix this issue, navigate with the windows explorer to the folder ``D:\RT-ThreadGithub\rt-thread\bsp\imxrt\imxrt1060-nxp-evk``. Within the folder, hold shift and right-click and choose ``ConEmu Here`` - the envTools will open up. Just copy and paste the complete scons command (``scons --dist-ide --project-path=D:\RT-ThreadStudio\workspace/blinky --project-name=blinky) into the envTools window and press enter. It should compile now.
    user@system D:\RT-ThreadGithub\rt-thread\bsp\imxrt\imxrt1060-nxp-evk
    > scons --dist-ide --project-path=D:\RT-ThreadStudio\workspace/blinky --project-name=blinky
    scons: Reading SConscript files ...
    Newlib version: 4.1.0
    make distribution....
    => imxrt1060-nxp-evk
    => start dist handle
    => copy imxrt bsp library
    => copy bsp drivers
    => copy bsp peripherals
    => components
    => include
    => libcpu
    => src
    => tools
    Update configuration files...
    suggest to use command scons --dist [--target=xxx] [--project-name=xxx] [--project-path=xxx]
    dist project successfully!
After this step, click finish in the still open import menu in RT-Thread Studio again, it should work now and generate the new project.
  • Navigate within Project Explorer through the Projectname to applications\ and open the main.c file.
  • Replace

    /* defined the LED pin: GPIO1_IO9 */
    #define LED0_PIN               GET_PIN(1, 9)
    
    int main(void)
    {
    #ifndef PHY_USING_KSZ8081
        /* set LED0 pin mode to output */
        rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
    
        while (1)
        {
            rt_pin_write(LED0_PIN, PIN_HIGH);
            rt_thread_mdelay(500);
            rt_pin_write(LED0_PIN, PIN_LOW);
            rt_thread_mdelay(500);
        }
    #endif
    }

    with

    /* defined the LED pin: GPIO1_IO8 */
    #define LED0_PIN               GET_PIN(1, 8)
    
    int main(void)
    {
    #ifndef PHY_USING_KSZ8081
        /* set LED0 pin mode to output */
        rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
    
        while (1)
        {
            rt_pin_write(LED0_PIN, PIN_HIGH);
            rt_thread_mdelay(500);
            rt_pin_write(LED0_PIN, PIN_LOW);
            rt_thread_mdelay(500);
        }
    #endif
    }

    and save the file. The User LED on the EVKB board is not on IO PIN 9, but 8. Also, this pin is shared with the ethernet controller - so if we enable this later, the User LED will not work anymore.

  • Click on the hammer icon ("Build 'Debug'") and it should compile the new software.
  • Click on the downward green arrow ("Flash Download") to download the program to the hardware board. The User LED should now be flashing.
  • There can be multiple issues at Download:
    • A window with "J-Link Emulator selection" pops up and asks for connection methods. This error means that RT-Thread Studio tries to program via Segger Link, which is the incorrect flash tool for the EVKB. If this comes up, please click no on the J-Link screen. Then check on the little black arrow attached to the Flash Download icon, that "DAP-LINK" is checked. Afterwards try Downloading again.
    • "pyocd.core.exceptions.TargetSupportError: Target type 'mimxrt1060' not recognized." If this error arises it can mean two things:
      • You did not enter the Chip Name correctly. Please check that the error is really mimxrt1060 - and no spelling issues are there. If there are, go to the Cogwheel Icon ("Debug configuration"), Debugger tab and correct the Chip Name within the Device name area. Click Ok to save and try again.
      • Scroll up through the error list and you might see the path of the pyocd software, e.g. RealThread\PyOCD\0.1.3 - this would mean you're running the default PyOCD 0.1.3 - which has some errors that will mean you cannot download to flash. Directly next to the "Flash Download" icon is the "SDK Manager", open it up and scroll down to the "Debugger_Support_Packages", "PyOCD". Choose the latest version (e.g. 0.2.0) and click on "Install packages". You can then select the old version(s) you have installed and click on "Delete packages". Afterwards close the SDK Manager. This should fix the issue.
  • If there are no issues with the Download, can you also "Open a Terminal" (computer screen icon close to "Flash Download"). And start with the correct settings (e.g. 115200 BAUD and the correct Serial port, should be chosen automatically if you already flashed a program before). You should see the RT msh console running on your EVKB and be able to send a "help" to get an overview over the device

    
    \ | /
    - RT -     Thread Operating System
    / | \     5.0.1 build May 28 2023 14:25:59
    2006 - 2022 Copyright by RT-Thread team
    msh >help
    RT-Thread shell commands:
    clear            - clear the terminal screen
    version          - show RT-Thread version information
    list             - list objects
    help             - RT-Thread shell help.
    ps               - List threads in the system.
    free             - Show the memory usage in the system.
    pin              - pin [option]
    reboot           - reset system
    
    msh >
  • To get a little bit further into a project, I replaced the main.c PIN definition and main() with following code

    // wrong definition, GPIO1_IO9 is ethernet leds on EVKB
    // #define LED0_PIN               GET_PIN(1, 9)
    
    // D8 (GPIO01-08) is the user led
    #define LED0_PIN               GET_PIN(1, 8)
    // SW5 (GPIO5-00) is the user button
    #define SW5_PIN                GET_PIN(5, 0)
    
    int main(void)
    {
    #ifndef PHY_USING_KSZ8081
        // set LED0 pin mode to output
        rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
        // set SW5 pin mode to pullup
        rt_pin_mode(SW5_PIN, PIN_MODE_INPUT_PULLUP);
    
        while (1)
        {
            if (!rt_pin_read(SW5_PIN)) {
                rt_pin_write(LED0_PIN, PIN_HIGH);
            } else {
                rt_pin_write(LED0_PIN, PIN_LOW);
            }
            /*
            rt_pin_write(LED0_PIN, PIN_HIGH);
            rt_thread_mdelay(500);
            rt_pin_write(LED0_PIN, PIN_LOW);
            rt_thread_mdelay(500);
            */
        }
    #endif
    }

    This will couple the LED to the status of the user switch (SW5): If its pressed, the LED will turn on, if its not pressed, the LED will stay off. Just save, re-compile and re-download.

Bugs

As seen there are some bugs already found:

  • RT-Thread Studio error upon trying to import a project
  • RT-Thread Studio failing to choose the correct debugger even though it was selected on creation/import of the project
  • pyocd error upon download of firmware to the NXP MCU due to old pyocd version shipped with RT-Thread Studio
  • Integration of the menuconfig tooling as "RT-Thread Settings" within RT-Thread Studio - but it just does not have any effect on the project

I hope that these issues get solved soon - but with the infos above you should be able to get started. I will see you in the next post - probably going through the project I made :).

balenaOS on the Advantech AIR-020X

Last week I posted the review of the Advantech AIR-020X with which I used to create the labSentinel 2 system.

I remarked that the hardware was great, however the software support and update capability of the system was severly lagging behind for an "industrial floor, always on" type of machine. Luckily, thats exactly what balena has been created for.

Even better, their environment already support Nvidia Jetson devices - also Nvidia Jetson Xavier NX modules. With the AIR-020X being a really nice carrierboard (and housing) for this module, I went to work.

Spoiler Alert - it works!

Installing balenaOS on the AIR-020X

1.) I setup an Ubuntu 20.04 LTS machine, installed npm and setup jetson-flash

2.) I went to https://www.balena.io/os and downloaded the latest NVIDIA JETSON XAVIER NX DEVKIT EMMC image (2.107.10) in the development version.

3.) Unzip the file after setting up jetson-flash and getting your AIR-020X into recovery mode. This means opening the bottom of the case by unscrewing the 4 philipps head screws, connecting the Micro USB port of the AIR-020X with your Ubuntu host computer, applying power to the AIR-020X, but do not yet press the power switch.

4.) There is foil/recovery switch next to the Micro USB connector and LAN port. You need to press and hold this switch and at the same time press the power on button of the unit for about 4 seconds.

5.) On Ubuntu, run lsusb | grep Nvidia - this should return a similar line to this

Bus 003 Device 005: ID 0955:7023 NVIDIA Corp. APX

Import is the ending "APX", which means it is in recovery mode.

6.) Now you can start the flash process

user@balenaTest:~/jetson-flash$ ./bin/cmd.js -f ./jetson-xavier-nx-devkit-emmc-2.107.10-v14.4.4.img -m jetson-xavier-nx-devkit-emmc

The .img value points to the unzipped image file, the -m tells the jetson-flash tool that we are running a Xavier NX system and want to install balenaOS on the internal eMMC module.

7.) This will now start the process which will take some minutes and also ask you for your sudo password. At the end you should see something like this:

[ 255.8670 ] Flashing completed

[ 255.8670 ] Coldbooting the device
[ 255.8696 ] tegrarcm_v2 --ismb2
[ 255.9454 ]
[ 255.9502 ] tegradevflash_v2 --reboot coldboot
[ 255.9530 ] Bootloader version 01.00.0000
[ 255.9984 ]
*** The target t186ref has been flashed successfully. ***
Reset the board to boot from internal eMMC.

8.) As soon as you reboot the device, you will be greeted with the balenaOS logo and can use it as any other balenaOS device.

Adding the AIR-020X to a fleet

If you want to use it e.g. in a fleet, I would recommend creating a new one with the device type Nvidia Jetson Xavier. This is important to allow sample projects to correctly work, as its basically the same thing as the more specialized version "jetson-xavier-nx-devkit-emmc" - but most demo projects just implement the former one :).

To now join the installed device onto your new fleet, download and install balenaCLI - login to your balena Cloud account and do a balena scan using balenaCLI to find your AIR-020X on the network.

-
  host:          56e1ef3.local
  address:       192.168.178.112
  osVariant:     development
  dockerInfo:
    Containers:        1
    ContainersRunning: 1
    ContainersPaused:  0
    ContainersStopped: 0
    Images:            1
    Driver:            overlay2
    SystemTime:        2023-03-10T14:05:52.568438957Z
    KernelVersion:     4.9.253-l4t-r32.7
    OperatingSystem:   balenaOS 2.107.10
    Architecture:      aarch64
  dockerVersion:
    Version:    20.10.17
    ApiVersion: 1.41

After that, you can easily join this device with

.\balena join 192.168.178.112
? Select fleet <yourFleetNameToSelect>
? Check for updates every X minutes 10
[Success] Device successfully joined balena-cloud.com!

... and voila, its online!

What does work?

Testing GPIO pins with a multimeter

The AIR-020X has a lot of custom GPIO chips, 2x RS485/RS232 interface, 1x CANbus interface, a second network interface and even a NVMe. Luckily, everything just works out of the box.

- HDMI works
- USB works
- onboard network card (dmesg + dhcp test, gets ip / works)
[   29.231807] eqos 2490000.ether_qos eth0: Link is Up - 1Gbps/Full - flow control rx/tx

- 2nd network card (dmesg + dhcp test, get ip / works)
[  104.307175] igb 0004:05:00.0 enP4p5s0: igb: enP4p5s0 NIC Link is Up 1000 Mbps Full Duplex, Flow 
- NVMe is recognized (lsblk)
nvme0n1      259:0    0 119.2G  0 disk
|-nvme0n1p1  259:1    0    96G  0 part
|-nvme0n1p2  259:2    0    64M  0 part
|-nvme0n1p3  259:3    0    64M  0 part
|-nvme0n1p4  259:4    0   448K  0 part
|-nvme0n1p5  259:5    0   448K  0 part
|-nvme0n1p6  259:6    0    63M  0 part
|-nvme0n1p7  259:7    0   512K  0 part
|-nvme0n1p8  259:8    0   256K  0 part
|-nvme0n1p9  259:9    0   256K  0 part
|-nvme0n1p10 259:10   0   300M  0 part
`-nvme0n1p11 259:11   0  22.8G  0 part
- can bus interface is auto loaded on boot (see ifconfig -a)
can0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          NOARP  MTU:16  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:10
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
          Interrupt:63
- gpio/dio, works, but bit3 does sadly not work
( more info: http://ess-wiki.advantech.com.tw/view/File:AIR-020-nVidia_GPIO.docx )
Pin Number AIR-020X AIR-020T AIR-020N
GPIO bit1 	393 	269 		38
GPIO bit2 	421 	425 		149
GPIO bit3 	265 	411 		65
GPIO bit4 	424 	264 		168
GPIO bit5 	418 	476 		202
GPIO bit6 	436 	396 		246
GPIO bit7 	417 	337 		169
GPIO bit8 	268 	338 		194

# set bit 1 as GPIO pin
echo 393 > /sys/class/gpio/export
# get value 0=low, 1=high
cat /sys/class/gpio/gpio393/value
# set direction out or in
echo out > /sys/class/gpio/gpio393/direction
# get direction
cat /sys/class/gpio/gpio393/direction
out
# set value on out pin
echo 1 > /sys/class/gpio/gpio393/value

test:
# 265, bit3 did not work on export
echo 393 > /sys/class/gpio/export
echo 421 > /sys/class/gpio/export
echo 265 > /sys/class/gpio/export
echo 424 > /sys/class/gpio/export
echo 418 > /sys/class/gpio/export
echo 436 > /sys/class/gpio/export
echo 417 > /sys/class/gpio/export
echo 268 > /sys/class/gpio/export

echo out > /sys/class/gpio/gpio393/direction
echo out > /sys/class/gpio/gpio421/direction
echo out > /sys/class/gpio/gpio265/direction
echo out > /sys/class/gpio/gpio424/direction
echo out > /sys/class/gpio/gpio418/direction
echo out > /sys/class/gpio/gpio436/direction
echo out > /sys/class/gpio/gpio417/direction
echo out > /sys/class/gpio/gpio268/direction

echo 1 > /sys/class/gpio/gpio393/value
echo 1 > /sys/class/gpio/gpio421/value
echo 1 > /sys/class/gpio/gpio265/value
echo 1 > /sys/class/gpio/gpio424/value
echo 1 > /sys/class/gpio/gpio418/value
echo 1 > /sys/class/gpio/gpio436/value
echo 1 > /sys/class/gpio/gpio417/value
echo 1 > /sys/class/gpio/gpio268/value
- com ports, running as RS-232 or RS-485 (not tested, but recognized)
( more info: http://ess-wiki.advantech.com.tw/view/AIR-020-RS-485 )
root@56e8bf3:/# ls /dev/ | grep ttyTH
ttyTHS0 <- COM1
ttyTHS1 <- COM2
ttyTHS4

More info to the hardware can be found in the Advantech Wiki.

GPU Demos

Last but not least I want to point you towards the nice balena Jetson tutorial which can be found here.

It will help you getting started with Jetson samples that are hosted here.

In the end I was able to also get CUDA acceleration to work and see this smoke demo:

Nothing like some GPU accelerated smoke

With that I am closing this post. It was surprisingly easy to get this device to work - the only thing left would be to get it to boot and to work from its internal NVMe storage, but other than that its a nice tool for working with GPU workloads like Edge Impulse.

Advantech AIR-020X Review

Normally, I am not getting review units. This is due to the fact that I am only hosting this small weblog, along some conference talks - and most companies would probably be better off to send their units along someone with a reach of Linus Tech Tips, or similar.

On the other hand - when I get the possibility to do a review, it can be a bit worrisome for the companies as well, as I am a very honest person. I have been working in tech for some time now and had the honor to build stuff which went to space - and came back to tell the tale. I know what I want in a unit - and what could be a problem.

With this out of the way, I was one lucky winner of the Advantech Edge AI Challenge 2022 and got an AIR-020X-S9A1 unit at no charge to be able to realize my labSentinel 2 project. By doing this project I learned a bit about the box and thought it would not be a bad idea to share my ideas with the readers of my blog - and also Advantech, so that they can improve upon their product. This review is not paid for, reflects my own thoughts and I got the mentioned unit for my project - the review was not a part of that deal. With that out of the way, lets get started.

The hardware

The AIR-020X comes very well packaged - having its own foam jacket which will save it from all but the most horrible abuse from postal services. Not that it would matter: The roughly 14 cm x 12 cm x 4,5 cm compact unit weighs in at nearly 850 gr and is built sturdy and robust - like a tank:

The most obvious part of the unit is its heatsink, which it does put to good use - but more on that topic later. Along with the computer itself comes a chinese printed starting guide and a short USB A to Micro B cable, which will be needed to factory reset and reflash the unit.

All in all, the AIR-020X is an impressive unit, including an Nvidia Jetson Xavier NX module with 8 GB RAM, 16 GB onboard eMMC, 128 GB M.2 Flash, 2x RS232/422/485, 1x CANbus, 1xDIO ("GPIO"), 2x 1 Gbit ethernet, 1x Fullsize mPCIe with nano SIM holder, 1x 4k HDMI Output, 2x USB 3.0 Type A, 1x USB Type C. The unit is powered by a 12-24 V DC power supply, which is an optional accessory.

Being an industrial unit, it uses an industrial type connector for power, which is an HT5.08 2 pole type:

As this connector is also not part of the base package and the USB C connector does not accept power delivery (and neither works in Display Port Mode) - it becomes a bit harder to power up the unit after receiving it. Finding a usable power supply within the sizable voltage range of 12 - 24 V (e.g. from an old Laptop) is fairly easy, but without the connector - it becomes a dead end until the next delivery is there. It would be useful to at least include one connector with the base unit. The usb cable is a nice addition, but could be left out (even though its very high quality) - along with the chinese manual. This could be replaced with a small card with direct links to the english and chinese PDF versions of the manual.

Opening up the unit reveals the internals - but not without a fight:

The used screws are perfectly fixed to the structure by using blue loctite - a touch I cannot recommend enough for the vibration resistance of the overall unit - but the screws themselves are made from extremely soft metal, so that - using the correct screwdriver - I stripped nearly all screws and had really issues removing all of them. Somehow this problem seems to exist for all the external black screws, the internal silver ones were of a lot higher quality. In my case I fixed the issue by replacing the screws with new ones and never had an issue anymore with them.

The internal structure is very well laid out, raising the M.2 drive onto a pole to keep it a bit further from the heat source / Xavier NX module which is just sitting on the other side of the PCB and directly sandwiches with the big heatsink.

Very welcome are also the addition of the two Raspberry Pi Style Camera connectors, although they are a bit hidden by the serial console cables. I understand that the unit should be as closed as possible for the use in factories, but I would have loved to see two small slits (possibly even with some IP/EMC gaskets to allow for protective shielding of those entry points) so that cameras on the outside of the case can be easily attached.

The mPCIe slot gives the system an additional expansion slot for e.G. UMTS or LoRaWAN modules and also the internal CR2032 cell for the RTC is a small but valuable detail.

The AIR-020X has some mounting points available on both system sides for additional wall mounting rails. Looking at the mounting points and the obvious use of the AIR-020 series in lab and factory settings, the inclusion of a DIN rail mount as available accessory could prove very useful to directly mount this small computer into an electrical cabinet.

The software

Booting up the system greats one with a very familiar picture: Ubuntu 18.04 is running on the machine in form of a tailored version of Nvidia Jetpack. This version by Advantech is only using the eMMC of the Xavier NX module to start the bootloader, but the actual data is kept on the M.2. This is a great idea for the longevity of the eMMC on the (currently hard to find) Xavier NX module - but comes with the drawback of additional needed customization other than "only" the PCB, included hardware, drivers and other changes made by Advantech in comparision to an Nvidia Developerboard for the same module.

This is a problem I also learned the hard way: I realized that the board was delivered with L4T 32.5.2 - not the current 32.7.x (JetPack 4.6.1) - so I updated this by hand. Just to have the board bootloop. This was the moment I took a closer look to the online presence of Advantech and the manual - just to learn that the recovery process was neither described, nor was the download of the image available. I got the needed recovery file as well as the documentation (which also included vital information on how to use the DIO (GPIO), RS422 and CANbus interface) and as able to restore the board to working order. Obviously there were multiple problems with this: First, the online available manual should contain all needed information regardings settings, ports, recovery, etc - secondly, the current (and maybe even last) images also need to be available online on their website - with checksums to be able to deploy these images safetly.

I also voiced my concerns regarding the high impact security issues / CVEs found in 32.5.2 - which would make the use of AIR-020 series an absolute liability in a production environment. I am glad to report that Advantech reacted to these concerns with providing a beta version of a new JetPack 4.6.1 Image. A short time afterwards, Advantech did add some information to their wiki:

On the download page you can find the AIR020A2AIM20UIV00004 entry for the Jetson NX JetPack 4.6.1 from 2022-07-20. This links to a Dropbox folder containing a the latest image (AIR020A2AIM20UIV00004_194.tar.gz / 2022-09-16).

With this latest image I was able to upgrade the AIR-020X to JetPack 4.6.1 and even do and apt upgrade to upgrade to L4T 32.7.2, at the time the latest L4T. However, this did not go as planed: After doing the upgrade and rebooting the device, it got caught in a bootloop. This bootloop kept on repeating for about 10 minutes until the device mysteriously started then working and came back on without issues. Obviously this would not be a graceful upgrade and did instill some concerns why this was a reproducible issue.

I am glad to report that Advantech has provided the latest image - which will eliminate several security issues. However, the changes needed in the manual as well as the provision of the recovery images (now via Dropbox?) and the secure provision of security updates to the unit remain. Maybe Advantech would think about starting to use balena.io to handle these issues?

Verdict

The Advantech AIR-020X is an extremely capable unit in a small form factor, sturdy built and highly reliable. Even with the latest JetPack 4.6.1 and abuse of the formerly not available 20 Watt mode I could not get this unit to heat up too much in my testing with labSentinel 2. There is still enough headroom available to use it in any kind of environment, which makes it a perfect choice for labs and factories - if Advantech can tackle the presented issues. Especially the ones regarding timely and secure availability of security patches and software updates. This also means availability of these images, fast adaption after release of official Nvidia updates and all needed documentation in one manual for public download. With these exceptions and some small kinks, Advantech is so close to building the perfect unit for their envisioned use case. I really hope they can close that last (security/software/manual) gap to an otherwise nearly perfect hardware - and with that create an recommendable product.

Edit: balenaOS

I got balenaOS working on the device - see here.

USB C power for the Nvidia Jetson Nano 4 GB dev board

The best way to power a Jetson Nano 4 GB dev board is by using a center positive, 5 V and at least 4 A barrel connector type power adapter. However, these are often bulky and not the best travel companion - while USB C power bricks are becoming more common and the relevant USB C sockets are getting build into nearly every device (maybe yours too, Apple?).

So I set out to build a USB C power adapter for the Jetson board.

By using an inexpensive USB C "trigger" combined with two 5V@3A step-down converters this did actually work.

The trick is setting the USB C trigger to request 20 V and using the 5 V converters in parallel to step-down the 20 V to 5 V - and then feeding the resulting voltage again in parallel to the barrel plug, like so.

For the curios among you now asking why I did not just set the trigger to 5 V and used it all alone: I tried this first, but it did not work. It was not able to provide enough current to support the operation of the Jetson at "MAXN mode" - it was constantly coming up with Overcurrent protection messages if pushed too hard.

I am happy with the result and shortend the wires after testing, putting everything into a neat small form factor.

With this change I can finally replace my old Jetson Nano power supply with something smaller than this chunky unit which I was gifted back in the day by the awesome Morlac :).

An active GNSS antenna for the CAM-M8Q breakout

I have been using multiple CAM-M8Q breakouts by Watterott and really am loving these units. They are small, reasonable priced and have the advantage of an integrated chip antenna. However, this also their small shortcoming: While the antenna is good enough for most outdoor jobs, you can run into sensitivity issues when deploying it indoors - if not setup next to a window. Luckily, the module has two additional u.fl connectors for RFin and RFout, meaning you can use an external antenna.

To accomplish this, you just need to remove the resistor R3 to position R1 - as outlined by the schematics:

Overview over the CAM-M8Q, copyright by Watterott

With this, you could attach an passive antenna, but an active one will not work, as no power is supplied from the module. But you can add this power insert with an inductor and an capacitor.

I did this with some SMD components, but did not add the insert "behind" the u.fl connector, but between both jumper points R1 should be using. So I can make this a part of the module.

This worked perfectly and the reception is great

As an antenna I am using the Navilock NL-202AA - I have not received any Galileo signal (even though it should be possible), but other than that I am very happy with the solution.

Thanks again to Mr. Watterott to pointing me to this StackExchange post which contained the solution for the power insert.

WD My Cloud Mirror Gen2 with Debian 11 and Linux Kernel 5.15 LTS

Intro

Since 2017 I have been using an Western Digital My Cloud Mirror Gen 2 which I bought at Amazons Black Friday (or similar) - because the included 2x 8 TB WD Red were even cheaper with the NAS than standalone. Using the NAS had been quite ok, especially the included Docker Engine and Plex Support were a nice to have, the included Backdoor in older Versions - not so much. Recently WD had their new "My Cloud OS 5" replace the old My Cloud OS 3 - and made things worse for a lot of people. As I don't want any more surprises - and more control over my hardware - I decided to finally go down the road and get Debian 11 with an LTS (5.15) Kernel running on the hardware. This is how it went.

Warning

Warning, these are just my notes on how to convert a My Cloud OS 3 / My Cloud Mirror Gen 2 device to a "real" Debian system. You will need to take your device fully apart, solder wires and lose the warranty. Additionally you will lose all your data and even brick the hardware if something goes wrong, I am taking in no way responsibility, neither can I give support. You're on your own now.

Step 0: Get Serial Console Access

Without a serial console, you will not be able to do anything here. You will need to completely disassembly the NAS and will lose all warranty. The plain motherboard will look like this. On the most right side you will see the pins for the UART interface you will need to solder to.

When you're done with that, connect your 3v3 TTL UART USB device like this:

... and connect to it via 115200 BAUD with Putty, TeraTerm Pro or any other software (Do not connect the 3v3 pin :)). It would be wise starting without the hard drives installed.

Step 1: Flashing U-Boot

The current U-Boot on the NAS is flawed, you need to replace it. I will be CyberPK here which did an awesome job explaining everything:

We have to prepare an usb drive formatted in Fat32, and extract the uboot at link into it and connect to usb port#2.

Connect the device to the serial adapter, poweron the device and start pressing '1' (one) during the boot until you can see the 'Marvell>>' Command Prompt
press ctrl+c
then

We will start here to change stuff and break stuff. But if I could give you one tip before you start: Please execute printenv once. Copy and paste all env variables and everything Uboots spits out. It could save your hardware one day. Thanks, Nico out!

usb start
bubt u-boot-a38x-GrandTeton_2014T3_PQ-nand.bin nand usb
reset

This will reboot the device. Access again the Command prompt and add the following envs, a modified version of the ones provided by bodhi at this post:

setenv set_bootargs_stock 'setenv bootargs root=/dev/ram console=ttyS0,115200'

setenv bootcmd_stock 'echo Booting from stock ... ; run set_bootargs_stock; printenv bootargs; nand read.e 0xa00000 0x500000 0x500000;nand read.e 0xf00000 0xa00000 0x500000;bootm 0xa00000 0xf00000'

setenv bootdev 'usb'

setenv device '0:1'

setenv load_image_addr '0x02000020'

setenv load_initrd_addr '0x2900000'

setenv load_image 'echo loading Image ...; fatload $bootdev $device $load_image_addr /boot/uImage'

setenv load_initrd 'echo loading uInitrd ...; fatload $bootdev $device $load_initrd_addr /boot/uInitrd'

setenv usb_set_bootargs 'setenv bootargs "console=ttyS0,115200 root=LABEL=rootfs rootdelay=10 $mtdparts earlyprintk=serial init=/bin/systemd"'

setenv bootcmd_usb 'echo Booting from USB ...; usb start; run usb_set_bootargs; if run load_image; then if run load_initrd; then bootm $load_image_addr $load_initrd_addr; else bootm $load_image_addr; fi; fi; usb stop'

setenv bootcmd 'setenv fdt_skip_update yes; setenv usbActive 0; run bootcmd_usb; setenv usbActive 1; run bootcmd_usb; setenv fdt_skip_update no; run bootcmd_stock; reset'

saveenv

reset

(This code was also modified by me to use the fatload instead of the ext2load)

With this, our NAS is ready.

Step 2: Build a kernel and rootfs

  • On your current linux machine, get yourself a copy / git clone of Heisaths wdmc2-kernel Repo
  • Get all dependencies installed according to this repo, I installed it on a Debian 11 machine
  • Replace the file content of wdmc2-kernel/dts/armada-375-wdmc-gen2.dts with the content of the real and improved dts for the WDMCMG2 (original from this link, copy available here) - but keep the file name still armada-375-wdmc-gen2.dts
  • Replace the file content of wdmc2-kernel/config/linux-5.15.y.config with the file from here (please know this config ain't perfect, but it will get you running. You can always file a PR and help me out ;))
  • Start the build process in wdmc2-kernel with ./build.sh
  • Mark: Linux Kernel, Clean Kernel sources, Debian Rootfs, Enable ZRAM on rootfs
  • Kernel -> Kernel 5.15 LTS
  • Build initramfs -> Yes
  • Debian -> Bullseye
  • Fstab -> usb
  • Rootpw -> whateverYouWant
  • Hostname -> whateverYouWant
  • Locales -> no changes, accept (or whatever you want)
  • Default locale for system -> en_US.UTF-8 (or whatever you want)
  • Tzdata -> Your region
  • Now your kernel and rootfs will be build

While this is on-going, get yourself a nice USB 2.0 or USB 3.0 stick prepared with

  • partition table: msdos
  • 1st partition: 192 MB, FAT32, label set to boot, boot flag enabled
  • 2nd partition: rest, ext4, label set to rootfs

When the kernel is done compiling and your usb stick is done, copy all the files (sda is the name of my usb stick

  • mkdir /mnt/boot /mnt/root
  • mount /dev/sda1 /mnt/boot
  • mount /dev/sda2 /mnt/root
  • mkdir /mnt/boot/boot
  • cp wdmc2-kernel/output/boot/uImage-5.15.* /mnt/boot/boot/uImage
  • cp wdmc2-kernel/output/boot/uRamdisk /mnt/boot/boot/uInitrd
  • tar -xvzf wdmc2-kernel/output/bullseye-rootfs.tar.gz --directory=/mnt/root/
  • rm -rf /mnt/root/etc/fstab
  • cp /mnt/root/etc/fstab.usb /mnt/root/etc/fstab
    // within /mnt/root/etc/fstab:
    // change all /dev/sdb to /dev/sdc if all two drive slots on the NAS are used <- this!
    // change all /dev/sdb to /dev/sda if no drive slots on the NAS are used
  • umount /mnt/boot /mnt/root

Step 3: First boot and getting things running

Insert the USB stick into the 2 slot of the NAS. Leave the drives still out and boot it up for the first time, watch it via terminal. Login at the end with root and your chosen password.

If it boots, you can shut it down again with shutdown -P now, unplug power, insert the drives and reboot.

First thing after the first boot with drives, your own initramfs / Ramdisk from your current setup:

  • cd /root/
  • ./build_initramfs.sh
  • cp initramfs/uRamdisk /boot/boot/uInitrd

Second, install MDADM for the RAID:

  • apt update
  • apt install mdadm
  • mkdir /mnt/HD
  • edit your /etc/fstab and add a mount point for your md/raid. I used the old drives with my old data on it like this (depending on the fact as which mdX it launches...)
/dev/md0        /mnt/HD         ext4    defaults,noatime,nodiratime,commit=600,errors=remount-ro        0       1
 

A lot of good knowledge about Ramdrives can be found here.

I would advise to do steps: 1. Folder2RAM, 2. Kernel Options, 4. Logrotate - option 3 did not work out for me.

To get the drive to sleep at some point, we need to reconfigure MDADM

dpkg-reconfigure mdadm
// monthly check ok 
// daily degration check ok
// monitoring disable

... and get hdparm working

apt install hdparm hd-idle

# hdparm config
, add in /etc/hdparm.conf 

/dev/sda {
#        apm = 127
#        acoustic_management = 127
        spindown_time = 120
#       spindown_time = 4
        write_cache = on
}

/dev/sdb {
#        apm = 127
#        acoustic_management = 127
        spindown_time = 120
#       spindown_time = 4
        write_cache = on
}

# Spindown Time means: 120 * 5 sec = 600 sec / 60 sec = 10 min
# apply it after saving the file with:
/usr/lib/pm-utils/power.d/95hdparm-apm resume

We can check the status of the drives with smartctl

smartctl -i -n standby /dev/sda
smartctl -i -n standby /dev/sdb

To get fan control working

apt install wget
wget -O mcm-fancontrol-master.tar.gz https://github.com/nmaas87/mcm-fancontrol/archive/refs/heads/master.tar.gz
tar -xvzf mcm-fancontrol-master.tar.gz
cd mcm-fancontrol-master/
cp fan-daemon.py /usr/sbin/
chmod +x /usr/sbin/fan-daemon.py
cp fan-daemon.service /etc/systemd/system/
systemctl enable fan-daemon
systemctl start fan-daemon

(You can change low temp and high temp in the /usr/sbin/fan-daemon.py to get the Fan to kick in later and also set DEBUG = True if you want to see some details in the systemctl status fan-daemon)

MDT Utils can be useful, just mentioning it here

apt install -y mtd-utils
cat /proc/cmdline
cat /proc/mtd

Samba ...

apt install samba --no-install-recommends
# change /etc/samba/smb.conf to your liking and setup your SMB

Plex ...

# Plex 
apt update
apt install apt-transport-https ca-certificates curl gnupg2
curl https://downloads.plex.tv/plex-keys/PlexSign.key | apt-key add -
echo deb https://downloads.plex.tv/repo/deb public main | tee /etc/apt/sources.list.d/plexmediaserver.list
apt update
apt install plexmediaserver
systemctl status plexmediaserver

Well, that's it.

Thanks a lot to all awesome contributors in the net:

Companion repo with files: https://github.com/nmaas87/WDMCMG2

[Gigabyte] BIOS Upgrade on old Gigabyte Motherboards

There are several old Gigabyte Motherboards like the GA-MA Series which uses their BIOS Included Q-Flash Utility for upgrading. This tool tries to access an attached USB Device in a file browser way to give you the choice on which file to flash. Most of the time, you won't be able to access your drive, as it will only be shown as "Floppy B". In truth, this means your USB drive is formated the wrong way: You should have only one partition on that stick, with size LESS than 128 MB and FAT as file system. Yeah, I figured that out the hard way ^^'. It will then be shown as "HDD 0-0" in Q-Flash and will provide your files for easy upgrading 🙂