Shop OBEX P1 Docs P2 Docs Learn Events
Using Python to control servo on basic stamp 1 project board? — Parallax Forums

Using Python to control servo on basic stamp 1 project board?

dmehling2dmehling2 Posts: 21
edited 2014-03-27 15:05 in BASIC Stamp
I have a basic stamp 1 project board with a USB to serial adapter. I also have a servo motor. I would like to know if it is possible to create scripts in Python that I can use to control the servo? I will not be using the stamp independently, but need to periodically send commands from my computer to the servo.
«1

Comments

  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-04 10:29
    dmehling2,

    Welcome to the Forums!!!!


    In answer to your question, I don't see why not...

    http://stackoverflow.com/questions/676172/full-examples-of-using-pyserial-package

    ...The real issue might be configuring the serial port depending on which platform you are running Python .... i.e. Windows, Linux, MAC, etc.

    As far as communication to the BS1, You should construct some type of SYNC character in your data transmission that the BS1 can recognize as a unique pattern.
  • Mike GreenMike Green Posts: 23,101
    edited 2014-03-04 11:35
    You also have to keep in mind that the Stamps can only do one thing at a time. There's no serial input buffer specifically. If the Stamp isn't listening to the serial port (with a SERIN), it won't see anything sent to it. Typically, the Stamp acts as the master. It periodically sends a request for information to the PC, then waits for a response rather than the PC periodically sending new information to the Stamp without regard for what the Stamp is doing.

    You want to keep the Baud to 2400 or less. The BS1 doesn't handle higher Bauds well at all.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-04 12:15
    Mike Green wrote: »
    You also have to keep in mind that the Stamps can only do one thing at a time. There's no serial input buffer specifically. If the Stamp isn't listening to the serial port (with a SERIN), it won't see anything sent to it. Typically, the Stamp acts as the master. It periodically sends a request for information to the PC, then waits for a response rather than the PC periodically sending new information to the Stamp without regard for what the Stamp is doing.

    You want to keep the Baud to 2400 or less. The BS1 doesn't handle higher Bauds well at all.

    When you say it periodically sends a request, how often would that be?
  • Mike GreenMike Green Posts: 23,101
    edited 2014-03-04 15:12
    How often? That depends on how quickly you want the PC to be able to change the servo position or speed. Servos work on a 20ms cycle with a control pulse sent to the servo once about every 20ms. A 2400 Baud character takes about 4ms. That would be enough to signal the PC which could respond with one or two characters taking 4 or 8ms to respond. That's still within the 20ms cycle time, so the Stamp could get updated every servo control cycle. If your "time to update" is more, the Stamp could do it less often, maybe once every 10 cycles (once every 200ms).
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-04 16:31
    I will be using the servo to turn a volume knob on an amplifier, and I would only need to make adjustments after a few seconds or minutes. If I understand you correctly, that should give me plenty of time for communication between the stamp and my computer to take place.

    I also need to clarify something because I have little experience with servos. Does a servo constantly require power to maintain its position? If I were to turn off the project board, would the servo be able to maintain its current position?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-04 16:45
    A Servo PAL would off-load some of the duties of a BS1 and eliminate some of the timing issues between receiving the RS232 and setting the proper pulse for the Servo.

    Servo PAL:
    http://www.parallax.com/product/28824



    Edit:
    In answer to your question ... "Does a servo constantly require power to maintain its position? If I were to turn off the project board, would the servo be able to maintain its current position?" - If you remove the pulse signal or power, the servo will remain in the last commanded position but will have no holding power or torque to keep it there. If an external force is applied, the Servo may be repositioned manually.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-04 16:55
    I would prefer not to purchase another component for this project. Based on what I am actually wanting to do with the servo, could I get by without it?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-04 16:57
    Yes, you could, but if you have timing issues due to single thread processing, the Servo PAL is an option.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-04 17:02
    Can you give me an example of the most likely timing issue? It's perfectly fine if the servo does not respond instantaneously, but I would hope it would happen within a couple of seconds. Also, I will have absolutely no other processes going on. The stamp only has the servo and the serial connection. How likely would it be for me to have a problem under those circumstances?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-04 17:09
    My guess is going to be if you run into a scenario where the BS1 is updating the Servo pulse and misses the oncoming RS232.

    1) A Polling mechanism from the BS1 that makes a request to the PC might solve this.

    2) ...or you could just blindly send several commands to the BS1 and hope that at least one will be read correctly. In this case though, if the BS1 doesn't properly "time out" then the pulse to the Servo might be dropped if it is not within the 20ms window.


    "The stamp only has the servo and the serial connection. How likely would it be for me to have a problem under those circumstances? " - You will probably be just fine ... I was simply offering an alternative, that would allow the BS1 to have a little more overhead to do other things if you wanted to expand your project functionality.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-04 17:16
    How would I create a polling mechanism?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-04 17:22
    The BS1, after so many cycles of updating the Servo would send a RS232 command to the PC
    The BS1 would then listen for a RS232 reply from the PC... if it didn't receive a reply it would need to timeout and continue operations
    If it does receive a reply, then it would update the value representing the position of the servo, and then continue operations
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-05 11:19
    I will have to figure out how to do that later. First, I want to come back to what you said in your first response.

    In terms of configuring the serial port, does it matter that I have a USB to serial adapter rather than a direct connection to a serial port? Would the python script be the same? (I am running Windows 7).

    How do I add a SYNC character to the data transmission? Is that something that's covered in a tutorial?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-05 12:31
    dmehling2,

    You just need to configure your PC so that Python can correctly communicate through the serial port ... This would be the first obstacle I would try to solve.

    As far as sending data to the BS1 or receiving data from the BS1 using a sync, this is purely in software for data alignment. For example if you sent the position of 1000 serially you would need to break it down into two bytes, since serial data only sends bytes. So sending 1000 might look like this ....

    3 , 232

    1000 = 3 * 256 + 232


    There is nothing (i.e. a SYNC) preventing the order of the numbers to get swapped along the way so they could end up as ...

    232, 3

    59395 = 232 * 256 + 3


    So 59395 is obviously not the number you want to send, so you send a SYNC character(s) that you identify in software as a starting point. IOW don't do anything else unless the software "sees" the SYNC. Usually the SYNC is constructed so that it is unique in a way that any combination of the data you are sending can not be reproduced and mistaken as a SYNC.

    i.e. !S would be two bytes a "!" or decimal 33 and a "S" or decimal 83, where upon your actual data would follow and the serial sequence might look something like ...

    33 83 3 232

    On the receiving end you look for the SYNC of 33 and 83 and don't process the remaining data (3 and 232) until the SYNC condition has been met.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-06 12:05
    That's all fairly incomprehensible to me at the moment. I only have a basic understanding of Python. What I really need is some sample code that I can study. At the moment I'm trying to read the documentation for pyserial.

    What kind of corresponding code would I need to write in PBASIC?
  • FranklinFranklin Posts: 4,747
    edited 2014-03-06 12:17
    What kind of corresponding code would I need to write in PBASIC?
    Corresponding to what? The communications are serial so you send and receive serial data from both languages.
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-06 12:20
    " I only have a basic understanding of Python" - The link I provided at the very beginning of this thread seems pretty straight forward. While I haven't done any RS232 serial programming in Python, I have done Web-Server based programming with Python and can tell from the way Python imports and uses libraries from the links below that RS232 should not be that trivial. Before using Pyserial, you will have to install it. Once installed the examples on the links below should provide a good start.


    Installing PySerial: (Linux)
    http://pyserial.sourceforge.net/pyserial.html

    Installing for Windows (contains more examples):
    https://pypi.python.org/pypi/pyserial


    PySerial code examples:
    http://pyserial.sourceforge.net/shortintro.html#




    EDIT: For reference ONLY:
    Here is a project I did a few years ago that controlled the position of a laser pointer using a PC talking to a BS2.
    The example shows how two different programs... one on the PC and the other on the BS2 communicate to drive two hobby servos.

    http://forums.parallax.com/showthread.php/80452-Stamp-controlled-Laser-Pointer

    Slight modification to the BS2 code would need to be done to convert it to BS1 code, but the idea is the same. In my BS2 code the SYNC characters are "LPO" followed by three bytes ... X position, Y position, and the Laser being ON or OFF.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-06 14:50
    Here is some code I stole from a blog and simplified. This basically represents what I understand so far.


    #!/usr/bin/python

    import serial
    import time
    import random

    ser = serial.Serial(port = "/dev/ttyUSB0", baudrate=2400)
    ser.close()
    ser.open()
    ser.write(5000)


    On the last line, I threw in the number 5000. It's just a random number that doesn't properly correspond yet to the stamp code. In place of that number, what would I put instead? Would it be the value that I want to send to the servo?

    At this point I'm still not clear about SYNC. Once I master the basics of pyserial maybe I will be able to work on figuring it out. That might take a few days.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-10 16:38
    I have been doing a lot of reading of the basic stamp manual over the last few days. I think this bit of sample code might be what I need for this project.


    ' {$STAMP BS1}
    ' {$PBASIC 1.0}

    SYMBOL SIn = 0
    SYMBOL Baud = N2400

    SYMBOL result = W1


    Main:
    SERIN SIn, Baud, ("ABCD"), #result
    DEBUG #result, CR
    GOTO Main
    END


    This seems to be a good start to me on knowing how to do basic serial communication. What corresponding Python code would work? Something like this:


    #!/usr/bin/python

    import serial
    import time
    import random

    ser = serial.Serial(port = "/dev/ttyUSB0", baudrate=2400)
    ser.close()
    ser.open()
    ser.write(ABCD, ??? )


    After the ABCD, I'm not sure what kind of values I can use or what the exact format is. I have done a lot of googling, but can't find any examples of that little detail.
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-10 17:09
    You won't be able to send and values greater than 255 unless you break down the Word variable into two separate bytes. This is not a limitation of Python or the Basic Stamp, this is just how serial works. So in order to send 5000 , you will need to break it down into two BYTES.


    i.e.

    N = 5000 ' defined as a WORD

    BYTE_H = N >> 8 = 19 <--- shifts N 8 bits to the right (effectively an integer divide by 256)
    BYTE_L = N & 255 = 136 <--- only looks at the lower byte

    You can double check this by which is also how you can re-build the value when receiving, or you can align your variables in a way that there is no conversion required at all. (see below)

    N = BYTE_H * 256 + BYTE_L


    Basic Stamp I variable definition
    SYMBOL N = W0
    SYMBOL Byte_H = B1
    SYMBOL Byte_L = B0
    

    By defining the variables like this in the Basic Stamp I, there is no need to apply the math... when you read the serial data just read it into Byte_H and Byte_L and N will automatically have the correct value.






    So when sending the data from Python you would send the string "ABCD" followed by
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-11 17:29
    The 5000 was just a random figure I plugged in. I'm supposing the types of numbers I would use are the values for the pulsouts to position the servos. I couldn't exactly remember the range of values.

    On your last line, you wrote:

    "So when sending the data from Python you would send the string "ABCD" followed by …"

    Did I miss something, or were you about to write something else? That is the last little bit that I'm really confused about. As I said before, I can't find any sample code that explains that part.

    Other than that, I think I have it just about figured out. I may come back here a few times in the next couple weeks to make sure I have all the details just right.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-13 14:21
    Any more suggestions on the PBASIC code?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-13 15:06
    The code you posted should work with the suggestion I made to read the BYTEs instead of a WORD.
    ' {$STAMP BS1}
    ' {$PBASIC 1.0}
    
    SYMBOL SIn = 0
    SYMBOL Baud = N2400 
    SYMBOL Result = W0 ' B1 = High Byte of W0 ; B0 = Low Byte of W0
    
    Main:
    SERIN SIn, Baud, ("ABCD"), B1 , B0
    DEBUG #Result, CR
    GOTO Main
    END 
    
    
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-14 13:32
    What about the rest of the Python code? After ser.write(ABCD) what comes next? Do the values I want to send go inside the parentheses and is there any kind of punctuation I need to include?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-14 13:37
    dmehling2,

    I don't know, I said at the beginning that I haven't written serial code for Python, but that the examples looked straight forward enough that it shouldn't be to difficult to figure out. Initially I just goggled "Serial programming in Python"

    ... The first hit was .... http://pyserial.sourceforge.net/

    http://stackoverflow.com/questions/676172/full-examples-of-using-pyserial-package

    based on the page above I would start with something like ... I know you can used the / and & operators to break the WORD variable, the question is if the serial routines are correct based on the examples provided on the web-page reference
    import serial
    
    N = 5000
    
    B1 = int( N / 256 )
    B0 = N & 255 
    
    ser = serial.Serial(0)  # open first serial port
    print ser.portstr       # check which port was really used
    ser.write("ABCD")      # write a string
    
    ser.write(B1)              # write a BYTE
    ser.write(B0)              # write a BYTE
    
    
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-15 09:53
    dmehling2,

    Any luck with this from the Python side of things?
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-16 12:09
    No, I have not had a chance to try this out yet. I may not get to it until later this week. I'm thinking I might need to join a Python forum to get additional help if needed. I realize that this is not really the right forum to be discussing Python in depth. But I do really appreciate your help. I'm sure I'll need more help in the near future to better refine my PBASIC code.
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-16 12:41
    dmehling2,

    Wish I could help you more with the Python, but I think there is adequate examples by just doing a Google search to find the correct answer you are looking for.
  • dmehling2dmehling2 Posts: 21
    edited 2014-03-19 12:27
    Can you explain a couple of things you mentioned earlier? First, I'm not sure what the ampersand is used for. What mathematical function does it perform?

    Also, what is a high byte and a low byte?
  • Beau SchwabeBeau Schwabe Posts: 6,563
    edited 2014-03-19 12:39
    The ampersand performs a logical bitwise AND between two variables.

    i.e.

    b'11100 & b'10101 would produce b'10100

    As far as HIGH byte and LOW byte is concerned ... in the computer world a variable needs to be represented with 1's and 0's or BITS

    1 - bit can only represent two numbers 0 and 1 since that is the only combination you can derive from a single BIT
    2 - bits can represent 4 numbers since there are a total of 4 combinations you can derive ... 00 (0) , 01 (1) , 10 (2) , 11 (3)
    4 - bits (Also called a NIBBLE) has 16 number combinations
    8 - bits (Also called a BYTE) has 256 number combinations
    16 - bits (Also called a WORD) has 65536 number combinations


    So in order to represent a number greater then 255 you need to use a WORD... A WORD can be composed as two BYTEs and often referred to as a HIGH byte or a LOW byte. The HIGH byte contain the MSB(most significant BITs) while the LOW byte contains the LSB(least significant BITs)


    Using the ampersand is an effective way of removing the HIGH byte and only looking ans isolating the LOW byte
    Dividing by 256 simply moves the HIGH byte of the WORD into the position of the LOW byte
Sign In or Register to comment.