Shop OBEX P1 Docs P2 Docs Learn Events
Question re cog_run and cog_end — Parallax Forums

Question re cog_run and cog_end

twm47099twm47099 Posts: 867
edited 2014-08-14 11:00 in Learn with BlocklyProp
I am having some difficulty understanding starting and stopping cogs using cog_run and cog_end.
The program below runs the ActivityBot using a bluetooth joystick controller app of an android tablet. I run the serial communication with the android tablet from a function (getbtjoy) in a separate cog. I use cog_run to start it and don't ever stop it. That works well.

I added a function (do_ping) in a new cog to use ping to measure distance and control an RBG Led as distance to an object decreases (green >= 12 inches, yellow < 12 in., red < 5in.) I use the soft buttons in the app to turn the ping cog on and off.

I had a few problems with that. There are 2 blocks of code below set off by // ***** where the problem code is / was located. I tried a few different things, but what I have now works. But I don't understand why the other methods didn't.

First, I attempted to have "int *cog = cog_run(&do_ping,20); and cog_end(cog) in the conditionals (second group of // *****) without previously declaring int *cog. I got errors during the build for not having cog previously declared.
I guessed that had to do something with them being in conditional statements. Is that correct?

Then I added the code (now commented out) in the first group of // *** that declared int *cog; and cog = 0; (I tried a few variations of this). The program built and ran, but once I turned on PING I could not turn it off and sometimes it would prevent anything else from running (no joystick or button control).

Then I added the cog_run, pause, cog_end code in the beginning and took out the previous declarations. Now everything runs well, although I get a brief ping response at the start. Once do_ping is started using a button, I can turn it off and on with button presses as desired.

I would like to be able to run this without having to initially run and end the do_ping cog. Can you explain why the declaration method didn't work and if it is possible to remove the initial cog_run and cog_end?

Thanks
Tom

/* C code for use with Propeller ActivityBot and Joystick BT Commander App by Kas - 

used with Samsung Tablet (c) T. Montemarano 2014-08-01,  MIT License
 Written for BT Commander V5 - Changed protocol -- need to do data TX to android
 coded button 6 as 'end' to terminate program
             button 5 as E-stop
             button 2 as Ping start - stop
 Uncomment print statements and Run with terminal and switch at 1 to debug
 
 App options setup
     Joystick Props -- behavior - deselect auto return to center
                             constraint - box
     Buttons Props -- display 6
     Advanced -- auto connect - on
                       Refresh Interval - 100ms  
                       Timeout - Off 
 Uses RN42 Blutooth module.  I'm using Sparkfun part that has same form as X-Bee socket
 Connect BT DO to pin 9 (Will be Propeller Rx), BT DI to Prop pin 8 (will be Prop Tx)
 Need to pair RN42 to android BT and connect (with ABot switch in position 1)
 To run ActivityBot 
 Load to EEPROM, switch to 2, ABot will beep (piezo setup as in Learn examples)
 After the beep and a second or so, the ABot will respond to the Joystick.  
    
 Joystick data is x = -100 (Full Left), y=-100 (Full down), x=+100 full right, y=+100 full up.
 The buttons are decoded as Ascii 'A' Button 1 lit, 'B' 1 grey, 'C' 2 lit, etc.  
Button code is sent when button is pressed.
 
 The Joystick zones are setup as approx +/- 15 y = 0 speed, +/- 15 x = drive straight fwd or back.
 abs(y) >15 = move fwd (JS positive), move backwards (JS negative),  
 Abs(x) > approx 15 = add differential to y speed to turn or pivot (x positive -turn right.
 Both x and y are scaled to set max speed and turning rates in the calculations of x2 and y2.  

 The app is free on Googleplay 'joystick BT Commander".  
The link on the app page leads to the details  regarding the data protocols used.
 
 */
 #include "simpletools.h" 
 #include "fdserial.h"
 #include "abdrive.h"
 #include "ping.h"
 
 volatile char c1[9];
 volatile int c0 = 1;
 volatile int a;
 volatile int joydata;
 int x; 
 int y;
 int z;
 int dist;
 
 fdserial *blut;
 
 void getbtjoy();
 void do_ping();
 
 int main()
 {
   freqout(4, 1000, 2000); // Speaker tone: 1 s, 2 kHz
   int x2;
   int y2;
   int xyval;
   int yspd = 0;
   int xspd = 0;
   cog_run(&getbtjoy, 22);
   drive_ramp(0, 0);
 
//  ******************************************************************************
 //  int *cog;           //  needed to declare cog outside of conditional
   int *cog = cog_run(&do_ping,20);      // need this and the next 2 statements?
   pause(50);
   cog_end(cog);
 //  cog=0;
 //  *********************************************************************************

   while(c0)
   {
     if(a==8) 
     {
       pause(50);        //   try to get rid of pause
       xyval = joydata;
       y2 = (xyval & 511) - 200;
       x2 = (xyval >> 10) -200;
       printi("x = %d\n", x2);
       printi("y = %d\n", y2);
       yspd = (abs(y2) / 15)* 15;         // scale the fwd & rev motion
       if(y2 <0) yspd = yspd / -2;         //  go slower in reverse  
       xspd = (abs(x2) / 15) * 4;          //  scale the turning
       if(x2<0) xspd = xspd * -1;
       printi(" left right %d %d\n",yspd+xspd, yspd-xspd);
       drive_ramp(yspd+xspd, yspd-xspd);         // drive, turning differential added to yspd
     }
     if(a==3) 
     {
       a=0;
       printi(" button = %d\n\n", c1[2]);

//  **************************************************************
       if(c1[2]=='C')                  // Button 2, on turns on ping
       {
         *cog = cog_run(&do_ping,20);  
         a=0;
       }
       if(c1[2]=='D')                // Button 2, off turns off ping
       {
         cog_end(cog);
         a=0;
       }
//  **************************************************************

     }
   } // end while c0
   drive_ramp(0, 0);     // stop movement at end of program
   //  printi(" end \n\n");
   cog_end(cog);       //  if ping running when quit is pressed, turn off
 } // end main
 

 

 void getbtjoy()
 {
   // fdserial * fdserial_open(int rxpin, int txpin, int mode, int baudrate)
   blut = fdserial_open(9, 8, 0, 115200); 
   while(c0) // continue until button 6 - quit - is pressed 
   {
     a=0;
     int i = 1;
     z = 1; 
     c1[1] = fdserial_rxChar(blut); // read first byte from blut
     if(c1[1] == 2) // if STX read next 7 bytes 
     { 
       z = 1;
       while(z)
       {
         i++;
          c1[i] = fdserial_rxChar(blut); 
         if((c1[i]==3) && (i==3 || i==8)) z = 0;
       }       // end while z, android string i = 2 => button, i = 7 => x,y values
       if(i==8)
       {
         a=8;
         x = (c1[2] - 48) * 100 + (c1[3] - 48) * 10 + (c1[4] - 48);
         y = (c1[5] - 48) * 100 + (c1[6] - 48) * 10 + (c1[7] - 48);
         joydata = (x << 10) | y;
       } // end if i= 8
 

       if(i==3) 
       {
         a=3;
         // printi(" button = %d\n\n", c1[2]);
       }
       if((i==3) && (c1[2]=='K' || c1[2]=='L'))      //  Button 6, ends program
       {
         c0 = 0;
         //printi(" end \n\n");
       }
       if((i==3) && (c1[2]=='I'  || c1[2]=='J'))     
       {                      //E-stop on button 5 press, using JS restarts movement
         drive_ramp(0, 0);
       }
     } // end if c1=2
   } // end while c0
 } // end getbtjoy
 

 void do_ping() 
 {
   while(1)
   {
     dist = ping_cm(5);
     if(dist<=20)            // turn on red LED
     {
       high(6);
       low(7);
     }
     else if(dist<=36)      // turn on yellow LED
     {
       high(6);
       high(7);
     }
     else                    //  turn on green LED
     {
       low(6);
       high(7);
     }
     pause(50);
   }   // end while
 }

Comments

  • twm47099twm47099 Posts: 867
    edited 2014-08-11 19:19
    I also tried using the old method for starting ping in a new cog without the initial start-pause-stop code -- the old method is from the C Learning tutorial on C functions to start an LED blinking in a new cog.

    That method used: int cog = cogstart(&blink, NULL, stack, sizeof(stack));

    I added in the statements for stack size and changed the function parameters to correspond with those required by that method (including changing &blinky to &go_ping). When I called it in the program above, Ping and the RBG LED turned on and detected distance, but it prevented anything else from being controlled. I couldn't find a cogstop function in the library so I couldn't try stopping the Ping cog.

    Any suggestions.

    Tom
  • edited 2014-08-12 10:14
    If you declare variables inside code blocks (like if), they go out of scope after code exits the block. To fix this, I declared int *cog globally which ensures that its value will persist through multiple visits to the if statements. I also increased the stack space that cog_run allocates for both cogs. It helps to be a little extravagant with stack space during prototyping, because if you don't declare enough, runtime bugs like one cog starting but not the other can occur. I'm not 100% sure that's the case this time, but let's give it a try. Try this. There may still be runtime bugs, but we have taken a step in the right direction.

    P.S. I marked changes with comments that have // <-- and/or // -->
    /* C code for use with Propeller ActivityBot and Joystick BT Commander App by Kas - 
    
    used with Samsung Tablet (c) T. Montemarano 2014-08-01,  MIT License
     Written for BT Commander V5 - Changed protocol -- need to do data TX to android
     coded button 6 as 'end' to terminate program
                 button 5 as E-stop
                 button 2 as Ping start - stop
     Uncomment print statements and Run with terminal and switch at 1 to debug
     
     App options setup
         Joystick Props -- behavior - deselect auto return to center
                                 constraint - box
         Buttons Props -- display 6
         Advanced -- auto connect - on
                           Refresh Interval - 100ms  
                           Timeout - Off 
     Uses RN42 Blutooth module.  I'm using Sparkfun part that has same form as X-Bee socket
     Connect BT DO to pin 9 (Will be Propeller Rx), BT DI to Prop pin 8 (will be Prop Tx)
     Need to pair RN42 to android BT and connect (with ABot switch in position 1)
     To run ActivityBot 
     Load to EEPROM, switch to 2, ABot will beep (piezo setup as in Learn examples)
     After the beep and a second or so, the ABot will respond to the Joystick.  
        
     Joystick data is x = -100 (Full Left), y=-100 (Full down), x=+100 full right, y=+100 full up.
     The buttons are decoded as Ascii 'A' Button 1 lit, 'B' 1 grey, 'C' 2 lit, etc.  
    Button code is sent when button is pressed.
     
     The Joystick zones are setup as approx +/- 15 y = 0 speed, +/- 15 x = drive straight fwd or back.
     abs(y) >15 = move fwd (JS positive), move backwards (JS negative),  
     Abs(x) > approx 15 = add differential to y speed to turn or pivot (x positive -turn right.
     Both x and y are scaled to set max speed and turning rates in the calculations of x2 and y2.  
    
     The app is free on Googleplay 'joystick BT Commander".  
    The link on the app page leads to the details  regarding the data protocols used.
     
     */
     #include "simpletools.h" 
     #include "fdserial.h"
     #include "abdrive.h"
     #include "ping.h"
     
     volatile char c1[9];
     volatile int c0 = 1;
     volatile int a;
     volatile int joydata;
     int x; 
     int y;
     int z;
     int dist;
     
     fdserial *blut;
     int *cog;                                    // <-- Add
     
     void getbtjoy();
     void do_ping();
     
     int main()
     {
       freqout(4, 1000, 2000); // Speaker tone: 1 s, 2 kHz
       int x2;
       int y2;
       int xyval;
       int yspd = 0;
       int xspd = 0;
       cog_run(&getbtjoy, 222);                   // <-- Increase stack size
       drive_ramp(0, 0);
     
        //  --> This is a separate local cog variable from the one in the conditional,
        //      so don't use. See global declaration that will be the same instance
        //      anywhere in the program.<--
        //  ******************************************************************************
        //  int *cog;           //  needed to declare cog outside of conditional
        //  int *cog = cog_run(&do_ping,20);      // need this and the next 2 statements?
        //  pause(50);
        //  cog_end(cog);
        //  cog=0;
        //  *********************************************************************************
    
       while(c0)
       {
         if(a==8) 
         {
           pause(50);        //   try to get rid of pause
           xyval = joydata;
           y2 = (xyval & 511) - 200;
           x2 = (xyval >> 10) -200;
           printi("x = %d\n", x2);
           printi("y = %d\n", y2);
           yspd = (abs(y2) / 15)* 15;         // scale the fwd & rev motion
           if(y2 <0) yspd = yspd / -2;         //  go slower in reverse  
           xspd = (abs(x2) / 15) * 4;          //  scale the turning
           if(x2<0) xspd = xspd * -1;
           printi(" left right %d %d\n",yspd+xspd, yspd-xspd);
           drive_ramp(yspd+xspd, yspd-xspd);         // drive, turning differential added to yspd
         }
         if(a==3) 
         {
           a=0;
           printi(" button = %d\n\n", c1[2]);
    
    //  **************************************************************
           if(c1[2]=='C')                  // Button 2, on turns on ping
           {
             cog = cog_run(&do_ping,120);  // <- Already a pointer, add stack space
             a=0;
           }
           if(c1[2]=='D')                  // Button 2, off turns off ping
           {
             cog_end(cog);
             a=0;
           }
    //  **************************************************************
    
         }
       } // end while c0
       drive_ramp(0, 0);     // stop movement at end of program
       //  printi(" end \n\n");
       cog_end(cog);       //  if ping running when quit is pressed, turn off
     } // end main
     
    
     
    
     void getbtjoy()
     {
       // fdserial * fdserial_open(int rxpin, int txpin, int mode, int baudrate)
       blut = fdserial_open(9, 8, 0, 115200); 
       while(c0) // continue until button 6 - quit - is pressed 
       {
         a=0;
         int i = 1;
         z = 1; 
         c1[1] = fdserial_rxChar(blut); // read first byte from blut
         if(c1[1] == 2) // if STX read next 7 bytes 
         { 
           z = 1;
           while(z)
           {
             i++;
              c1[i] = fdserial_rxChar(blut); 
             if((c1[i]==3) && (i==3 || i==8)) z = 0;
           }       // end while z, android string i = 2 => button, i = 7 => x,y values
           if(i==8)
           {
             a=8;
             x = (c1[2] - 48) * 100 + (c1[3] - 48) * 10 + (c1[4] - 48);
             y = (c1[5] - 48) * 100 + (c1[6] - 48) * 10 + (c1[7] - 48);
             joydata = (x << 10) | y;
           } // end if i= 8
     
    
           if(i==3) 
           {
             a=3;
             // printi(" button = %d\n\n", c1[2]);
           }
           if((i==3) && (c1[2]=='K' || c1[2]=='L'))      //  Button 6, ends program
           {
             c0 = 0;
             //printi(" end \n\n");
           }
           if((i==3) && (c1[2]=='I'  || c1[2]=='J'))     
           {                      //E-stop on button 5 press, using JS restarts movement
             drive_ramp(0, 0);
           }
         } // end if c1=2
       } // end while c0
     } // end getbtjoy
     
    
     void do_ping() 
     {
       while(1)
       {
         dist = ping_cm(5);
         if(dist<=20)            // turn on red LED
         {
           high(6);
           low(7);
         }
         else if(dist<=36)      // turn on yellow LED
         {
           high(6);
           high(7);
         }
         else                    //  turn on green LED
         {
           low(6);
           high(7);
         }
         pause(50);
       }   // end while
     }
    
    
  • twm47099twm47099 Posts: 867
    edited 2014-08-12 19:10
    If you declare variables inside code blocks (like if), they go out of scope after code exits the block. To fix this, I declared int *cog globally which ensures that its value will persist through multiple visits to the if statements. I also increased the stack space that cog_run allocates for both cogs. It helps to be a little extravagant with stack space during prototyping, because if you don't declare enough, runtime bugs like one cog starting but not the other can occur. I'm not 100% sure that's the case this time, but let's give it a try. Try this. There may still be runtime bugs, but we have taken a step in the right direction.

    P.S. I marked changes with comments that have // <-- and/or // -->

    Andy,
    Thanks for the help. As you stated, the solution was declaring
    int *cog;
    
    as a global and removing the * from 'cog' when the function is called in the conditional. I tried reducing the stack size to what i orignally had and it still worked. But since the effect on code size is negligible, for the final version, I kept them as you suggested.

    Do you think it would be useful to post the final code once I clean it up (using bluetooth to control activityBot) and if so in which forum?

    I still get confused about pointers and when to use * and &. Maybe a pointer tutorial for the C - Learning system would be useful?

    Thanks again,

    Tom
  • edited 2014-08-12 19:24
    Hi Tom,

    Ah, good, thanks for the update. I'm glad that cleared it up. Yes, a tutorial on pointers would be in order. The suggestion is causing a traffic jam of bad pun titles in my head though, starting with Pointers on Pointers. :) Yes, it would be great if you posted the final project. The Projects forum came to mind first, but this (Learn) and Robotics are also good candidates. Your call.

    Andy
  • David BetzDavid Betz Posts: 14,516
    edited 2014-08-12 20:34
    twm47099 wrote: »
    int cog = cogstart(&blink, NULL, stack, sizeof(stack));
    FYI, you don't need the & in front of the function name in this call or in a call to cog_run since C always passes functions by reference. This is similar to the way arrays are passed as parameters to functions. However, the & doesn't seem to hurt. I'm not sure if it is officially allowed by the C standard.
  • edited 2014-08-13 13:23
    Thank you David, we will update our documentation accordingly.

    Andy
  • David BetzDavid Betz Posts: 14,516
    edited 2014-08-13 17:49
    Thank you David, we will update our documentation accordingly.

    Andy
    By the way, I think it looks better with the & so I don't blame you for writing it that way. I'm just not sure it is officially allowed. This is one thing I don't like about C, the implicit & operator for arrays and functions.
  • twm47099twm47099 Posts: 867
    edited 2014-08-13 18:23
    David Betz wrote: »
    By the way, I think it looks better with the & so I don't blame you for writing it that way. I'm just not sure it is officially allowed. This is one thing I don't like about C, the implicit & operator for arrays and functions.

    Dave and Andy,
    I tried my program without the & for each cog_run, and as Dave stated it worked the same as with them.

    Tom
  • twm47099twm47099 Posts: 867
    edited 2014-08-13 20:05
    Andy,
    I found another issue and have a question.
    If ping was running when ending the program by pressing the exit button on the bluetooth device, ping kept running after the main program had ended. So I had added a cog_end(cog) just before the last } of main. That is in the in the code you had marked up.

    But I found that sometimes restarting the code (pressing the reset button and having code reloaded from eeprom), that pressing the ping button on the BT device would not restart ping and the program would no longer accept any commands from the BT device.

    The ping button on the BT device is a toggle and the first time pressing the button if ping had been running when exit was pressed would be ping stop. So the program was trying to stop a cog that had not been started. To fix that I added a flag "w" that was reset to 0 at the start of main, set to 1 when do_ping was started, and reset to 0 when do_ping was stopped. When receiving a command to end ping I added a test for w. The code segments that were changed are below.

    But that leads to another question. Since the ping cog kept running when main ended, I assume that the getbtjoy() cog is also still running. When I press reset I believe it stops, but is there any reason to stop it before ending main??

    These are the code changes to not try to stop a non-running do_ping()

    Tom

    volatile int joydata;  
    int x;   
    int y;
    int w;                          // <-- added
    
    int xspd = 0;    
    w = 0;               // 0 -- ping is not running at start of main  <--- added
    cog_run(&getbtjoy, 222);
    

    if(c1[2]=='C')                  // Button 2, on turns on ping       
     {          
       cog = cog_run(&do_ping,120); 
       w = 1;                                                  // <--- added 
       a = 0;       
      }       
    if(c1[2] == 'D' && w)                  // Button 2, off turns off ping       <-- added test for w
    {          
      cog_end(cog);  
      w = 0;                                    // <-- added
      a=0;        
    }
    
       } // end while c0    
    drive_ramp(0, 0);                   // stop movement at end of program    
    //  printi(" end \n\n");    
        if( w ) cog_end(cog);           //  if ping running when quit is pressed, turn off  <-- added test for w
      } // end main
    
  • edited 2014-08-14 11:00
    Thanks for reporting this Tom. Something similar to what your code does should be added right inside cog_run and cog_end.

    Whether or not to shut down your peripheral cogs depends on whether or not the application needs to continue running after the main function has stopped. Another thing to consider would be if a device can enter a state that the main function cannot bring it back from during initialization. For example, let's say the initialization sends a sequence to the device that would only make sense if the device had been properly shut down previously. A power reset might put the device into the receptive state, but pressing and releasing the RST button might not. So, if your application expects that the RST button might be pressed and released, the initialization sequence will need a way to wake the device back up.

    There are some delays built into the beginning of the program that can add up. There is a 0.7 second delay built into simpletools to prevent Windows machines from mistaking print messages from the Propeller as messages from a plug and play serial mouse. The abdrive library could also stand to load calibration data more quickly (it's on my list). So, if certain tasks are performed before that, the main function might miss them.

    Andy
Sign In or Register to comment.