All posts by fash

Bash tips part 1

Best Practices

#!/bin/bash     # Set shebang .. interpreter (sh/python/bash)
#set -x            # debug flag, shows all output and variables
set -e             # exit when an error occurs, Dont use this when sourcing a scriptor using bash ./scriptnaam gebruikt. 
set -u             # exit when a variable isn't defined
set -o pipefail # exit when a pipe command fails
# Add comments to your scripts!

PIPEFAIL Example
grep string /nonexistenddir/file | sort # Does NOT give an error, sort works!
So echo $? gives 0
When set -o pipefail is set, above example will print 1 or 2

Bash options example

#!/bin/bash

usage ()
{
    echo >&2 "usage: $0 <list-of-options>"
}

main ()
{
    [ $# -lt 1 ] && usage
    INSTALL_DIR=`dirname $0`
    for i in $@; do
        echo "$INSTALL_DIR $i"
    done
    
}

main $@

Execute output from a script.

Sometimes i write scripts which print the command I want to have executed. Most of the times to check the generated commandline.
simple example

ls | grep 2000 | while read ; do echo rm -f "$REPLY" ;done

Above only prints the lines
rm -f <filename with 2000>
rm -f <other filename with 2000>

Instead of removing the echo command, you can add a | bash , to have it executed.

ls | grep 2000 | while read ; do echo rm -f "$REPLY" ;done | bash

Or even shorter ( Use !! for previous command )

!! | bash 

History

Search with CTRL-R in your command history, and use !<number> to execute this command again.

touch file1 file2 file3 file4
chmod 664 !* # will chmod only the files

When you used typed
systemctl stop httpd
and want to start again use replace
^stop^start
This will take previous command containing stop and places stop with start and executes this.

Toggle history on/off with “set -o history”, depending on your setup you can use “<space>command to be executed”

History log original username per date when sudo is being used
(creates .history.20230103.fash in /root/ )

HISTFILE=~/.history.$(date +%y%b%d).$(who am i | awk '{ print $1 }')

Skip first 2 lines and join 2 lines

#skip 2 lines
tail -n +3

#join 2 lines
sed 'N;s/\n/,/'

#3rd line from a file
sed "3q;d" /etc/hosts

#join on line line using comma's
paste -sd "," -

Find Tricks

#Remove empty directories
find ??? -empty -type d -delete

#Find multiple extentions
find archieven/ \( -name "*.png" -o -name "*.xml" \)
-print0 to handle filenames with spaces

Remove huge directory structures FAST

mkdir /tmp/empty ; rsync -a --delete /tmp/empty /path/blah

Check program installed and in path

which zenity >/dev/null 2>/tmp/err || ( echo "zenity not found, please install" ; exit 1 )

Change directory to location script for relative path usage

cd $(dirname $0)  
ls relativesubdir/

IFS (Internal Field Separator)

IFS=$' '     # internal field separator, strings split here
for f in $(cat /etc/hosts) ; do echo $f ;done
#outputs 127.0.0.1
#        localhost
IFS=$'\n\t'     # internal field separator, split end of line
for f in $(cat /etc/hosts) ; do echo $f ;done
# Outputs 127.0.0.1 localhost 

Difference for and while read example

echo "This will print every word" > text  # Places text in file
echo "second line" >> text                # Append text
for f in $(cat text) ; do echo $f ;done   # for example
This 
will
print 
every
word
second
line

cat text | while read f ; do echo $f ; done # Read example
This will print every word
second line

Correct way to make a tempfile

tempfile=$(mktemp -d -t /tmp/log.$$)    # Temp file, with unique name $$ is the process number

Usage in script :
ls > $tempfile

Cleanup:
rm -f "/tmp/$tempfile"    # Remove

Direcory empty test

[ "$(ls -A /tmp)" ] && echo "Not empty" || echo "Empty" # test directory Empty/filled
test -d /tmp/1 && rmdir /tmp/1           # Removes a directory when it exists, will give an error when NOT empty

Using Expand

On directories archive30_tmp till archive35_tmp setting recursive chmod 2775 

find achief{30..35}_tmp -type d -exec chmod 2775 {} \;
 
or
 
mkdir tmp{1..3} # will create tmp1 tmp2 tmp3

echo pr{ut,utser}s # Outputs "pruts prutsers"

Test root user

if [ $USER == "root" ] ; then … ; fi # execute only when root
if $USER is empty, this will give an error.

if [ w$USER == "wroot" ] works 
qouting $USER also, but qouting a number using less/greater test could be problematic

Mount test

grep -qs /media /proc/mounts && echo "/media is mounted"

Date tricks

datum=$(date +%Y%m%d)                    # datum is yyyymmdd 20230103
today=$(date +%F)
tomorrow=$(date --date="next day" +%F)
p3=$(date --date="$p2" +%F)
dater=$p3
#now=$(date +%s --date="1 days ago")
now=$(date -d $(date --date="1 days ago" +%F) +%s)
p3epoch=$(date --date="$p3" +%s)
dater=$(date -d "$p3 1 year" +%F)

date -d @<UNIX timestamp> # Timestamp to date

Size test in directory

if [ "$(df /tmp |grep -v Available | awk {' print $4 }')" -lt 1000000 ] ; then echo "not enough free in /tmp" ;fi  

Parallel Tasks

4 parallel jobs  
find jpg -type f -name \*.jpg -print0 | xargs -0 -n1 -P4 ./convert.sh

Cluster ssh trick

Using Clusterssh at work
## clusterssh trick
Only needed to do stuff on server having a certain directory

    clusterssh storageservers # ssh to 24 storage servers
    sudo su -
    cd /bricks/*/backup2 # change directory to this if exists
    cd /alternatedir/brick0*/backup2 # change to this alternate directory if it exists 
# directories not found? then you are still in /root
    pwd | grep testdir || logout # no testdir in found subdirs? (there are non in /root so you will be logged out)
    id | grep myuser && logout # if mortal user? then logout again .. you will be disconnected from servers not containing the backup2 directories
    Do your work as root 

Forgot to sudo?

systemctl restart httpd (wont work as user)
sudo !!
(this will do  "sudo systemctl restart httpd")

Top 5 homedir users

du -hsx /home/* | sort -rh | head -5

Removing a huge file takes a long time .. lets truncate it

: > hugefile
rm hugefile

Check memory banks using dmidecode

sudo dmidecode| grep  -i -B1 "Form Factor" (B1 means BEFORE 1 line, A is AFTER)
 
        Size: 8192 MB
        Form Factor: DIMM
--
        Size: 8192 MB
        Form Factor: DIMM
--
        Size: No Module Installed
        Form Factor: DIMM
--
 
sudo dmidecode -t memory | grep -i size
        Size: 4096 MB

--
sudo lshw -short -C memory
H/W path            Device      Class          Description
==========================================================
/0/0                            memory         96KiB BIOS
/0/1000                         memory         4GiB System Memory
/0/1000/0                       memory         4GiB DIMM RAM

Script log replay

    script --timing=/tmp/time.txt /tmp/script.log
start your script
    scriptreplay -t /tmp/time.txt /tmp/script.log
replays

Deleted file takes up disk space (not being released)

lsof

httpd 12209 root 6w REG 253,3 5233639424 2097234 /var/log/httpd/access_log-20200711 (deleted)

cd /proc/12209/fd

find /proc/12209/fd -ls | grep '(deleted)'

121613145 0 l-wx------ 1 root root 64 Jul 10 12:04 /proc/12209/fd/6 -> /var/log/httpd/access_log-20200711\ (deleted)

: > "/proc/12209/fd/6"                 # Truncate

Keyword in line grep

cat file | grep -o 'skip_reason.*' # till end
cat file | grep -o 'skip_reason.*tillhere' 

Remove space filename

mv *\ * a 
rm *\ * 

Upper to lower case

 tr '[:upper:]' '[:lower:]'

Whole directories to lowercase

#!/bin/bash
#print usage 
if [ -z $1 ];then
        echo "Usage :$(basename $0) parent-directory"
        exit 1
fi

#process all subdirectories and files in parent directory
all="$(find $1 -depth)"



for name in ${all}; do
        #set new name in lower case for files and directories
        new_name="$(dirname "${name}")/$(basename "${name}" | tr '[A-Z]' '[a-z]')"

        #check if new name already exists
        if [ "${name}" != "${new_name}" ]; then
                [ ! -e "${new_name}" ] && mv -T "${name}" "${new_name}"; echo "${name} was renamed to ${new_name}" || echo "${name} wasn't renamed!"
        fi
done

echo
echo
#list directories and file new names in lowercase
echo "Directories and files with new names in lowercase letters"
find $(echo $1 | tr 'A-Z' 'a-z') -depth

exit 0

Sort by lenght

awk '{ print length(), $0 | "sort -n" }' 

Order / Delimiter AWK

echo 8.168.192.in-addr.arpa. | awk -F  "." '/1/ { print $3,$2,$1 }'

From – Till using AWK

cat file | awk '/Title1/,/-----/' 

Replace Tabs

sed -e 's/\t/ /g'

Sed Case Insensitive

sed s/a/b/gI 

And using lynx std in

cat index.html | lynx --dump -stdin

Random sleep

sleep .$[ ( $RANDOM % 4 ) + 1 ]s
#remove . for whole seconds instead of 10th of seconds

Copy pasting a lot from terminal , use xclip to copy to clipboard

xclip -sel c < bigsourcefile.c

Cache dir in memory for high IO processes

#!/bin/bash
[ ! -d /ramcache ] && mkdir -p /ramcache
mount -t tmpfs -o size=2G,nr_inodes=5k,mode=777 tmpfs /ramcache

Have a dual boot machine? And want to remotely start the other OS?
reboot2windows.sh

WINDOWS_TITLE=`grep -i "^menuentry 'Windows" /boot/grub/grub.cfg|head -n 1|cut -d"'" -f2`
sudo grub-reboot "$WINDOWS_TITLE"
sudo reboot

Move files in subdirectory per create hour

for f in $(seq -w 0 23) ; do find . -type f -print0 | xargs -0 -r stat -c "%y %N" | grep -E " $f:" | cut -f2 -d\' | while read ; do mkdir -p $f ; mv "$REPLY" $f/ ; done ;d

Exif time adjust

#Movies and images
exiftool '-*CreateDate-=5' '-*ModifyDate-=5' '-DateTimeOriginal-=5' dir/
exiftool '-FileCreateDate<CreateDate' '-FileModifyDate<CreateDate' dir/

Boardgames

Don’t know what game to buy?
Head over to boardgamegeek.com

Ratings, wiki, manuals, reviews, forum and more
Get you own collection list online

Our list is here
Link https://boardgamegeek.com/collection/user/fashice

My top 5 games (at the moment) are:

  • Uboot
  • Memoir ’44
  • Monster Slaughter
  • Lord of the Rings
  • Codenames Duet

U-BOOT: The Board Game is a fully cooperative, real-time tabletop game of WW2 submarine warfare. It allows 1 to 4 players to assume the roles of the Captain, the First Officer, the Navigator, and the Chief Engineer on board of a type VIIC U-boat. The game is driven by a companion app, allowing for an unprecedented level of realism, as well as a challenging enemy A.I. which will push your skills to the limit. The action unfolds both on the strategic and the tactical scale, always demanding teamwork, efficient crew management, and quick situation assessment.

Uboot game with own 3D printed submarine with leds

Memoir ’44 is a historical boardgame where players face-off in stylized battles of some of the most famous historic battles of World War II including Omaha Beach, Pegasus Bridge, Operation Cobra and the Ardennes. The game includes over 15 different battle scenarios and features a double-sided hex game board for both beach landings and countryside combat. Each scenario mimics the historical terrain, troop placements and objectives of each army. Commanders deploy troops through Command and Tactic cards, applying the unique skills of his units — infantry, paratrooper, tank, artillery, and even resistance fighters — to their greatest strength.

Monster Slaughter is a tactical game inspired by horror movie classics, where each players takes control of a family of three monsters: father, mother and child, each with their own stats and family power. Their objective is to scour the cabin looking for five guests, find their hiding spot and “take care” of them! Each victim hides in a pile of cards that monsters must search through to find them, gathering items and traps as they do. Once revealed, a guest’s miniature is put on the board and can be attacked!

However, each player has secretly set a killing order for these guests, and killing them in order is worth more points. They can use their item cards to defend the guests against other monsters or scare the victims away to other rooms, so the guests die in a more favorable order!

Monster Slaughter

Some DIY own build games are:
Shogi (Japanese Chess)
https://www.henriaanstoot.nl/2012/05/31/shogi-game-lasercut/
Crokinole (Canadian game)
https://www.henriaanstoot.nl/2018/06/14/made-a-crokinole-game/

ChatGPT IA generated chats/texts

ChatGPT is a large language model chatbot developed by OpenAI based on GPT-3.5. It has a remarkable ability to interact in conversational dialogue form and provide responses that can appear surprisingly human.

So i asked stuff like:

Thats a pretty good joke about a 6502 CPU. I tought it would be a hard question

The question : “what are women looking for in a man”
Gave a lot of lines :
No generic answer
Lines with Respect, Humor, Kindness, Confidence

Then it stopped with:

Typical .. even AI is baffled by women.

Below a chat session from a great friend of mine Tyrone

Tyrone: Ok could you write me a commodore c64 assembly routine to make the screen white?

Certainly! Here is a simple assembly routine that will clear the screen and set all of the pixels to white on a Commodore 64:

Copy code

ldx #$00
ldy #$00 
clear_screen: 
sta $d800,x 
inx 
cpx #$40 
bne clear_screen 

This code will loop through all of the screen memory locations and set the value at each location to $00, which will turn all of the pixels white. The inx and iny instructions are used to increment the X and Y registers, which are used to keep track of the current screen position. The cpy instruction is used to compare the value in the Y register to $20, and the bne instruction is used to branch back to the clear_screen label if the values are not equal, causing the loop to continue until the entire screen has been cleared.

I hope this helps! Let me know if you have any questions.



Tyrone: Well a big code but i wanted to make the screen the color white not with a character space .. this is also white but it's more logical that the color is named then the white space this code should be three lines of could can you do this?

Certainly! Here is a simple assembly routine that will clear the screen and set all of the pixels to white on a Commodore 64:

Copy code

ldx #$00
ldy #$00 
clear_screen: 
sta $d800,x 
inx 
cpx #$40 
bne clear_screen 

This code will loop through all of the screen memory locations and set the value at each location to $ff, which will turn all of the pixels white. The inx instruction is used to increment the X register, which is used to keep track of the current screen position. The cpx instruction is used to compare the value in the X register to $40, and the bne instruction is used to branch back to the clear_screen label if the values are not equal, causing the loop to continue until the entire screen has been cleared.

I hope this helps! Let me know if you have any questions.



Tyrone: This is wrong here is a hint use registers d020 and d021 for screen and border color 

I apologize for the mistake in my previous response. Here is the corrected code that will set the screen and border colors to white on a Commodore 64:

Copy code

lda #$0f
sta $d020
sta $d021 

This code will store the value $0f in the memory locations at $d020 and $d021, which are the registers that control the screen and border colors on the Commodore 64. The value $0f corresponds to the color white.

I hope this helps! Let me know if you have any questions.



Thank you ai 

Weird comix, doodles and drawings i did

Blind parents often think their children are blind due to genetics.
Miffy learns DNS (Domain Name resolving)
Biological fly fly swatter trap

Here comes the plane!! ..
Fear for flying learned at a young age
More realistic spiderman

Postman, please drop some catfood in the mailbox every day?
Stopcontact = powersocket, spijkers = nails, aquariumslang met water = hose with water, lampje = lamp and using a clothespin to turn light off or dimming it
If you know .. you know
Waldorf and Statler (Vincent and me)
A friend and I having both backpains, mixing pills and alcohol will do the trick? Made a blender image to post in our Mattermost channel

ABC to Animation for Concertina

I want to take simple ABC Music notation tunes and convert these to animations which shows you which keys to press and to push or pull.

This is a work in progress.
Script is using ImageMagick and ffmpeg

I started this in bash, and i should rewrite this in python.

Bash script which generates the images

#!/bin/bash
convert -size 800x600 canvas:white white.png
x=55
y=55
draw=""
for xx in $(seq 1 6); do
for yy in $(seq 1 5); do
	startx=$(( $x * $xx + 100))
	starty=$(( $y * $yy + 200))
if [ $xx -gt 3 ] ; then startx=$(($startx + 200)) ;fi
draw="$draw  -draw \"circle $startx,$starty $(( $startx - 10)),$(( $starty - 10 ))\""
done
done
echo convert $draw white.png          draw_circle.png | bash

for xx in $(seq 1 6); do
for yy in $(seq 1 5); do
        startx=$(( $x * $xx + 100))
        starty=$(( $y * $yy + 200))
if [ $xx -gt 3 ] ; then startx=$(($startx + 200)) ;fi
convert -fill white -stroke black $draw  -draw "circle $startx,$starty $(( $startx - 10)),$(( $starty - 10 ))" draw_circle.png $xx-$yy.png
convert  -draw "rectangle 300,50 500,100"  $xx-$yy.png push-$xx-$yy.png
convert  -draw "rectangle 150,50 650,100"  $xx-$yy.png pull-$xx-$yy.png
done
done

Example movie generated using random notes/length pause

for f in $(seq 1 50) ; do
pushpull=$(shuf -n1 -e push pull)
row=$(shuf -n1 -e 1 2 2 2 2 2 3 4 4 5 5 5 6)
col=$(shuf -n1 -e 1 1 1 2 2 3)
pauze=$(shuf -n1 -e 0 0 0 0 0 0 0 0 1 )
length=$(shuf -n1 -e 1 1 1 1 2 2 3 4)
file="$pushpull-$row-$col.png"
if [ $pauze == "1" ]; then
file="draw_circle.png"
fi
for f in $(seq 1 $lenght) ; do
echo "file $file"
echo "duration 0.5"
done
done

list to mp4

ffmpeg -f concat -i list  -vf fps=10 -pix_fmt yuv420p test.mp4

Above movie shows left and right hand. Keys to press.
And the bar above represents PULL/PUSH

Todo/In progress:

  • abc parser ( started )
  • Push/Pull as text in above movie also note to play??
  • Rewrite to python

Servo’s Rotary encoders, Node-red and Mqtt

Multiple rotary encoders are controlling a servo based lock. 3 players have to work together to open the lock.

Secure MQTT rotary encoder (Can be used over the internet)
Servo based lock
Lock Mockup
Node red test logic

Arduino Rotary button (mqtt)

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <time.h>
#include <PubSubClient.h>

#define encoderCLK 5   //D1
#define encoderDT 4    //D2
int servoAngle = 0;
int crntCLK;
int prvsCLK;
String myString;
char ang[50];

#ifndef SECRET
const char ssid[] = "MYSSID";
const char pass[] = "MSSIDPASS";

#define HOSTNAME "rotary1"

const char MQTT_HOST[] = "securemqttserver";
const int MQTT_PORT = 8883;
const char MQTT_USER[] = "user"; // leave blank if no credentials used
const char MQTT_PASS[] = "pass"; // leave blank if no credentials used

const char MQTT_SUB_TOPIC[] = "escape/" HOSTNAME "/in";
const char MQTT_PUB_TOPIC[] = "escape/" HOSTNAME "/out";
const char MQTT_PUB_TOPIC_angle[] = "escape/" HOSTNAME "/angle";

#ifdef CHECK_CA_ROOT
static const char digicert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIUXEEQRLHhYox8a95YiAYX/wQ/XeMwDQYJKoZIhvcNAQEN
----8< snip snap
CyLjTT2rtllw==
-----END CERTIFICATE-----
)EOF";
    #endif

    #ifdef CHECK_PUB_KEY
    // Extracted by: openssl x509 -pubkey -noout -in ca.crt
    static const char pubkey[] PROGMEM = R"KEY(
    -----BEGIN PUBLIC KEY-----
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxx
    -----END PUBLIC KEY-----
    )KEY";
    #endif

    #ifdef CHECK_FINGERPRINT
	// Extracted by: openssl x509 -fingerprint -in ca.crt
    static const char fp[] PROGMEM = "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD";
    #endif
#endif

//////////////////////////////////////////////////////

#if (defined(CHECK_PUB_KEY) and defined(CHECK_CA_ROOT)) or (defined(CHECK_PUB_KEY) and defined(CHECK_FINGERPRINT)) or (defined(CHECK_FINGERPRINT) and defined(CHECK_CA_ROOT)) or (defined(CHECK_PUB_KEY) and defined(CHECK_CA_ROOT) and defined(CHECK_FINGERPRINT))
  #error "cant have both CHECK_CA_ROOT and CHECK_PUB_KEY enabled"
#endif

BearSSL::WiFiClientSecure net;
PubSubClient client(net);

time_t now;
unsigned long lastMillis = 0;

void mqtt_connect()
{
  while (!client.connected()) {
    Serial.print("Time: ");
    Serial.print(ctime(&now));
    Serial.print("MQTT connecting ... ");
    if (client.connect(HOSTNAME, MQTT_USER, MQTT_PASS)) {
      Serial.println("connected.");
      client.subscribe(MQTT_SUB_TOPIC);
    } else {
      Serial.print("failed, status code =");
      Serial.print(client.state());
      Serial.println(". Try again in 5 seconds.");
      /* Wait 5 seconds before retrying */
      delay(5000);
    }
  }
}

void receivedCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Received [");
  Serial.print(topic);
  Serial.print("]: ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
}

void setup()
{
  pinMode (encoderCLK,INPUT_PULLUP);
  pinMode (encoderDT,INPUT_PULLUP);
  prvsCLK = digitalRead(encoderCLK);
  Serial.begin(115200);
  Serial.println();
  Serial.println();
  Serial.print("Attempting to connect to SSID: ");
  Serial.print(ssid);
  WiFi.hostname(HOSTNAME);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(1000);
  }
  Serial.println("connected!");

  Serial.print("Setting time using SNTP");
  configTime(1 * 3600, 0, "pool.ntp.org", "time.nist.gov");
  now = time(nullptr);
  while (now < 1510592825) {
    delay(500);
    Serial.print(".");
    now = time(nullptr);
  }
  Serial.println("done!");
  struct tm timeinfo;
  gmtime_r(&now, &timeinfo);
  Serial.print("Current time: ");
  Serial.print(asctime(&timeinfo));

  #ifdef CHECK_CA_ROOT
    BearSSL::X509List cert(digicert);
    net.setTrustAnchors(&cert);
  #endif
  #ifdef CHECK_PUB_KEY
    BearSSL::PublicKey key(pubkey);
    net.setKnownKey(&key);
  #endif
  #ifdef CHECK_FINGERPRINT
    net.setFingerprint(fp);
  #endif
  #if (!defined(CHECK_PUB_KEY) and !defined(CHECK_CA_ROOT) and !defined(CHECK_FINGERPRINT))
    net.setInsecure();
  #endif

  client.setServer(MQTT_HOST, MQTT_PORT);
  client.setCallback(receivedCallback);
  mqtt_connect();
}

void loop()
{
   crntCLK = digitalRead(encoderCLK);

 if (crntCLK != prvsCLK){
      // If the encoderDT state is different than the encoderCLK state then the rotary encoder is rotating counterclockwise
        if (digitalRead(encoderDT) != crntCLK) {
          servoAngle ++;

        }
        else {
          servoAngle --;
         }
         Serial.println(servoAngle);
          String myString = String(servoAngle);
          myString.toCharArray(ang, myString.length() + 1);
          client.publish(MQTT_PUB_TOPIC_angle, ang, false);
 }
  prvsCLK = crntCLK; 
  
  now = time(nullptr);
  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.print("Checking wifi");
    while (WiFi.waitForConnectResult() != WL_CONNECTED)
    {
      WiFi.begin(ssid, pass);
      Serial.print(".");
      delay(10);
    }
    Serial.println("connected");
  }
  else
  {
    if (!client.connected())
    {
      mqtt_connect();
    }
    else
    {
      client.loop();
    }
  }

  if (millis() - lastMillis > 5000) {
    lastMillis = millis();
    client.publish(MQTT_PUB_TOPIC, ctime(&now), false);
  }
}

Arduino 3 servos using mqtt

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Servo.h> 
Servo lock1; 
Servo lock2;
Servo lock3;

const char* ssid = "MYSSID";                // WiFi SSID
const char* password = "MYSSIDPASS";        // WiFi Password
const char* mqtt_server = "MQTTSERVER";  // IP Broker MQTT
const char* topic_lock1 = "escape/servo/lock1";
const char* topic_lock2 = "escape/servo/lock2";
const char* topic_lock3 = "escape/servo/lock3";
 
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {

  Serial.begin(115200);
  lock1.attach(D1);
  lock2.attach(D2);
  lock3.attach(D3);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
 String string;
 Serial.print("Message arrived [");
 Serial.print(topic);
 Serial.print("] ");
 for (int i = 0; i < length; i++) {
 string+=((char)payload[i]); 
 }
 Serial.print(string);
 Serial.print(" toInt ");
 int pos = string.toInt(); 
 Serial.println(pos);

 
 if ( strcmp(topic, topic_lock1) == 0 ) {
 Serial.print("lock1 ");
 Serial.println(pos);
 lock1.write(pos); 
 }
 if ( strcmp(topic, topic_lock2) == 0 ) {
 Serial.print("lock2 ");
 Serial.println(pos);
 lock2.write(pos); 
 }
 if ( strcmp(topic, topic_lock3) == 0 ) {
 Serial.print("lock3 ");
 Serial.println(pos);
 lock3.write(pos); 
 }
 
 delay(15); 
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266servolocks")) {
      Serial.println("connected");
      client.subscribe(topic_lock1); 
      client.subscribe(topic_lock2); 
      client.subscribe(topic_lock3); 
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
void loop() {   
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  delay(100);
}

Two minute timelapse of oyster mushrooms growing.

#!/bin/bash
# capture images from Foscam ( 5 minute interval )
while true; do
wget -q "http://IP-WEBCAM:88/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=adminuser&pwd=adminpass" -O $(date +%Y%m%d%H%M).jpg
sleep 300
done

# from jpg to mp4
ffmpeg -f image2 -r 24 -pattern_type glob -i '*.jpg'   -vcodec libx264 -profile:v high444 -refs 16 -crf 0 -preset ultrafast -vf scale=1920:1080 paddo.mp4

If your ipcam can’t add timestamps, use below part to add text (replace in above script)

...
...
wget -q "http://IP-WEBCAM:88/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=adminuser&pwd=adminpass" -O /tmp/1.jpg
convert -fill white -pointsize 30 -draw "text 10,30 '$(date)'" /tmp/1.jpg $(date +%Y%m%d%H%M).jpg
rm -f /tmp/1.jpg 
...
...

Escape game world generator

A world map generator in php.
This php script selects randomly 3 cities from a CSV file and draws these on a worldmap.
No cities wil be choosen which have could cause a drawing overlap.
Every player can see the same generated worldmap with a countdown timer.

CSV example with places and coordinates (cities.csv)

London,905,412
Amsterdam,929,414
Wellington,1722,867
Costa Rica,524,640
New Delhi,1270,514
New York,567,477
Tokio,1548,500

generate.html

<html><body bgcolor=black>
<header>
  <div class="menu_area"></div>
  <center>
<p id="demo" style="text:white;"></p>
  </center>
</header>
<style>
html, body, header {
    overflow: hidden; /* Hide scrollbars */
    height: 100%;
    text: white;
}
header {
    background-image: url('world.php');
    background-size: cover;
}
  p {
  color: white;
  font-size: large;
  font-family: Verdana, Arial, sans-serif;
  font-size: 42px;
 }
</style>
<script>
	Date.prototype.addHours= function(h){
    this.setHours(this.getHours()+h);
    return this;
}

var countDownDate = new Date().addHours(1).getTime();
  var x = setInterval(function() {
  var now = new Date().getTime();
  var distance = countDownDate - now;
  var days = Math.floor(distance / (1000 * 60 * 60 * 24));
  var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
  var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
  var seconds = Math.floor((distance % (1000 * 60)) / 1000);

  document.getElementById("demo").innerHTML = days + "d " + hours + "h "
  + minutes + "m " + seconds + "s ";

  if (distance < 0) {
    clearInterval(x);
    document.getElementById("demo").innerHTML = "EXPIRED";
  }
}, 1000);
</script>
</body></html>

Used world image to draw on.

The squares and city names are dynamically drawn using php GD lib.

apt-get install apache2 php php-gd

php example:

// create image from source
$src = imagecreatefromjpeg('world.jpg');
$dest = imagecreatetruecolor(1920, 1080);
// 1920x1080
imagecopy($dest, $src, 0, 0, 0,0, 1920, 1080);
// assign color
$white = imagecolorallocate($dest, 255, 255, 255);
// rectangle example
imagerectangle($dest, $x, $y, $x2, $y2, $white);
// add text
Imagettftext($dest, 24, 0, $x, $y, $white, $font, "Text");
// set header output as image
header('Content-Type: image/jpg');
// output to browser
imagejpeg($dest);
// output to file
imagejpeg($dest, 'generatedworld.jpg');
// Free memory
imagedestroy($dest);
imagedestroy($src);

world.php is included as image, it dynamically generates the map

<?php

putenv('GDFONTPATH=' . realpath('.'));

// Name the font to be used (note the lack of the .ttf extension)
$font = 'tahoma.ttf';
$city1="";
$city2="";
$city3="";

// Create image instances
$src = imagecreatefromjpeg('world.jpg');
$dest = imagecreatetruecolor(1920, 1080);

// Copy
imagecopy($dest, $src, 0, 0, 0,0, 1920, 1080);

$green = imagecolorallocate($dest, 255, 255, 255);



$rows = file("cities.csv");
$len = count($rows);
$rand = [];
while (count($rand) < 1) {
    $r = rand(0, $len-1);
    if (!in_array($r, $rand)) {
        $rand[] = $r;
    }
}
foreach ($rand as $r) {
    $csv = $rows[$r];
    $data = str_getcsv($csv);
    $city1="$data[0]";
    $x1=$data[1];
    $y1=$data[2];
}

imagerectangle($dest, $x1-50, $y1-50, $x1+50, $y1+50, $green);
imagerectangle($dest, $x1-49, $y1-49, $x1+49, $y1+49, $green);
imagerectangle($dest, $x1-10, $y1, $x1+10, $y1-1, $green);
imagerectangle($dest, $x1, $y1-10, $x1-1, $y1+10, $green);
Imagettftext($dest, 24, 0, $x1-50, $y1-60, $green, $font, "$city1");

while($city2 == "") {

$rows = file("cities.csv");
$len = count($rows);
$rand = [];
while (count($rand) < 1) {
    $r = rand(0, $len-1);
    if (!in_array($r, $rand)) {
        $rand[] = $r;
    }
}
foreach ($rand as $r) {
    $csv = $rows[$r];
    $data = str_getcsv($csv);
    $x2=$data[1];
    $y2=$data[2];
    $deltax=abs($x2-$x1);
    $deltay=abs($y2-$y1);
}
if($data[0] != $city1 && $deltax > 100 && $deltay > 100 ){
	$city2=$data[0];
}

}


imagerectangle($dest, $x2-50, $y2-50, $x2+50, $y2+50, $green);
imagerectangle($dest, $x2-49, $y2-49, $x2+49, $y2+49, $green);
imagerectangle($dest, $x2-10, $y2, $x2+10, $y2-1, $green);
imagerectangle($dest, $x2, $y2-10, $x2-1, $y2+10, $green);
Imagettftext($dest, 24, 0, $x2-50, $y2-60, $green, $font, "$city2");


while($city3 == "") {

$rows = file("cities.csv");
$len = count($rows);
$rand = [];
while (count($rand) < 1) {
    $r = rand(0, $len-1);
    if (!in_array($r, $rand)) {
        $rand[] = $r;
    }
}
foreach ($rand as $r) {
    $csv = $rows[$r];
    $data = str_getcsv($csv);
    $x3=$data[1];
    $y3=$data[2];
    $deltax=abs($x3-$x1);
    $deltay=abs($y3-$y1);
    $deltax1=abs($x3-$x2);
    $deltay1=abs($y3-$y2);
}
if($data[0] != $city1 && $data[0] != $city2 && $deltax > 100 && $deltay > 100 && $deltax1 > 100 && $deltay1 > 100 ){
	$city3=$data[0];
}
}
imagerectangle($dest, $x3-50, $y3-50, $x3+50, $y3+50, $green);
imagerectangle($dest, $x3-49, $y3-49, $x3+49, $y3+49, $green);
imagerectangle($dest, $x3-10, $y3, $x3+10, $y3-1, $green);
imagerectangle($dest, $x3, $y3-10, $x3-1, $y3+10, $green);
Imagettftext($dest, 24, 0, $x3-50, $y3-60, $green, $font, "$city3");


// Output and free from memory
header('Content-Type: image/jpg');
imagejpeg($dest);
imagejpeg($dest, 'generatedworld.jpg');


imagedestroy($dest);
imagedestroy($src);
?>

Movie to txt in dutch

In the past i’ve converted some VHS movies speech to text, using all kinds of tools.
Lets use some opensource tools!

pip install moviepy
pip install SpeechRecognition

Create a python script with the following:
(Called mine wav2txt.py)

import math, contextlib
import speech_recognition as sr
from moviepy.editor import AudioFileClip
movie_audio_file_name = "movieadiofile.wav"
with contextlib.closing(wave.open(movie_audio_file_name,'r')) as f:
    frames = f.getnframes()
    rate = f.getframerate()
    duration = frames / float(rate)
total_duration = math.ceil(duration / 60)
r = sr.Recognizer()
for i in range(0, total_duration):
    with sr.AudioFile(movie_audio_file_name) as source:
        audio = r.record(source, offset=i*60, duration=60)
    f = open("transcription.txt", "a")
    f.write(r.recognize_google(audio, language="nl-NL"))
    f.write(" ")
f.close()

Now convert a movie to wav using below.

ffmpeg -i /fileserver/path/koolhoven.mkv movieaudiofile.wav

run python3 wav2txt.py

output
(Note .. these are not timestamped for subtitles)
I only needed the things being said in the home movie recordings as text.

Ik zit hier in de film The James Dean aan de
wereld voorstelde en daarmee de tienerfilm ingeleverd introduceren zelden werden onrustige 10 asiel zo mooi blootgelegd als ik deze film van Nicolas bij en dat wordt dan meteen toevallig even de mooiste
titels ooit wel eens autocross vanavond kijken we naar de kom ik nog even veel zomer dat je voor het eerste meisje Zoem de eerste baantje

etc..