Saving, converting & cloud-syncing screenshots from the Psion MC400

Using a 30 year old laptop in 2021:

While developing the OPL code and shell scripts that handle tweeting and emailing directly from the MC400 I wanted to collect screenshots of the code in action on the MC. The SIBO/EPOC16 machines all have the built-in feature where the key-combination Shift-Ctrl-Psion-S stores a screenshot in a file called SCREEN.PIC in the root folder of the internal disk (e.g. LOC::M:\SCREEN.PIC). This file is in the Psion .PIC/.ICN format and a DOS-based program “WSPCX.EXE” exists to convert to .PCX format.

The screendump can be manually copied to the Pi, or moved automatically via an OPL prog if required. Once on the Pi it is automatically converted to .PNG format, synced with Google Drive and a bitly.com shortened link to file is written into a TXT file so it can be copied/pasted into tweets or emails that are sent from the MC. Cloud sync to Google is performed by rclone so obviously that has to be installed and set up to be able to sync/link the file and to use the Bitly.com link shortening API you need to register an account and request an Access Token.

SCRN_SAV.OPL – waits for screen dump file to be created and then automatically moves it to the Pi for conversion

A script on the Pi watches /home/pi/mc400/IMAGE/INBOX using the inotifywait command (part of the inotify suite) and if it detects the file SCREEN.PIC has been created & closed it then runs some further commands to convert, sync and store the shortened link to the file. Because the screenshots are large (32020 bytes – “large” compared to the available 256k RAM/storage and “large” when transferred over the 19200 baud serial link! The transfer takes approximately 25 seconds.) the first inotifywait when the file is created starts another inotifywait which waits until it is closed.

Once the file is closed it is moved to another directory where a headless dosbox-x instance runs the Psion WSPCX.EXE DOS program to convert to .PCX format. The dosbox-x emulator is configured to exit once the conversion is done. Next, convert (part of the ImageMagick suite) converts to .PNG format. Then it’s moved to /home/pi/mc400/IMAGE/OUTBOX with a date/timestamp added to the filename.

rclone is used to sync the file to Google drive and then retrieve the long google URL link to the file.

A shell script accessing the bitly.com API then shortens the long URL to a much shorter http://bit.ly URL and writes this to a .TXT file with the same root as the other picture files…

So, for example “SCREEN.PIC” gets converted/copied to:

 SCREEN2021-09-16-211205.PIC
 SCREEN2021-09-16-211205.PCX
 SCREEN2021-09-16-211205.PNG
 SCREEN2021-09-16-211205.TXT

.PIC original file, .PCX version, .PNG version and the .TXT file that contains the shortened link to the file on Google Drive.

The shell script to process the image is installed as a service on the Pi, the service spec is included below too. Save this file as
/lib/systemd/system/mc400_image.service
Then issue the commands
sudo systemctl daemon-reload
sudo systemctl enable mc400_image.service
sudo systemctl start mc400_image.service
sudo systemctl status mc400_image.service

systemd service status


Shell script –/home/pi/mc400/scripts/mc400_watch_image.sh:

#!/bin/bash
/usr/bin/inotifywait -m /home/pi/mc400/IMAGE/INBOX/ -e create  |
  while read dir action file; do
    
    if [[ "$file" == "SCREEN.PIC" ]] ; then
      /usr/bin/inotifywait -qq /home/pi/mc400/IMAGE/INBOX/SCREEN.PIC -e close_write
      sleep 1
      # ls -la /home/pi/mc400/IMAGE/INBOX/
      mv /home/pi/mc400/IMAGE/INBOX/SCREEN.PIC /home/pi/wspcx/SCREEN.PIC
      SDL_VIDEODRIVER=dummy /usr/bin/dosbox-x -conf /home/pi/wspcx/dosbox-x-0.83.16.conf /home/pi/wspcx/WSPCX.BAT -exit
      sleep 1
      /usr/bin/convert /home/pi/wspcx/SCREEN.PCX /home/pi/wspcx/SCREEN.PNG
      nameroot=SCREEN`date +"%Y-%m-%d-%H%M%S"`
      mv /home/pi/wspcx/SCREEN.PIC /home/pi/mc400/IMAGE/OUTBOX/${nameroot}.PIC
      mv /home/pi/wspcx/SCREEN.PCX /home/pi/mc400/IMAGE/OUTBOX/${nameroot}.PCX
      mv /home/pi/wspcx/SCREEN.PNG /home/pi/mc400/IMAGE/OUTBOX/${nameroot}.PNG
      sleep 1
      /usr/bin/rclone sync /home/pi/mc400/IMAGE/OUTBOX/ gdrive:mc400/IMAGE/OUTBOX/
      /home/pi/mc400/scripts/short.sh `/usr/bin/rclone link gdrive:mc400/IMAGE/OUTBOX/${nameroot}.PNG` > /home/pi/mc400/IMAGE/OUTBOX/${nameroot}.TXT
    fi
  done


shell script to use the bit.ly URL shortener:

#!/usr/bin/env bash
# Bitly Generic access token
Accesstoken=XXXXXXXXXXXXXXXXXXXXXXXXXXX
api=https://api-ssl.bitly.com/v4/shorten
longurl=$1
  if [[ ! $longurl ]]; then
    echo -e "Error URL Missing"
    exit 1
  fi
# Curl request
curl -s -H Authorization:\ $Accesstoken -H Content-Type: -d '{"long_url": "'"$longurl"\"} $api | cut -f3 -d"," | cut -c9- | cut -f1 -d\"


OPL to automatically move SCREEN.PIC to the Pi:

REM
REM Move any LOC::M:\SCREEN.PIC screen dumps
REM to an attached computer for processing
REM
REM v0.0.01 21/09/2021 @zedstarr
REM 

PROC Main:
  local l%
  
  SCREEN 70,12
  
  WHILE 1
    PRINT DATIM$,"Waiting for LOC::M:\SCREEN.PIC..."
    DO
      REM check every 10s
      PAUSE 200
    UNTIL EXIST("LOC::M:\SCREEN.PIC")
    PRINT DATIM$,"Found LOC::M:\SCREEN.PIC"
    REM Wait until dumping is truly finished
    PAUSE 100
    REM If link not connected then re-check every minute
    ONERR borked::
    l%=EXIST("REM::C:\MCLINK.EXE")
    borked::
    ONERR OFF
    IF l%
      PRINT DATIM$,"Copying to REM::C:\IMAGE\INBOX\SCREEN.PIC"
      TRAP COPY "LOC::M:\SCREEN.PIC","REM::C:\IMAGE\INBOX\SCREEN.PIC"
      IF ERR
        Showerr:(ERR)
        GET
        RETURN
      ELSE
        PRINT DATIM$,"Deleting LOC::M:\SCREEN.PIC"
        TRAP DELETE "LOC::M:\SCREEN.PIC"
        IF ERR
          Showerr:(ERR)
          GET
          RETURN
        ENDIF
      ENDIF
    ELSE
      PRINT DATIM$,"Waiting for LINK..."
      PRINT "       Error: ",ERR,ERR$(ERR)
      PAUSE 1000
    ENDIF
    PAUSE 200
  ENDWH
ENDP

PROC Showerr:(val%)
  PRINT "Error: ",val%,err$(val%)
  GET
ENDP

Catching errors when remote filesystem is disconnected…
%d bloggers like this: