Announcement

Collapse
No announcement yet.

Please Help, problem with buffer overwriting when writing to our cloned Legacy Module

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Please Help, problem with buffer overwriting when writing to our cloned Legacy Module

    Hi Everyone,

    We are having a problem with some buffering that we hope someone can help us with. We have been having a summer camp for the last few weeks and one project that we are doing is to put together a temporary "core legacy module" so we can start programming using the new SDK. We currently don't have the funds to buy the actual one so we have to go this route. We have looked at the code extensively but our Java teacher is not here in the summer and we are stuck on this problem.

    The problem:
    Currently, we have the controller working for the most part, the android recognizes the controller, we can drive in teleop mode using opMode K9TankDrive, we can read and reset the encoders. The problem happens when we do consecutive writes to each of a motor controller's motor ports in the loop method where the 2nd write value is changing. It seems like the first write to motor1 is being overwritten by the 2nd write to motor2

    Code:
    loop() {
     motorLeft.setPower(left);
     motorRight.setPower(right);
     }
    If the 2nd write doesn't change every loop then the first write will go through. This also happens if there is a write in the Start() method to say "reset encoder" and then another one in the loop() method to "setPower" on the same controller.

    Our hypothesis:
    From what we can tell, the android app has a loop that does the following for one port on the legacy module:

    1. write 20 bytes to "Core Legacy Module" (we then write these bytes using i2c to "DCMotor Controller" registers (motor power, mode, current encoder, target encoder)

    2. read back a buffer of 20 bytes, lets call is CacheBuffer (we send back the values the were just written to us)

    3. process a FIFO list of commands that were sent from the OpMode start() and loop() and create a new 20 byte buffer to send in step 1.
    4. Loop back to 1.

    Now, when a write command is executed in the loop() method, say motorLeft.setPower(left), the android does the following:

    1. Make a copy of the current read CacheBuffer (last values that were written to legacy module)
    2. Write the new power value into the copied buffer
    3. Compare all 20 bytes of the copied buffer to the original CacheBuffer
    4. If different, add a write command to a FIFO that contains the new whole 20 byte buffer. Loop to 1.
    5. If the same, loop to 1

    If a second write is done, say motorLeft.setPower(Right), then the above happens again and a new write command is added to the FIFO. This time with the entire 20 byte CacheBuffer. The problem is that the first write in the FIFO was not executed yet and this new command over writes the Left motor's value.

    We think the writes are queued up in the FIFO and executed at one time to create a new buffer to write in step 1 of the main loop.

    We have observed that it takes two writes to the "Core Legacy Module" to get the two motor commands to be written. The first write will only contain the value for the Right motor, since the Left motor command was overwritten. The second time though the loop, the Right motor value will have been read back into the CacheBuffer and this time not added to the FIFO. This will allow the Left motor value to be added to the buffer and written out.

    We have also observed that a write done in the Start() method will always be over written by writes in the loop() method and will never be sent. You can see in the example NxtEncoderOp that the encoders are reset in the Start() method, but then they are checked in the Loop() method and if not reset, the write command is then sent again. This works but it takes a few writes to get it done.

    Also, if a loop is executed with values changing every loop, the 1st write will never be done. We see:
    Left Right
    0 0
    0 1
    0 2
    0 3

    Code:
     loop() {
     motorLeft.setPower(left++;
     motorRight.setPower(right++);
     }
    Thank you very much for looking at our problem. If anyone can try the above code out with a real "Core Legacy Module" that would help tell us is we are doing something wrong. We followed the code by tracing what the setPower method did in Android Studio. Maybe someone with more knowledge of Java could look at the code and see if this is really what is happening.

    Thank you so much.

  • #2
    How much of the K9TankDrive have you changed? Can you share the source?

    From the original code both "left" and "right" are reset with every call to loop() so I'm not sure what you would expect by issuing left++.

    Comment


    • #3
      Originally posted by dlevy View Post
      How much of the K9TankDrive have you changed? Can you share the source?

      From the original code both "left" and "right" are reset with every call to loop() so I'm not sure what you would expect by issuing left++.
      We were just saying that K9TankDrive works ok because it is using joysticks and when writing to both right and left motors, there will probably always be time when the 2nd write will be the same between two loops so a redundant write will not be needed, allowing the 1st write to go through. We didn't change any of the SDK code.

      The example that I am giving with the left++ and right++ power setting is another test we did in a new opMode. It is just an example to show that when the writes are changing each time through the loop, the first write never gets sent to the Core Legacy Module and the left motor stays at zero.

      The source code we have is for a TI Launch pad microcontroller that is acting as the Core Legacy Module. It basically just processes each incoming packet and writes it's contents over the i2c bus to the Tetrix DCMotorController. It then returns the same packet when the Android reads from the Core Legacy Module.

      Our basic problem is that later writes overwrite earlier ones to any registers on a single DCMotorController. eg. if there are three writes to different registers (motor1 power, motor2 power, mode-reset_encoder), during the first pass through the loop the reset_encoder will mask the other two. Only on the next loop will it be in the cache and not be sent. This will allow the motor2power write to be sent. The motor1power will still be masked. Only on the third write will they all be written. But this only work if none of them has changed from loop to loop. Also, writes in the Setup() method never work. None of the opMode examples have writes in the Setup() method except NxtEncoderOp and it rewrite the values again in the loop method.

      Thanks very much

      Comment


      • #4
        I believe what dlevy was saying was the left++ does not return an incremented value of 1. I believe that (left++ sets the left to left plus one and returns true (1) if it succeeded. Also, I believe the values written to the motor speeds and hardware are not applied until the loop terminates, if what I read was correct.
        So, maybe, that loop should be:
        Code:
         
        loop() {
            //Increment the left variable, and set the motor speed to the value of the variable.
            left++;
            motorLeft.setPower(left);
        
            //Increment the right variable, and set the motor speed to the value of the variable.
            right++;
            motorRight.setPower(right);
        }
        Also, that TI Lauchpad package for emulating a Core Legacy Module could be made open source to better facilitate helping with your project.

        Comment


        • #5
          Originally posted by dmssargent View Post
          I believe what dlevy was saying was the left++ does not return an incremented value of 1. I believe that (left++ sets the left to left plus one and returns true (1) if it succeeded. Also, I believe the values written to the motor speeds and hardware are not applied until the loop terminates, if what I read was correct.
          So, maybe, that loop should be:
          Code:
           
          loop() {
              //Increment the left variable, and set the motor speed to the value of the variable.
              left++;
              motorLeft.setPower(left);
          
              //Increment the right variable, and set the motor speed to the value of the variable.
              right++;
              motorRight.setPower(right);
          }
          Also, that TI Lauchpad package for emulating a Core Legacy Module could be made open source to better facilitate helping with your project.
          Thanks dmssargent,

          I'm sorry about the typo, should be motorLeft.setPower(left++). The left++ will return the current value of left before incrementing left.

          We tried your code above with the same result.

          left right
          0 0
          0 1
          0 2
          0 3
          0 4

          Yes, the motor values are written to the output buffer at the end of the loop. They are using at FIFO Queue to queue up the writes but the last write is overwriting the previous.

          We will work on posting the source.

          Comment


          • #6
            Originally posted by dmssargent View Post
            I believe what dlevy was saying was the left++ does not return an incremented value of 1.
            That's true. If you want the setPower to receive the incremented value you can also use :
            Code:
            setPower(++left);
            instead of
            Code:
            left++.
            setPower(left);
            However my point was that left++ would indeed be incremented but that incremented value would not be retained over multiple calls to loop() if the variable is being reset earlier in the method i.e. "float left = -gamepad1.left_stick_y;"

            Example
            Code:
            loop() {
               float left =1;
            ......
               left++
            }


            The value of left would always be 2 not matter how many times loop() is called.

            Comment


            • #7
              dlevy is right about that if the left or right variable are being set to a value before the increment expression is reached the incrementing expression may always be the same. I was getting to the true/false bit in objects like cin (C++) messed up with operator expressions.

              Here is C++: (not that the OP needs it, but it gives a explanation of what it is doing)
              pre-increment and pre-decrement operators increments or decrements the value of the object and returns a reference to the result.
              post-increment and post-decrement creates a copy of the object, increments or decrements the value of the object and returns the copy from before the increment or decrement.
              http://en.cppreference.com/w/cpp/lan...perator_incdec

              Here is the Java:
              The increment/decrement operators can be applied before (prefix) or after (postfix) the operand. The code result++; and ++result; will both end in result being incremented by one. The only difference is that the prefix version (++result) evaluates to the incremented value, whereas the postfix version (result++) evaluates to the original value. If you are just performing a simple increment/decrement, it doesn't really matter which version you choose. But if you use this operator in part of a larger expression, the one that you choose may make a significant difference.
              http://docs.oracle.com/javase/tutori...bolts/op1.html

              Can you post the entire loop code, post a link to it in GitHub or Pastebin, or are you just using the orignal K9 Tank Drive OpMode with the previously mentioned modification?

              Comment


              • #8
                Can you share the code of the interface so we can help debugging?

                Comment


                • #9
                  I'm sorry, the value of left and right are defined and initialized above as member variables of the class.

                  Don't worry so much about that example. The bigger problem we are having is when writes are done in the Start() method that don't get written. Or when you need to write multiple registers and the values change each loop iteration the cache is always invalid.

                  Thanks

                  Comment


                  • #10
                    So, if the Start method only writes one register does that register become valid, or the nothing in the Start method work regarding writing out to the your Legacy Module? Can you sniff for any corruption coming out of the "robot" and into the "Legacy Module"?

                    Comment


                    • #11
                      Thanks HagertyRobotics for the very clear bug report. With it we were able to quickly locate the bug with the legacy module driver.

                      What is happening is during the loop method only the last write to a given NXT device is being saved. All other writes are being lost. This bug was introduced about a week before the beta code went live. The USB Motor / Servo controller do not see this bug.

                      Obviously this is a high priority bug.

                      Comment


                      • #12
                        Great work!

                        Btw wat is the "official" procedure for a bug reports?
                        Will you just follow the forum or is a github issue the preferred route?

                        Comment


                        • #13
                          Originally posted by dmssargent View Post
                          So, if the Start method only writes one register does that register become valid, or the nothing in the Start method work regarding writing out to the your Legacy Module? Can you sniff for any corruption coming out of the "robot" and into the "Legacy Module"?
                          Correct, none of the registers written in the start method become valid when there are writes happening in the loop().

                          We just tried an experiment to see if we put a delay after the start() method, would it cause a "write buffer" to be called and allow the writes in the start() method to be executed. To do this we just skipped writing during the first loop() pass. This worked and allowed the write in the start method to be written to the Legacy Module. Below is the code that works. Note: the RESET_ENCODER write in the start() method was still masked by the SetTargetPosition(10000) and the targetPosition register was not written.

                          Code:
                          	public void start() {
                          		motorRight = hardwareMap.dcMotor.get("right");
                          		motorLeft = hardwareMap.dcMotor.get("left");
                          		//motorLeft.setDirection(DcMotor.Direction.REVERSE);
                          
                          		wheelController = hardwareMap.dcMotorController.get("wheels");
                          
                          		motorLeft.setChannelMode(DcMotorController.RunMode.RESET_ENCODERS);
                          		motorLeft.setTargetPosition(100000);
                          	}
                          
                          	/*
                          	 * This method will be called repeatedly in a loop
                          	 * 
                          	 * @see com.qualcomm.robotcore.eventloop.opmode.OpMode#run()
                          	 */
                          	@Override
                          	public void loop() {
                          
                          		if (loops++ > 0) { // skip first loop;  Loop initialized to 0 in class.
                          			motorLeft.setPower(0.10);
                          			motorRight.setPower(0.20);
                          		}
                          	}
                          The question of whether there is an I/O cycle between start() and loop() was discussed in an earlier thread http://ftcforum.usfirst.org/showthre...Start()-method I think Jonathan Berling said that there was an I/O cycle between but we are not seeing the effects of it.

                          Could anyone try it on a real Core Legacy Module and see if the encoders get reset, with and without the first loop delay.
                          Could anyone check the code in the SDK (follow a setPower() call for each implementation until you get to the queue write call)

                          Thanks

                          Comment


                          • #14
                            Thanks Jonathan! I hit send and then saw your post.

                            That is great because we were pulling our hair out trying to figure out what was wrong.

                            Comment


                            • #15
                              Originally posted by pbrier View Post
                              Great work!

                              Btw wat is the "official" procedure for a bug reports?
                              Will you just follow the forum or is a github issue the preferred route?
                              I monitor the forums pretty closely. I don't always have time to respond, but if I see an issue I create a ticket on our internal issue tracker.

                              Comment

                              Working...
                              X