Shop Learn P1 Docs P2 Docs
P1 Serial SBUS out Driver — Parallax Forums

P1 Serial SBUS out Driver

pic18f2550pic18f2550 Posts: 387
edited 2021-03-22 10:32 in Propeller 1

I am looking for some PASM code(P1) on the subject.
I want to control several servos over one line.


I have pasted the last status here. :)


  • iseriesiseries Posts: 1,284
    edited 2021-03-18 10:17

    I wrote one several years ago but it's in C and not SPIN.

    I believe Jonny Mac is back porting his version for the P2.


  • JonnyMacJonnyMac Posts: 8,311
    edited 2021-03-19 01:45

    @pic18f2550 said:
    I am looking for some PASM code(P1) on the subject.
    I want to control several servos over one line.


    I've started on an S.BUS transmitter for the P2 becuase it's really easy; you can use a smart pin UART and the rest of the code is in Spin -- no PASM required. This is the working part of it (not tested -- use at your own risk).

    pri sbus_output(looptix) | t, i                                 ' this method loaded into Spin2 cog
      t := getct()
        busyflag := true                                            ' disallow updates while packing bytes
        pack_sbus()                                                 ' pack 18 channels into 23 bytes
        busyflag := false                                           ' updates okay now
        tx_sbus()                                                   ' send last packet
        waitct(t += looptix)
    pri pack_sbus() | i, j, tmp, bc
      i := 1                                                        ' initialize locals
      longfill(@j, 0, 4)
      sbus[0] := $0F                                                ' start byte
      repeat while (i < 23)                                         ' pack 22 bytes
        repeat while (bc < 8)                                       ' 8 bits / byte
          tmp |= (channel[j++] << bc)                               ' get value, position, add to tmp
          bc += 11                                                  ' bump bit count
        sbus[i++] := tmp                                            ' save byte
        tmp >>= 8                                                   ' remove this byte from tmp
        bc -= 8                                                     ' adjust bit count
      sbus[23] := 0                                                 ' clear flags
      if (channel[16] > 1023)                                       ' set flag if >= 50%
        sbus[23] |= %0001
      if (channel[17] > 1023)                                       ' set flag if >= 50%
        sbus[23] |= %0010
      sbus[24] := 0                                                 ' end byte
    pri tx_sbus() | i, b, p
    '' Transmit packed bytes to S.BUS
    '' -- adds even parity bit if necessary
      repeat i from 0 to 24
        b := sbus[i]                                                ' get raw byte
        p := ones b                                                 ' count ones in byte
        if (p.[0])                                                  ' if odd
          b.[8] := 1                                                '  make even 
        b.[9] := 1                                                  ' extra stop bit
        wypin(txp, b)                                               ' send channel byte
        repeat while (pinr(txp) == 0)                               ' wait for it to finish

    For the P1 this will all have to be PASM. I don't think that will be a problem, though, as I've done unpacking in PASM for the P1 and the P2, and I've certainly done a few custom UARTS in the P1.

  • pic18f2550pic18f2550 Posts: 387
    edited 2021-03-19 15:55

    I think that's enough as a basic framework.


  • JonnyMacJonnyMac Posts: 8,311
    edited 2021-03-19 01:43

    I haven't done much work on my S.BUS for the P1, but this is what I'm thinking for the transmit subroutine. It takes care of adding the parity bit (if needed) and inverting the output.

    ' transmit byte in txout
    tx_byte         and       txout, #$FF                   wc      ' get parity of txout
        if_c        or        txout, PARITY_BIT                     ' add parity bit if odd # of 1s
                    or        txout, STOP_BITS                      ' add stop bits                   
                    shl       txout, #1                             ' add start bit
                    mov       txbits, #12                           ' start + 8 + parity + 2 stop
                    mov       txtimer, cnt                          ' start bit timer
                    add       txtimer, bittix
    :loop           shr       txout, #1                     wc      ' get bit (lsbfirst)
                    muxnc     outa, txmask                          ' output (inverted)
                    waitcnt   txtimer, bittix                       ' let bit timer expire
                    djnz      txbits, #:loop
    tx_byte_ret     ret
    ' -------------------------------------------------------------------------------------------------
    PARITY_BIT      long      %00_1_00000000
    STOP_BITS       long      %11_0_00000000

    What you'll notice is that the output treated as a standard, true-mode serial value until it is written to the TX pin with muxnc; this instruction handles the inversion.

  • JonnyMacJonnyMac Posts: 8,311
    edited 2021-03-19 04:07

    This is what I'm thinking vis-a-vis reading channels (words) from the hub and writing them to a cog array (bytes).

    ' pack s.bus
    pack            mov       hubwork, p_chhub                      ' point to channels (hub)
                    mov       t1, #$0F                              ' set start byte
                    mov       sbidx, #0
                    call      #wr_sbus
                    mov       sbidx, #1                             ' pack 1..23
                    mov       sbwork, #0                            ' clear workspace
                    mov       bc, #0                                ' clear bitcount
    :loop1          rdword    t2, hubwork                           ' get 11-bit channel value
                    add       hubwork, #2                           ' bump channel pointer
                    mins      t2, #0                                ' keep in bounds
                    maxs      t2, CH_MAX
                    shl       t2, bc                                ' move into position
                    or        sbwork, t2                            ' add into workspace
                    add       bc, #11                               ' update bits in sbwork
    :loop2          cmpsub    bc, #8                        wc      ' enough bits to store byte?
        if_nc       jmp       #:loop1                               '  if no, get next channel
                    mov       t1, sbwork                            ' copy for storage
                    and       t1, #$FF                              ' truncate to 8 bits
                    call      #wr_sbus                              ' write to cog storage
                    add       sbidx, #1                             ' increment storage index
                    shr       sbwork, #8                            ' remove last byte from workspace
                    cmpsub    sbidx, #23                    wc      ' last byte of data?
        if_nc       jmp       #:loop2                               '  if no, pack next byte
                    mov       t1, #0                                ' clear for digital channels
                    rdword    t2, hubwork                           ' read ch17
                    add       hubwork, #2
                    cmp       t2, CH_MID                    wc
        if_a        or        t1, #%0001
                    rdword    t2, hubwork                           ' read ch18
                    cmp       t2, CH_MID                    wc
        if_a        or        t1, #%0010
                    mov       sbidx, #23                            ' write digital channels
                    call      #wr_sbus
                    mov       t1, #$00                              ' set end byte
                    mov       sbidx, #24
                    call      #wr_sbus

    This is the subroutine for storing a value in the sbus array

    ' write value in t1 to sbus.sbidx
    wr_sbus         mov       widx, #sbus                           ' point to sbus array
                    add       widx, sbidx                           ' add index
                    movd      :write, widx                          ' update :write instruction
    :write          mov       0-0, t1                               ' write value to table
    wr_sbus_ret     ret
  • JonnyMacJonnyMac Posts: 8,311
    edited 2021-03-19 20:18

    I needed a mental break from a work project so I thought I'd take a few minutes to isolate my channel values to S.BUS array PASM code. It works! My style is to test chunks of 'big' projects in isolation and then merge the working pieces. Running at 80MHz the PASM code takes about 24us -- and this included writing the test values to the hub instead of a cog. This makes sense give similar code running on the P2 at 200MHz takes less than 4us. The P2 is running 2.5x as fast as the the P1, and P2 instructions take 2 clocks versus 4 for the P1.

  • @pic18f2550 Can you recommend a low- to medium-cost S.BUS servo? I'd like to experiment with output (other than feeding it to a decoder cog).

  • JonnyMacJonnyMac Posts: 8,311
    edited 2021-03-22 00:21

    Okay. I based my question on this line from your first post.

    I want to control several servos over one line.

    I really don't care about controlling servos, either, but I'd like to have a couple for testing, anyway. Like you, I will usually be talking to other smart devices (e.g., Ronin camera stabilizer).

  • `Hello,
    I am looking for some PASM code(P1) on the subject.
    I want to control several servos over one line.


    I don't want to control servos.

  • Sorry, :(
    but my ESCs can be controlled like servos.
    Most of the ESCs I know are still controlled with PWM.
    To avoid unnecessary questions of understanding I have written servos. :)

  • pic18f2550pic18f2550 Posts: 387
    edited 2021-03-22 09:51

    Startbyte = 11110000b (0xF0) ??

    You probably want to do the parity and inversion in the "fullduplexserial.spin".

    Your code is nicely compiled.

    Because I have to save some time I put everything into a COG.
    This takes care of the packing and sending of the data.
    This also makes it easier to monitor if the COG is still alive. (runtime monitoring):)
    In case of failure, the remaining ESCs on the other COGs can release their reserves to avoid a crash.

    Translated with (free version)

  • I'm just skimming the scraps of data that are floating around on the net.
    There is also a return channel on the same line.
    I noticed this in an example of servo programming.
    Let's see if I get an answer from flyduino on the subject of programming.

  • Startbyte = 11110000b (0xF0) ??

    No, the start byte if 0x0F. Whomever posted that was looking at an oscilloscope which displays bits as they arrive; what they didn't account for is that serial bytes are transmitted LSB first.

    I'm just skimming the scraps of data that are floating around on the net.

    And there is a lot of bad information -- I was scratching my head, too. I finally bought an inexpensive transmitter/receiver pair and configured the receiver for S.BUS output so I could look at it with a 'scope and capture the data. I have a working P2 receiver/decoder object demonstrated in this thread:


    I used this to understand the workings of S.BUS. It's not difficult once one understands that inverted serial data that uses 9 bits (8 plus even parity) and how data packing/unpacking works. I also have a bit of test code that configures one P2 pin as an 9-bit transmitter and one as a 9-bit receiver so I could test transmitting and receiving bytes as they appear in the S.BUS packet. Again, I do things in chunks and then bring the working chunks together. I'm really swamped on a work project right now so I haven't had time to finish my P1 transmitter or receiver objects, but I do have all the working "chunks."

  • Thanks for the info.
    I wanted to buy with not yet another receiver that I do not need afterwards mer.

    Have you ever given a servo a new ID?

    I would have preferred a P2 as well.
    Ader the part can be soldered poorly with the soldering iron.
    I do not mean the 100 legs, rather the strange ground plane under the housing.
    The current consumption should also be under 250mA.
    All things why I am still tinkering with the P1.

  • Have you ever given a servo a new ID?

    No. I do not own any S.BUS servos. I'm trying to find some reasonably-priced S.BUS-compatible servos for testing my object code. Again, I can use the Propeller (1 or 2) to test my code, but as with my S.BUS receiver test, I want to have an external part to verify my code.

  • Jon: there are some Futaba S3072HV's on Amazon, but that "reasonably-priced" criteria might be a bit of a problem. :(

  • Yeah, I'm sure they're worth the price for somebody who will actually use them, but I'm just running tests and they would end up in a parts drawer.

  • I also like collecting bits to play with, but I nearly fainted when I saw the price. Nice servo. Over priced. Pass.

  • dgatelydgately Posts: 1,540
    edited 2021-03-23 00:18

    @JonnyMac said:

    Have you ever given a servo a new ID?

    No. I do not own any S.BUS servos. I'm trying to find some reasonably-priced S.BUS-compatible servos for testing my object code.

    Maybe (Arg, such a huge URL, but looks to be an affordable solution for testing...):


  • I have some of those, Dennis -- they're cool (like low-cost Dynamixels). They have their own protocol; they are not S.BUS servos.

  • @JonnyMac said:
    I have some of those, Dennis -- they're cool (like low-cost Dynamixels). They have their own protocol; they are not S.BUS servos.

    Good to know that they are not S.BUS!

  • Is there readily available code for them?

  • JonnyMacJonnyMac Posts: 8,311
    edited 2021-03-23 17:57

    I have some experimental code that handles the basic features; I am going to port to the P2 (as a proper object), finish the feature set, then back-port to the P1. I can send you what I have via PM, as this is out-of-scope with the OP's thread.

  • Thanks Jon,please send via pm.

  • @pic18f2550 I got my P1 S.BUS receiver code working -- just in time for my visit to the mother ship (Parallax) tomorrow. It seems to work fine here, please let me know if it works for you, too.


  • @JonnyMac
    Thanks, with this I can check my TX to see if it is doing what it is supposed to.

    Say hello to the flagship.

    I'm working outside, so everything takes a little longer now.

  • Sorry, it just dawned on me that the code I posted last night was for S.BUS RX on the P1 -- my friend in the movie business needs that. With the RX working, I can get TX going without a lot of problems (and use the RX code to test). Forgive me confusion after several long days.

  • No problem.:)
    Everything is OK.

  • JonnyMacJonnyMac Posts: 8,311

    My friend John ( reached out last week asking if I had a P1 S.BUS TX driver so I took a small break an looked at some code I wrote a while back. The time away allowed me to see and fix a PASM error and I feel pretty confident in the code now. I have looked at the output on a Logic Analyzer with a specific set of test values. I also wrote a test program to read the values from the transmitter using my S.BUS receiver and show them on-screen -- everything lines up. Finally, John sent me a little board that will take S.BUS in and provide PWM signals out -- that works, too.

    I have some S.BUS servos (gift from a friend) but no programmer for them, so I ordered one today. Still, I feel confident in the driver. If you have any problems, please let me know.

Sign In or Register to comment.