The place where random ideas get written down and lost in time.

2024-12-29 - Kotlin Web

Category DEV

It's time to look again at Kotlin for web development. Last time I tried it, a few years ago, it was embryonic and really not suitable for usage. Since then things have changed.

The entry point is Kotlin Multi Platform. It has now a version of Jetpack Compose: https://www.jetbrains.com/lp/compose-multiplatform/

The goal is to evaluate that against my use of Dart / Flutter on Firebase GCP.

There's currently some uncertainty in the future of Flutter. Dart should survive a bit longer as it still seems to be used internally by core projects, however the same can't be said of Flutter according to the water tank hearsay.

Evaluation criteria:

  • How to build a Kotlin MP project.
  • What can Compose do for the web.
  • Overall app architecture.
  • Integration with Firebase authentication.
  • Integration with Firebase DB.
  • Ecosystem of 3rd party libraries.
  • Ease of maintenance over time.

These are all points where Flutter more or less excels. Dart seemed like a barrier at first, until I understood it as basically “Java/Kotlin meets Typescript”. After that, it just made sense and it's easy to work with and pick it up again when I only tweak a project once a year.

The “web UI” aspect of Flutter is familiar -- it's basically ReactJS, but easier to use, and better documented.

The Flutter doc is very strong, and the IJ plugin is a delight to use.

Click here to continue reading...


2024-11-11 - ESP32 with Arduino-esp32, IDF 5.1.x, and C++20

Category DEV

Time to try again if this combo finally works.

My main motivation is to use C++20 Modules.

(TL;DR Summary: it works… and yet it doesn’t. C++ Modules require a very specific compilation ordering due to cross module dependencies, and the Arduino CLI simply cannot do that. However with ESP-IDF and CMake, it should be fine.)


2024-11-09 - Rig4j: Addressing Pagination

Category DEV

There are 2 issues, which keep haunting me:

  • The “N+1” problem with the first page repeating some but not all of the index.
  • Using the “next page” is “page N” and “next page” after that is “N-1” till page 1.

I have reverse ordering. The design is that each blog page has a stable URL with N articles each, so let’s say 10: page 1 is 1..10, page 2 is 11..20, etc. up to page N that “fills” up to N articles so that the last page always has an overlap with the index.

It’s telling that even I get surprised when I click on the “Next Page” link on the index and end up on “Page 6” rather than “Page 1”.

Issue 1: Previous / Next Links

The other issue is the navigation links.

I went from this, which was problematic:
        ⇐ Previous Posts                Next Posts ⇒
to this, which IMHO has other issues:
        ⇐ Previous Page                Next Page ⇒

The problem with “previous/next post” is that it’s ambiguous: “previous” is implied to mean “page N-1” or “older in time”, and “next” would mean “page N+1” or “newer in time”. But because the pages are in reverse chronological order, the “next” link actually points to an older-in-time content and page N-1. That’s the reverse of what one would expect.

I think we can solve that conundrum by renaming the links to be more technically correct:
        ⇐ Newer Page                Older Page ⇒
        ⇐ Newer Post                        Older Post ⇒

That’s a small trivial change that I think that would be less ambiguous:

  • Newer / Older only imply time-based ordering, not index-based ordering.
  • We’re not implied what is a “previous” or a “next page” anymore.
  • Navigation is “[page N+1] << Newer || current is page N || Older >> [page N-1]”.

⇒ Done. Implemented. Seems very nice.

Also added an “{{If.IsIndex}}” command in the template so that we can have “Newer Post” in the single blog full page vs “Newer Posts” in the index blog pages. This required adding a crude support for nested {{If}}...{{Endif}}.

Click here to continue reading...


2024-10-06 - PiFace on Raspberry Pi 4 with Linux Kernel 6.x

Category DEV

Pi5 uses the latest version of the “Raspberry Pi OS”, which is currently Deban bookworm with a Linux kernel 6.6.51. That breaks some stuff expected by the Piface “pifacedigitalio” library.

The first (unexpected) change is that we can’t use PIP to install packages in the system-wide Python provided by the Debian install. Instead a virtual env is enforced to ensure such packages do not break the entire system.

There is apparently a flag “pip --break-system-packages” that works around that check, and interestingly that option is not in the man page.

For a quick test, there’s some value in using a virtual env folder that I’m going to trash later.

The next realization is that none of the stuff works anymore:

Error “No such file or directory: '/sys/class/gpio/gpio25/value”

On an RPi 1, that means the user is not part of the group “gpio”.

Here it is apparently due to the fact that the GPIO driver has been changed and the “sysfs” paths are no longer valid. Fun. The core issue seems to be with GPIO interrupts here: https://github.com/piface/pifacecommon/blob/master/pifacecommon/interrupts.py

This describes the (now deprecated) sysfs interface: https://www.kernel.org/doc/Documentation/gpio/sysfs.txt

Translating these:

  • The old way was to “echo 25 > /sys/class/gpio/export”
  • That would make create/expose “/sys/class/gpio/gpio25/” at which points the pseudo files for direction etc could be used.

However:

  • Instead now we have /sys/class/gpio/gpiochip512/base (512) + /sys/class/gpio/gpiochip512/ngpio (58) which means this controller handles gpio’s in the range 512..up to 570 excluded. Then there’s “gpiochip570” which has base=570 and ngpio=8.
  • “$ gpioinfo | grep 25” ⇒ this lists all the pins and we can find that “GPIO25” is actually #25 (from 0-base) on that gpiochip0 which is actually the giochip512 above.
  • 512 + 25 = 537… so that’s the new “sysfs” number for GPIO25.
  • $ echo 537 | sudo tee /sys/class/gpio/export
    • ⇒ And that makes “/sys/class/gpio/gpio537/” appear.

Click here to continue reading...


2024-09-15 - Analysis: DroidVNC-NG VNC Server

Category DEV

https://play.google.com/store/apps/details?id=net.christianbeier.droidvnc_ng

and

https://github.com/bk138/droidVNC-NG

There are 2 things that interest me here:

  • This app auto-starts using the Accessibility API. I want to understand that API so that I can use it in TCM, since Android 14 does not support the old “Boot Receiver” method.
  • What are the steps needed to embed a VNC server in an app?
    • A use case would be TCM sharing its own view, without access to the entire tablet. Is it worth it to even consider that, or just install DroidVNC-NG on the side?

Accessibility Service API

https://developer.android.com/guide/topics/ui/accessibility/service

What it is:

It doesn’t seem like an AccessibilityService could start an app directly per se.

However it could potentially use GLOBAL_ACTION_HOME to invoke the Home app, then explore the hierarchy and click on the relevant app icon to trigger it. It’s not clear whether the service can do that on its own; it’s likely it needs to respond to a user gesture first.

ACTION_BOOT_COMPLETED and RECEIVE_BOOT_COMPLETED

Click here to continue reading...


2024-08-04 - Kotlin KMP

Category DEV

https://kotlinlang.org/docs/multiplatform.html 

Evaluate this against my needs:

  • Android development
  • Web development
  • Existing libraries

Rationale: KMP is all about the “multiplatform” aspect. I have zero multiplatform projects.

Android development: I don't do Android/iOS parity, so is there a point over Android Kotlin native? I doubt so.

Web development: This is competing with Flutter. I'm fine with Dart and Flutter. It beats anything React, whilst being mostly the same design. There are good libraries to interact with GCloud, Firebase, and it's been easy to find 3rd party libraries when needed.

Framework updates are the only thing tedious.

Before starting the Flutter way, I remember trying “Kotlin for the web” and that was not good. Was it KMP? Worth another look. Criterias are IDE support, Firebase integration support, framework updates, distrib size.


2024-07-09 - Godot GDSCript Static Typing

Category DEV

GDSCript: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/index.html

https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/static_typing.html

var damage = 10.5                                ⇒ no typing, this is essentially a Variant

var damage:= 10.5                                ⇒ inferred typing

var damage: float = 10.5                ⇒ explicit typing

For constants:

const MOVE_SPEED = 50.0                        ⇒ inferred (= and := are the same)

const MOVE_SPEED := 50.0                ⇒ inferred

const MOVE_SPEED: float = 50.0        ⇒ explicit

For functions:

func sum(a: float = 0.0, b: float = 0.0) -> float: …

func sum(a := 0.0, b := 0.0) -> float:

Which types to use:

https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/static_typing.html#what-can-be-a-type-hint

Types can be:

  • Variant, void (only function return, see below)
  • Built-in types, global classes, inner classes (gdscript has inner classes?!)
  • Native classes, enums (gdscript has enums?!)
  • Constants that are preloaded as class types (see below)

func blah() -> Variant: …         ⇒ forces the function to return something

func blah() -> void: …                 ⇒ forces the function to not return a value

Click here to continue reading...


2024-06-03 - Tablet 10’ “G10” and RTAC

Category DEV

https://amzn.to/4bJ0iUw is the 10 Inch Android 12 Tablet I’m using for Enphase Enlighen.

It has a 1280x800 screen.

Even though it’s a “production” one, I can run a debug app from Android Studio.

When enabling developer settings, there’s a Drawing > “Plus petite largeur d'écran” to control DPI:

  • 432 dp ⇒ makes all takes larger (simulates smaller screen)
  • 592 dp ⇒ the default value.
  • 601 dp ⇒ better for Enphase (better view of the graph)
  • 740 dp ⇒ makes everything look smaller (simulates larger screen like 1920x1200)

When trying out RTAC, the font for the throttles is way too big. It’s calibrated for the Chuwi’s 1920x1200 display. I realize I should customize it by “screen size”.


2024-04-28 - Conductor and Java 1.8 vs Java 11

Category DEV

A few months ago I updated the Conductor project to new libraries, notably switching to Kotlin DSL 1.9. That made building with Java 11 a requirement.

In turn, that broke the way the Kotlin DSL is loaded under JMRI. I’m not sure why.

So fine, I reverted the Gradle files to a “Java 1_8” compatibility mode, and reverted to the older Kotlin DSL 1.6. This works fine when building the fat JAR, but it fails when trying to run the tests. It works fine when I actually build with a Java 1.8 JDK instead.

When building with Java 11 and running the tests, I get this cryptic gradle error: “Incompatible because this component declares an API of a component and the consumer needed a runtime of a component

To be clear, the “build” part works. I get the fatJAR just fine. It’s the testing that fails, and only when doing it from Gradle on the command line. In IJ, I get the same behavior by selecting Conductor > All Tests on the root project.

I should try to replicate it with the gradle --info flag to see if that gives me more details on what is really breaking.

Another limitation is that only LibUtils v2 works with Java 1.8. LibUtils v3 already has Mockito dependencies that require Java 11.

But that seems all academic anyway. What I really want is to build with a Java 11 compatibility target since at this point even JMRI requires 11.

Then once I get that working, I need to retry with Kotlin DSL 1.9.

One thing for sure is from now on, when updating, I need to account for these scenarios:

  • Testing does not work the same way under IJ or under command-line Gradle.
  • Kotlin DSL loading does not work the same in standalone (simulator) vs from JMRI.


2024-02-18 - C++ unique_ptr

Category DEV

It’s time to update SDB to use more unique_ptr and/or shared_ptrs.

Some simple examples come to mind:

class SdbModManager {

    std::vector<SdbMod*> _mods;

    std::map<String, SdbMod*> _modsmap;

    std::vector<SdbSensor*> _sensors;

    std::vector<SdbServer*> _servers;

    std::vector<SdbBlock*> _blocks;

}

All these should be either unique or shared pointers to clarify the ownership.

There are 2 or 3 aspects to clarify:

  • How a new object is created.
  • How a ptr is “moved” into the vector.
  • Users of the vector.

For each collection, I have a simple getter that returns the vector as a reference:

const std::vector<SdbServer*>& servers() const  { return _servers; }

and then I use that in a for/auto loop:

for(auto* s: _manager.servers()) { … s->name() …  }

The other kind of usage is the vector for Events. One goal here is to change that from a struct holding to holding unique ptrs, with the goal that events get automatically released when pulled out of the vector and used.

So first, I need to write down a few things about how to use unique_ptrs correctly.

https://en.cppreference.com/w/cpp/memory/unique_ptr

#include <memory>

auto p = std::unique_ptr<Class>(new Class())

std::unique_ptr<Class> function() {

  return std::unique_ptr<Class>(new Class());

}

C++ ref mentions creating the instances using std::make_unique(), but apparently I don’t have that in the ESP IDF projects (N.D.L.R. it should be in ESP IDF 5.x but I’m using the previous one).


 Generated on 2025-01-18 by Rig4j 0.1-Exp-f2c0035