Shop OBEX P1 Docs P2 Docs Learn Events
Parsing serial data in SPIN; converting ASCII hex to a decimal number — Parallax Forums

Parsing serial data in SPIN; converting ASCII hex to a decimal number

K6MLEK6MLE Posts: 106
edited 2015-06-21 21:26 in Propeller 1
Is there a library program for either of these tasks? The serial data will be coming in as ASCII hex and once a particular field is grabbed, I need to convert the values to decimal. Any help will be greatly appreciated, as I'm currently trying to learn SPIN and put it to immediate use.

Comments

  • Cluso99Cluso99 Posts: 18,069
    edited 2015-06-15 18:28
    Kye wrote a general purpose object that does many conversions including string conversions. It's in the OBEX but I am sorry I cannot recall it's name and it's on my other laptop. Perhaps someone might chime in else I will post it's name later today.
  • william chanwilliam chan Posts: 1,326
    edited 2015-06-15 18:39
    For the each string digit that you receive, you just deduct the ASCII of zero to get the digit's value to be added to an accumulator.
    Handle A-F separately.
    For each subsequent digit received, multiply the accumulator with 16 before adding that digit.
    Done.
  • kwinnkwinn Posts: 8,697
    edited 2015-06-16 08:50
    K6MLE wrote: »
    Is there a library program for either of these tasks? The serial data will be coming in as ASCII hex and once a particular field is grabbed, I need to convert the values to decimal. Any help will be greatly appreciated, as I'm currently trying to learn SPIN and put it to immediate use.

    By ASCII hex I assume you mean you are receiving the ascii digits zero to nine and letters A to F (or a to f). Pretty simple to convert that to a 32 bit binary number and then use the serial object functions to display them as decimal numbers. Just do the following:

    1. Set the number variable to zero.
    2. Make sure the characters are valid (0-9, A-F, a-f)
    3. Add 9 to the character if it is not numeric
    4. Mask off the 4 lsb's
    5. Add the 4 lsb's to the number variable
    6. Shift the number variable left 4 bits
    7. Repeat 2 to 6 for a maximum of 8 hex digits

    You may want to look at Numbers by Jeff Martin and Simple Numbers by Chip Gracey in the OBEX. IIRC JonnyMac also had some number conversion routines in his Nuts & Volts articles.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-16 20:39
    One of the best methods I've seen for receiving hex values is in the object "Parallax Serial Terminal.spin". The method "StrToBase" lets you specify which type of data is to be received. The method is private so you'll need to change it to public to use. This method is used by the methods DecIn, HexIn and BinIn.

    The firmware for the Eddie robot parses incoming hex data. The latest version of the Eddie firmware (which will also work with Arlo hardware) can be found on my GitHub.

    https://github.com/ddegn/EddieFirmware
  • K6MLEK6MLE Posts: 106
    edited 2015-06-16 21:40
    I should have mentioned that this is my first introduction to SPIN and Object Oriented programming, in general. What would help me at this point is a code snippet that will simply read a string of serial data (in this case it's 24 bytes) that starts with a "$" and ends with a carriage return. Once this data is read, I want to spit it out to the TV_text object, so it appears on a little monitor hooked up to my Prop.

    This code should be enough of an example to get me heading in the proper direction!

    Thanks for the help!
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2015-06-16 23:48
    K6MLE wrote: »
    I should have mentioned that this is my first introduction to SPIN and Object Oriented programming, in general. What would help me at this point is a code snippet that will simply read a string of serial data (in this case it's 24 bytes) that starts with a "$" and ends with a carriage return. Once this data is read, I want to spit it out to the TV_text object, so it appears on a little monitor hooked up to my Prop.

    This code should be enough of an example to get me heading in the proper direction!

    Thanks for the help!

    As a matter of interest I am testing out a VGA text object in Tachyon and it could just as well be a TV object too. There is of course a lot of number and string handling built into Tachyon, I wouldn't think it would hardly be any bother at all. What does the string look like and what to you need to do with it?
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-17 01:13
    I don't have a TV connected to my Propeller right now but I think this should read in a hex value.
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
    OBJ
    
      text : "tv_text"
      pst : "Parallax Serial Terminal"
    
    PUB start | i
    
      'start term
      pst.Start(115_200)
      
      text.start(12)
      'text.str(string(13,"   TV Text Demo...",13,13,$C,5," OBJ and VAR require only 2.8KB ",$C,1))
      text.out($C) ' set color
      text.out(6)
      pst.Clear
      pst.Home
      
      repeat
        text.str(string(13, $C, 6, "Enter Hexadecimal Value"))
        pst.str(string(11, 13, "Enter Hexadecimal Value"))
        result := pst.HexIn
        text.str(string(13, "You entered $"))
        pst.str(string(11, 13, "You entered $"))
        text.hex(result, 8)
        pst.hex(result, 8)
        
    

    The only pst call you really need is "pst.HexIn". The other pst calls display text to the terminal. This text should also be displayed on the TV.

    The method "HexIn" requires a carriage return to enter the data. Any characters which aren't part of a hexadecimal number are ignored. The leading "$" can be included or not. It won't change the value of the number.

    The longest hex value one can hold in a long variable is 8 digits long. If your 24 characters hold values of multiple numbers, you'll need to use a carriage return to separate the values to different variables.
  • K6MLEK6MLE Posts: 106
    edited 2015-06-17 14:12
    Hello Peter,

    The project at hand is to take the serial data coming from my weather station ( Peet Bros. Ultimeter 100) and display some of the data on a wall-mounted display, so it's viewable from across the room. The data string looks like this:
    $ULTW002C00DD03060505,CR,LF

    I will want to watch for the "$" character and then ignore the "ULTW" portion. Following that, the data breaks down as follows:
    002C = Wind speed in MPH.
    00DD = Wind direction in degrees
    0306 = Last detected outdoor temperature in degrees F.
    0505 = Long term rain total, in inches.

    In testing the serial receive portion of the code, I wanted to be able to read the string and display the elements using the TV-text object. Once this works, the data will be fed into a case statement for direction and light an appropriate LED on a compass rose I already have waiting. The wind speed will be next and is to be displayed on a 3-digit multiplexed 7-segment display. My thought were to use the Propeller for this and just have the LED routine and 3-digit routine grab their data from a set of global variables and assign each of the routines to their own cog. My old way of doing this was to use a PIC microcontroller and have the multiplexed display get interrupted by the serial port, so the data could be updated. New data comes in every 5 minutes.

    Clearly, my experience with PicBasicPro isn't helping me at all with SPIN! While I'd like to learn SPIN, I wouldn't be adverse to trying this in a FORTH environment (my favorite language, actually!!).

    Thank you for your input!!
  • K6MLEK6MLE Posts: 106
    edited 2015-06-17 14:18
    Hi Duane,

    Being REAL new to SPIN, I only minimally understand the code you posted. My hope is to use a low-order I/O pin (7 in this case) for serial data reception. What keeps getting me stumped is the syntax of SPIN related to the handling of string data. It appears the data needs to be read into an array type of variable. It's unclear to me if this can be done all at once, or if it needs to be done byte-by-byte.

    Once the data is stored in an array, it appears relatively easy (I think!) to parse the array and load up appropriate variables to be able to display the data.

    My reply to Peter gives an illustration of the serial data format.

    Thanks for your input!!
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-17 15:17
    K6MLE wrote: »
    It appears the data needs to be read into an array type of variable. It's unclear to me if this can be done all at once, or if it needs to be done byte-by-byte.

    There are of course multiple ways to approach this. In the code below I read the full packet at once and then parse out the four digit hex values.
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      CHARACTERS_TO_SKIP = 4
      DATA_FIELD_SIZE = 4
      
    VAR
    
      long windSpeed, windDirection, temperature, rainTotal
    
      byte dataBuffer[pst#MAXSTR_LENGTH + 1]
      byte smallBuffer[5]
      
    OBJ
    
      text : "tv_text"
      pst : "Parallax Serial Terminal"
    
    PUB start | startDataPtr
    
      'start term
      'pst.Start(115_200) ' The "Start" method assumes one is using pins 31 and 30 for rx and tx.
      pst.StartRxTx(7, 30, 0, 115_200) ' change "115_200 to baud you're using 
      
      text.start(12)
      'text.str(string(13,"   TV Text Demo...",13,13,$C,5," OBJ and VAR require only 2.8KB ",$C,1))
      text.out($C) ' set color
      text.out(6)
      'pst.Clear
      'pst.Home
      
      repeat
        bytefill(@dataBuffer, 0, pst#MAXSTR_LENGTH + 1)
        text.str(string(13, 13, $C, 6, "Waiting For Data", 13))
        'pst.str(string(11, 13, 11, 13, "Waiting For Data", 11, 13))
        'pst.StrIn(@dataBuffer)
        startDataPtr := @dataBuffer
        repeat
          result := byte[startDataPtr++]
        until result == "$" or result == 0
    
        ifnot result
          text.str(string(13, "Valid Data Not Found"))
          'pst.str(string(11, 13, "Valid Data Not Found"))
          next ' skip to next loop of repeat
    
        startDataPtr += CHARACTERS_TO_SKIP
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        windSpeed := StrToBase(@smallBuffer, 16)
        
        startDataPtr += DATA_FIELD_SIZE
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        windDirection := StrToBase(@smallBuffer, 16)
    
        startDataPtr += DATA_FIELD_SIZE
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        temperature := StrToBase(@smallBuffer, 16)
    
        startDataPtr += DATA_FIELD_SIZE
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        rainTotal := StrToBase(@smallBuffer, 16)
        
        text.str(string(13, "Wind Speed = "))
        text.dec(windSpeed)
        text.str(string(" mph"))
        'pst.str(string(11, 13, "Wind Speed = "))
        'pst.dec(windSpeed)
        'pst.str(string(" mph"))
    
        text.str(string(13, "Direction = "))
        text.dec(windDirection)
        text.str(string(" degrees"))
        'pst.str(string(11, 13, "Direction = "))
        'pst.dec(windDirection)
        'pst.str(string(" degrees"))
    
        text.str(string(13, "temperature = "))
        text.dec(temperature)
        text.str(string(" F"))
        'pst.str(string(11, 13, "temperature = "))
        'pst.dec(temperature)
        'pst.str(string(" F"))
    
        text.str(string(13, "Rain = "))
        text.dec(rainTotal)
        text.str(string(" inches"))
        'pst.str(string(11, 13, "Rain = "))
        'pst.dec(rainTotal)
        'pst.str(string(" inches"))
    
    PRI StrToBase(stringptr, base) : value | chr, index
    {Converts a zero terminated string representation of a number to a value in the designated base.
    Ignores all non-digit characters (except negative (-) when base is decimal (10)).}
    
      value := index := 0
      repeat until ((chr := byte[stringptr][index++]) == 0)
        chr := -15 + --chr & %11011111 + 39*(chr > 56)                              'Make "0"-"9","A"-"F","a"-"f" be 0 - 15, others out of range     
        if (chr > -1) and (chr < base)                                              'Accumulate valid values into result; ignore others
          value := value * base + chr                                                  
      if (base == 10) and (byte[stringptr] == "-")                                  'If decimal, address negative sign; ignore otherwise
        value := - value
    
    

    If "StrToBase" were made public from within the object "Parallax Serial Terminal" then the calls of "StrToBase(@smallBuffer, 16)" could be changed to "pst.StrToBase(@smallBuffer, 16)" and the method "StrToBase" could be deleted from the top object.

    Again I didn't test this code with a TV attached. You might need to add some formatting commands to get the data displayed as you'd like it on the TV.

    This is what it looks like in the serial terminal:
    Wind Speed = 44 mph
    Direction = 221 degrees
    temperature = 774 F
    Rain = 1285 inches
    
    Waiting For Data
    
    Wind Speed = 44 mph
    Direction = 221 degrees
    temperature = 774 F
    Rain = 1285 inches
    
    Waiting For Data
    

    I manually entered "$ULTW002C00DD03060505,CR" into the terminal twice. A line feed character wasn't sent with my test but I'm pretty sure this won't make a difference since the code watches for the "$" character to indicate the start of the message.
  • AribaAriba Posts: 2,690
    edited 2015-06-17 17:01
    You can also parse the serial data while you receive the bytes. There is anyway a small rx-buffer in the serial objects, so buffer it again in a string is not necessary.
    Because the W character comes right before the data and never else, you can just wait for this character and then read the 4 hex values.

    Here is a simple Spin code (untested):
    CON
      _clkmode  = xtal1 + pll16x
      _xinfreq  = 5_000_000
      
    VAR
      long  speed, direction, temperature, rain
      
    OBJ
      ser :  "FullDuplexSerial"
      tv  :  "TV_Text"
          
    PUB Main
      ser.start(7,30,0,9600)   '<-- set pin (7) and baudrate (9600) here
      tv.start(12)             '<-- TV basepin
      repeat
        repeat until ser.rx == "W"
        speed := readHex4
        direction := readHex4
        temperature := readHex4                                         
        rain := readHex4
    
        tv.str(string(1,13,"speed: "))
        tv.dec(speed)
        tv.str(string("   ",13,"dir:   "))
        tv.dec(direction)
        tv.str(string("   ",13,"temp:  "))
        tv.dec(temperature)
        tv.str(string("   ",13,"rain:  "))
        tv.dec(rain)
        tv.str(string("   "))
    
    PRI readHex4 : v
      repeat 4
        v := v<<4 + lookdownz(ser.rx : "0123456789ABCDEF")
    

    Andy
  • K6MLEK6MLE Posts: 106
    edited 2015-06-17 18:07
    I gave the code a try, with the only change being the baud rate (the weather station transmits at 2400). It didn't receive the data ... it appears the data polarity needs to be able to receive TRUE as opposed to INVERTED data. The weather station sends in non-inverted format. Not sure how to change it ... the mode variable doesn't seem to address this.

    Duane Degn wrote: »
    There are of course multiple ways to approach this. In the code below I read the full packet at once and then parse out the four digit hex values.
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      CHARACTERS_TO_SKIP = 4
      DATA_FIELD_SIZE = 4
      
    VAR
    
      long windSpeed, windDirection, temperature, rainTotal
    
      byte dataBuffer[pst#MAXSTR_LENGTH + 1]
      byte smallBuffer[5]
      
    OBJ
    
      text : "tv_text"
      pst : "Parallax Serial Terminal"
    
    PUB start | startDataPtr
    
      'start term
      'pst.Start(115_200) ' The "Start" method assumes one is using pins 31 and 30 for rx and tx.
      pst.StartRxTx(7, 30, 0, 115_200) ' change "115_200 to baud you're using 
      
      text.start(12)
      'text.str(string(13,"   TV Text Demo...",13,13,$C,5," OBJ and VAR require only 2.8KB ",$C,1))
      text.out($C) ' set color
      text.out(6)
      'pst.Clear
      'pst.Home
      
      repeat
        bytefill(@dataBuffer, 0, pst#MAXSTR_LENGTH + 1)
        text.str(string(13, 13, $C, 6, "Waiting For Data", 13))
        'pst.str(string(11, 13, 11, 13, "Waiting For Data", 11, 13))
        'pst.StrIn(@dataBuffer)
        startDataPtr := @dataBuffer
        repeat
          result := byte[startDataPtr++]
        until result == "$" or result == 0
    
        ifnot result
          text.str(string(13, "Valid Data Not Found"))
          'pst.str(string(11, 13, "Valid Data Not Found"))
          next ' skip to next loop of repeat
    
        startDataPtr += CHARACTERS_TO_SKIP
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        windSpeed := StrToBase(@smallBuffer, 16)
        
        startDataPtr += DATA_FIELD_SIZE
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        windDirection := StrToBase(@smallBuffer, 16)
    
        startDataPtr += DATA_FIELD_SIZE
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        temperature := StrToBase(@smallBuffer, 16)
    
        startDataPtr += DATA_FIELD_SIZE
        bytemove(@smallBuffer, startDataPtr, DATA_FIELD_SIZE)
        rainTotal := StrToBase(@smallBuffer, 16)
        
        text.str(string(13, "Wind Speed = "))
        text.dec(windSpeed)
        text.str(string(" mph"))
        'pst.str(string(11, 13, "Wind Speed = "))
        'pst.dec(windSpeed)
        'pst.str(string(" mph"))
    
        text.str(string(13, "Direction = "))
        text.dec(windDirection)
        text.str(string(" degrees"))
        'pst.str(string(11, 13, "Direction = "))
        'pst.dec(windDirection)
        'pst.str(string(" degrees"))
    
        text.str(string(13, "temperature = "))
        text.dec(temperature)
        text.str(string(" F"))
        'pst.str(string(11, 13, "temperature = "))
        'pst.dec(temperature)
        'pst.str(string(" F"))
    
        text.str(string(13, "Rain = "))
        text.dec(rainTotal)
        text.str(string(" inches"))
        'pst.str(string(11, 13, "Rain = "))
        'pst.dec(rainTotal)
        'pst.str(string(" inches"))
    
    PRI StrToBase(stringptr, base) : value | chr, index
    {Converts a zero terminated string representation of a number to a value in the designated base.
    Ignores all non-digit characters (except negative (-) when base is decimal (10)).}
    
      value := index := 0
      repeat until ((chr := byte[stringptr][index++]) == 0)
        chr := -15 + --chr & %11011111 + 39*(chr > 56)                              'Make "0"-"9","A"-"F","a"-"f" be 0 - 15, others out of range     
        if (chr > -1) and (chr < base)                                              'Accumulate valid values into result; ignore others
          value := value * base + chr                                                  
      if (base == 10) and (byte[stringptr] == "-")                                  'If decimal, address negative sign; ignore otherwise
        value := - value
    
    

    If "StrToBase" were made public from within the object "Parallax Serial Terminal" then the calls of "StrToBase(@smallBuffer, 16)" could be changed to "pst.StrToBase(@smallBuffer, 16)" and the method "StrToBase" could be deleted from the top object.

    Again I didn't test this code with a TV attached. You might need to add some formatting commands to get the data displayed as you'd like it on the TV.

    This is what it looks like in the serial terminal:
    Wind Speed = 44 mph
    Direction = 221 degrees
    temperature = 774 F
    Rain = 1285 inches
    
    Waiting For Data
    
    Wind Speed = 44 mph
    Direction = 221 degrees
    temperature = 774 F
    Rain = 1285 inches
    
    Waiting For Data
    

    I manually entered "$ULTW002C00DD03060505,CR" into the terminal twice. A line feed character wasn't sent with my test but I'm pretty sure this won't make a difference since the code watches for the "$" character to indicate the start of the message.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2015-06-17 18:20
    K6MLE wrote: »
    Hello Peter,

    The project at hand is to take the serial data coming from my weather station ( Peet Bros. Ultimeter 100) and display some of the data on a wall-mounted display, so it's viewable from across the room. The data string looks like this:
    $ULTW002C00DD03060505,CR,LF

    I will want to watch for the "$" character and then ignore the "ULTW" portion. Following that, the data breaks down as follows:
    002C = Wind speed in MPH.
    00DD = Wind direction in degrees
    0306 = Last detected outdoor temperature in degrees F.
    0505 = Long term rain total, in inches.

    In testing the serial receive portion of the code, I wanted to be able to read the string and display the elements using the TV-text object. Once this works, the data will be fed into a case statement for direction and light an appropriate LED on a compass rose I already have waiting. The wind speed will be next and is to be displayed on a 3-digit multiplexed 7-segment display. My thought were to use the Propeller for this and just have the LED routine and 3-digit routine grab their data from a set of global variables and assign each of the routines to their own cog. My old way of doing this was to use a PIC microcontroller and have the multiplexed display get interrupted by the serial port, so the data could be updated. New data comes in every 5 minutes.

    Clearly, my experience with PicBasicPro isn't helping me at all with SPIN! While I'd like to learn SPIN, I wouldn't be adverse to trying this in a FORTH environment (my favorite language, actually!!).

    Thank you for your input!!

    To parse the string in Forth I would cheat and preprocess by just inserting spaces in the correct places then pass this to Forth as ULTW 002C 00DD 0306 0505 with ULTW being the deferred (execute at end of line) function to read in those four hex numbers, easy peasy.

    Now here's another take on a remote display as I have done this in some projects. I use a dedicated Prop mounted on a VGA monitor that is running 512x384 bitmapped graphics including scalable font routines and interface to this over serial, RS485 in fact. The reason for the dedicated Prop is that it uses up pretty much all it's memory running graphics. So the main Prop does what it does and communicates with the VGA Prop (which btw also supports a keypad) and this monitor can be hundreds of metres away too, or you can have more than one. How does that sound?
  • K6MLEK6MLE Posts: 106
    edited 2015-06-17 18:30
    Sounds very interesting, but perhaps a bit of overkill? Do you have any detail on how to set this up with Tachyon?

    I took a look at Tachyon, but I'm unsure how to load it, along with EXTEND, and then have much room left for development. Would I want to add some external memory somehow?
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-06-17 19:43
    The third parameter of PST (currently 0) is where to invert the data. Take a look at the source object of PST and it should give you the options. FullDuplexSerial is a similar object.

    Here is a link to Kye's String Handling Object
    http://obex.parallax.com/object/579
  • kwinnkwinn Posts: 8,697
    edited 2015-06-17 20:06
    K6MLE wrote: »
    I gave the code a try, with the only change being the baud rate (the weather station transmits at 2400). It didn't receive the data ... it appears the data polarity needs to be able to receive TRUE as opposed to INVERTED data. The weather station sends in non-inverted format. Not sure how to change it ... the mode variable doesn't seem to address this.

    Changing the mode from zero to one when you start FullDuplexSerial will invert the data. Duane's code in post 11 should then do what you need although it looks like you need to add some decimal points. Implementing Ariba's suggestion in post 12 is also a good idea.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2015-06-17 20:55
    K6MLE wrote: »
    Sounds very interesting, but perhaps a bit of overkill? Do you have any detail on how to set this up with Tachyon?

    I took a look at Tachyon, but I'm unsure how to load it, along with EXTEND, and then have much room left for development. Would I want to add some external memory somehow?


    There is plenty of room left over as Tachyon code is very compact and after EXTEND I typically load hardware headers, SDCARD, EASYFILE, WIZNET drivers, EASYNET, and then my "development" files :)

    However I think I've made the whole thing pretty easy to get working with as besides the intro document which will also run you through the steps of loading EXTEND which is really only pasting into a serial terminal with line delays, there is also the easy way of just loading the binary Explorer image which is just the kernel + EXTEND plus some little extras. As I mentioned earlier I am testing some VGA/TV code in the current kernel which I probably will finalize in the next day of two when I get around to it. So with this kernel you can do your large text display with Tachyon in the one chip.

    EDIT: To answer your first question about more detail on a remote VGA Prop solution I refer to an old post.
  • Mike GreenMike Green Posts: 23,101
    edited 2015-06-17 21:47
    Another option would be to look at the routines (methods) called getAnyNumber and hexDigit in FemtoBasic (from the Propeller Object Exchange roughly lines 1260 through 1305). This is the main numeric constant scanning routine in FemtoBasic. It gets its characters from a buffer whose address is provided in a global variable called "tp". It returns the number scanned stopping at the first invalid character. It can accept decimal values, hexadecimal ($), binary (%) and quoted single characters. On an error, it uses abort to exit and passes the address of an error message to abort.

    It can be easily modified to get its input from some other place (like a keyboard or serial input) and some other error handling technique could be used. There's no overflow checking, but this could be easily added.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-17 23:30
    kwinn wrote: »
    it looks like you need to add some decimal points.

    If one searches the forum for "DecPoint" they should find a method to display scaled integers with a decimal point added in the appropriate location.

    There's a link to Rich's forum search page at the top of post #1 of my index (see my signature for link).
  • JonnyMacJonnyMac Posts: 9,102
    edited 2015-06-18 09:15
    I've been doing a lot of parsing lately which means I've added lots of code to my generic strings library. After you've captured the formatted string, you can parse it like this:
    pub parse_data(p_str) | pos, tbuf 
    
      pos := str.first("W", p_str)                                   ' find position of "W"
    
      bytemove(@tbuf, p_str+pos+1, 4)                                ' copy 4 characters immediately after W
      wspeed := str.hex2dec(@tbuf, 4)                                ' convert to decimal
    
      bytemove(@tbuf, p_str+pos+5, 4) 
      wdir := str.hex2dec(@tbuf, 4)   
    
      bytemove(@tbuf, p_str+pos+9, 4) 
      otemp := str.hex2dec(@tbuf, 4)  
    
      bytemove(@tbuf, p_str+pos+13, 4)
      rain := str.hex2dec(@tbuf, 4)
    


    Since you're new to Spin it may not be obvious that I'm using the local long tbuf as a 4-byte work buffer (locals are always declared as longs, but can have their constituent parts manipulated). As with Duane's excellent example, this returns everything in whole numbers, though I suspect some fields should be displayed in tenths.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-06-18 11:08
    Jon,

    Since your hex2dec method doesn't require a terminating zero in the string you don't even need to move the characters to a temporary buffer.

    Doesn't this do the same thing? (No need to answer.)
    pub parse_data(p_str) | pos
    
      pos := str.first("W", p_str)                                   ' find position of "W"
    
      wspeed := str.hex2dec(p_str+pos+1, 4)                                ' convert to decimal
      wdir := str.hex2dec(p_str+pos+5, 4)   
      otemp := str.hex2dec(p_str+pos+9, 4)  
      rain := str.hex2dec(p_str+pos+13, 4)
    

    I doubt this version is easier to understand for someone new to Spin. It think there are probably more ways to parse this data with Spin than there are people who program in Spin.

    Eliminating the need for a terminating zero gives "hex2dec" a big advantage over "StrToBase" in many applications.
  • JonnyMacJonnyMac Posts: 9,102
    edited 2015-06-18 11:51
    You're absolutely right.

    I do tend to write verbose code, optimizing very late in the process.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2015-06-21 21:26
    K6MLE wrote: »
    Sounds very interesting, but perhaps a bit of overkill? Do you have any detail on how to set this up with Tachyon?

    I took a look at Tachyon, but I'm unsure how to load it, along with EXTEND, and then have much room left for development. Would I want to add some external memory somehow?

    If you are still interested I have the 32x15 VGA object integrated into Tachyon but it also draws big characters on the screen as well using big dot symbols in a 5x7 matrix. There are also box and turtle drawing commands and it's all so easy to use. I'm glad I finally got around to doing this as I do have quite a few applications and the big digit mode means I don't have to resort to 512x384 bitmap graphics.

    Even with the bloated Explorer image there is still 3.6k code/name space available (that's a lot of Tachyon code!) as well as the 2K BUFFER normally used by the SD card FAT32 system. You can also load EXTEND without all the bloat plus if you have a 64kB EEPROM you can even free up about 8 or 10k of hub memory by compacting the dictionary into the top 64k of the EEPROM.

    Check out the latest in the Tachyon thread for more info, there's a link to the latest VGA Explorer image.
  • tomcrawfordtomcrawford Posts: 1,126
    edited 2019-03-09 22:42
    Oops, I posted to the wrong thread.
Sign In or Register to comment.