Category Archives: Uncategorized

scratch Art Project

Having some spare time in the scratch course at school, we did a short ‘computer art project’.

A short introduction to the topic was presented, some materials taken from the following web pages:
https://gigers.com/blog/computerkunst-mit-snap-wiederholung-und-zufall/
– https://monoskop.org/images/2/25/- Reichardt_Jasia_ed_Cybernetic_Serendipidity_The_Computer_and_the_Arts.pdf
http://joachim-wedekind.de/Downloads/AtlasComputerkunst_redsize.pdf

The task for the kids, 7th class, was to use simple geometrical patterns combined with some randomness.

Using scratchClient, a pen plotter axidraw SE/A3 was connected for the class room. A standard laptop was used for a plotter server, caching the kids graphics and spooling to the hardware.

Kids worked on RPi4 boards, scratch3 local and sccratchClient (for scratch3) as a backend.

Results

The kids hat a lot of fun working on this project. Within 90 minutes, there have been about 500 images produced (a lot of them just to see how ‘randomness works’), a total of about 420.000 graphical commands.

Some results produced have been plotted. The images presented here have been converted from the scratch graphical commands to svg images, then to pixel graphics.

The images are published with a copyright to the school.

copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024
copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024
copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024
copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024

copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024

copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024

copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024

copyright Immanuel Kant Gymnasium Leinfelden Echterdingen 2024

System Context

The system fortunately worked pretty good. The scratchClient for the plotter used UDP broadcast to find a plotter server on a laptop. ScratchClient then used a ‘plotter’ adapter so send commands to plotter server.
The plotter server software then caches the results of the eight groups in the course. For the hardware axidraw, the plotterServer scales the output and does some drawing optimizations like reducing redundant pen lift up, down operations.

For the scratch project, a sample project was provided which demonstrated the ‘how it works’. From this, the kids developed their own idea.

Basic plotter commands are

  • begin, end: The wrapper for the plotter Server to separate images
  • up, down: pen move commands
  • goto_x_y x=, y= : absolute move
  • change_x, x=: change x position
  • change_y, y=: change y position.

All commands updated local stage and simultaneously have been sent to plotter server.

Here a sample scratch snippets to draw some nested squares:

The related output on the plotter server is showing the canvas printed aka the stage visible on the scratch workstations. Some adjustment knobs for pen lifting are needed.

plotter server graphical user interface

Indendation problems in python

Indentation is a part of code structure in python.

Indentation problems cause error messages like

IndentationError: unindent does not match any outer indentation level
IndentationError: expected an indented block after ‘for’ statement on line 3


There are many, really many tutorials in the web about this topic.
https://python.land/python-tutorial#Pyt … r_features
https://peps.python.org/pep-0008/#indentation
https://www.w3schools.com/python/gloss_ … tation.asp

Usual problems are:
– various deep levels, e.g. 3 blank or 4 blank deep mixed (use 4 blanks)
– mix of blanks and tabs for indentation.

Thonny is a common IDE, editor for beginners. See https://thonny.org/
Too see the difference between tabs and blanks, it is good to have ‘highlight tab characters’ enabled in the options.

thonny_options.PNG

Then in the thonny editor, tabs are displayed in a special color. Usually a good idea to replace tabs by blanks.

thonny_tab.PNG

The sample shows two tab in second line.

There are text editor around which allow better visibility of all these invisible characters in a text. Here an example of a blank followed by a tab in second line.

editor_show_all_characters.PNG

This is from “Notepad++”; good to have such a tool in the toolbox to fix stubborn problems.

servo for scratchClient

During a conversation on scratch forum there have been quite a few issues to get a servo run on someones computer.

Here a video on installation scratchClient on a new sd card, run scratchClient and prepare a script in scratch1.4.

install scratchClient and running servo sample.

The sequence is shortened a bit. Especially the system update is at multiple speed.

When the servo moves, there is noticeable jitter. Caused by delays in python and operating system.

scratchClient and scroll_phat_hd

There is a nice little led matrix moard from PIMORONI, “SCROLL PHAT HD”. The form factor is for the pi zero, but it runs well also with a pi 3.

scroll_phat_hd

scratchClient offers support for this board.

The interface allows to set pixel with brightness, write large and somewhat smaller text and of course clear the display.

There is a sample scratch script in scratch/scrollphathd/scroll_phat_hd.sb

There are quite a few commands from scratch which need parameters.
This is not a trivial task with scratch. Here a variable is used as a ‘command’-Variable ‘sph_command which receives operation names optionally with parameters.


Clear the display. There is also a broadcast command for this ‘clearDisplay’


Set a pixel at x=1, y=1, brightness = 1.0. Valid ranges are x in [0..16], y in [0..6], brightness is [0.0..1.0]. Values out of scope are ignored.


Draw a box between two pixels, here between [0;0] and [17;7] with brightness 0.3. This command effectively sets all led to on.

There are two text variables provided with a 5*7-font and a 3*5 font. Not all chars are supported. The 5*7 font basically supports ascii, the 3*5 font supports digits and some extra chars as ,;.:-_+\/.
The fonts are defined in ‘pseudographics’ in a python file and can easily be expanded.

There is a sample config file available, start scratchClient with

cd ~/scratchClient
python src/scratchClient.py -c config_scrollphathd

The adapter code has been written based on code from pimoroni.

minecraft and scratchClient

On raspberry pi, there is a local minecraft release available which supports a python API.

There is a MinecraftAdapter available for scratchClient which supports this API and allows to do basic operations.

For most of the operations, there are coordinate values to be set first before the operation is performed.

Example: set position of ‘person’.

Example: set Block

There is a sample scratch application in scratch/minecraft/minecraft.sb which demonstrates the basic concepts.

There are some demo scripts in minecraft.sb. One of these writes ‘scratch’ to the landscape. There is no ‘printString’-Function in the adapter. The pixel are defined in a local list, containing x and y-values of pixels. The data for this array are generated by a small python script and then imported into the list.

The python code is in scratch/minecraft/scratch_x_y.py. Run it by

python scratch_x_y.py > scratch_x_y.txt

and import the txt-file into the array.

The python code is quite simple.

f0="                         *          *        "    
f1="                         *          *        "
f2=" ***   ***  * **   ***  ***    ***  * **     "
f3="*     *   * **  *     *  *    *     **  *    "
f4=" ***  *     *      ****  *    *     *   *    "
f5="    * *   * *     *   *  *  * *   * *   *    "
f6="****   ***  *      ****   **   ***  *   *    "

def printPixel(f, level):
    for i in range(len(f)):
        if '*' == f[i]:
            print(i)      # print x-koordinate
            print(level)  # print y-koordinate
            
printPixel(f6, 1)
printPixel(f5, 2)
printPixel(f4, 3)
printPixel(f3, 4)
printPixel(f2, 5)
printPixel(f1, 6)
printPixel(f0, 7)

The pixels are defined in ‘pseudographic’. It is simple to add more text, decorations or extend the output by colors ( * ==> green, # ==> red) and adjust the scratch script to take each third line for the color information.
But keep in mind that ‘the world’ is limited size for minecraft for py and coordinates should be kept in range.

python timing loop accuracy

On a raspberry pi in python, it is not possible to produce accurate timings in μs range. The question is how accurate the timing is in real live.

The issue was raised by someone asking a question in raspberry pi forum on an 80Hz timing loop “the values may not always be accurate at the milliseconds level”.

To measure this, a simple timing loop was set up in python, and the timing was measured with the help of an arduino due. The arduino due runs at 84MHz and is ideal as a versatile measuring tool for this sort of problems.

Python timing loop. The loop should produce a square wave with 12.5ms or 80Hz.

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

pin_AO = 17
GPIO.setup(pin_AO, GPIO.OUT)

dt = 0
diff = 0.0115
while True:
    drun = diff - dt
    
    t0 = time.time()
    for _ in range(0, 1000):
        GPIO.output(pin_AO, GPIO.LOW)
        time.sleep(0.001)
        GPIO.output(pin_AO, GPIO.HIGH)
        time.sleep(drun)
    t1 = time.time();
    # calculate the deviation from ideal value 12.5 ms.
    dt = (t1 - t0) / 1000 - 0.0115 - 0.001
    print(dt)

The idea is to adjust timing based on average of last 1000 pulses.

The output of the arduino due is printed to Serial and looks like (values in μs):

...
12508
12509
12509
12514
12513
12516
12513
12509
12510
12512
12510
12515
12510
12515
12514
12515
12508
12512
12508
12514
12514
...

Grouping the output values into 10 μs slots and calculating the the number of events per group yields this bar chart. There are prox 27.000 events included the chart.

12-5ms

X-axis is slot times in μs; y-axis is count of events in the slot.

The printout of the script is (shortened)

9.77396965027e-06
0.000155053138733
1.0488986969e-05
0.000154378175735
1.00150108337e-05
0.000154267787933
9.80496406555e-06
0.000156002044678
8.39877128601e-06
0.000155970096588

This demonstrates some oscillation. Values like 0.00015 and ‘something- e-05’ are repeating. The two peaks in the diagram reflect this behavior.

Summary: precise timing with loops in python is accurate only to prox 0.2 ms.

rotary encoder performance

Rotary encoders produce two waveforms, shifted by 90 degrees, which allow to decode position and direction of movements.

The two waveforms look as given in the chart. Only one direction is shown.

encoder

In raspberry pi forum, someone asked for the performance of a python program to decode these signals.

The code used to validate python program is

import RPi.GPIO as GPIO
import time

GPIO.setmode (GPIO.BCM)
GPIO.setwarnings(False)
pin_A = 22   
pin_B = 27

Encoder_Count = 0      # Encoder Count variable
   
def do_Encoder(channel):
    global Encoder_Count
    if GPIO.input(pin_B) == 1:
        Encoder_Count += 1
    else:
        Encoder_Count -= 1

GPIO.setup (pin_A, GPIO.IN, pull_up_down=GPIO.PUD_UP)         # pin input pullup
GPIO.setup (pin_B, GPIO.IN, pull_up_down=GPIO.PUD_UP)         # pin input pullup

GPIO.add_event_detect (pin_A, GPIO.FALLING, callback=do_Encoder)   # Enable interrupt

lastCount = 0
t0 = time.time()
while(1):
    t0 += 20
    time.sleep(t0 - time.time())
    print ("{e:d} diff={d:d}".format(e=Encoder_Count, d = Encoder_Count-lastCount))
    lastCount = Encoder_Count

As a driver for these signals I use an arduino DUE.

The results for the python code on a Raspberry Pi 3 are

freq       exp     measure
   10Hz    200         200
  100Hz   2000        2000
  500Hz  10000       10000  +-2
 1000Hz  20000       20000 +-25
 2000Hz  40000       40000 -40
 3000Hz  60000       60000 -660
 5000Hz 100000     -100000 +-20, but received negative values, so python too slow to get matching level

Up to 3.000 pulses, the results are quite reliable. For a 1.000RPR encoder, these are 3 rotations per second.

Usually I recommend to use an atmel328-processor as a slave processor to handle time sensitive operations. With an arduino UNO, 16MHz, the results are precise up to 15.000Hz.
There is no complete arduino needed. A bare atmel328, clocked from GPIO4, using level shifters for clock and serial line, would be a cost effective solution.
The sketch uses interrupts for the event inputs:

const byte ip2 = 2;
const byte ip3 = 3;
volatile long counter = 0;

void ip2Int() {
  byte iip2 = digitalRead(ip2);
  byte iip3 = digitalRead(ip3);
  if ( iip2 == 1 ) {
    if ( iip3 == 1 ) {
      counter --;
    }
    else
    {
      counter ++;
    }
  }
  else // ip2 == 0
  {
    if ( iip3 == 1 ) {
      counter ++;
    }
    else
    {
      counter --;
    }
  }
}

void ip3Int() {
  byte iip2 = digitalRead(ip2);
  byte iip3 = digitalRead(ip3);
  if ( iip2 == 1 ) {
    if ( iip3 == 1 ) {
      counter ++;
    }
    else
    {
      counter --;
    }
  }
  else // ip2 == 0
  {
    if ( iip3 == 1 ) {
      counter --;
    }
    else
    {
      counter ++;
    }
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("encoder");

  pinMode(ip2, INPUT);
  pinMode(ip3, INPUT);

  pinMode( 13, OUTPUT);
  pinMode(pin_sync, INPUT );
  
  attachInterrupt(digitalPinToInterrupt(ip2), ip2Int, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ip3), ip3Int, CHANGE);
}

void loop() {
  long c0 = 0;
  noInterrupts();
  c0 = counter;
  interrupts();

  Serial.println(c0);
  delay(10000);
}

This code runs up to 15.000Hz, at 20.000 Hz it fails.

An obvious optimization is to change the interrupt routine and avoid the digitalRead-function for the atmel328-processor.

void ip2Int() {
  uint8_t pd = 0b00001100 & PORTD;  // pins 2,3 are PD2, PD3
  if ( pd == 0b00001000 || pd == 0b00000100 ) {
    counter ++;
  }
  else {
    counter --;
  }
}

void ip3Int() {
  uint8_t pd = 0b00001100 & PORTD;
  if ( pd == 0b00001100 || pd == 0b00000000 ) {
    counter --;
  }
  else {
    counter ++;
  }
}

This code is much faster and update rates till 50.000Hz work perfect.

With handcrafted assembler code for the interrupt-routine I would expect to extend performance even more.

Last option investigated was an arduino feather M0 board running at 48MHz.
This produces good results up to 30.000Hz, at 40.000Hz there are failures. With the results from optimized atmel328-code, there is quite a lot of optimization expected.

For the fast running signals, timing accuracy is crucial. To work around this problem, a sync pattern was used: an additional sync signal indicated start and end of measurement from generator to measurement device. Pulse sequence was 10 sec in each case.

encoder_sync

For faster signals, either optimized assembler code needs to be used, or a hardware solution with a FPGA.

Updated 2017-02-19: optimized atmel code.

scratchClient singleton reworked

scratchClient needs to run alone, as a ‘singleton’, on one computer. The reason is that concurrent access to GPIO, SPI or other resources could result in unpredictable results.

First approach was to use a pid-file with some logic. At startup, scratchClient writes its own process identification ‘pid’ into a file scratchClient.pid. When another scratchClient is started, it looks for existing pid-file, doublechecks if a process with the recorded pid is running. In this situation, the new started process stopped.

In practice, this situation arised quite often when kids started scratchClient with wrong config file and simply closed the terminal. On a fresh start in a new terminal, the ‘old’ pid file was still existing or even the previous process was still running somewhere. These situations asked too much linux knowledge from kids and teachers and often resulted in unnecessary reboots of the computers.

A better solution is that a new start of scratchClient causes previously started processes to stop. This should work inside one computer and work independent from user permissions on the process: a new scratchClient started by ‘pi’ should be able to stop a previously started process by ‘root’.

The new singleton mechanism uses a socket to achieve this behavior. On startup of scratchClient, socket 42003 is opened as a TCPServer and listens to commands.
A new started process will get errors on opening same socket as a listener, switches to client mode and issues a shutdown command to the server. Next step is then to open TCPServer on this socket.

The video shows this effect with two terminal sessions.

In upper window, scratchClient is started. Another start in lower window causes the first process to stop.

This behavior now is default for scratchClient.

The command line allows to select the singleton mechanics:

-singletonPID        when multiple instances are running, report other instance and 
                     terminate
-singletonIPC        when multiple instances are running, terminate other instance
                     used port 42003 (default from 2017-02-14)
-singletonNONE       no singleton policy applied. For debug only

Summary: new singleton mechanism introduced in scratchClient since Feb 2017.

 

.scratch.ini

Scratch 1.4 on raspberry pi has some possibilities to tweak the behavior.
This is done with a properties file ‘.scratch.ini’ which is usually located in /home/pi.
Note the dot in front of the file name, which makes it a hidden file for linux.
You need to enable the ‘hidden file’-feature in filemanager to see these files.

fontscale=1.2
Change the default size of fonts used for all the texts.
Values are floating point numbers, good values are [0.7 .. 1.5]

remoteconnectiondialog=0
Prevents the little sensor block dialogue to tell the using that remote sensing has been tuned on from popping up, useful for many projects
Values are [0, 1]

gpioserver = 0
Disables the gpioserver, 1 enables it, 2 makes it be on by default
Values are [0, 1, 2]
Please note that with the scratch start script, the ‘pigpiod’-code is started independent from this setting. On pi A, this takes considerable cpu power with 15-20%.

presentation= 0
Will open Scratch in presentation mode; ‘1’ will open Scratch in presentation mode
Values are [0, 1]

language=en
Some language descriptor fitting the two-letter ISO 639-1 standard
For values see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes

Values I never tested:

home = directory for home
visibledrives = a list of drive names (Windows) or root directories (Mac) to add to the file dialogues.
share = 0 or non-0. 0 means do not allow sharing of project files
proxyserver = IP for a server
proxyport = port number for the server
renderplugin = 0 or non-0. 0 will try to force not using the PAngoCairo font renderer and likely make things ugly.

Post is based on https://www.raspberrypi.org/forums/viewtopic.php?p=1031498#p1031498