Using a 30 year old laptop in 2021:
As the MC400 has no native networking capability we can’t send email directly from the machine itself. But, if you attach to another computer that can send email via the serial link(s) then it becomes possible. I could have just used the terminal and sent email directly from the Raspberry Pi command line but I wanted something that felt more like an actual email client running on the MC itself. This solution uses msmtp on the Pi to send email so obviously it requires it to be installed and configured as a prerequisite. Emails will appear to come from the account that is configured within the msmtp settings.
I created an OPL program that asks the user for the “To:” address, “Subject:” and message body. The program creates an RFC5322 compliant file with all this information in and writes it to
REM::C:\EMAIL\INBOX\EMAIL.TXT directly on the Pi’s filesystem. This is possible as I have the DOS “
MCLINK.EXE” running on the Pi in the dosbos-x emulator, but obviously this relies on the Link app running on the MC400 too so the first thing the OPL does is check to see that it can see the Pi’s filesystem. The dosbox-x config uses the USB-serial adapter
COM1 and mounts
The OPL also implements “Bring” functionality – basically a one shot copy/paste: highlight some text in another application e.g. text processor then switch back to
EMAIL.OPO and “bring” the text. This is based on some old OPL code from 1995 that utilises system calls (credit: Colly Myers, David Wood and Tom Dolbilin).
I found a generic email icon on the web, resized it to 32 x 32 and converted it to PCX format using the GIMP, then converted it to the Psion .PIC format using Psion’s own DOS tool
WSPCX.EXE. Renaming in to .ICN allows the translated .OPO file to be installed in the MC400 system with this icon.
A script on the Pi watches
/home/pi/mc400/EMAIL/INBOX using the inotifywait command (part of the inotify suite) and if it detects that the file EMAIL.TXT has been created it then runs some further commands to pipe the RFC5322 compliant text through the msmstp mail sending prog.
EMAIL.TXT file from the
/home/pi/mc400/EMAIL/INBOX directory is moved to
/home/pi/mc400/EMAIL/OUTBOX with the current date/time added to the filename.
The shell script to process the emails is installed as services on the Pi, the service unit file is included below too. Save this file as
Then issue the commands
sudo systemctl daemon-reload
sudo systemctl enable mc400_email.service
sudo systemctl start mc400_email.service
sudo systemctl status mc400_email.service
(all code also available on GitHub)
OPL code –
REM REM Simple Email sending from the Psion MC400 REM REM - needs appropriate handling of files written REM to an attached device to actually send email! REM REM v1.0.01 19/09/2021 @zedstarr REM PROC Mail: LOCAL ret%,fName$(128),text$(255),body$(127),subj$(64),dest$(64),version$(6) LOCAL address%,handle%,mode%,k%,len% version$="1.0.01" REM Check "The Link" is connected ONERR borked:: k%=EXIST("REM::C:\MCLINK.EXE") borked:: ONERR OFF IF NOT k% PRINT PRINT " PSION MC400 Simple Email Sender v"+version$+" (c) 2021 @zedstarr" PRINT PRINT " ERROR: Can't connect to PsiBridge" PRINT " Check serial connection." GET ELSE fName$="REM::C:\EMAIL\INBOX\EMAIL.TXT" SCREEN 80,12 WHILE NOT ERR PRINT PRINT " PSION MC400 Simple Email Sender v"+version$+" (c) 2021 @zedstarr" PRINT " (on a blank line - <ESC> to quit, L<Enter> to Bring data)" PRINT PRINT " To:>", TRAP EDIT dest$ IF ERR=-114 RETURN ENDIF IF dest$="L" dest$=bring$: CLS ELSE BREAK ENDIF ENDWH WHILE NOT ERR PRINT PRINT " Subject:>", TRAP EDIT subj$ IF ERR=-114 RETURN ENDIF IF subj$="L" subj$=bring$: CLS PRINT PRINT " PSION MC400 Simple Email Sender v"+version$+" (c) 2021 @zedstarr" PRINT " (on a blank line - <ESC> to quit, L<Enter> to Bring data)" PRINT PRINT " To:>",dest$ ELSE BREAK ENDIF ENDWH WHILE NOT ERR PRINT PRINT " Message:>", TRAP EDIT body$ IF ERR=-114 RETURN ENDIF IF body$="L" body$=bring$: CLS PRINT PRINT " PSION MC400 Simple Email Sender v"+version$+" (c) 2021 @zedstarr" PRINT " (on a blank line - <ESC> to quit, L<Enter> to Bring data)" PRINT PRINT " To:>",dest$ PRINT PRINT " Subject:>",subj$ ELSE BREAK ENDIF ENDWH mode% = $0001 OR $0020 OR $0100 ret%=IOOPEN(handle%,fName$,mode%) IF ret% Showerr:(ret%) RETURN ENDIF text$="To: "+dest$+"\nFrom: MC400 SIBO <firstname.lastname@example.org>\nSubject: "+subj$+"\n\n"+body$+"\n" address%=ADDR(text$)+1 : len%=len(text$) ret%=IOWRITE(handle%,address%,len%) IF ret% Showerr:(ret%) RETURN ENDIF ret%=IOCLOSE(handle%) IF ret% Showerr:(ret%) RETURN ENDIF ENDIF ENDP PROC Showerr:(val%) PRINT "Error: ",val%,err$(val%) GET ENDP rem The following code is based on "LPC.OPL" rem (Original code written by Colly Myers) rem (Translated into OPL by Tom Dolbilin) rem (adapted for the MC with help from David Wood at Psion) PROC Bring$: global srvPid% rem PID of Link server global buf$(255) rem Buffer to receive data global ioSem% rem The I/O semaphore handle local wsrvPid% rem PID of Window server local fmt& rem Message buffer local pfmt% rem Pointer to message buffer local name$(15),form%(3),n%,ret%,ret$(255) local w%(2) rem --- Get Shell's pid rem Note: on the MC, SYS$SHLL.IMG holds Link server data rem on the Series 3/3a SYS$WSRV.IMG holds Link Server data rem change name$ to "SYS$WSRV.*" for Series 3/3a name$="SYS$SHLL.*" wsrvPid%=call($188,addr(name$)+1) rem --- Get the handle of our I/O semaphore call($78b,0,2,0,(call($88) and $fff)+33,addr(ioSem%)) rem --- Get the Link Server's pid pfmt%=addr(fmt&) srvPid%=call($683,wsrvPid%,4,0,addr(pfmt%)) rem MessSendReceiveWithWait if srvPid%<0 showerr:(srvPid%) return "" elseif srvPid%=0 at 2,11 print "Nothing to bring" pause 40 at 2,11 print " " return "" endif rem --- Get the name of the Link Server call($a88,srvPid%,0,0,0,addr(name$)+1) pokeb addr(name$),call($b9,0,0,0,0,addr(name$)+1) w%(1)=$2 rem --- Request a rendering ret%=talk%:(w%(1),0) if ret%=0 rem --- Server prepared to render data while 1 ret%=talk%:(addr(buf$)+1,255) : rem Get the server data if ret%<0 if ret%=-36 rem End of file ret%=0 rem Avoid an error print endif break endif rem --- Display the data received pokeb addr(buf$),ret% ret$=peek$(addr(buf$)) endwh endif if ret%<0 showerr:(ret%) endif return ret$ ENDP PROC Talk%:(a%,b%) local arg1%,arg2% rem Message parameters - keep in order local stat%,count%,ret% arg1%=a% arg2%=b% call($0583,srvPid%,$21,0,addr(arg1%),addr(stat%)) rem MessSendReceiveAsynchronous while 1 iowait if stat%<>-46 : rem The result has been returned ret%=stat% break else count%=count%+1 : rem Count up all other signals endif endwh rem --- Put back spare signals if any while count% iosignal rem call($382,ioSem%,0,0,0,0) rem SemSignalOnce count%=count%-1 endwh return ret% ENDP
Shell script –
#!/bin/bash /usr/bin/inotifywait -m /home/pi/mc400/EMAIL/INBOX/ -e create | while read dir action file; do # echo "The file '$file' appeared in directory '$dir' via '$action'" # do something with the file if [[ "$file" == "EMAIL.TXT" ]] ; then # echo "`date` Emailing..." sleep 3 /usr/bin/printf "`cat /home/pi/mc400/EMAIL/INBOX/EMAIL.TXT`\n\n-- \nSent from a Psion MC400\nhttps://bit.ly/MC400cxf\n" | msmtp `cat /home/pi/mc400/EMAIL/INBOX/EMAIL.TXT | cut -c5- | cut -f1 -d\\` sleep 2 mv /home/pi/mc400/EMAIL/INBOX/EMAIL.TXT /home/pi/mc400/EMAIL/OUTBOX/EMAIL`date +"%Y-%m-%d-%H%M%S"`.TXT fi done
[Unit] Description=PSION MC400 email helper [Service] Restart=on-failure RestartSec=10 ExecStart=/home/pi/mc400/scripts/mc400_watch_email.sh WorkingDirectory=/home/pi/mc400 SyslogIdentifier=mc400 User=pi Group=pi StandardOutput=null [Install] WantedBy=multi-user.target