
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.

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. I had to use the linux pv
tool to slow down copying files straight into the INBOX folder when testing, as just plain copying from the linux command line creates/writes/closes the file too fast for the delays I’d put around the inotify watchers and the scripts never get triggered ;-D
this command sets a limit of 1024 bytes/s:
pv -L 1024 SCREEN.PIC > mc400/IMAGE/INBOX/SCREEN.PIC
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 commandssudo systemctl daemon-reload
sudo systemctl enable mc400_image.service
sudo systemctl start mc400_image.service
sudo systemctl status mc400_image.service
(all code also available on GitHub)
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
mc400_image.service
file:
[Unit]
Description=PSION MC400 image conversion helper
[Service]
Restart=on-failure
RestartSec=10
ExecStart=/home/pi/mc400/scripts/mc400_watch_image.sh
WorkingDirectory=/home/pi/mc400
SyslogIdentifier=mc400
User=pi
Group=pi
StandardOutput=null
[Install]
WantedBy=multi-user.target