Help understanding XBee Tutorial Propeller code
Falcon
Posts: 191
I'm a total noob to the Propeller. I'm studying the Propeller code in the XBee Tutorial before trying to modify it for my own project.
I understand that the "cognew(acceptData,@Stack)" command starts a new cog to run the acceptData method, and I know that "Send Control" is a Method within the Bot_Tilt_Controller OBJECT.
Question: What does the "SendControl" Command on the line after the cognew(acceptData,@Stack) for?
falcon
I understand that the "cognew(acceptData,@Stack)" command starts a new cog to run the acceptData method, and I know that "Send Control" is a Method within the Bot_Tilt_Controller OBJECT.
Question: What does the "SendControl" Command on the line after the cognew(acceptData,@Stack) for?
falcon
PUB Start ' Configure XBee XB.start(XB_Rx, XB_Tx, 0, XB_Baud) ' Initialize comms for XBee XB.AT_Init ' Fast AT updates XB.AT_ConfigVal(string("ATMY"), MY_Addr) XB.AT_ConfigVal(string("ATDL"), DL_Addr) offset := 90 * (clkfreq / 200) ' offset for sensor data conversion scale := clkfreq / 800 ' scale for sensor data conversion accel.start(Xout_pin,Yout_pin) ' start accelerometer cognew(acceptData,@Stack) ' start cog to accept incoming data SendControl Pub SendControl repeat if ina[PB] == 1 ' if button pressed XB.str(string("pppppp")) ' send handful of p's for pan map XB.delay(500) else ' Read and calulate -90 to 90 degree for forward Forward := (accel.x*90-offset)/scale * -1 ' Read and calulate -90 to 90 degree for turn Turn := (accel.y*90-offset)/scale ' scale and mix channels for drive, 1500 = stopped Left_Dr := 1500 + Forward * 3 + Turn * 3 Right_Dr := 1500 - Forward * 3 + Turn * 3 ' send drive data(d) to bot XB.tx("d") XB.DEC(right_Dr) XB.tx(13) XB.DEC(left_Dr) XB.tx(13) XB.delay(100)
Comments
The "Start" method does the things the program only needs to do once; the method "SendControl" is the main loop of the program.
Occasionally when testing a program it can be useful to keep a cog alive after some code has been executed. In this case a simple "repeat" will keep the cog alive.
All of my experience is with PBasic and it's ingrained into my programming logic to make everything flow in a loop, and that Subroutines are just part of a continuing loop. This is where I'm having a problem wrapping my head around Spin. I can pick apart an individual Method and understand how it works but I still haven't grasped the overall flow logic. I do have the PE kit and am re-reading parts of it every day.
Example: I'm working on an XBee/Propeller-based robot remote control. It will read three joysticks with an ADC0838 (because it's faster than 6 RCTime commands), read switch positions with a 74HC165 and send this data via XBee. I have all of these working on a breadboard individually. (Later on I plan to receive and display telemetry and switch position data from the robot using LEDs and a uOLED display.) I'm struggling with the "flow" of the TX code. My PBasic sense says do each in a big loop, but I know the Propeller will allow me to launch each (XBee, ADC0838 and 74HC165) into a separate cog so changes can be gathered and transmitted as soon as they occur. Would this be the best approach?
falcon
1) With the Spin code for the 74HC165 just occupied with reading in the switch settings into a variable, the code is simple. You start it and forget it.
2) Similarly with the Spin code for the ADC0838. Its only job is to cycle through the 8 channels of the ADC0838 and read each channel in turn into a variable. It's simple code, straightforward to write and debug, and interacts with nothing else.
3) Your main routine continually takes the most recent values in the variables and packages them up into a packet that it gives to the FullDuplexSerial transmit routine (and, in the future, to the LEDs and uOLED module via another copy of FullDuplexSerial).
4) There's a nice 4 channel serial I/O routine in the Object Exchange that uses a single cog for up to 4 serial channels and has a higher aggregate throughput than FDS ... also does flow control which you'll need at high speeds. You'd use one channel for the xBee and a 2nd for the uOLED.
5) If you want to work with assembly, #1 and #2 could each be done in assembly. Again, they're each straightforward and interact with nothing else (other than the memory locations where they deposit their results).
Yes, you're not the first person I've seen try to write code for the Propeller in a similar fashion as they had with the Basic Stamp. Fortunately for myself, I had a lot of experience with C. The methods of Spin are very similar to the functions in C (IMO).
I've got a similar project I've been working on for a while. I'm using one of Rayman's 4.3" touchscreens with a PlayStation 2 controller as a robot remote. The PS2 controller is nice since it takes care of digitizing joystick movements and button presses for you (not that I think it's a better method than the way you're doing it).
I recently needed some 74HC165 chips for one of my projects. I'm sure there are lots of good objects for reading a 74HC165, but I wanted to make sure I understood how the chip worked so I translated the StampWorks example into Spin. Here's the thread (is it a thread if there's only one post?) with my version. If you haven't used a '165 before, my Propeller version of the StampWorks' program might be helpful in seeing how the '165 works (as Mike said, the code is simple).
Have you seen Paul K's XBee remote? It's a thing of beauty.
I think Prop to Prop communication can be very challenging. You need to decide what kind of protocol you're using. Are you using control characters to indicate beginnings and endings of messages or are you stating the length of the message as part of the message heading?(rhetorical, you don't have to answer) If you use control characters then you need to stick with ASCII characters for the data. IMO, if you're using ASCII characters it's easier to use ASCII hex since the numbers take fewer digits and you can have each data field always be the same width with each transmission. And if you use acknowledgements and error checking things get even more complicated. I don't want to scare you off but don't be surprised when you find making a remote isn't as easy as originally planned.
One thing I really like about using a transceiver as a remote is not only are you giving the robot commands, but you can also receive data back from the robot. Only a few high end RC transmitters are starting to use telemetry and then you're limited to the parameters that the RC unit supports. By using a XBee or other transceiver (Nordic nRF24L01+) you can have any parameter you want sent back to your remote.
Which OLED display are you using? I've had a lot of bad luck with OLEDs. I've killed at least two (probably more but I'm repressing the memories of blue smoke). I really like how they look and that they don't need a backlight. I'd like to find one that's reasonably durable (or maybe I just need to be more careful).
As Mike says, a four port serial object will be very useful. I think the best version is Tracy Allen's FullDuplexSerial4portPlus_0v3. It watches for framing errors, has adjustable rx and tx buffers and fixes the flow control bugs in Tim Moore's original code.
It's possible to have multiple cogs using the same serial object but you need to be very careful when you do. I think it's better to have all the serial communication take place from one cog and have the other cogs update variables and set flags to let the communication cog know when there's new data to send.
BTW, if you or anyone else is interested in using a PS2 controller with a Prop, I have an improved (IMO) object for reading a PS2 controller (another single post thread). It's the only PS2 (Propeller) object I know of that reads the PS2's analog buttons (12 of them).
Yeah...I've drooled over that one a few times. A lot of work in that design.
I struggling with this now. I'm not sure which is the best way to ensure the RX knows exactly what each incoming variable is for. Based on the XBee Tutorial, the ASCII character "start-of-string identifier or delimiter" seems to be a stable option. And I've used that in all of my BS2 code.
I actually have a FrSKY TX/RX set-up on my Futaba 9C right now but I need more than 8 or 9 channels, and I like the telemetry but I agree that the limitations are a problem. The info that I want to return is nothing like what you'd need from an RC plane.
I've been eyeing one from 4D systems in Australia, and a Nokia that I saw an Object for, but that one from Rayman looks pretty good and should have good code examples since he's a frequent forum contributor.
I'll take a look at that too. I've used a PS2 controller using some BS2 code from a N&V article.
I appreciate you taking the time to help me along. I really want to learn the Propeller but my learning curve is pretty steep.
falcon
I did get an XBee transmitting the three joysticks' scaled ADC readings to another XBee and then to the PST.
I then modified the code to have the transmitted joystick data drive servos. I have just one connected for testing. I'm getting a few servo jitters but no controlled movements. Any ideas where I might be going wrong? I borrowed heavily from the XBee Tutorial code but I'm not sure I meshed it together correctly with the code I had working before the servo code was added.
Transmitter Code
Receiver Code
Based on the "XBee_Object" I have, I think you just set "analog[1]" to zero. I think your program has a better chance of working if you remove the line (and others like it).
It would be helpful if you either attached an archive of your project or at least link to non-library objects you're using. I must have an old version of XBee_Object since it doesn't have the method "RxDecTime".
IMO, I think your code would be easier to read if you were consistent with capitalization (or lack of capitalization) of variable names.
I'm not sure what you meant by this reference to "an archive".
I re-wrote the code on both TX and RX to use a single variable: Analog on the TX and DataIn on the RX. See attached filesjm_adc0838_ez.spinRemote RX_11_07_2012.spinRemote TX_11_07_2012.spinServo32v7.spinXBee_Object.spin
I was scaling the joystick data on the TX side but thought maybe sending the word-sized variable across the XBee was a problem so I moved the scaling function to the RX side. The TX now sends just the 0-255 byte representing the joystick positions.
The RxDecTime command was part of the XBee Tutorial code I borrowed. I didn't fully understand it's use but I thought that it had to be there. I've removed it and did not see any negative effects.
I tried to minimize and clean-up the code on both ends but added the PC.tx function to see the data on the PST.
As you can see, my TX approach is to send a Delimiter (“A”) followed by each Byte of data as they are read by the ADC0838. On the RX side I am using the Delimiter to “align” the incoming data. I then scale it, send it to the Servo Method in it’s own cog, and send it to the PST to monitor the incoming data. Do you think this is a "workable" approach, or is there a more efficient way to go about this.
I see the following on the PST:
1038
1044
1061
1038
1044
1085
All six lines vary from +/- ~6 when joystick 1 is moved. That might be solved with some .01uF filter caps on the joysticks. I have one servo attached to P2. It jitters in response to joystick 1 movements but holds its position when I try to manually move it so it must be getting the pulse chain to keep it positioned..
It looks like the servo is following the scaled joystick data but the data is not moving through the 750-2250 range I expected. Do you see anything in my code that would prevent the full 0-255 byte range to be transmitted? Or scaled?
Thank you,
falcon
I've looked at your code and see several problems.
One of your big problems is your sending a single byte that's not the larger number scaled down to a byte but rather you're just sending the least significant byte digit (character) of the ADC value (and reading it in incorrectly).
Edit: I see now that the ADC doesn't need to be scalled down since it's only 8-bits.
First get rid of most byte variables. Only use them when your using ASCII characters which you wont be using many of (at least directly, the serial object will take care of these for you).
So with analog a long in your TX code, add a carriage return to this portion of code:
Now when the XBee object converts the value of "Analog" to ASCII characters you can use the XBee object's rxDec method to convert the ASCII characters back to a variable.
In your RX code also change "DataIn" to a long and use the following code:
By an "archive" I mean using the Prop Tool's archive feature. You need to compile (F9, F10 or F11) the project before you can archive it. Under File\Archive "name of object"\Project... This will add all the files in the project to a single zip file. I don't have "jm_adc0838_ez" so I don't know what the range of possible return values are. With an archive, I would have had this object.
I doubt you'll need the ADC objet in your RX object. I'll try to find some code to help with converting joystick values to servo values. It basically a linear equation with the ADC value as "x" and the servo value the "y" in "y = mx + b". I usually have some sort of "dead zone" near the center of the joystick where small changes are ignored since many joysticks don't always return to the exact same center position.
Edit: I see that the ADC your using is an 8-bit chip so you don't really need to use longs for "Analog" or "DataIn" (I still think it's a good idea).
I sure do miss all of the BS2 DEBUG formatting commands. It was way easier to get data displayed the way you want. I'm sure SPIN has even more formatting options but I've just not discovered them yet. That being said, the results of this code is always justified to the left whether its a 1-, 2- or 3-digit number. Is there a way to justify right?
So, about those servos? I will actually use channels 1 & 2 to control a Sabertooth 2x25 motor controller that I have. That unit has several modes but the simple serial may be my choice. The first channel expects a 0-127 byte with 63 as the center, and 128 - 255 controls channel 2 with a 192 center. Channels 3 & 4, and 5 & 6 will control typical servos in pan/tilt mechanisms.
falcon
Remote RX_11_09_2012 - Archive [Date 2012.11.09 Time 18.04].zipRemote TX_11_09_2012 - Archive [Date 2012.11.09 Time 18.03].zip
"Parallax Serial Terminal.spin" lists the control characters for formatting text sent to "Parallax Serial Terminal.exe". These control characters should work with other serial objects as long as you use "PST.exe" as the PC's terminal window.
Here's the list of control characters (from "PST.spin"):
The only way I know of to right justify is to move the starting position of the text based on its length. You could repeatedly divide a value by 10 to determine how many spaces to add before a number to make it right justified. There might be some text formatting objects available but I don't know where to find them (if they exist). There are other ways of determining the length of a number such as converting it to a string with the "numbers" object and then using "strsize" to find its size.
While you could certainly communicate with the Sabertooth serially, I think it would probably be easier to just use pulses like the servos since you're already using the servo object.
I’m now working on the 74HC165 code and using the 74HC165_120929e Object. I changed it to reflect the pins I’m using and commented out a lot of the PST commands because I didn’t need it to be displayed from the TX Object. I launch a new cog to read the 74HC165 under the top Method, then added a PUB Switch_Read Method . I added “XB.dec(xInputs)” to the end of the Start Method to transmit the 74HC165 data after the last ADC channel. I’m not sure if I should do that or use a different Delimiter and send the 74HC165 data separately.
Problem: The joystick data is only displayed (and servo only reacts) if the “Switch_Read” command after the cognew statement in the TX code is commented out. I do get a "0" on the PST after the last ADC channel. Am I launching the 74HC165_120929e Object’s Method correctly?
Question: What actually returns the data in the xInputs variable to the TX Object?
Question: Would I be better off copying the applicable 74HC165_120929e code into my TX Object rather than make a call to it?
Remote RX_11_10_2012 - Archive [Date 2012.11.10 Time 19.29].zip
Remote TX_11_10_2012 - Archive [Date 2012.11.10 Time 19.27].zip
falcon
The joystick function is working 100%. Lots of reading and trail & error. I'll need to address "dead zones" at some point but the code is now functional.
On the TX end, I have the 74HC165 Shift Register reading 8 pushbuttons and displaying the results correctly on the TX-end Prop Demo board P16-P23 LEDs. I'm having trouble getting that data sent over the XBees for display on the RX-end Prop Demo board.
Questions:
In the TX-end Object, I have one Method to read the joysticks via the ADC, and a separate Method to read the 74HC165. How do I get the results of the 74HC165 sent over the XBee?
Since I have it as a Global variable, shouldn't it automatically be available to the Method that includes the XBee code?
Do I need to (or is there even a way to) be sure the 74HC165 data is available when the XBee is ready to send it?
I use a Delimiter to ensure the RX knows when the joystick/ADC data is being sent. Should I use a different Delimiter to signal the 74HC165?
I appreciate any and all comments and responses.
falcon
Remote RX_11_12_2012 - Archive [Date 2012.11.12 Time 18.01].zip
Remote TX_11_12_2012 - Archive [Date 2012.11.12 Time 18.01].zip
Here is a code snippet I use for receiving data. It checks for the delimiter, and repeats until it has a valid delimiter at the right spot, the right time.
I think any time you're trying to receive values from a data source via an Xbee (whether it's a shift register, a method, object, etc...) you should have a mechanism that fulfills the function of a start delimiter. This could be an actual delimiter like you're using, or a definitive "dead band" time that has to be present before transmitting/receiving, etc. I'm sure there are numerous ways a person could use to ensure the right data is transmitted/received at the right time, but it's always a good idea to use a start delimiter
Which Xbee's are you using? Certain Xbee's cannot use the "AT_Init" method without modifying it in the Xbee_Object. Some models have a minimum time of 200 instead of 100ms to enter AT mode.
And one other thing- if you need your comms to be a little more fail-safe, try this when you change any type of AT configuration setting:
It just ensures your attempt to change the value "takes." I'm sure there are better ways to do this, but this one is pretty simple and helps.
Oyeah- and... I'm a noob at this stuff too. So if you have questions or see something wrong, be sure to hear out the more seasoned vet's around here! :cool:
Yes, global variables are available to all methods within the object (spin file).
When new 74HC165 data is available it could set a flag so the method in charge of sending data over the XBee knows there's something to send. The sending method could then clear the flag.
As for delimiter and what not, that's a tough one. I still struggle with trying to find a good communication protocol.
For now I use a header that includes a start of message character, then a character to indicate what kind of message is being sent followed by the actual data in ASCII characters (as you are doing by using the "dec" method). I tend to use ASCII hex since it's easier to have the values all the same length and I don't need delimiters between data values.
Here are some notes from one of my Prop to Prop com projects:
Since I want my remote to work on more than one robot, I include a destination identifier and origination identifier. If you use start and end of message characters, you're limted to sending ASCII characters since you want to make sure an end of message character isn't part of the data. This can easily double the about a data you need to send. If you use a protocol like the Dynamixel's AX-12a servos, you include the size of the transmission as part of the header so you don't have to worry if there's a control character in the data. There are several Dynamixel objects availble for the Propeller. If/when transmission speed becomes an issue (and you're more comfortable programming the Prop) you might want to take a look at them.
Thanks,
falcon
I think you're doing the right thing with tackling small sections. If you look at my code snippet today, it might seem like it's "good code"... which it is, if you follow the golden rule of "If the code works as intended, it's good code." BUT- if you look at this same snippet a year from now you'll say "Oh man, that code is junk. I could do the same thing faster, better, with less code" and blah blah blah. It's all part of the learning curve. As you keep at it, more will sink in and you'll figure out better ways to do stuff.
Best of luck to you, please keep us posted on how you're project is turning out!
Robert
Am I calling your 74HC165_120929e Method correctly? Do I need to pass it a variable? Shouldn't it continue to "ping" the 74HC165_120929e Mainloop Method the way I have it under the repeat command? I did change all instances of xInputs to Switches. 74HC165_120929e works perfectly by itself but I'm not sure I'm getting the variable back from it.
I didn't like how my code was evolving into a BS2-like loop instead of making calls to the necessary Objects so I restructured the TX Object. I want a separate cog launched for the ADC, 74HC165, XBee and Demo Board LEDs [P16..P23]. If the ADC and 74HC165 are continuously updating their respective variables then the variables should always have the most current data when the XBee "collects" the data to transmit. The result is LED P23 lit constantly lit, and garbled text on the PST.
Do I have the commands/statements in the Main Method in the proper order? Do the cognew commands have to be in a certain place?
Remote TX_11_14_2012 - Archive [Date 2012.11.14 Time 19.39].zip
Thanks again,
falcon
The '165 program I wrote was intended to be a simple program to show how the '165 could be used with a Propeller. It doesn't have the needed methods to use as a child object.
I stripped away the debug code and made the object usable as a child object. I haven't tested it yet since I don't have a breadboard with any 74HC165 chips on it right now.
While using multiple cogs can greatly improve the way a program works, it's easy to run out of cogs. The program you posted actually used all eight cogs. I trimmed it down to four cogs (I think you could probably get rid of two more).
Since I/O pins are "or"ed together, if one cog sets an I/O pin high, another cog can not set it low. I try to make it a point of setting all I/O states of a pin from a single cog. I also call "Start" and "Init" methods from within the cog that will be calling methods in the child object. Many objects don't require you to only call its methods from a single cog, but there are few that do so I just make it a habit of making all calls to an object from within a single cog (this is just a good idea, not always necessary).
Variables in one object can not be read from a different object directly. You need to use return values or pass pointer between the objects to let them share memory locations.
I added a return value to the '165 object so the parent object needs to call the "ShiftIn" method each time it wishes to read from the shift register.
I also added a "newDataFlag" I had mentioned previously. The method send information over the XBee waits until there is new data before sending a transmission.
It makes me nervous to post a program I haven't tested, but I don't have all the hardware you are currently using wired up to a Propeller right now.
I wasn't able to get the LEDs to display the switch data read from the 74HC165 using the child Object you included. I didn't actually check any other part since I want to get this bit working first. I pared my TX Object down to what I believe are the bare essentials to "use" your 74HC165_121114a Object to no avail. I did verify that all my components work using my code from 12 Nov.
Do you see any obvious inconsistencies that would prevent the TX code from correctly calling your 74HC165 child Object? I did see that the TX code uses the command Switches := SR165.ShiftIn(BITS_IN) to call the 74HC165 while the 74HC165 child Object code includes the line PUB ShiftIn(bitsToRead). Should they be the same?
TX code
Your 74HC165_121114a Object
Thanks,
falcon
Change this section of the Init method:
To:
As I mentioned, I haven't tested the code I posted which is almost asking for it not to work.
If the above change doesn't fix things, I'll try out the '165 code myself. I found I still have some '165 chips wired up on one of my breadboards so it shouldn't be too hard to test now.
I was able to get the switches to display on the TX Demo Board LEDs by commenting out the line repeat while newDataFlag . Since the newDataFlag will be initially set to 0, the code should drop through the repeat while newDataFlag command the first time through. It seems to hang there.
The 74HC165_121114a Object includes the following and returns the data as it should
dira[clock] := 1 ' Set needed pins to outputs.
outa[load] := 1
dira[load] := 1
Will the XB_Send Method automatically run after the cognew(XB_Send, @stack3) command is executed? Or do I have to direct the code there?
falcon
Remote TX_11_17_2012b - Archive [Date 2012.11.17 Time 19.59].zip
I forgot to add the call to the Start method of for the XBee serial.
I moved the display serial start to the same location.
It wouldn't matter in this case, but I like to call the start method of an object from the same cog that will be accessing the object. There are some objects that don't play well when more than one cog makes calls to them.
The cognew statement starts the XB_Send method. You don't need to call it elsewhere.
I have the joysticks and switches displaying correctly on the PST off of the RX. The Demo board LEDs light corresponding to the switches. However, I cannot get the RX Demo board LEDs to light.
It should be as simple as adding this to the Start Method
dira[16..23]~~
outa[16..23]~
and
outa[16..23] := DataIn
to light them, but the LEDs do not light. Any ideas about that?
I also added code in the RX Object to use a 74HC595 (that will eventually connect to a ULN2803 Darlington Array and 8 relays.) Should the "jm_595_ez" Object have it's own cog?
As I have it written, the 74HC595 is not lighting the LEDs to match the switch actions on the TX side. Will the command "SR595.out1(DataIn)" not send DataIn to the "jm_595_ez" Object?
Remote TX_11_20_2012 - Archive [Date 2012.11.20 Time 18.46].zip
Remote RX_11_20_2012 - Archive [Date 2012.11.20 Time 18.46].zip
falcon
Your remote RX_11_20_20 object is a little odd?
Is this intentional? (I've put a **** where it seems odd in the code snippet) You've started this same object in 2 different cogs. Just wasn't sure if you intended to?
And remove those two commands from your Start method. I think you need the cog that's running the ServoControl method to set up the dira and outa... once that's done, the last bit of your code (at the bottom of your ServoControl method) should be able to drive the pins.
I based that on reply #2, although I might not have gotten that totally correct. I assumed the cognew command would need to be told where to continue, but I guess that's not needed since the ServoControl Method directly follows the cognew(SERVOControl,@stack2) command.
just tried it with the first "SERVO.Start" commented out and it ran good.
Thanks,
falcon
That does sound like progress. Great!
I think Rforbes has this figured out.
That sounds kind of familiar for some reason?
The "or"ing of the I/O pins was done for a good reason, but it makes it easy to write buggy code for the Prop. I think setting I/O pins from the wrong cog has got to be one of the most common mistakes made by Prop programmers.
I can't tell you how many times I've seen bugs caused by setting I/O pins from the wrong cog. Not all of these observed bugs were in my own code.
It doesn't look like the '595 object needs to be run in its own cog. I haven't used JonnyMac's '595 object so I cann't answer your other questions about it.
I assume there was some demo code that came with the object to show how to use it?
I looks like Rforbes caught a couple of other errors.
I hope you're having fun. To me this is really fun stuff.
You could should change:
To:
If you don't have an endless loop in your start method (in the top object) you ought to call another method (with an endless loop) before the start method ends in order to keep the cog (cog #0) alive.
That helped too. Thanks. The RX Demo board LEDs are now responding. However, random LEDs are lighting up on the RX end when a TX switch is pushed, and the RX PST data is "jumpy" too. The LEDs on the TX end are stable. I might have to put some small caps on the RX LEDs to try to eliminate that. I'd like to use a second Delimiter for the switch data but I'm not sure exactly how to have the two different data streams transmitted without stepping on each other. Duane Degn added the NewDataFlag to the TX code. Maybe that (or that concept) could be used to have each data stream take its turn.
falcon