SERIN handling variable length data, a non-blocking form of SERIN
Al3
Posts: 6
Hello, I would like to process commands received on the serial port from a PC which could have the following form:
"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:- W<data>
- a request to store data on some eeprom
- R
- a request to read all data on some eeprom
- WB<addr><value>
- a request to store some value at a particular address
- S
- a request to receive status
"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...
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
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
' 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.
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.
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
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.
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.
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.
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.
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".
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.