• Installing JLCPCB's JLCONE desktop app on an RPM system

    3 days ago 0 comments

    (In the above screenshot showing a successful conversion I won't show the Home page because I don't want to show my orders, but feel free to use my referral link above. 😉)

    Update 2026-08-24: JLCPCB has announced that their $2 for 5 signature offer and cheap shipping rates are now only available from the JLCONE desktop app, so it's timely that I found a way to install it on my RPM system. When I looked recently cheap shipping wasn't available on the web page any more.

    JLCPCB promises coupons and discounts if you submit orders via the desktop app instead of their webpage. When I went to look for the download, I saw that Windows, Mac (two flavours), and Linux are supported, but the Linux app is a DEB package. I have a RPM based system. I asked JLC if they were planning a RPM package, or perhaps a Flatpak or Appimage for wider coverage. They replied: We're thinking about it. In the meantime I decided to take things into my own hands.

    I turned to alien, a converter between Linux package formats which I have used in the past.

    I did:

    # alien -r -k -c jlcone-1.0.67.deb

    -r means to RPM, -k means keep version, -c means also convert {pre,post}{install,rm} shell scripts. You should first check that the scripts are safe by doing dpkg -e jlcone-1.0.67.deb

    After some churning, a jlcone-1.0.67-1.x86_64.rpm was generated. When you install this you have to accept that it has no signing key. Fortunately there were no additional dependencies for the package.

    Most files were installed in /opt/JLCONE. Here is the list of files:

    # rpm -ql jlcone
    /opt/JLCONE
    /opt/JLCONE/LICENSE.electron.txt
    /opt/JLCONE/LICENSES.chromium.html
    /opt/JLCONE/chrome-sandbox
    /opt/JLCONE/chrome_100_percent.pak
    /opt/JLCONE/chrome_200_percent.pak
    /opt/JLCONE/chrome_crashpad_handler
    /opt/JLCONE/icudtl.dat
    /opt/JLCONE/jlcone
    /opt/JLCONE/libEGL.so
    /opt/JLCONE/libGLESv2.so
    /opt/JLCONE/libffmpeg.so
    /opt/JLCONE/libvk_swiftshader.so
    /opt/JLCONE/libvulkan.so.1
    /opt/JLCONE/locales
    /opt/JLCONE/locales/af.pak
    /opt/JLCONE/locales/am.pak
    /opt/JLCONE/locales/ar.pak
    /opt/JLCONE/locales/bg.pak
    /opt/JLCONE/locales/bn.pak
    /opt/JLCONE/locales/ca.pak
    /opt/JLCONE/locales/cs.pak
    /opt/JLCONE/locales/da.pak
    /opt/JLCONE/locales/de.pak
    /opt/JLCONE/locales/el.pak
    /opt/JLCONE/locales/en-GB.pak
    /opt/JLCONE/locales/en-US.pak
    /opt/JLCONE/locales/es-419.pak
    /opt/JLCONE/locales/es.pak
    /opt/JLCONE/locales/et.pak
    /opt/JLCONE/locales/fa.pak
    /opt/JLCONE/locales/fi.pak
    /opt/JLCONE/locales/fil.pak
    /opt/JLCONE/locales/fr.pak
    /opt/JLCONE/locales/gu.pak
    /opt/JLCONE/locales/he.pak
    /opt/JLCONE/locales/hi.pak
    /opt/JLCONE/locales/hr.pak
    /opt/JLCONE/locales/hu.pak
    /opt/JLCONE/locales/id.pak
    /opt/JLCONE/locales/it.pak
    /opt/JLCONE/locales/ja.pak
    /opt/JLCONE/locales/kn.pak
    /opt/JLCONE/locales/ko.pak
    /opt/JLCONE/locales/lt.pak
    /opt/JLCONE/locales/lv.pak
    /opt/JLCONE/locales/ml.pak
    /opt/JLCONE/locales/mr.pak
    /opt/JLCONE/locales/ms.pak
    /opt/JLCONE/locales/nb.pak
    /opt/JLCONE/locales/nl.pak
    /opt/JLCONE/locales/pl.pak
    /opt/JLCONE/locales/pt-BR.pak
    /opt/JLCONE/locales/pt-PT.pak
    /opt/JLCONE/locales/ro.pak
    /opt/JLCONE/locales/ru.pak
    /opt/JLCONE/locales/sk.pak
    /opt/JLCONE/locales/sl.pak
    /opt/JLCONE/locales/sr.pak
    /opt/JLCONE/locales/sv.pak
    /opt/JLCONE/locales/sw.pak
    /opt/JLCONE/locales/ta.pak
    /opt/JLCONE/locales/te.pak
    /opt/JLCONE/locales/th.pak
    /opt/JLCONE/locales/tr.pak
    /opt/JLCONE/locales/uk.pak
    /opt/JLCONE/locales/ur.pak
    /opt/JLCONE/locales/vi.pak
    /opt/JLCONE/locales/zh-CN.pak
    /opt/JLCONE/locales/zh-TW.pak
    /opt/JLCONE/resources
    /opt/JLCONE/resources.pak
    /opt/JLCONE/resources/app-update.yml
    /opt/JLCONE/resources/app.asar
    /opt/JLCONE/resources/apparmor-profile
    /opt/JLCONE/resources/default_app.asar
    /opt/JLCONE/resources/package-type
    /opt/JLCONE/snapshot_blob.bin
    /opt/JLCONE/v8_context_snapshot.bin
    /opt/JLCONE/version
    /opt/JLCONE/vk_swiftshader_icd.json
    /usr/share/applications
    /usr/share/applications/jlcone.desktop
    /usr/share/doc/jlcone
    /usr/share/doc/jlcone/changelog.gz
    /usr/share/icons/hicolor
    /usr/share/icons/hicolor/256x256
    ...
    Read more »

  • Large round LED displays

    06/15/2026 at 01:44 0 comments

    Large displays are of interest to me so I was intrigued by a large display I recently saw in public. It was a round screen about a metre in diameter with horizontal strips of RGB LEDs. It was translucent because of gaps between the "lines". One advantage is that it would be less susceptible to wind as it lets air through. It also allows you to see what's behind it. I don't think it folds; it looks rigid. Here it's used to attract attention with a live video feed. A search readily finds manufacturers and prices are in the order of hundreds of $.

  • Replacing floating point by fixed point for the AHTxx sensors

    05/31/2026 at 08:35 0 comments

    The AHTxx series of sensors, e.g. AHT20, AHT30, measure temperature and humidity. The sensor returns 20-bit values for measurements. They are to be converted to °C and %RH by the formulae given above.

    All the libraries I've seen so far do the calculation in floating point. For a small MCU that doesn't have native floating point this pulls in extra library code. I wanted to see if this could be done with fixed point arithmetic.

    First the humidity calculation. If we load the raw value into a 32-bit variable, multiplying by 100 will not overflow as the max value is 2^20*100. So we do this:

    uint32_t l = raw * 100;

    Next instead of dividing by 2^20, we divide by 2^16. This can be done by shifting right 16 bits (effectively taking the MSW of the 32-bit value).

    uint16_t w = l >> 16;

    Now the integer part of the humidity % is in the top 12 bits and the sixteenths of % in the bottom 4 bits. We store these separately. For display we can convert the fractional part to a number between 0 and 10000 in steps of 625, and then use as many decimal digits of that as we want (the accuracy won't be as good as 1/16th %RH or °C anyway). Even a 16-entry lookup table will suffice.

    hum->frac = w & 0x0F;
    hum->whole = w >> 4;

    A similar calculation is applied to the temperature, except that we multiply by 200. For the temperature range of this sensor this also will not overflow 32 bits. After isolating the integral part we subtract 50. Here's a catch: If the result is negative, then we must add 1 to the integral part and subtract the fractional part from 1 (actually 0b10000 since it's in sixteenths) so that both parts are non-positive.

    This has been tested with an ESP8266 module (because of the 3.3V supply requirement) driven by an Arduino sketch.