PAC / RS232 Advice Needed

Discussion in 'General Discussion' started by xrmichael, Jan 28, 2008.

  1. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    I am trying (Not very successfully tho) to interpret some zone status messages sent from a piece of equipment connected to a ctouch rs232 port, the unit sends out 6 lines of data evey 2 seconds of 13 hex bytes these lines of data contain info i want to then graphical represent on the ctouch unit.

    At the moment i can initialise the port and send out data with no problem, but i cannot seem to find a way logically! put some code together to read in the data strings and extract the parts relevant to me. The 2 second data broadcast from the units also stops if it is busy carrying out other tasks, and then resumes.

    Monitoring the output from the unit it also sends out a load of other information that is not relevant, that seems to complicate the issue.

    Any help gratefully received as my wife is getting fed up of finding me asleep head in laptop.
     
    xrmichael, Jan 28, 2008
    #1
  2. xrmichael

    filpee

    Joined:
    May 31, 2006
    Messages:
    204
    Likes Received:
    0
    Location:
    Western Australia
    How long is a piece of string?


    Maniuplating RS232 strings is usually pretty easy.

    Post the string you have captured and what your trying to do with it.

    Whats the device you are talking to?

    Do you have the protocol? If so post that up also.


    Other then that theres not really much anyone here can do to help.


    ------------------------------
    Assuming your device sends out a slightly different header you could do something like

    if (stringBuffer[3] = $20) then do something //in otherwords if the third hex code is $20 then do something
    else
    clearbuffer(stringBuffer) //otherwise clear the buffer

    (I know thats not in piced language but you should see where I'm going with this.)
     
    filpee, Jan 28, 2008
    #2
  3. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    The status string as follows.

    0x55 0x0B 0x20 0x00 0x00 0x00 0xFF 0x09 0x00 0x00 0x44 0x34

    6 of these stings will be sent every 2 secs the protocol for this

    byte 0 - start of packet alway 0x55
    byte 1 - command length
    byte 2 - 0x20 indicates status message
    byte 3 - zone id ranges from 0x00 to 0x05
    byte 4 - reserved
    byte 5 - 0x00 zone off 0x02 zone on 0x03 zone mute
    byte 6 - source id
    byte 7 - zone level % value
    byte 8 - na
    byte 9 - na
    byte 10 - zone level actual value
    byte 11 - checksum

    I would need to extract and act on bytes 2,3,5,6,7 or 10
     
    xrmichael, Jan 28, 2008
    #3
  4. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    PS Thanks it starts to get a little clearer. Any other feed back in relation to the string would be handy.
     
    xrmichael, Jan 28, 2008
    #4
  5. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    I think the part thats throwing me is.

    If pac starts running at a different time from the 232 device.
    Could the pac read into the buffer half of a string.
    Also the 232 devices will be sending loads of other data strings.

    What sort of syncs the whole thing together.
     
    xrmichael, Jan 28, 2008
    #5
  6. xrmichael

    znelbok

    Joined:
    Aug 3, 2004
    Messages:
    1,151
    Likes Received:
    17
    That is where you would check that the first byte of data in the buffer is 0x55.

    If it is not then ignore, if it is - then continue processing the data.

    To be extra secure you might say if the first = 0x55 and the third = 0x20 then it is a status message.

    Out of curiosity, what is the device

    Mick
     
    znelbok, Jan 28, 2008
    #6
  7. xrmichael

    filpee

    Joined:
    May 31, 2006
    Messages:
    204
    Likes Received:
    0
    Location:
    Western Australia


    The PAC has a string buffer that reads in and stores your string.
    If you started your string handling by confirming the first 3 bytes...
    Code:
    if (stringBuffer[1] = $55 AND stringBuffer[2] = $command length AND stringBuffer[3] = $20)
    ... its pretty likely that the next 9 bytes are what your looking for. So starting from the start of your string you might want to save the string to a variable.
    Code:
    for x=1;x<12;x++{
       mySavedString[x] := stringBuffer[x]
    }
    clearBuffer(stringBuffer)
    now mySavedString has the data you want and stringBuffer is ready for the next capture.

    because stringBuffer is a fixed size (I would fix it to the max size your reading in) it acts as First In First Out buffer. Data in on the left pushes old data out on the right.
     
    filpee, Jan 28, 2008
    #7
  8. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    A little headway made i can read from the serial into a variable and then display its contents with writln, then the confusion starts again.

    If i monitor the 232 device with a port monitor a get nice lines of information that seem to correlate with the protocol document.

    But the data that i get in piced is just lots of squares and symbols, i have tried reading the port with

    ReadSerial(1, sERIAL1IN, 'U');
    ReadSerial(1, sERIAL1IN, #55);
    ReadSerial(1, sERIAL1IN, '');

    The document doesn't state what they use as a termination character is this relevant ?
     
    xrmichael, Jan 28, 2008
    #8
  9. xrmichael

    Darren Senior Member

    Joined:
    Jul 29, 2004
    Messages:
    2,361
    Likes Received:
    0
    Location:
    Adelaide, South Australia
    It is not clear whether you have this working or not. Here you say you can read the data, but down below you imply you can't. Do you mean that you can read a single message, but not the subsequent ones ?

    This is because these are not ASCII characters and hence are generally not "printable".
    It is very relevant. Without knowing the termination character, it is not possible to determine easily where one command stops and the next starts.

    It is possible that they don't use a termination character. If this is the case, you need to write additional logic (similar to that posted here by others) which allows you to work out where the start of the command is.

    I would suggest :
    1. Install PICED V4.1 if you don't already have it.
    2. Read the logic help file topic "Serial IO Examples"

    If you can wait for another week or so, there should be an update to PICED which includes a new logging feature which greatly simplifies working out what data is coming from a serial device. This helps enormously in resolving issues when the protocol is not fully defined.
     
    Darren, Jan 29, 2008
    #9
  10. xrmichael

    paulw11

    Joined:
    Dec 4, 2006
    Messages:
    97
    Likes Received:
    0
    Location:
    Blue Mountains, NSW, Australia
    Hi,

    My standard method for processing packets like this is to build a state machine. Basically you have an integer that indicates what state you are in, then every time you receive a byte you use the current state to decide you should do.

    For example:


    State 1:
    if byte= 55 then buffer[0]=byte and set state=2 and set count=1
    State 2:
    buffer[count]=byte
    increment count
    if count=12 then state=3
    State 3:
    Compute checksum
    if checksum is ok then process packet
    clear buffer
    set state=1

    Using this system you should typically miss at most two packets. Assuming that the first 55 that you see is actually in the middle of a packet (zone volume for example) then you will collect the next 12 bytes which is the rest of this packet and the first 3 bytes of the next packet, however your checksum should not compute (Checksums aren't infallible but it should be pretty reliable). You will then sit in state 0 until the next 0x55 is received. There is still the chance that this 55 is also in the middle of the packet so you may discard some more packets but it should settle down eventually.

    You could also add some more robustness by timing the interval between bytes. Assuming that the inter-byte delay is less than 1 sec and your packets are sent every 2 seconds, if this byte was received more than 1 second after the previous one you can reset to state 0

    Paul
     
    paulw11, Jan 29, 2008
    #10
  11. xrmichael

    filpee

    Joined:
    May 31, 2006
    Messages:
    204
    Likes Received:
    0
    Location:
    Western Australia

    Not in this case as from what you posted previously the 2nd byte char indicates to the receiver how long the message will be thus removing the need for a terminating character.
     
    filpee, Jan 29, 2008
    #11
  12. xrmichael

    paulw11

    Joined:
    Dec 4, 2006
    Messages:
    97
    Likes Received:
    0
    Location:
    Blue Mountains, NSW, Australia
    That's a good point. I missed that. You would need to allow for the command length byte in the state machine if you used one - I assumed (incorrectly) a fixed length message.

    Also, if you are only interested in "status" messages (byte 2 =0x20) you can factor that in to the state machine too.

    Paul
     
    paulw11, Jan 29, 2008
    #12
  13. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    Reply to post by Darren

    Sorry, about the vauge post. I mean the serial port is open and data is coming in and getting stored/written to the global variable serial1in.

    I presume piced can only show the ASCII conversation on the incoming hex data

    I premise again i should use ReadSerial (1, sERIAL1IN, '');

    could i use #55 as the termination this is at the start of every line (would the logic look back wards at the last message ?

    I have piced 4.1 i will read up again in the io examples. Looking forward to the new version tho.

    Is there a way to stop the logic engine and then look at the contents of a variable/serial buffer to see what was captured.


    Reply to Paul/flipee - i will try this also and update.

    To confirm using the ReadSerial (1, sERIAL1IN, ''); command on every loop the system will place the total contents of the serial buffer in to the variable every loop?
     
    xrmichael, Jan 29, 2008
    #13
  14. xrmichael

    Darren Senior Member

    Joined:
    Jul 29, 2004
    Messages:
    2,361
    Likes Received:
    0
    Location:
    Adelaide, South Australia
    At the moment, yes. The new version shows you the hex (non-printable) characters as well.

    You could write some code to log the incoming data :

    Code:
    { global variables section }
    ReceivedString : string;
    
    { procedures }
    procedure LogReceivedString;
    var
      i : integer;
      s, ss : string;
    begin
      s := '';
      for i := 1 to length(ReceivedString) do
      begin
        if (ord(ReceivedString[i]) < 32) or (ord(ReceivedString[i]) > 126) then
        begin
          Format(ss, '<', ord(ReceivedString[i]):0, '>');
          Append(s, ss);
        end
        else
          Append(s, ReceivedString[i]);
      end;
      WriteLn('Received "',s,'"');
    end;
    
    { module code }
    ReadSerial(1, ReceivedString, '');
    LogReceivedString;
    
    If there are no termination characters, then yes.

    You could, but you would have problems if #55 occurred in the middle of the message.


    No. You can write data to the log for debugging purposes.

    Correct. You might get bits of the message by doing this. You will need to keep a string to store a buffer of all of the incoming data until you have a complete message. You can then use the techniques suggested by Paul to get the data out of the message.
     
    Darren, Jan 29, 2008
    #14
  15. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    Paul / Filpee, is this the right direction for state machine / buffer the code actually now follows the state of zone 1 (i g abced zone off and abcdef zone on). But i think i could simplify this ? or i have i missed the point.

    Also THANK YOU ALL for your help, i think i should have listened more at school.

    ReadSerial(1, SERIALIN, ''); {READS PORT ON EACH LOOP}
    WRITELN ('SERIALIN 1 = ', SERIALIN); {WRITE LINE TO CONFIRM COMMS OK}
    IF SERIALIN[1]=#085 THEN
    BEGIN
    BUFFER1[0]:='A';
    COUNTER:=1;
    END;
    IF SERIALIN[2]=#011 THEN
    BEGIN
    BUFFER1[1]:='B';
    COUNTER:=COUNTER+1;
    END;
    IF SERIALIN[3]=#032 THEN
    BEGIN
    BUFFER1[2]:='C';
    COUNTER:=COUNTER+1;
    END;
    IF SERIALIN[4]=#000 THEN
    BEGIN
    BUFFER1[3]:='D';
    COUNTER:=COUNTER+1;
    END;
    IF SERIALIN[5]=#000 THEN
    BEGIN
    BUFFER1[4]:='E';
    COUNTER:=COUNTER+1;
    END;
    IF SERIALIN[6]=#002 THEN
    BEGIN
    BUFFER1[5]:='F';
    COUNTER:=COUNTER+1;
    END;
     
    xrmichael, Jan 30, 2008
    #15
  16. xrmichael

    filpee

    Joined:
    May 31, 2006
    Messages:
    204
    Likes Received:
    0
    Location:
    Western Australia
    This compiles fine but its not tested.

    What are you 'actually' trying to do with this?

    this code just prints out the zone and its status, I ran out of time to do the rest.


    Code:
    {constants}
    comPort = 1;
    
    {variables}
    serialIn: string;
    position : integer;
    
    {code}
    ReadSerial(comPort,serialIn,'');{read port}
    WriteLn('Serial In 1 = ', serialIn);{print port to message window}
    position := pos(#55,serialIn);{find #55}
    if position <> 0{if #55 is found} then
    begin
      if pos(#20, serialIn) = position+2 {check pos+2 for status #20} then
      begin
        case serialIn[position+3] of    {pos+3 = zone id}
          #00 : WriteLn('Zone 1');
          #01 : WriteLn('Zone 2');
          #03 : WriteLn('Zone 3');
          #04 : WriteLn('Zone 4');
          #05 : WriteLn('Zone 5');
        end;
        case serialIn[position+5] of     {pos+5 = off/on/mute}
          #00 : WriteLn(' is off');
          #01 : WriteLn(' is on');
          #03 : WriteLn(' is muted');
        end;
        {pos+6 = source id}
        {pos+7 = zone level %}
        {pos+10 = zone level actual}
      end;
    end;
    
     
    filpee, Jan 30, 2008
    #16
  17. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    The writeln was just to make it easier for me to see what was going on, the intention one i one i am reading the data OK is to have the ctouch display the state and level of the 6/12 zones in the system.

    Thanks for the code, i will have a go with it later.
     
    xrmichael, Jan 30, 2008
    #17
  18. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    At last i can now read the on/off state of 6 zones ever 2 seconds and pass this info to ctouch GA's for use on the Ctouch.

    Then the confusion arises again i have captured the volume level in to a string RECEIVEDDATA[10] but don't seem to be able to stringtoint or stringtoreal to present this information to the system io?

    Also i have to express all incoming data as the #085 (hex 55) 3 digit decimal equivalent why would this be.

    Thanks again.
     
    xrmichael, Jan 31, 2008
    #18
  19. xrmichael

    filpee

    Joined:
    May 31, 2006
    Messages:
    204
    Likes Received:
    0
    Location:
    Western Australia
    can you post up your code so we can see what your doing

    I assume you mean your saying that you cannot use #55 to represent hex 55 but #085 does work?

    Maybe duncan can shed light on this?
     
    filpee, Jan 31, 2008
    #19
  20. xrmichael

    xrmichael

    Joined:
    Nov 20, 2006
    Messages:
    113
    Likes Received:
    1
    Location:
    uk
    Rs232

    HEX STRINGS

    Transmit and receive of hex strings only works if i enter the 3 digit dec equivalent. so getting the ctouch (or pac i also tested) to send or read hex55I have to enter in #085, its not a problem just adds a bit more time.

    CODE

    {REQUEST ZONE STATE CONSTANTS}
    REQZONE01STAT=#085#004#105#000#062;

    {Enter Variable definitions here}
    SERIALIN : STRING; {THIS STORES SERIAL INCOMING DATA}
    COUNTER1 : INTEGER; {I USE THIS TO CLEAR BUFFER}
    COUNTER2 : INTEGER; {I USE THIS TO CLEAR BUFFER}
    POSITION : INTEGER; {THIS DEFINES A POSITION IN A STRING}
    ReceivedData : STRING;

    {ZONE STATUS VARS HERE}
    ZONE1STATUS : STRING;
    ZONE1SOURCE : INTEGER;
    ZONE1VOL : INTEGER;

    {Enter Initialisation code here}
    OpenSerial(1, 1, 57600, 8, 1, 0, 0);
    COUNTER1 := 0;
    COUNTER2 := 1;

    mod1

    WriteSerial(1, #085#004#105#000#062); { REQUEST ZONE 1 STATUS }
    DELAY (2) {WAIT 3 SECONDS}

    mod2
    ReadSerial(1,serialIn,'');{read port}

    WRITELN ('SERIALIN = ', SERIALIN); {test comms ok remove this when complete}

    position := pos(#105,serialIn);{find #55}

    WRITELN ('POSITION = ', POSITION); {test line remove when done}

    if (POSITION > 0) AND (SERIALIN[POSITION]=#105){LOOKING FOR #69 ZONE STAT MESSAGE} AND (SERIALIN[POSITION+1]=#001)
    {LOOKING FOR #01 ACK} AND (SERIALIN[POSITION+2]=#000) {LOOKING FOR #000 ZONE 1} then

    Copy(ReceivedData, SERIALIN, POSITION,12);{COPYS SERIAL DATA TO STRING}

    {********REMOVE WHEN WORKING********}

    WRITELN ('DATA = ', RECEIVEDDATA[1]);
    WRITELN ('DATA = ', RECEIVEDDATA[2]);
    WRITELN ('DATA = ', RECEIVEDDATA[3]);
    WRITELN ('DATA = ', RECEIVEDDATA[4]);
    WRITELN ('DATA = ', RECEIVEDDATA[5]); {ZONE STATUS ON OFF MUTE}
    WRITELN ('DATA = ', RECEIVEDDATA[6]); {ZONE SELECTED SOURCE}
    WRITELN ('DATA = ', RECEIVEDDATA[7]); {ZONE VOL AS A %}
    WRITELN ('DATA = ', RECEIVEDDATA[8]); {ZONE BASS LEVEL}
    WRITELN ('DATA = ', RECEIVEDDATA[9]); {ZONE TREB LEVEL}
    WRITELN ('DATA = ', RECEIVEDDATA[10]); {ACTUAL VOL LEV}
    WRITELN ('DATA = ', RECEIVEDDATA[11]);
    WRITELN ('DATA = ', RECEIVEDDATA[12]);

    {********REMOVE WHEN WORKING***********}

    ZONE1STATUS:=(RECEIVEDDATA[5]);
    ZONE1SOURCE:=(RECEIVEDDATA[6]);
    {************************}ZONE1VOL:=StringToInt(ReceivedData[7]);{**COPIES*******************************}


    once ZONE1STATUS = #002 THEN SetCBusState("COUNTING 1", "AV Control", "ZONE 1 ON OFF", ON);
    once ZONE1STATUS = #000 THEN SetCBusState("COUNTING 1", "AV Control", "ZONE 1 ON OFF", OFF);

    {CLEARBUFFER}
    FOR COUNTER1:= 1 TO 23 DO
    BEGIN
    RECEIVEDDATA[COUNTER2]:=' ';
    COUNTER1:=COUNTER1+1;
    COUNTER2:=COUNTER2+1;
    END;
    COUNTER1:=0;
    COUNTER2:=1;

    The line with ************* after it wont compile i am trying to get this byte of the string into integer so i can use a systemio int levelto move a slider on the screen.

    Cheers.
     
    xrmichael, Jan 31, 2008
    #20
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.