Shop OBEX P1 Docs P2 Docs Learn Events
SERIN handling variable length data, a non-blocking form of SERIN — Parallax Forums

SERIN handling variable length data, a non-blocking form of SERIN

Al3Al3 Posts: 6
edited 2013-07-02 12:18 in BASIC Stamp
Hello, I would like to process commands received on the serial port from a PC which could have the following form:

  1. W<data>
    1. a request to store data on some eeprom
  2. R
    1. a request to read all data on some eeprom
  3. WB<addr><value>
    1. a request to store some value at a particular address
  4. S
    1. a request to receive status
Let's look at the first one for example:
"W,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16"
meaning write 1 at address 1, value 2 at address 2 and so on

The send data could be formatted more compactly as the following, ie. a series of 2 bytes representing a hex value.
"W0102030405060708090a0b0c0d0e0f10"
or even a single byte for example
"Wabcdefghijklmnop"
This could be interpreted as write value 65 at 1 and 66 at 2, etc...


I am now using the following for processing the serial port data:

BaudMode CON 16572 '8bit, no parity, inverted


'
[ ReadSerialPort ]
MonitorSerialPort:
DO
' Wait to receive a command from the .NET program
' On the serial port attached to the board
' receive command:
' W 15 255

SERIN 16, BaudMode, [command, DEC eeprom_add, DEC i2cWork ]
' debug to the PC '"cmd=", STR command,
SEROUT 16, BaudMode, [" addr=", DEC eeprom_add, " value=", DEC i2cWork, CR]
temp = i2cWork

' Select on the commmand
SELECT command
CASE "R"
GOSUB ReadCatridge
GOSUB SendData
CASE "W" '
GOSUB WriteCatridge
GOSUB ReadCatridge
GOSUB SendData
CASE "S" '
GOSUB SendStatus
CASE ELSE
GOSUB ErrorNotification
ENDSELECT

PAUSE 1
LOOP
RETURN

'
' 20130628
' Send SW1, SW2 status
' Input: none
SendStatus:
SEROUT 16, BaudMode, ["P4=", DEC IN4]
SEROUT 16, BaudMode, ["P5=", DEC IN5]
RETURN



Problem #1)

How to go about reading and store the 16 values/bytes into an array like the following?

MyData VAR Byte(16)

I am in control of both sides of the communication. I am looking for advice as to what is the best way to handle this, eg. simplify, generalize the command format.

.

The board needs to 1) report status changes to the PC for example buttons being pressed, 2) receive requests from the PC, for example write this data, or change status of this LED, ...
Do I need 2 PIC16F57 controllers to achieve this?


Problem #2)

I am having to pad a command like "R" with dummy parameters like so "R 0 0" in order to fit the current coding


Problem #3)

The SERIN command is blocking, as far as I understand , which means even though the SERIN is inside a loop the chip can't do anything until it completes. For example I would like, while waiting for a request from the PC, to poll periodically the status of a couple of Pins and report their status to the PC.

Is there a form of SERIN which is non blocking?, in other words it peaks at the serial port and if there is no data it terminates.


Your guidance in this is greatly appreciated. Thank you.

Comments

  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-07-01 10:31
    Some questions...is the BASIC Stamp Module just handling these serial requests from the PC? Can the PC send the data in binary form rather than string format or are you using a terminal to send the commands?
  • Al3Al3 Posts: 6
    edited 2013-07-01 11:12
    Some questions...is the BASIC Stamp Module just handling these serial requests from the PC? Can the PC send the data in binary form rather than string format or are you using a terminal to send the commands?

    Hi Chris,
    The BASIC code is downloaded to the controller and when the board is powered it should go into some processing loop.
    1) Notifies PC of status changes, eg. a switches changing state (some pin going 0/1)
    2) Receives request+data from the PC for example one request is to write some values to an eeprom connected to the PIC, during this process it activates (program/standby) LEDs connected to some of its pins.
    Let me know if you need more info.

    I could format the data sent from the C# side in any format you let me know of: I used text (string) because it's more human :)

    The PC side is in C# (via WPF or Windows Forms application)

    I am new to this PIC programming with your BASIC so you are boss as to how I should code.

    One idea is for the PC to continuously send a status command (eg. every 1000ms) this will allow the SERIN to break out and the loop to poll the needed pins to report SW1/SW2 statuses. Occasionally, on PC user intervention it would send a request for doing an activity like mentioned above, write or read some other I2C connected device to the PIC processor.

    Ideally I would've wanted the switches status on the PIC side reported asynchronously, whenever they occurred, but jugging from the "limited" commands and 9600 baud issue I run into this PIC controller is underpowered. So continuously polling from the PC side might work, I have not coded this idea yet.

    Thanks for helping me out.
    -Al
  • Al3Al3 Posts: 6
    edited 2013-07-01 12:16
    You can ignore the "debug statements"
    ' debug to the PC '"cmd=", STR command,
    SEROUT 16, BaudMode, [" addr=", DEC eeprom_add, " value=", DEC i2cWork, CR]

    Because I don't have the BASIC stamp IDE while connected via the USB port I did not know how to debug how the incoming stream was getting parsed except to send it back to the PC :) Let me know if there is a better alternative.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-07-01 12:16
    Typically you'd want to keep the data being sent more human if it is going to and/or coming from a terminal where a human operator needs to be able to read it directly. The reason you want to keep it binary is less conversion and more speed in processing of the serial data by the BASIC Stamp Module. As for reporting switch changes back to the PC, the way you have it now you cannot get these status updates without sending a request for it. The reason is that your code will sit at the SERIN command indeifnitely the way it is written until a command comes in at which time the command will be evaluated and some action taken.

    By keeping the values binary they can also be passed directly to the device in that format. You can always format for DEBUG output while keeping the data in its native format. In a PC to BASIC Stamp Module communication system you have two ways you can handle data flow.

    1) PC initiates communication - in this manner the PC sends commands every second for example, as per your example. The BASIC Stamp Module will be waiting at SERIN for a command and act upon it, then immediately go back to waiting for the next command. If the BASIC Stamp Module has nothing else to do during this time you're fine and everything works as expected.

    2) BASIC Stamp Module initiates communication - in this manner the BASIC Stamp Module sends a request to the PC for a command and then waits for said command. In between this action it is free to do what it wants and can send updates to the PC asynchronously.

    One caveat to the first method is that if the BASIC Stamp Module is not at the SERIN waiting when the PC sends the command it is lost. This can happen if the application sends requests asynchronously rather than every second. In theory the BASIC Stamp could use a timeout on the SERIN to allow it to process other tasks, however that would mean the chances of missing a command go up. I prefer the first method when the BASIC Stamp Module doesn't have to process anything else while waiting for a command. Then you don't have to limit yourself to every second if the requests from the application are based on human operator input. Such as a person clicking a button on a form. Again, it boils down to the BASIC Stamp Module getting the data back to the PC and being ready to receive the next command.

    As for handling arrays of data...the SERIN command can receive data into an aray. You could have the incoming data dropped directly into the array, but you'd probably want to send that data delayed from the command to give the BASIC Stamp Module time to setup for the second SERIN command.
  • Al3Al3 Posts: 6
    edited 2013-07-01 14:04
    Thanks for suggesting method 2) where the BASIC is periodically prompting for work.

    Can you give me SERIN or such, actual code examples of how to handle various structured requests from the PC? I have no problem formatting the data in binary to meet your specs.

    How do I send the mentioned commands for example:
    W,v1,v2,....,v16
    receive a write command with 16 byte values
    S
    send status
    WB,addr,value
    receive some command with pair of values


    with method 1) polling from the PC what would be fastest frequency in micro-seconds, assuming 4800 continues to work, the PC can send requests without any loss of incomming data.

    Thanks.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-07-01 15:30
    Without knowing the exact format and bytes coming from the PC I wouldn't be able to give an example, but if you look in the BASIC Stamp Manual/Help Files at the SERIN command it shows how to format it for various things. Because you're sending data in different formats you will probably have to handle things in a less straightforward manner. In other words, you can't use a standard format for the command and data, which would be ideal. You may have to have an exception for the array data. But I would still format my incoming commands and data in a consistent format for reliability. On my PC to MCU code I tend to prefix my data with an "!" (exclamation point) followed by a 2-letter identifier, then the data. I usually keep it the same length for consistency.

    As to your second question, if you're clicking on a form to request data there's no reason the BASIC Stamp shouldn't be able to keep up with events as they're clicked, based on what you're describing you want to do. You should plan everything in advance, including your comand bytes, format of the data, etc. Keeping the data in native format means you can pass it right along as a parameter without having to decode or reformat it. Definitely start with a list of all functions and the details of those though. In fact 9600bps should work if all you're using for a formatter in SERIN is a WAIT command.
  • Al3Al3 Posts: 6
    edited 2013-07-02 11:23
    Hi Chris I am still stuck. Initially I thought I would provide both the ability to send either 16 bytes data to be written to some device or the ability to write 1 byte at a specific address. Something like:
    WR<16 bytes> or WB<1 byte-addr><1 byte-value>


    I did read the SERIN help page for over 2 days before I decided to post. It's terrible documentation for one carrying BS1 documentation into "current" documentation is a poor approach. And simply put, there isn't enough clear practical examples.


    All languages I know including BASIC on PC we typically have string processing functions like Split(,), Left(,) to make sense of the serial buffer. I understand I am dealing with a limited power chip, show me how it is supposed to be done.


    The closest example I found in the SERIN help pages was data like "pos: xxxx yyyy" where they give the following code:
    SERIN 1, 16468, [WAIT ("pos: "), DEC yCoord, DEC yCoord]


    How about if we are to process both "pos: xxxx yyyy" and "loc: xxxx yyyy zzzz" what good does a WAIT do in this case?. WAIT seems only useful if you have a fixed repeating prefix in a consistent data stream.


    In your suggestion, you say you typically use "!" (exclamation point) followed by a 2-letter identifier, then the data. Does that mean that the answer to my subject is "stay away from variable length data" ?


    I have no problem sending/processing binary data I presume you mean send <! 1 byte><id 2 bytes><n-bytes>


    How do I write the SERIN to read say 16 bytes into MyData VAR Byte(16) array? Here is my guess work


    Cmd VAR Byte(2)


    SERIN 1, 16572, [WAIT ("!"), STR Cmd\2, ??then what? MyData??


    Can I then write something like this to process what I received?


    SELECT Cmd
    CASE "WR":
    DEBUG CR
    CASE "WB":
    DEBUG Cr
    ENDSELECT


    The data from the PC better always be 1+2+16 = 19 bytes long?


    For the WB (write-byte) command I would expect on the first 2 bytes (addr+value) to be meaningful and right pad the remaining 14 bytes as zeros, the make the SERIN work?


    Feel free to suggest however you would want to do it, given the sample functionality I am trying to get at. Thanks for your help.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2013-07-02 12:18
    The WAIT modifier tells the SERIN command to wait for the specified string before receiving data ino the variables in the brackets. Without this it might be posible that you want to receive several bytes but maybe missed the previous command and got the tail end of it resulting in the data being in the wrong values and worse, stuck until the next command. It is important in a situation like yours, but you don't need so many characters. I tend to use "!" (exclamation point) and then wait for it, plus the 2-character prefix before receiving data. This 3-character preamble ensures that the data is aligned in the following variables.

    Regarding PC based BASIC, of course you have more memory and can handle strings in such a language. On embedded microcontrollers strings are a bit more difficult, especially with limited memory. Besides, in your case, which what you're trying to do it seems to me that strings aren't even an issue. You need an array and these are supported, Once again, due to limited resources I would try to not think of your commands in a verbose/string format, but as simple byte commands. For example, "W" could be write, "R" could be read. One character versus two makes no difference except in the amount of resources used to accomplish the same thing, so you may as well think in smaller terms. The following SERIN statement (assuming RX is your input pin and Baud is your baud rate) waits for the characters "!CS" then receives the command, 16-bit address and 1-byte data.
    SERIN RX, Baud, [WAIT("!CS"), command, address.LOWBYTE, address.HIGHBYTE, ioData)
    

    If the command called for a block write of say, 16 bytes, then, once the command was parsed you could have another SERIN formatted as below to receive the 16 bytes into an array named, "buffer".
    SERIN RX, Baud, [STR buffer\16]
    

    This assumes that a 16-byte array named, "buffer" has already been declared. In the first SERIN the address was sent as lowbyte then higbyte. Once read the variable address (assuming it was declared as a word variable) will contain the 16-bit address. There is no need to combine the data as it is automatic when formatting it this way.
Sign In or Register to comment.