Serial IO on Raspberry Pi with BBCSDL

Discussions related to network, internet and socket programming; also to serial, parallel, Bluetooth, USB etc.
SteveF
Posts: 4
Joined: Tue 18 Dec 2018, 20:23

Serial IO on Raspberry Pi with BBCSDL

Post by SteveF »

I have been working on serial communications on Raspberry Pi using BBCSDL and have made some progress. Seeing some other questions about this I wanted to post my results for others but am not certain if content and formatting are correct. I am looking for some feedback and will try to get this into shape. Thanks for any help or suggestions.

The hardware setup is very simple. I am using an FTDI USB to serial converter on the Raspberry Pi side since that is by far the easiest. I am using a model 3 B+ and the FTDI converter was immediately recognized as ttyUSB0. I think that would be the case on any Pi except maybe Pi Zero. On the PC side the serial connection is plugged into a serial port.

I am posting 4 short test programs. There are 2 pairs, each with a PC side and a Raspberry Pi side. I tried to attach the programs but received a wrong file extension message so I am just embedding them in this post since they are so short.

First pair: PCsertest1.bas and RPisertest1.bas
RPisertest1.bas: A basic file, runs under BBCSDL. Opens the port and an output file to log results. Sends all possible bytes from 0 to 255 sequentially and then reads same byte echoed back by PC program and logs it. Exits after sending 0xFF. The program is located in /home/pi/Desktop/BBC/examples on my setup. BBCSDL is located in /home/pi/Desktop/BBC.
There are some statements in the program just to test various commands such as STR$, GET$, etc. As you can see the program as structured does not rely on being able to read the number of characters in the input buffer.

REM a test of serial port on Raspberry Pi 11/6/2018 SF
REM This program sends every ASCII character from 0 to 255, prints and logs the echoed characters and then quits

OSCLI "ldattach " + "-1 -8 -n -s 9600 0 /dev/ttyUSB0 " + ";"
OSCLI "stty " + "raw " + ";"

1 serial% = OPENUP("/dev/ttyUSB0.")
2 fileout% = OPENOUT("/home/pi/Desktop/BBC/examples/sertestoutpi.txt")

3 t% = 0
testbuf% = 0
fileout$ = STR$(fileout%)
serial$ = STR$(serial%)
4 PRINT#fileout%,fileout$," ",serial$," "
5 BPUT#fileout%,"Transmitting and Receiving Data"+CHR$(13)+CHR$(10)

6 FOR i% = 0 TO 255
7 BPUT#serial%,i%
10 t% = 0
t% = BGET#serial%
t$ = STR$(t%)
15 PRINT t$;" ";
16 PRINT#fileout%, t$
17 BPUT#fileout%, 32
20 NEXT i%

21 CLOSE#serial%
22 CLOSE#fileout%
23 END

PCsertest1.bas: This is the PC end of the test running under BBC BASIC V6.02a. It opens the port and a log file and then waits for a character. The timeout delay is set very long so that you can easily start PCsertest1.bas and then start the test program on the Pi. When the last byte (0xFF) is received the program terminates.

REM THIS IS THE PC END OF THE TEST PROGRAM FOR BBC BASIC SERIAL COMMS ON RASPBERRY PI
REM 11/6/2018 SF
serial% = OPENUP("COM1: baud=9600 parity=N data=8 stop=1")
fileout% = OPENOUT("C:\Sensigent\software\sertestoutpc.txt")
t% = 0
timeout% = 0
serial$ = STR$(serial%)
fileout$ = STR$(fileout%)
PRINT#fileout%,serial$,fileout$
BPUT#fileout%, "Receiving and Transmitting at PC"+CHR$(13)+CHR$(10)

10 testbuf = EXT#serial%
IF testbuf > 0 THEN
t% = BGET#serial%
PRINT t%;" ";
t$ = STR$(t%)
PRINT#fileout%, t$
BPUT#fileout%, 32
BPUT#serial%,t%
timeout% = 0
ENDIF
timeout% = timeout% + 1
IF timeout% > 3000000 THEN GOTO 20
IF t% <> 255 THEN GOTO 10

20 PRINT "Done"
CLOSE#serial%
CLOSE#fileout%
END

Second pair: PCsertest2.bas and RPisertest2.bas
PCsertest2.bas: Also running under BBC BASIC V6.02a. Since the first set of test already demonstrated all ASCII characters can be sent/received the PC side just sends the number of characters commanded by the user.

REM PC side for a test of serial port on Raspberry Pi
REM This one sends a specified number of characters each time through the loop
REM User inputs the number of characters to send. Entering 0 terminates program

10 serial% = OPENUP("COM1: baud=9600 parity=N data=8 stop=1")
PRINT "channel # ", serial%, " handle ", @hfile%(serial%)

20 INPUT k%
c% = 41
FOR i% = 0 TO k%-1
BPUT#serial%,c%
NEXT i%

IF k% > 0 THEN GOTO 20
CLOSE#serial%
END

RPisertest2.bas:
The Raspberry Pi side either checks and prints the number of characters in the buffer, or checks and, if non-zero, gets the characters in the buffer and prints them. This test program demonstrates the use of system calls to find the file designator and to determine the number of characters waiting in the serial input buffer.

REM THIS IS THE Pi END OF THE TEST PROGRAM FOR BBC BASIC SERIAL COMMS ON RASPBERRY PI
REM 11/27/2018 SF

REM Command line to open port, set parameters, and set line discipline to raw (no changes to characters)
OSCLI "ldattach " + "-1 -8 -n -s 9600 0 /dev/ttyUSB0 " + ";"
OSCLI "stty " + "raw " + ";"

REM Open the port and find the file designator used to check the input buffer for characters
serial% = OPENUP("/dev/ttyUSB0.")
SYS "fileno", @hfile%(serial%)!28 TO fildes%

REM "magic number" that tells IOCtl what you actually want to do, in this case check the serial buffer
FIONREAD = &541B

10 PRINT " "
PRINT "Channel # = "; serial%;" File Des = "; fildes%

tb% = 0
t% = 99

PRINT " "
PRINT " c = check buffer size , g = check buffer and get all chars, q = quit "

INPUT k$

IF k$ = "q" THEN GOTO 80

REM Use ioctl from command line to find number of characters in serial buffer
SYS "ioctl", fildes%, FIONREAD, ^tb% TO t%

PRINT "tb%=";tb%;" t%=";t%

IF k$="c" OR tb%=0 THEN GOTO 10

FOR i% = 1 TO tb%
c% = BGET#serial%
PRINT " Character # ";i%;" is ";c%
NEXT i%

GOTO 10

80 PRINT "Done"
CLOSE#serial%
END

That's it. I will check again in a few days to see if there is any feedback. Thanks.

guest
Posts: 272
Joined: Mon 02 Apr 2018, 09:12

Re: Serial IO on Raspberry Pi with BBCSDL

Post by guest »

SteveF wrote:
Tue 18 Dec 2018, 21:17
That's it. I will check again in a few days to see if there is any feedback. Thanks.
Listing the programs in [code] [/code] tags would have made them easier to read (and easier to copy/paste); one of the advantages of the forum is the ability to include formatted code listings in posts. Perhaps the admin might consider adding them to your message (if you don't).

There are examples of some pretty poor coding practices in the programs you listed: looping using 100% CPU time unnecessarily, implementing a timeout not actually based on time, and using GOTOs. I'm sure you are well aware how undesirable they are, but since these are simply demo programs illustrating a principle they may perhaps be forgiven. Just so long as you don't do the same things in a 'real' application! ;)

Zaphod
Posts: 78
Joined: Sat 23 Jun 2018, 15:51

Re: Serial IO on Raspberry Pi with BBCSDL

Post by Zaphod »

To expand on Richard's comments it looks like you are a newcomer to BBC BASIC and many of the programming methods that are available to you.
So here are a few hints.
The 'Code' insert command is the 5th icon on the toolbar. That makes the code look like this below if you just paste your code between the tags.

Code: Select all

      REM THIS IS THE Pi END OF THE TEST PROGRAM FOR BBC BASIC SERIAL COMMS ON RASPBERRY PI
      REM 11/27/2018 SF

      REM Command line to open port, set parameters, and set line discipline to raw (no changes to characters)
      OSCLI "ldattach " + "-1 -8 -n -s 9600 0 /dev/ttyUSB0 " + ";"
      OSCLI "stty " + "raw " + ";"

      REM Open the port and find the file designator used to check the input buffer for characters
      serial% = OPENUP("/dev/ttyUSB0.")
      SYS "fileno", @hfile%(serial%)!28 TO fildes%

      REM "magic number" that tells IOCtl what you actually want to do, in this case check the serial buffer
      FIONREAD = &541B

      REPEAT
        PRINT " "
        PRINT "Channel # = "; serial%;" File Des = "; fildes%
        tb% = 0
        t% = 99
        PRINT " "
        PRINT " c = check buffer size , g = check buffer and get all chars, q = quit "
        INPUT k$
        IF k$ = "q" THEN
          PRINT "Done"
          CLOSE#serial%
          END
        ENDIF
        REM Use ioctl from command line to find number of characters in serial buffer
        SYS "ioctl", fildes%, FIONREAD, ^tb% TO t%
        PRINT "tb%=";tb%;" t%=";t%
        IF k$<>"c" AND tb%<>0 THEN
          FOR i% = 1 TO tb%
            c% = BGET#serial%
            PRINT " Character # ";i%;" is ";c%
          NEXT i%
        ENDIF
        WAIT 1
      UNTIL FALSE

      PRINT "Escape"
      CLOSE#serial%
      END
Then there is the code itself. I cannot make any comment about its function as I don't have the hardware to run it on but I will make a couple of suggestions about programming style. We have all written code like that and years later we look at it and cringe, it is part of the learning process.
I have taken your program and I think have kept the same logic but now using a REPEAT to do the looping. And so that it does not zip around that loop at silly speed wasting CPU cycles and power I have added a WAIT statement. This hands control back to other threads or just sits idle for a wee while.
The arguments against GOTO are many but particularly it can lead to difficult to read code and makes it easy to make logical mistakes. Structured programming helps keep the logic more linear as it were. The indentation in the IDE also helps you see the loops and logical steps that GOTO obscures.
It does take a little thought to start with but quickly becomes second nature. For instance if you ever see a need to jump back to the start then a WHILE or REPEAT loop will probably do that better and more efficiently. If you ever see yourself using IF THEN GOTO to jump over some code then you can just invert the logic of the test make it into a multi-lined IF THEN and put the ENDIF where you wanted to jump to. Richard's additions to BBC BASIC allow you to EXIT from the structures if need be, and then the code 'falls through' to the code beyond that structure.
In this little code snippet the input of a "g" seems to be ignored. I imagine this is just 'work in progress'.
I think a little time looking at the example programs will be helpful with regards to the points above and also look at what happens when your code is run by people that will try all the silly inputs that you never thought of. Things like validating inputs and checking and reporting error conditions will be found on most of Richard's examples.
I hope this helps and welcome to the fold...

Z

SteveF
Posts: 4
Joined: Tue 18 Dec 2018, 20:23

Re: Serial IO on Raspberry Pi with BBCSDL

Post by SteveF »

This is a continuation of my post from December 2018. The following example code will simulate sending a command from the Raspberry Pi and receiving a response from a device attached to the serial port. To keep the hardware simple the program only requires that the Rx and Tx pins be connected to each other. The pins can be connected either at a "logic" voltage level (typically 0 to 3.3 on the Pi) or the RS-232 level (typically about -5 and +5 volts these days). The program can also be used with a USB to serial adapter although you will need to substitute the proper ID for the port for "ttyAMA0" (usually ttyUSB0).

Code: Select all


   10 REM Initialize
   20 fildes% = 0
   30 FIONREAD = &541B
   40 commopen% = 0
   50 DIM tb{n%}
   60 tb.n% = 0
   70 tb% = 0
   80 k% = 1
   90 SERTIMEOUT = 100
  100
  110 REM Open serial port
  120 OSCLI "*/home/pi/software/frag.sh"
  130 OSCLI "ldattach " + "-1 -8 -n -s 9600 0 /dev/ttyAMA0 " + ";"
  140 OSCLI "stty " + "-F /dev/ttyAMA0 " + "9600 " + "raw" + ";"
  150 comm% = OPENUP("/dev/ttyAMA0.")
  160 SYS "fileno", @hfile%(comm%)!28 TO fildes%
  170 PRINT "Opened comm port on channel ", comm%, fildes%
  180 REM Clear any garbage
  190 WHILE FNLOB%
  200   byte& = BGET#comm%
  210 ENDWHILE
  220
  230 REM Send a variable number of characters from 1 to 12 terminated by new line
  240 WHILE k% < 13
  250   serout$ = ""
  260   FOR i% = 1 TO k%
  270     byte& = 64 + i%
  280     BPUT#comm%,CHR$(byte&);
  290     serout$ = serout$ + CHR$(byte&)
  300   NEXT i%
  310   BPUT#comm%,CHR$(10);
  320
  330   REM Receive the echoed characters (serial port is just looped back)
  340   serin$ = ""
  350   timeout = TIME + SERTIMEOUT
  360   REPEAT
  370     WAIT 0
  380     lob% = FNLOB%
  390     WHILE lob%
  400       byte& = BGET#comm%
  410       IF byte& <> 10 serin$ += CHR$(byte&)
  420       lob% -= 1
  430     ENDWHILE
  440   UNTIL byte& = 10 OR TIME > timeout
  450   IF byte& <> 10 THEN PRINT "Timeout!"
  460
  470   PRINT serout$ " " serin$
  480   PRINT "k%=";k%;" LEN(serout$)=";LEN(serout$);" LEN(serin$)=";LEN(serin$)
  490   IF serout$ <> serin$ THEN PROCrefreshPort
  500   IF k% = 11 THEN PROCrefreshPort : REM Simulates a serial error o test closing and re-opening port
  510   k%+=1
  520   INPUT k$ : REM If you want to look at individual results
  530 ENDWHILE
  540
  550 CLOSE#comm%
  560 PRINT "DONE , type enter to exit "
  570 INPUT k$
  580 END
  590
  600 REM Check number of characters in input buffer
  610 DEF FNLOB%
  620 SYS "ioctl", fildes%, FIONREAD, tb{} TO commopen%
  630 IF commopen% <> 0 THEN PRINT "FNLOB commopen%=";commopen%
  640 tb% = tb.n%
  650 =tb%
  660
  670 REM Close and re-open the serial port if something is not right
  680 DEF PROCrefreshPort
  690 LOCAL test$
  700 WAIT 10
  710 CLOSE#comm%
  720 comm% = OPENUP("/dev/ttyAMA0.")
  730 SYS "fileno", @hfile%(comm%)!28 TO fildes%
  740 WHILE FNLOB%
  750   byte& = BGET#comm%
  760 ENDWHILE
  770 PRINT "Closed and re-opened comm port on channel ", comm%, fildes%
  780 WAIT 10
  790 ENDPROC

The only other code required is a very short script call "frag.sh" which stops the ldattach process used to configure the serial port if it is running when the program starts.

Code: Select all


#!/bin/bash
kill -9 $(pidof ldattach)
echo "frag"

To use the program create a directory called "software" under your "Pi" home directory and place comtst.bas and frag.sh in the directory. Connect the Rx and Tx pins together at a convenient place then load and run the program with BBCSDL. The program has been tested with BBCSDL v1.17a.

Thanks to Richard Russell for his invaluable assistance with this test program. Please leave any comments or questions as a reply.

SteveF

RichardRussell
Site Admin
Posts: 593
Joined: Tue 15 Oct 2019, 09:10

Re: Serial IO on Raspberry Pi with BBCSDL

Post by RichardRussell »

SteveF wrote:
Sat 14 Nov 2020, 01:04
Please leave any comments or questions as a reply.
As discussed elsewhere, I am not enthusiastic about this code:

Code: Select all

  190 WHILE FNLOB%
  200   byte& = BGET#comm%
  210 ENDWHILE
The FNLOB% function returns not a Boolean (as its use above would imply) but the number of bytes 'waiting' in the serial input buffer. It's also quite a 'heavyweight' function so not one that it is desirable to call more often than necessary. So I would rather do this to 'flush' the buffer:

Code: Select all

      lob% = FNLOB%
      WHILE lob%
        byte& = BGET#comm%
        lob% -= 1
      ENDWHILE
Hardly worth mentioning, but this line seems to be entirely spurious:

Code: Select all

  690 LOCAL test$
Not only is the variable test$ not used anywhere, it's the only LOCAL statement in the program!
I am suffering from 'cognitive decline' and depression. If you have a comment about the style or tone of this message please report it to the moderators by clicking the exclamation mark icon, rather than complaining on the public forum.