MariaDB 11.8 adds Vector Datatype

The lat­est Mari­aDB release adds a Vec­tor datatype, which is great for stor­ing and query­ing embed­dings. Embed­dings are basi­cal­ly a rep­re­sen­ta­tion of con­cepts for AI/GPT. I’ve used this when cre­at­ing a knowl­edge­base that can have ques­tions asked effi­cient­ly against it even at scale.

RustFS for Local Development S3-Compatible Storage

RustFS for Local Development S3-Compatible Storage

S3 and S3-com­pat­i­ble stor­age is an indis­pens­able com­po­nent of mod­ern web appli­ca­tions, espe­cial­ly when deploy­ing with con­tain­ers like Dock­er and Kubernetes.

When devel­op­ing an appli­ca­tion local­ly, although Lar­avel and oth­er frame­works have uni­fied stor­age helpers allow­ing switch­ing between file sys­tems (usu­al­ly local and S3), there are many quirks of object stor­age that will be over­looked if using a local filesystem.

Rather than use a host­ed S3 sys­tem dur­ing devel­op­ment, we can save costs, laten­cy (and time) and band­width using a local S3-com­pat­i­ble serv­er. MinIO was the go-to until recent­ly with con­tro­ver­sial license changes. RustFS is a good choice for local devel­op­ment. Here is how to set it up with Dock­er and Lar­avel for local development:

RustFS Dock­er:


1
2
3
4
5
6
7
8
9
10
11
docker run \
  -p 9000:9000 \
  -p 9001:9001 \
  -v rust-data:/data \
  -v rust-logs:/logs \
  -e RUSTFS_ACCESS_KEY=rustfsadmin \
  -e RUSTFS_SECRET_KEY=rustfsadmin \
  -e RUSTFS_CONSOLE_ENABLE=true \
  -e RUSTFS_SERVER_DOMAINS=storage.mydevdomain.test \
  -e RUSTFS_SIG_HEADER_WHITELIST="if-modified-since,range" \
  rustfs/rustfs:latest
Argu­mentMean­ing
1
dock­er run
Launch­es a new con­tain­er from the spec­i­fied image.
1
-p 9000:9000
Maps port 9000 inside the con­tain­er to port 9000 on your host. This is usu­al­ly where RustFS serves file stor­age, to be served publically.
1
-p 9001:9001
Maps port 9001 inside the con­tain­er to port 9001 on your host. RustFS web admin console.
1
-v rust-data:/data
Mounts a Dock­er vol­ume called 
1
rust-data
to 
1
/data
in the con­tain­er. This stores your per­sis­tent file data.
1
-v rust-logs:/logs
Mounts a Dock­er vol­ume called 
1
rust-logs
to 
1
/logs
in the con­tain­er. All logs gen­er­at­ed by RustFS go here.
1
-e RUSTFS_ACCESS_KEY=rustfsadmin
User­name for web admin con­sole access. Don’t use this in production!
1
-e RUSTFS_SECRET_KEY=rustfsadmin
Pass­word for web admin con­sole access. Don’t use this in production!
1
-e RUSTFS_CONSOLE_ENABLE=true
Enables the RustFS web con­sole, which allows you to mon­i­tor and man­age stor­age via a browser.
1
-e RUSTFS_SERVER_DOMAINS=storage.mydevdomain.test
Defines the domain(s) RustFS should respond to. 
1
-e RUSTFS_SIG_HEADER_WHITELIST=“if-modified-since,range”
Spec­i­fies which HTTP head­ers are allowed for signed requests. In prac­tice I’ve found this is required when serv­ing large files.
1
rustfs/rustfs:latest
The Dock­er image to run (lat­est ver­sion of RustFS).

To con­fig­ure this as a stor­age disk in Lar­avel, we can either cre­ate a new disk, or alter the S3 disk. You’ll have to decide which approach to take, which may depend on if you have mul­ti­ple disks and providers.

The admin inter­face of RustFS is very easy to use. If you are intend­ing files to be pub­lic and using Lar­avel, it is essen­tial to set your file sys­tem as pub­lic, and also set your RustFS buck­et as pub­lic. From expe­ri­ence you will just get silent errors with no error logs to help you.

config/filesystems.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'visibility' => 'public',
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
            'throw' => false,
            'report' => false,
            'http' => [
                'verify' => env('AWS_SSL_VERIFY', true),
            ],
        ],

From expe­ri­ence I’ve found that RustFS requires use_path_style_endpoint, as the URL struc­ture has the buck­et name as the first path in the URL, con­trary to S3. This is the same in many com­pat­i­ble servers.

Dur­ing devel­op­ment, the option to ignore self-signed cer­tifi­cate errors is important.

The final step is to set up a reverse proxy on your favorite web serv­er to serve the files. Here’s an exam­ple con­fig in Nginx (don’t for­get to update your hosts file):


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
server {
    listen 80;
    listen 443 ssl;
    server_name storage.mydevdomain.test bucketname.storage.mydevdomain.test;

    location / {
        proxy_pass http://127.0.0.1:9900;

        # Preserve client + host info (important for S3-style services)
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # RustFS / S3 compatibility tweaks
        proxy_http_version 1.1;
        proxy_set_header Connection "";

        # Large uploads support
        client_max_body_size 0;
        proxy_request_buffering off;
    }

    # SSL (Laragon)
    ssl_certificate "/etc/ssl/mycert.crt";
    ssl_certificate_key "/etc/ssl/mykey.key";
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
    ssl_prefer_server_ciphers on;

    charset utf-8;
}

That’s it! Now you have local S3-com­pat­i­ble stor­age for local devel­op­ment, that won’t cost you in stor­age and ingress/egress fees, has low laten­cy and won’t use up your bandwidth.

Using CSS Parameters to Stop Content Shifting with Dynamic Vue/React Content

Con­tent Shift­ing, also known as Con­tent Jump­ing, hap­pens when ele­ments of a web page change height while a page is load­ing. This can be dis­ori­en­tat­ing to users, and in extreme cas­es can cause prob­lems if a user clicks on an ele­ment as it moves, caus­ing unde­sired input. It’s also expect­ed in 2021 that search engines will begin penal­is­ing con­tent that exhibits con­tent shifting.

To avoid con­tent shift­ing, the ele­men­t’s height would be set regard­less of the dynam­ic con­tent it contains.

When using Vue or React JavaScript frame­works, I’ve found that CSS para­me­ters are use­ful. In my exam­ple, a Vue app loads dynam­ic con­tent and dis­plays it. Unfor­tu­nate­ly this caus­es con­tent shifting.

To avoid this, I pass the num­ber of rows in the HTML/view:


1
<div class="container" style="--preload-row-count: {{ $count }};">...</div>

In my CSS/SASS stylesheet, I then use this para­me­ter in a cal­cu­la­tion. There are two columns, so the num­ber of rows is divid­ed by 2. It is then mul­ti­plied by the height of a sin­gle row, and a min­i­mum height is set for the con­tain­er so that it will not shift:


1
2
3
4
.container{
    --calculated-board-rows: calc( var(--preload-row-count) / 2);
    min-height: calc( var(--calculated-board-rows) * 50px );
}

This fair­ly sim­ple method is an effec­tive way to stop con­tent shift­ing using CSS para­me­ters and calculations.

Twilio Studio — IVR and Chat Bots

Twilio Studio — IVR and Chat Bots

I’ve used Twilio for a while for pro­gram­mat­i­cal­ly send­ing and receiv­ing SMS mes­sages. There’s also a visu­al edi­tor called Stu­dio that can be used to make call and mes­sage flows:

It can be con­nect­ed to Twilio Autopi­lot to make AI-pow­ered bots. Tasks are trained with sam­ple phras­es. These sam­ple phras­es are vari­a­tions on what would be said to trig­ger an action e.g. ‘Call recep­tion,’ ‘Front desk,’ ‘Talk to a human.’

An exam­ple that comes to mind, is mak­ing a call han­dling sys­tem for an office. Rather than a voice menu that details each option fol­lowed by a num­ber, the caller could sim­ply say who they want­ed to talk to or what their request was about, and the sys­tem would han­dle it. This is far more respect­ful of the caller’s time com­pared to hav­ing them lis­ten to a long list of choices.

It works with SMS and voice calls, and seems a good way to build an IVR (Inter­ac­tive Voice Menu) sys­tem. TwiML can be used for more com­pli­cat­ed tasks, while still using Studio/Autopilot. The pric­ing is a lit­tle high­er than if you were to use a self-host­ed sys­tem, but there are so many com­pli­cat­ed func­tion­al­i­ties it seems well worth pay­ing the extra, as it would save time and reduce complexity.

I built a remote con­trolled car that used Twil­io’s cell­phone ser­vice. You can read about it here.

ESP32 E‑Paper Status Display

ESP32 E‑Paper Status Display

Using an ESP-32 board with an embed­ded E‑Paper dis­play, I cre­at­ed a gad­get that shows sta­tus infor­ma­tion from my web server.

E‑Paper, also known as E‑Ink, only needs pow­er when being updat­ed, and uses no pow­er between updates. This means that the gad­get can be pow­ered for weeks from a recharge­able battery.

The pur­pose of this gad­get is to put on my wall or desk, and show reg­u­lar­ly updat­ed impor­tant infor­ma­tion on my web serv­er, to keep informed of web site prob­lems and sta­tis­tics. The infor­ma­tion dis­played can be eas­i­ly changed, for exam­ple to the lat­est weath­er, news, cur­ren­cy prices or any­thing that can be accessed via the inter­net. E‑Paper means it uses a very small amount of pow­er and heat, com­pared to a com­put­er dis­play or television.

You can view my code on GitHub if you are inter­est­ed in mak­ing your own.

ESP32 E-Paper Display
ESP32 E-Paper Display
ESP32 E-Paper Display
Telepresence Internet Controlled 4G/LTE Long Range Robot Car

Telepresence Internet Controlled 4G/LTE Long Range Robot Car

For a long time I have want­ed to build a remote con­trolled robot car capa­ble of being con­trolled via the Inter­net, at long ranges using 4G/LTE cel­lu­lar con­nec­tiv­i­ty. So I did.

I used a Rasp­ber­ry Pi 3B+, an Adafruit DC and Step­per Motor Hat, and a Log­itech C930e USB UVC web­cam.

Build­ing the robot
Chas­sis with four motors
Chas­sis with four motors and top sec­tion attached
Com­plet­ed robot 
Com­plet­ed robot

The robot is capa­ble of con­nect­ing to the Inter­net using Wi-Fi. I was able to slight­ly increase the effec­tive Wi-Fi range by using a Mikrotik router and alter­ing the hard­ware retries set­ting and frame life set­tings. The inten­tion was to quick­ly recov­er from trans­mis­sion errors and avoid con­ges­tion. This dis­card­ed video pack­ets that could not be deliv­ered in real time, and kept the net­work clear for when trans­mis­sion would be suc­cess­ful. I also used ipt­a­bles and man­gle to alter the DSCP of the live video stream pack­ets with the same intention.

To enable a long range con­nec­tion, I used Twilio Pro­gram­ma­ble Wire­less to con­nect to local 4G/LTE cel­lu­lar net­works. I sub­stan­tial­ly low­ered the data rate to around 250 Kbps to make trans­mis­sion more reli­able and reduce costs, and was able to get a vir­tu­al­ly flaw­less live feed.

Twilio Wire­less Inter­net of Things Starter Pack
Mon­i­tor­ing 4G/LTE data usage with Twilio Pro­gram­ma­ble Wireless

The live video and audio stream uses FFMPEG for com­pres­sion and stream­ing, and has a pletho­ra of set­tings to tune. I took the time to tune bitrate, buffer­ing, keyframe inter­val. I also ensured the web cam­era was able to native­ly encode video with UVC at the select­ed res­o­lu­tion to reduce the load on the Rasp­ber­ry Pi’s CPU. Video laten­cy was often under a sec­ond, which is impres­sive espe­cial­ly con­sid­er­ing the round trip involved.

Robot remote­ly con­trolled via the internet

The con­trol sys­tem uses Let’s Robot (now Remo.tv), based at Cir­cuit Launch in Cal­i­for­nia, which has a com­mu­ni­ty of robot builders who love to cre­ate and share their devices. The pro­gram­ming lan­guage of choice is Python, and I also linked to an exist­ing API I had cre­at­ed in JavaScript with Node and PM2.

Mission 1

Mis­sion 1 — 30 minute Night Voyage

The first 4G/LTE long range mis­sion was suc­cess­ful, and the web­cam was good enough to be used at night. Dif­fer­ent mem­bers of the com­mu­ni­ty took turns to dri­ve the robot. It didn’t always dri­ve straight, so we had to dri­ve for­ward and turn to the left at reg­u­lar inter­vals. The robot drove for around 30 min­utes, and then got stuck when it fell down a side­walk. I had to quick­ly dri­ve to retrieve it =)

Mission 2

Mis­sion 2 — Involved Drama

The sec­ond mis­sion was intend­ed to dri­ve from my loca­tion to a friend work­ing at a local busi­ness. How­ev­er half way through the mis­sion, a sus­pi­cious mem­ber of the pub­lic grabbed the robot, threw it in a trash can, and called the police. I wait­ed for the police and calm­ly explained that the robot was an edu­ca­tion­al project in telep­res­ence, and also told the per­son report­ing the robot that there were no hard feel­ings, despite inter­fer­ing and dam­ag­ing my per­son­al property.

Police!

Mission 3

As part of the com­mu­ni­ty site, it is com­mon to leave your robot open to be con­trolled. While unat­tend­ed, a sneaky indi­vid­ual drove my robot into a void of the house and man­aged to get it cov­ered in spi­der webs and oth­er filth, as you can see below. Thanks.

Cov­ered in cobwebs
Very dirty

I found that cats were very curi­ous about the robot invad­ing their ter­ri­to­ry, as you can see below: 

A curi­ous cat inves­ti­gates the robot

I was very pleased with how the project worked, and had the oppor­tu­ni­ty to use Python, Node, and fine-tune wire­less net­work­ing and live video stream­ing, and of course remote­ly con­trol the robot as I had want­ed to do for a long time.

If you want to build your own robot, the guide to ‘build­ing a Bot­ting­ton’ is a great place to start.

Update: Twilio saw this post and gave me a $20.00 cred­it. Thank you 😁