Shop OBEX P1 Docs P2 Docs Learn Events
Two Propeller Communication — Parallax Forums

Two Propeller Communication

Is there anyone tried sort of i2c between two propellers? Since I can't just wait for prop2, I was wondering if sort of i2c can make two prop1s communicate. Since I need them to exchange lots of data with fast speed, I was planning to design one cog for each prop1 send and read while another cog for each can read and send. Would that be waste of cogs?

Also, is there a way I can attach an extra eeprom ? - What i'm trying is using machinne learning algorithm that needs lots of data....

Please let me know if you have any related thoughts..
«1

Comments

  • There are lots of communications objects in the ObEx that you could use for this. One high speed protocol is this one. I2C is complex and relatively slow. You can attach extra EEPROMs and/or replace the existing EEPROM with a larger one (up to 128K rather than the standard 32K or 64K). Most of the I2C drivers in the ObEx support multiple/large EEPROMs and you can use other pairs of I/O pins to attach more devices. If you need lots of data, why not just use an SD card? If you're only going to be using the SD card with the Propeller and don't have to transfer data to a PC, you won't need the FAT compatibility. Tachyon Forth is one programming system that uses a virtual memory scheme with a non-PC compatible SD card format for adding large amounts of fast (cached) storage. Another option, particularly if there's mostly reading of data and only rare writing, would be an SPI serial flash chip. These come in multiple MB sizes and 8-pin packages. There are drivers in the ObEx that simplify their use.
  • ElectrodudeElectrodude Posts: 1,621
    edited 2016-05-04 19:24
    Why do you want to use I2C? Just make both props share a crystal, and use the prop system clock as your clock.

    Some people have written special protocols for prop-to-prop communication with transfer rates on the order of megabytes per second. I can't remember who, though, and have never needed one.

    I think you might be able to (ab)use the FullDuplexSerial driver or anything based off of it and set the baud to something ridiculously high (say, clkfreq) on both propellers. They'll both go as fast as they can, and if the maximum receive speed is the same as the maximum transmit speed, and I think they are the same because of how FullDuplexSerial and descendents use coroutines, it will work.


    If you need to store lots of data, I'd recommend an SD card instead of an EEPROM. But you can easily attach another EEPROM if you really want to: attach it to the same pins, but don't connect the A2, A1, and A0 lines all to 0 (GND), and use i2c#EEPROM | %xyz, where xyz are the values you connected A2, A1, and A0 to. Since there are 8 possible values for 3 bits, you can connect up to 8 EEPROMS like this.

    EDIT: I'm really good at loading pages, not looking at them for 15 minutes, and then replying.
  • jmgjmg Posts: 15,148
    ... Since I need them to exchange lots of data with fast speed...

    Numbers always help, what exactly is " lots of data" and "fast speed" ??
    and what size Packets work best ?
    Do you need handshake built into the link ?

    Do you really need full duplex, or is half duplex OK ?

  • hylee101001hylee101001 Posts: 48
    edited 2016-05-05 17:58
    Thanks all. I haven't planned it in detail yet. With propeller 1, I have used already 7 cogs and and memory alot (~250 longs remaining currently). But, I wanted to add more features on that code. So, the number of data is like ~ 2000 and need to calculate matrix operations for 2000 by 2000 matrix for every iteration. Though exact number are not yet determined, I want them to be ~100 Hz or little less. So, if the iteration gets slower, I wanted to distribute it to couple more cogs because they are similar calculations for different axises.(- I am trying to implement gaussian or polynomial kernel regression) SO.. I don't know.. I'm just trying to figure out if this plan would be plausible or not - with matlab, it was simple to use dot products instead of for loops which made it very fast.
  • There are likely ways to free up both cogs and memory, but you'll have to share more about what you've done and planned. There's always a huge penalty for splitting functionality across processors and memory systems. You have to be careful about how you divide things up.
  • hylee101001hylee101001 Posts: 48
    edited 2016-05-10 16:05

    Oh, never mind. I put waitcnt and it works. Thanks.
    ====================================================================================

    Okay, I have tried with uart with 115200 baud rate because after comparing the prop commnication object, it looks the speed is not that different and uart can send and receive data with one a cog. - let me know if I'm not precise.

    Anyway, I tried some message receive and transmit. But, the parsing is little tricky. My set up is using characters. For example when I send Euler angle,

    repeat i from 0 to 2
    com.str(String("c"))
    if (long[eAnglePtr] =>0)
    axis := 2*i + 1
    else
    axis := 2*i + 2

    com.dec(axis)
    if (||(long[eAnglePtr]) < 10)
    com.str(String("0000"))
    elseif (||(long[eAnglePtr]) < 100)
    com.str(String("000"))
    elseif (||(long[eAnglePtr]) < 1_000)
    com.str(String("00"))
    elseif (||(long[eAnglePtr]) < 10_000)
    com.str(String("0"))
    com.dec(||(long[eAnglePtr]))


    And another prop receives:
    0M11200M21200M31200M41200M51200M61200c100487c400494c517930M11200M21200M31200M41200M51200M61200c100487c400494c517930M11200M21200M31200M41200M51200M61200c100487c400493c517930M11200M21200M31200M41200M51200M6

    (There's another message M1 from M6 that is all positive 1200)

    The reason why I used this rather than binary is because the order of message can be changed without updating receiver's code if I add or modify message.

    Anyway, to parse the message, I used the same code as I parsed message from pc to prop.


    PUB communicate
    repeat
    if com.RxCount
    readCharArray_prop
    com.RxFlush
    else
    sendCtrlRef

    PRI char2ASCII(charVar) ' currently not used
    result := byte[charVar]
    ' Don't know how, but this returns ascii code of char

    PRI ASCII2Dec(ASCII)
    result := ASCII - 48

    PRI readCharArray_prop | newPWM, newPidProperty, newRequest, newMode
    varChar2 := varchar
    varChar := com.CharIn
    if (48=<varChar AND varChar=<57) 'btw 0-9
    newValue := newValue*10 + ASCII2Dec(varChar)

    elseif(varChar == 77) ' M -> motor
    type := 1 'next 5 digits are (motornumber & pwm)
    elseif(varChar == 99) ' c -> comp filter
    type := 2 ' next 5 digits are mode types


    if (type==1)
    if 11099 < newValue AND newValue < 63000
    motorNumber := newValue/10000
    newPWM := newValue//10000
    case motorNumber
    1: long[pulsePtr][0] := newPWM
    2: long[pulsePtr][1] := newPWM
    3: long[pulsePtr][2] := newPWM
    4: long[pulsePtr][3] := newPWM
    5: long[pulsePtr][4] := newPWM
    6: long[pulsePtr][5] := newPWM
    type := 0
    newValue := 0

    elseif (type == 2)
    if 1_00000 =< newValue AND newValue =< 6_18100
    axisNumber := newValue/100_000
    value := newValue//100_000
    case axisNumber
    1: long[eAnglePtr][0] := value
    2: long[eAnglePtr][0] := -value
    3: long[eAnglePtr][1] := value
    4: long[eAnglePtr][1] := -value
    5: long[eAnglePtr][2] := value
    6: long[eAnglePtr][2] := -value
    type := 0
    newValue := 0
    value := 0
    axisNumber := 0

    But, somehow it doesn't parse it well unlike that it parsed ok message from pc. It says all zero.. Would it be because, from pc, the message doesn't keep coming into prop, but this one has continuously incoming message?


    Can anyone notice any bugs? If you have an idea about how to communicate better and efficient, please let me know






  • Start by posting a description of what you're trying to communicate. It's nearly impossible to help with bugs without a description of what the code is supposed to do. I'm guessing, but probably your basic unit of communications is some kind of numeric parameter followed or preceded by some kind of command. I'd suggest starting with one or more numbers separated by commas or semicolons followed by a one or two letter code. If you want, you could allow hexadecimal and binary numbers preceded by $ or % with the hexadecimal "a" through "f" in lower case and the command codes all in upper case. There's a nice number parser that's part of FemtoBasic (in the Propeller Object Exchange) ["getAnyNumber" and "hexDigit" - near the end].
  • JonnyMacJonnyMac Posts: 8,929
    edited 2016-05-10 20:05
    I do a lot of simple parsing -- key word being "simple" -- and I think you can simplify your process and make it more manageable.

    As we say here in Hollywood... for your consideration. Note that the process is divided into small (atomic) chunks.
    pub main | k                                                        
                                                                     
      setup                                                          
                                                                     
      term.rxflush 
    
      repeat
        k := term.rxcheck 
        case k
          "m", "M" : get_motor
          "c", "C" : get_comp_filter
    
        process_values
    
    
    pub get_motor | k 
    
      k := term.rx                                                  ' get motor #
    
      if ((k => "0") and (k =< "9"))  
        motor[k - "0"] := get_dec(4)
    
    
    pub get_comp_filter
    
      filter := get_dec(5)
    
    
    pub get_dec(digits) | value, sign, k
    
      value := 0
    
      k := term.rx
      if (k == "-")
        sign := -1
      elseif ((k => "0") and (k =< "9"))
        sign := 1     
        value := k - "0"
        --digits
      else
        return 0              
      
      repeat digits
        k := term.rx                
        if ((k => "0") and (k =< "9"))
          value *= 10        
          value += k - "0"   
        else
          quit    
          
      return sign * value                    
      
    
    pub process_values
    
      ' do something with values in hand
    

    The only "problem" with my approach is that it requires a separator between fields. Since you're going with human readable text, you might consider adding this (space or comma) as it will help you manually debug strings.
  • hylee101001hylee101001 Posts: 48
    edited 2016-05-10 22:37
    This is awesome. It looks way simpler than mine. I need to try this style. Thanks!

    I actually have a question. If human-readability is not considered, does this still require some separators btw the tokens? Actually, when I send the msg with my previous codes, it didn't work well if there's no waitcnt(cnt +clkfreq/10) from the sender-side. In this case, I guess the waitcnt worked as sort of separator. So, what happens to prop buffer if there's no separator? - I thought, "M" and "C" are working as separator as well as "starter" of the token.
  • I actually have a question. If human-readability is not considered, does this still require some separators btw the tokens?

    Yes. You'll note in the get_dec() method a non-digit character will cause an early exit. If this is M or C it will be lost to the stream using this strategy. By inserting a field separator, this problem goes way.

    The way I've structured the code it does processing on-the-fly, but you may want to check the size of your RX buffer for if you're blasting out a very long command string.
  • Oh I see. With this, I don't even need to pad zeros at the front to match up the number of digits.
  • Oh I see. With this, I don't even need to pad zeros at the front to match up the number of digits.

    Exactly.

  • You might also try something like this untested code. It's the same as JonnyMac's code, except term.rx is moved to a consume method and k is made global and is set by consume:
    VAR
    
      byte k ' holds current character
    
    pub main
    
      setup
    
      term.rxflush
    
      consume ' there is no current character; get the first character
    
      repeat
        case consume
          "m", "M" : get_motor
          "c", "C" : get_comp_filter
    
        process_values
    
    
    pub get_motor | n
    
    
      if ((k => "0") and (k =< "9"))
        n := consume - "0" ' get motor number now; we can't get it later, it will be gone
        motor[n] := get_dec(4)
    
    
    pub get_comp_filter
    
      filter := get_dec(5)
    
    
    pub get_dec(digits) | value, sign
    
      value := 0
    
      if (k == "-")
        sign := -1
        consume
      elseif ((k => "0") and (k =< "9"))
        sign := 1
        value := consume - "0"
        --digits
      else
        return 0
    
      repeat digits
        if ((k => "0") and (k =< "9"))
          value *= 10
          value += consume - "0"
        else
          quit
    
      return sign * value
    
    
    pub process_values
    
      ' do something with values in hand
    
    pri consume
      ' return previous character, get next character
    
      result := k
      k := term.rx
    
    

    This allows you to not lose characters on early exit. For example, "M01C2;" will set motor[0] to 1 and filter to 2, even though both numbers were too short. As JonnyMac said, his code would drop the "C2" if you sent it that. (The semicolon in my example is to terminate the get_dec after the 2. Otherwise, it would just sit there waiting for more characters. You could have another command instead of the semicolon if you had more commands to send, or you could send all 5 expected digits. You could also use a newline or any other character that doesn't do anything else.)

    It does this by holding the last-received character in k, and only accepting another character when it found a use for the previous one. So, if get_dec gets a "C" and doesn't know what to do with it, it returns without consuming the "C". When control gets back to the main loop, it consumes the "C" and then calls get_comp_filter.

    You could remove the argument to get_dec and make it go until it got an invalid character, if you wanted to.

    Something like this parser (a very simple recursive ascent parser) can be used to parse pretty much anything. More advanced ones have more helper functions (like get_motor and get_comp_filter) that call each other. In fact, I'm pretty sure that Prop Tool's compiler uses a recursive ascent parser, although it's more complicated in that it operates on whole tokens at a time and not just single characters.
  • hylee101001hylee101001 Posts: 48
    edited 2016-05-21 00:27
    I found something to be done on the "sender" side. Because I was not using the buffer fully. For instance,

    com.str(String("c"))
    com.dec(x)
    com.str(String("d"))
    com.dec(y)

    ...

    In that way, I guess the buffer is only filled with a byte, "c" and send, then the buffer is filled with a long, x and send and so on.

    So, I wonder how I can concatenate all characters and longs together..?

    Also, is there a specific time for the sender to wait so that the receiver buffer shouldn't be overwritten? If so, how much time do I need to pause?
  • The characters are put into a buffer and sent at whatever Baud is specified so at most speeds they're concatenated. The default buffer size for FullDuplexSerial is 16 bytes although it can be changed to other sizes up to 255 bytes. Several alternative serial objects have different buffer sizes.
  • I really like Beau Schwabe's High Speed communication. (12mbit?)

    It is able to handle more then one P1 but needs basically 2 Cogs per Prop. One is a sender, the other the receiver.

    So the data will be send around and around like in a loop. You basically create a shared HUB ram area between the Cogs and can share Mailboxes like done on one Prop, but with multiple Props.

    The interesting part here is that you do not need to write a parser for your data stream or create a communication protocol at all. So you can use most of the standard objects without change. But all Props loose two Cogs and two pins and the amount of hub ram to hold the shared data.

    How you use the shared hub buffer is up to you. Its just hub-ram accessible from Spin or Pasm, but identically on all Props.

    Sadly there is no support for locks over multiple Props, so you have to take care of that by yourself.

    Worth looking at it.

    here some links:

    http://forums.parallax.com/discussion/99222/propeller-demo-14-5-meg-baud-high-speed-prop-to-prop-serial-communication

    http://forums.parallax.com/discussion/comment/694610#Comment_694610

    http://forums.parallax.com/discussion/134641/demo-high-speed-multi-prop-to-prop-communication

    Enjoy!

    Mike


  • msrobots wrote: »
    I really like Beau Schwabe's High Speed communication. (12mbit?)

    Pretty much a ready made solution for providing cog/IO expansion.
  • hylee101001hylee101001 Posts: 48
    edited 2016-05-21 20:47
    Thanks Mike. I will try the link soon.

    For all, I have a question about my code. I think the parse algorithm works well with "Parallax Serial Terminal.spin". But when I tried with 4fullduplexseiral.spin" negative values becomes to zero - especially for 'a' , and 'b' for less often times. I think the parser cog works fine but debugging code is not working well. Just for test case, each object has only one connection for now. Can anyone notice a possible bug? - Odd thing is that if only ParallaxSerialTerminal objects are used, there was no issue at all..

    For sure, the incoming value format is a#;b#;c#;d#;a#b#;c#; .. and so on.
    where # are any integers between -18000 to 18000

    Thank you.


    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
    OBJ
    
      debug   : "ParallaxSerialTerminal.spin"
      sensors : "fullDuplexSerial4port_tier2.spin" 
      'comm    : "fullDuplexSerial4port_tier2.spin" 
    VAR
      ' communication variables
      long sensorCogId, sensorCogStack[255]
      long sensorByte
    
    
      long lock
    
      ' state variables
      long eAngle[3], distGround
    
    
    long  db     
    long  att    
    
      ' pid variables
    
    
      ' throttle and motor variables
      
      ' debugging
      'long tick, tock, tick2, tock2, dt, dt1, dt2, dt3, dt4, dt5, dt6, dt7, dt8
      'long isChecked
      
    PUB main
      db :=0
      att :=1
      lock := 0
      
      startSensor
      
           
      debug.quickStartDebug
    
    
    
      {repeat
        if sensors.rxisin(att) > 0
          debug.char(db, sensors.charin(att))
       }
    
    
      repeat
        if (lock == 0)
      '    tock := cnt
     '    isChecked := false
          debug.clear
          debug.str(String("eu x: "))
          debug.dec(eAngle[0])
          debug.newline
          debug.str(string("eu y: "))
          debug.dec(eAngle[1])
          debug.newline
          debug.str(String("eu z: "))
          debug.dec(eAngle[2])
          debug.newline
      {
          if (IsChecked==1)
            dt := (tock - tick)
            if (dt >0)  
              dt1 := dt
    
          dt2 :=  (tock2-tick2)
            
          debug.str(db,String("Update Rate:"))
          if (dt2>0)
            debug.dec(db,clkfreq/(dt1 + dt2))
          else
            debug.dec(db,clkfreq/dt1)
          debug.str(db,String("Hz")) 
          debug.newline(db)  
        }
          waitcnt(cnt + clkfreq/10)
    
    PRI stopSensor
     if sensorCogId
       cogstop(sensorCogId)
    
    PRI startSensor  
    
      stopSensor
      sensorCogId := cognew(readSensor, @sensorCogStack)
    
    PUB readSensor
    
      sensors.quickStartSensorInputs
      sensors.rxFlush(att)
      consumeAttSensor
      repeat
        case consumeAttSensor
          "a" : getEulerPitch
          "b" : getEulerRoll
          "c" : getEulerYaw
          "d" : getDistGround     
                                 
    PRI consumeAttSensor
    
      if (sensors.rxisin(att) > 0)
        result := sensorByte
        'debug.char(db, sensorByte) 
        sensorByte := sensors.CharIn(att)
    
    
    PUB getEulerPitch
    '  tock := cnt
    '  tick2 :=cnt 
    '  isChecked := 1
      lock := 1
    
      'debug.str(db, String("["))
      'debug.str(db, String("A"))
      'debug.char(db, sensorByte)
    
      eAngle[0] := getDec
      'eAngle[0] := 123
      'debug.dec(db, eAngle[0]) 
      'debug.str(db, String("]")) 
    '  tock2 :=cnt 
    '  tick := cnt
    '  isChecked := 0
      
    PUB getEulerRoll
      eAngle[1] := getDec
    
    PUB getEulerYaw
      eAngle[2] := getDec
      lock := 0
      
    PUB getDistGround
      distGround := getDec    
      'sensors.rxFlush(att)
    
    PRI getDec | value, sign, answer
    
      value := 0
      if (sensorByte == "-")
        sign := -1
        consumeAttSensor
      elseif ((sensorByte => "0") and (sensorByte =< "9"))
        sign := 1
        value := consumeAttSensor - "0"
      else
        answer := 8
    '    return answer
    
      if(answer <> 43)
        'debug.str(db, string("im here")) 
        repeat 
          if ((sensorByte => "0") and (sensorByte =< "9"))
            value *= 10
            value += consumeAttSensor - "0"
          else
            quit
        answer := sign*value
       ' debug.str(db, string("(answer:"))
       ' debug.dec(db, answer)
       ' debug.str(db, string(")"))
          
      return answer
    
    
    
      
    


  • msrobots,

    "I really like Beau Schwabe's High Speed communication. (12mbit?) ... It is able to handle more then one P1 but needs basically 2 Cogs per Prop" .... No, only ONE cog is required to maintain background communication.

    If you only have two Props connected, then one needs to be a "sender" while the other needs to be a "receiver"
    If you have three or more Props only ONE needs to be a "sender" while all of the others are "receivers" ... This is just to initiate the communication flow. The "buffer" can be updated by any Propeller at any time thus no need to have a sender and receiver per Propeller.
  • where can I get the " High Speed communication" objects?
  • Yet another case of disappearing files?

    Since the Forum upgrade most of my code that I wrote simply seems to be gone.
    Even the OBEX is void of any code I provided.

    It's been almost two years since I was let go from Parallax and the Propeller is no longer my processor of choice. Consequently my heart and head aren't there anymore.
  • That's a shame, Beau. I always find your posts to be worth reading. I have very little OBEX stuff in my library but I snapped up your "Beau-net" right away. Can't believe that it's not more widely used. I wonder if I could make it work with PropBASIC....hmmm.
  • Yet another case of disappearing files?

    Since the Forum upgrade most of my code that I wrote simply seems to be gone.
    Even the OBEX is void of any code I provided.

    It's been almost two years since I was let go from Parallax and the Propeller is no longer my processor of choice. Consequently my heart and head aren't there anymore.

    Beau,

    Most, if not all, the code you wrote while working for Parallax is now under the author "Parallax Inc" in the OBEX.

  • where can I get the " High Speed communication" objects?

    This is the latest I have.

  • hylee101001hylee101001 Posts: 48
    edited 2016-05-22 16:16
    For vss, how much voltage do I need? Is it okay to use voltage from the prop itself? From the client demo, it looks all bytes are broken. I don't know why.
  • For vss, how much voltage do I need? Is it okay to use voltage from the prop itself? From the client demo, it looks all bytes are broken. I don't know why.

    VSS is ground. The Propeller should be powered from 3.3 volts.

  • Publison ...

    "Most, if not all, the code you wrote while working for Parallax is now under the author "Parallax Inc" in the OBEX" ... and WHY is that exactly? ... It makes any updates impossible to manage.
  • msrobots wrote: »
    I really like Beau Schwabe's High Speed communication. (12mbit?)

    It is able to handle more then one P1 but needs basically 2 Cogs per Prop. One is a sender, the other the receiver.

    So the data will be send around and around like in a loop. You basically create a shared HUB ram area between the Cogs and can share Mailboxes like done on one Prop, but with multiple Props.

    Just a note to say that my little game console (in the signature) uses that high speed serial driver to share the video ram between the two propellers, one way only (from CPU to GPU), in case some of you are looking for another practical application.
  • hylee101001hylee101001 Posts: 48
    edited 2016-05-22 22:34
    Hmm. I connected all resistors (100 and 330 ohms) and pins as instruction in the file. (vss as ground as well). And, I ran on server side then F10 on client side. Codes are not changed at all. But I couldn't figure it out working. Is there any other setting that I'm missing other than two props and resisters?
    My messy bread board
Sign In or Register to comment.