{
  perSystem = { libMy, pkgs, ... }:
  let
    genImgsPy = pkgs.python3.withPackages (ps: with ps; [ pillow ]);
    genImgs = pkgs.writeScriptBin "gen-imgs" ''
      #!${genImgsPy}/bin/python
      import itertools
      import os

      from PIL import Image

      imgs = ['bg.png']
      for fname in imgs:
        with Image.open(os.path.join('assets/', fname)) as img:
          w = img.width
          h = img.height

          data = [(r >> 3) << 11 | (g >> 2) << 5 | (b >> 3) for r, g, b in img.getdata()]

        basename = os.path.splitext(fname)[0]
        with open(os.path.join('main/img/', basename + '.c'), 'w') as f:
          f.write(
            '#include <inttypes.h>\n\n'
            '#include "lvgl.h"\n\n'
            'static const uint16_t _data[] = {\n')

          for group in itertools.batched(data, 20):
            f.write('  ' + ' '.join(map(lambda i: f'{i:#x},', group)) + '\n')

          f.write('};\n\n')

          f.write(
            f'const lv_image_dsc_t ui_img_{basename} = {{\n'
             '  .header.magic = LV_IMAGE_HEADER_MAGIC,\n'
             '  .header.cf = LV_COLOR_FORMAT_RGB565,\n'
            f'  .header.w = {w},\n'
            f'  .header.h = {h},\n'
             '  .data_size = sizeof(_data) * 2,\n'
             '  .data = (const uint8_t *)_data,\n'
             '};\n')
    '';
  in
  {
    devenv.shells.firmware = libMy.withRootdir {
      packages = with pkgs; [
        esp-idf-esp32s3
        picocom
        lv_font_conv
        genImgs
      ];

      env = {
        # PICO_SDK_PATH = "${pkgs.pico-sdk}/lib/pico-sdk";
      };

      scripts = {
        init.exec = ''
          idf.py set-target esp32s3
        '';
        gen-fonts.exec = ''
          for s in 120; do
            DEBUG='*' lv_font_conv --font assets/Tungsten-Bold.ttf --bpp 4 --size $s -r 0x20-0x7F --no-compress \
              --format lvgl --lv-include lvgl.h --lv-font-name lv_font_tungsten_"$s" -o main/font/tungsten_"$s".c
          done
        '';
      };
    };
  };
}