Sending simple emails from the Psion MC400

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 /dev/ttyUSB1 as COM1 and mounts /home/pi/mc400 as C:\

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).

emailing some emailings…

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.

Finally, the 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
/lib/systemd/system/mc400_email.service
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 – MAIL.OPL:

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 <mc400.sibo@gmail.com>\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 – /home/pi/mc400/scripts/mc400_watch_email.sh:

#!/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

mc400_email.service file:

[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
%d bloggers like this: