Shop OBEX P1 Docs P2 Docs Learn Events
Switch - Case Question - C Code — Parallax Forums

Switch - Case Question - C Code

ajwardajward Posts: 1,130
edited 2015-04-01 10:37 in General Discussion
Still working on my hybrid Boe/Activity Bot and I've run into a bit of a problem. I'm using Switch - Case to determine a course of action... that is selecting a function to turn Bob based on the return from a Ping.
Everything seems to work, up to the point of switch-case selecting the proper function. I've Googled and can't find quite what I'm looking for. Does anyone have a link to a detailed explanation of the switch-case statement?

Many thanks,

Amanda

Edit: I'll probably have more questions. This is the most complex C program I've written and I have almost certainly overlooked something critically "trivial". ;-)

Comments

  • davejamesdavejames Posts: 4,047
    edited 2015-03-12 20:45
    Hi Amanda,

    Try this link:
    http://www.cplusplus.com/doc/tutorial/control/

    About 3/4s way down is the discussion on switch/case.
  • twm47099twm47099 Posts: 867
    edited 2015-03-12 21:12
    Amanda,
    I have an example that I wrote for my ActivityBot where I combined a couple C Learning Tutorials to use of a remote control (Sony DSLR Camera SIRC remote) and whiskers that take control when the bot runs into something. To give immediate action when a whisker hits, I run that in a separate cog. The program is somewhat complex. But the remote control part uses the Case statements. One thing to remember is that each of the case arguments has to be a constant. In my program I used #define to assign the SIRC remote value to a more descriptive constant name (e.g. #define fwd 58 ). If you have a Sony compatible remote and know the numeric codes for the keys, you could actually change the numeric values in the define statements and run the program. There is a program in the Learning C tutorials that reads and displays SIRC codes from a remote.

    This was one of my early programs so it is probably a lot more complex than it needs to be, but I think it illustrates the use of the switch-case statement with the use of "break" to end or exit each case.

    Tom
    /*
      SIRC Remote control 8.c
    
      This worked - There is no delay between whisker touching and bot stopping.  
        Before using separate cog for whiskers, there was a noticible delay, and whisker fully 
        compressed before bot wold stop.
      This version stops, backs up, turns 90 deg from obstacle and stops.  
        Must press remote key to restart motion.  Consider changes to allow continuing motion wo 
        need to press remote key.
        
      Try multiple cogs - new cog to handle whiskers
      http://learn.parallax.com/propeller-c-tutorials
    
      Connect 38 kHz IR receiver to Propeller P10
      Decodes button presses on Sony-compatible IR remote 
      Commands Activitybot to move straight, pivot 45 deg, move in a curve,
       increase or decrease speed and curve radius, and stop. 
      Also has whisker code for obstacle avoidance.  
      This has ramping to speed.  Increment = 4 ticks per 1/50 sec (4/0.020 sec).  
      To get from 0 to 100 takes 25 increments = 0.5 sec
     
      Load EEProm and Run
    
    */
    
    #include "simpletools.h"                      // Libraries included
    #include "sirc.h"
    #include "abdrive.h"                          // abdrive library
    
          // Sony camera remote control aliases 
    
    #define fwd 58       //  forward
    #define stp 57        //   stop
    #define rvs 59        // reverse
    #define pl 62          // pivot left
    #define pr 63          //  pivot right
    #define cvl 56         //  curve left
    #define cvr 61         //  curve right
    #define ispd 74       // increase speed
    #define dspd 75      // decrease speed
    
    
    void whisker(void *par);
    
    unsigned int stack[40 + 50];
    static volatile int bumpflg;
    
    int main()                                    // main function
    {
      int spd = 50;                 //  initial speed once fwd command given
      int dflg = 0;                 // direction flag = -1 fwd or rev, 0 = stopped, 1 = curve left, 2 = curve right
      drive_speed(0, 0);
      sirc_setTimeout(1000);                // -1 if no remote code in 1 s
      bumpflg = 0;
      freqout(4, 2000, 3000);                       // Speaker tone: 2 s, 3 kHz
      cogstart(&whisker, NULL, stack, sizeof(stack));
      while(1)                                   // Repeat indefinitely
      {                                         
               //  decode signal from P10
        int button = sirc_button(10);     //  get code from remote
        switch(button)
        {
          case stp :
            if(bumpflg)
            { pause(2000);
              break;
            } 
          drive_ramp(0, 0);          //  Center button - Q
          dflg = 0;
          break;
    
          case fwd :
            if(bumpflg)
            { pause(2000);
              break;
            }
           drive_ramp(spd, spd);      //  Top Arrow - M
           dflg = -1;
           break;
    
          case rvs :
            if(bumpflg)
            { pause(2000);
              break;
            }
           drive_ramp(0, 0);
           drive_ramp(-spd, -spd);      //  Down Arrow 
           dflg = -1;
           break;
    
          case pr :                                     //    Right Arrow - N
            if(bumpflg)
            { pause(2000);
              break;
            }
          drive_ramp(0, 0);
          drive_ramp(13, -13);                             //  Turn Right 45 deg & stop
          pause(1000);
          drive_ramp(0, 0);
          dflg = 0;
          break;
    
          case pl :                                   //  Left Arrow - P
            if(bumpflg)
            { pause(2000);
              break;
            }
          drive_ramp(0, 0);
          drive_ramp(-13, 13);                             //  Turn Left 45 deg & stop
          pause(1000);
          drive_ramp(0, 0);
          dflg = 0;
          break;
    
          case cvr :              //    Curve Right - L
            if(bumpflg)
            { pause(2000);
              break;
            }
            if (spd<25) spd=25;
          drive_ramp(spd, spd-20);
          dflg = 2;
          break;
    
          case cvl :            //    Curve Left  - K
            if(bumpflg)
            { pause(2000);
              break;
            }
            if (spd<25) spd=25;
          drive_ramp(spd-20, spd);
          dflg = 1;
          break;
    
          case ispd :      //    Increase speed
            if(bumpflg)
            { pause(2000);
              break;
            }
            if (dflg == 1)      // curving left - increase spd and move in curve at new spd
            {
              spd=spd+5;                                // Up rocker - H
              if (spd > 100) spd = 100;
              drive_ramp(spd-20, spd);
              }
            else if (dflg == 2)      // curving right - increase spd and move in curve at new spd
             {
              spd=spd+5;                                // Up rocker - H
              if (spd > 100) spd = 100;
              drive_ramp(spd, spd-20);
              }
            else if (dflg == -1)      // straight ahead - increase spd and move at new spd
             {
              spd=spd+5;                                // Up rocker - H
              if (spd > 100) spd = 100;
              drive_ramp(spd, spd);
              }
            else 
             {
              spd=spd+5;                                // Up rocker - H
              if (spd > 100) spd = 100;
              }                                          
            break;                                                 // Finish up rocker
    
          case dspd :         //  decrease spd
            if(bumpflg)
            { pause(2000);
              break;
            }
            if (dflg == 1)      // curving left - decrease spd and move at new spd
            {
              spd=spd-5;                                // dn rocker 
              if (spd < 25) spd = 25;
              drive_ramp(spd-20, spd);
              }
           else if (dflg == 2)      // curving right - decrease spd and move at new spd
            {
              spd=spd-5;                                // dn rocker 
              if (spd < 25) spd = 25;
              drive_ramp(spd, spd-20);
              }
            else if (dflg == -1)      // straight ahead - decrease spd and move at new spd
             {
              spd=spd-5;                                // dn rocker 
              if (spd <0) spd = 0;
              drive_ramp(spd, spd);
              }
            else                                  //  stopped
             {
              spd=spd-5;                                // dn rocker 
              if (spd <0) spd = 0;
              }  
            break;                                       // end decrease 
       default :
            break;
          }
      }  
    }
    
    void whisker(void *par)
    {
      while(1)
      {
        // If whisker pressed, avoid obstacle.
       // First just right whisker - input(8) is 0 if whisker is pressed
        if(! input(8))
          {
            drive_speed(0,0);
            bumpflg = 1;                               // Set flag so main will not give conflicting drive commands
            drive_ramp(-60, -60);                  // Back up 0.5 seconds
            pause(500);
            drive_ramp(0,0);
            drive_speed(-26, 26);                   // Turn left 1 seconds -- 90 deg
            pause(1000);
            drive_ramp(0, 0);                             //  stop
          }
    
        // Just left whisker - input(7)
        else if(! input(7))
          {
          drive_speed(0,0);
          bumpflg = 1;
          drive_ramp(-60, -60);                  // Back up 0.5 seconds
          pause(500);
          drive_ramp(0,0);
          drive_speed(26, -26);                   // Turn right 1 seconds -- 90 deg
          pause(1000);
          drive_ramp(0,0);                         // stop
          }
        bumpflg = 0;                                //  clear flag, allow main to have control
      }
    }
    
    
    
  • ratronicratronic Posts: 1,451
    edited 2015-03-13 16:48
    Amanda I am not a C expert but here is an example of Activitybot code that works for me using the switch statement to pick an operating mode then carry out that task.
    /*                                            Object detect - tracking Activitybot w/ Raspberry Pi + Pi cam                                                     */  
    #include "simpletools.h"             
    #include "abdrive.h"
    #include "servo.h"
    #include "fdserial.h"
    #include "ping.h"
    
    void camTrack(void);                /* camera platform tracks detected object - default mode */
    void robotFollow(void);             /* robot follows tracked object */
    void scanCam(void);                /* camera platform scans horizontal */
    void robotPlay(void);                /* robot gets a servo workout */
    void robotReset(void);              /* robot servo's reset to default positions */
    void track(void);                       /* get detected object location/area data packet from Pi or a new mode byte from Xbee serial terminal */ 
    
    #define CENTER 1500
    #define MINSRV 750
    #define MAXSRV 2250
    #define PAN 8
    #define TILT 9
    #define PINGSRV 16
    #define KP 3
    #define SETPOINT 7
    
    fdserial *piSer;
    fdserial *xBee;
    int mode = 1;
    int x1 = CENTER;
    int y1 = CENTER;
    int max, maxArea, pos, x, y, z;
    
    int main() { 
      set_directions(27, 26, 0b11);
      piSer = fdserial_open(6, 7, 0, 115200);      /* open serial connection to Raspberry Pi */ 
      xBee = fdserial_open(11, 10, 0, 115200);  /* open connection to Xbee serial terminal */ 
      servo_set(PAN, CENTER);           
      servo_set(TILT, CENTER);           
      servo_set(PINGSRV, CENTER);   
      pause(500);
      
      while(1) {
        switch(mode) {
          case 1: camTrack(); break;
          case 2: robotFollow(); break;
          case 3: scanCam(); break;
          case 4: robotPlay(); break;
          case 5: robotReset(); break;
          default: break;
        }
        track(); 
      }         
    }        
    
    void camTrack() {     /* mode 1*/
      drive_ramp(0, 0);
      while(1) {
        track(); 
        toggle(26);
        if (x < -20) x1 -= 50;
        if (x < -7) x1 -= 5;
        if (x > 20) x1 += 50; 
        if (x > 7) x1 += 5;
        if (y < -20) y1 -= 50;
        if (y < -7) y1 -= 5;
        if (y > 20) y1 += 50;
        if (y > 7) y1 += 5;
        if (x1 > MAXSRV) x1 = MAXSRV;
        if (x1 < MINSRV) x1 = MINSRV;
        if (y1 > MAXSRV) y1 = MAXSRV;
        if (y1 < MINSRV) y1 = MINSRV;      
        servo_set(PAN, x1);
        servo_set(TILT, y1);
        servo_set(PINGSRV, 3000 - x1);
        if (mode != 1) {
          servo_set(PAN, CENTER);
          servo_set(TILT, CENTER);
          servo_set(PINGSRV, CENTER);
          pause(500);
          break;
        }        
      }                     
    }   
    
    void robotFollow() {  /* mode 2 */                 
      servo_set(PAN, CENTER);
      servo_set(TILT, CENTER);
      servo_set(PINGSRV, CENTER);
      for(;;) {
        if (fdserial_rxReady(piSer) != 0) {
          track();
          int err = SETPOINT - maxArea;
          int speed = KP * err;
          drive_rampStep(speed + x, speed - x);
        } 
        if (mode != 2) {
          robotReset();
          break;
        }
      }               
    }  
    
    void scanCam() {     /* mode 3 */     
      drive_ramp(0, 0);
      servo_set(PAN, MINSRV);
      servo_set(TILT, CENTER);
      pause(500);
      max = 0;
      for (int i = MINSRV; i < MAXSRV; i++) {
        servo_set(PAN, i);
        if (fdserial_rxReady(xBee) != 0) {
          robotReset();
          break;
        }      
        if (fdserial_rxReady(piSer)); {  
    //      track();
          if (fdserial_rxReady(xBee) != 0) {
            robotReset();
            break;
          }
          if (maxArea > max) {
            max = maxArea;
            pos = i;
          } 
        }                                  
      }
      pause(250);
      print ("  %d", pos); 
    }                 
     
    void robotPlay() {     /* mode 4 */     
      drive_ramp(-30, 30);
      for(;;) {
        if (fdserial_rxReady(xBee) != 0) {
          robotReset();
          break;
        }      
        x = CENTER;
        y = CENTER;
        servo_set(PAN, x);
        servo_set(TILT, y);
        servo_set(PINGSRV, y);
        pause(250);
        while(1) {
          if (fdserial_rxReady(xBee) != 0) {
            robotReset();
            break;
          }        
          servo_set(PAN, x++);
          servo_set(TILT, y--);
          servo_set(PINGSRV, 3000 - y);
          if (x == MAXSRV) {
            while(1) {
              if (fdserial_rxReady(xBee) != 0) {
                robotReset();
                break;
              }                 
              servo_set(PAN, x--);
              servo_set(TILT, y++);
              servo_set(PINGSRV, 3000 - y);
              if (x == MINSRV) {
                break;
              }              
            }                   
          }          
        }        
      }
    } 
    
    void robotReset() {   /* mode 5 */                    
      drive_ramp(0, 0);
      servo_set(PAN, CENTER);
      servo_set(TILT, CENTER);
      servo_set(PINGSRV, CENTER);
      pause(500);
    }
                            
    void track() {           /*  Get detection data packet from Pi or get new mode byte from Xbee */ 
      fdserial_rxFlush(piSer);           /* Stay here until data packet or mode byte received */                                            
      while(1) {                                   
        if (fdserial_rxReady(xBee) != 0) {
          mode = fdserial_rxTime(xBee, 1) - 48;
          toggle(27);
          break;
        } 
        if (fdserial_rxCheck(piSer) == 33) {
          if (fdserial_rxTime(piSer, 1) == 38) {
            x = fdserial_rxTime(piSer, 1);
            y = fdserial_rxTime(piSer, 1);
            z = fdserial_rxTime(piSer, 1);
            maxArea = z + fdserial_rxTime(piSer, 1) * 256;
            x -= 55;
            y -= 40;
            toggle(26);
            break;
          } 
        }
      }                         
    }
    
  • ajwardajward Posts: 1,130
    edited 2015-03-14 07:00
    Thanks guys!!!

    As I look at things more, I'm less sure that Switch-Case (My implementation of it anyhow.) is causing my headaches.

    What is supposed to happen is:
    Bob runs in a straight line till he gets within ~8 inches of an obstacle, then stops and the Ping scans 45 and 90 degrees, left and right.
    The code then determines which of the four directions is the longest, turns the 'bot in that direction and starts off in a straight line again.
    The last option is to do a 180 and take off again.

    The problem is Bob will make a turn, but never the way I think he should and sometimes goes to the default of doing a 180 when there are better options.

    Things I (may) have overlooked:
    I may not be allowing enough time for Ping to get each distance into the array.
    I need to make sure that the "longest distance" is far enough to be useful.
    I need to make sure I'm getting the sub-scripts for the array right. (I kind of dislike arrays, but they "can" be pretty useful!)

    Not asking for solutions here. Just thinking out loud... in print. I thought about posting my code, but at this point, I didn't want y'all to spend your entire weekend laughing! ;-)

    Lastly, a shameful admission! Although I've found C horrendous for larger projects, using it to program my 'bot is rather enjoyable... even preferable to Spin.

    Now... off for that second cup of coffee! Keep tinkering!

    Amanda
  • idbruceidbruce Posts: 6,197
    edited 2015-03-14 08:20
    Amanda

    Can you post a zip file archive of your project?

    If you are using SimpleIDE, under the Project menu, there is a sub-menu item labeled Zip. If you click Zip, it will archive the entire project, so that you may upload and then we may be able to provide more worthy assistance in your endeavors.
  • ajwardajward Posts: 1,130
    edited 2015-03-15 19:42
    Hi All...

    Attached is the zip for Bob's navigation program, 'tis far from perfect. It kind of works except when Switch/Case selects a turn routine, it never seems to be the right one. I know I have to fine tune the distance analysis and possibly add more delay time after the Ping reads a distance. Anyway, here it is...

    Bob_roam1.zip
  • TorTor Posts: 2,010
    edited 2015-03-16 00:23
    There's one (non-switch-related) issue with the code. When a function has no parameters it should be declared as '(void)', not just '()'. Without the explicit 'void' (e.g. int ctr_ping() should be int ctr_ping(void) for example), the C compiler falls back to K&R C which does not have any checks for parameters. So the declarations at the top of main.c, for example
    // Declare functions                        << Comment
    int ctr_ping();                             // Declare function to aim Ping
    ..
    
    will not serve the purpose of a declaration, which is to tell the compiler what the function expects. And it will let wrong usage slip by. Example:
    int ctr_ping();       // K&R C declaration
    int ctr_ping2(void);  // ANSI C declaration
    
    int main (void)
    {
            ctr_ping(4);  // <-- Compiler will not notice the error 
            ctr_ping2(4); // <-- Compiler will complain, as it should
            return (0);
    }
    
    $ cc -Wall -c example.c
    example.c: In function 'main':
    example.c:7:2: error: too many arguments to function 'ctr_ping2'
      ctr_ping2(4);
      ^
    example.c:2:5: note: declared here
     int ctr_ping2(void);
         ^
    
    Notice how declaring the parameter list as 'void' (no parameters) lets the compiler detect when by mistake a function is called with a parameter when there is supposed to be none.

    -Tor
  • idbruceidbruce Posts: 6,197
    edited 2015-03-16 14:41
    Amanda

    Someone recently said... There is no such thing as too many comments....... Well now, I may beg to differ :)

    I realize you are just learning, so I will just say that there is no need to comment the obvious. If I am not mistaken, I believe the archive below should solve your problem, but I do not have your bot to test. Additionally, I doctored up the file to give you an idea of what it should look like, however there are a variety of programming techniques and this is just mine. I just noticed the tabs got messed up, but you should get the idea.

    You had a problem in eval_dist() and turn_Bob(). Hope this works for you :) Compare these functions, and you will see what I did.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-17 15:42
    Well? Does it work like you wanted it to?
  • GenetixGenetix Posts: 1,754
    edited 2015-03-17 18:55
    Amanda,

    When I have comments that wrap around to the next lline I either indent under the statement or break it into pieces so all the \\ line up.
    statement;
      // Place looooooooooooooooooooooooooooooooooong comment here
    
    statement;          // Comment starts here.........
                        // ...and continues here!
    

    Also, at the top of the program you can use a comment block instead of a comment line.
    /* Comment block starts here
       Goes here
       and here
       etc.
       The compiler ignores anything within
       That's enough
    */     // End of comment block
    

    It's also a good idea to comment functions with what they do, their inputs (if any), and their outputs (if any).
    /* This function does nothing but kill time.
       It has no inputs or outputs
    */     // Onto the next function...
    
    /* This function takes input from the user and spits gibberish back to the screen.
       Input: a line of text from the keyboard.
       Output: scrambled text send to the display
    */     // Moving on...
    
    /*
      Depending on the toggle switch setting, this function actuates different devices.
      Inputs: 4 Toggle switch connected to Pins 0 to 3.
              Switch 1 - Turns LED on and off
              Switch 2 - Makes speaker beep periodically when on.
              Switch 3- Turns a motor on and off.
              Swicth 4 - Makes the motor spin CW or CCW.
      Outputs: 3 Outputs on Pins 4 to 7
              Pin 4 - Red LED (Active-High or cathode is connected to ground)
              Pin 5 - Connected to + of Speaker with the other end connected to ground (Active-High output or On when output is High)
              Pin 6 - Connected to Enable pin of motor driver IC
              Pin 7 - Connected to Direction pin of motor driver IC
      All for now until I decide to add more stuff.
    */
    // Code follows
    

    It's also always a good idea to have blank lines between groups of statements that do different things and functions or code blocks.
    It's also good practice to put comments before blocks of code explaining what they do.
    // Define I/O Pins
    
    // Define hardware constants
    
    // Declare Variables for hardware
    
    // Initialize whatever hardware
    
    // Get data from hardware using library
    
    // Do something with input data
    
    // Send output to device using library
    
    // Check for errors
    
    // Resend data or print error message
    
    etc.
    
  • ajwardajward Posts: 1,130
    edited 2015-03-17 20:24
    idbruce wrote: »
    Well? Does it work like you wanted it to?

    I've only had time to glance at the code. My "part-time" job has me swamped at the moment. I appreciate you looking and will test it soon. Thank You!

    Amanda

    Oh... all the comments were just me being goofy. ;-)
  • GenetixGenetix Posts: 1,754
    edited 2015-03-18 17:07
    Amanda,

    Sorry about grilling you over your comments since it looks a lot better in SimpleIDE than Wordpad.

    I've deciphered most of the code since my C is very rusty and I don't have an ActivityBot.
    Idbruce fixed some errors especially dealing with Arrays. Remember that an array starts at element 0 even though you refer to it at Sector 1.
    I see a lot of repeating code that would be better done with a function. For example, your multiple beeps before moving to a sector.
    I myself prefer to define constants for things that are easily changed. Idbruce may know whether to use #degine or const.
    I do know that #define is used by the Pre-processor, same as the BASIC Stamp editor, while const is treated just like other variables including having a memory location assigned to it.

    I do wonder why you use the Differential Drive library instead of the Servo library for the Ping Servo.
    The Ping servo is a standard servo while differential drive robots use continuous rotation servos.
    There is even a lesson and some code on how to use the standard servo with C here:
    http://learn.parallax.com/propeller-c-simple-devices/standard-servo

    I also see that you have 2 Global variables that aren't being used. I don't see why you just don't have counter be a global variable since you only use it for looping.
    The other one, direction, doesn't appear to be used anywhere.

    After studying the ActivityBot tutorial I can see where you got some of your code from.
    This program would be a lot cleaner if you first defined what it's supposed to do and then build and test it one function at a time.
    Say for example get the Ping servo code working, and also Bob's movement code working before you add them to Main.
    It's also a good idea to have separate programs that test each piece of hardware to be sure it's working properly.
  • GenetixGenetix Posts: 1,754
    edited 2015-03-18 17:15
    Amanda,

    Sorry about grilling you over your comments since it looks a lot better in SimpleIDE than Wordpad.

    I've deciphered most of the code since my C is very rusty and I don't have an ActivityBot.
    Idbruce fixed some errors especially dealing with Arrays. Remember that an array starts at element 0 even though you refer to it at Sector 1.
    I see a lot of repeating code that would be better done with a function. For example, your multiple beeps before moving to a sector.
    I myself prefer to define constants for things that are easily changed. I see that Andy uses const in the Propeller C Tutorials.
    http://learn.parallax.com/node/157
    http://learn.parallax.com/propeller-c-simple-protocols/bit-masks-better-code
    http://learn.parallax.com/propeller-c-simple-protocols/half-duplex-serial

    I do wonder why you use the Differential Drive library instead of the Servo library for the Ping Servo.
    The Ping servo is a standard servo while differential drive robots use continuous rotation servos.
    There is even a lesson and some code on how to use the standard servo with C here:
    http://learn.parallax.com/propeller-c-simple-devices/standard-servo

    I also see that you have 2 Global variables that aren't being used. I don't see why you just don't have counter be a global variable since you only use it for looping.
    The other one, direction, doesn't appear to be used anywhere.

    After studying the ActivityBot tutorial I can see where you got some of your code from.
    This program would be a lot cleaner if you first defined what it's supposed to do and then build and test it one function at a time.
    Say for example get the Ping servo code working, and also Bob's movement code working before you add them to Main.
    It's also a good idea to have separate programs that test each piece of hardware to be sure it's working properly.
    [/******************************************************************************/
    /*                                                                            */
    /******************************************************************************/
    /*
     Basic Navigation Program
     Frankensteined from many sources
     Works okay so far
    
    P4 - (+)PiezoSpeaker - GND
    
    P16 -----> White |
    Vsupply ---> Red |- Servo (Ping)
    GND -----> Black |
    
    GND ----------> GND |
    5V ------------> 5V |- Ping)))
    P17 - 2.2K ---> SIG |
    
    */
    /******************************************************************************/
    /******************************************************************************/
    /*                                  INCLUDES                                  */
    /******************************************************************************/
    #include "abdrive.h"          // ActivityBot navigation with encoders + servos. 
    #include "ping.h"             // Ping))) Ultrasonic Distance Sensor. 
    #include "simpletools.h"      // General library
    #include "servo.h"            // Servo control - up to 14 servos with 1 core. 
    /******************************************************************************/
    /******************************************************************************/
    /*                                 CONSTANTS                                  */
    /******************************************************************************/
      // I/O Pins
    const int piezoPin      = 4;                 // PiezoSpeaker
    const int servoPin      = 16;                // Ping Servo
    const int pingPin       = 17;                // Ping)))
      // System
    const int delay         = 500;              // 500 ms = 1/2 sec
      // Piezo Sounds
    const int duration      = 1000;             // 1000 ms = 1 sec.
    const int resetDuration = 2000;             // 2000 ms = 2 sec.
    const int beepFrequency = 500;              // 500 Hz
    const int frequency     = 3000;             // 3000 Hz
      // Ping Servo
    const int det_rng       = 20;               // detection_range (35cm) [20 cm]
    const int servoCenter   = 1450;             // Servo Center Position (us)
              // Rotate Ping forward (980 ~90 degrees, 1450 ~90 degrees)   [1400 us]
    const int servoDelay    = 3000;             // 3000 ms = 3 sec.
    /******************************************************************************/
    /******************************************************************************/
    /*                                 PROTOTYPES                                 */
    /******************************************************************************/
    int ctr_ping(void);       // Center the Ping Servo
    int eval_dist(void);		 // Determine largest distance sector
    int scan_ahead(void);		 // Five sector scan using Servo Position Table
    int turn_Bob(void);       // Turn Bob towards largest distance
    int signal(int);          // Signal using the PiezoSpeaker one or more times
    /******************************************************************************/
    /******************************************************************************/
    /*                                  GLOBALS                                   */
    /******************************************************************************/
    int array_pos;      // Ping distance array Position with the greatest distance
                        // Set by Eval_Dist for use by Turn_Bob
    int counter;        // Counter variable for Looping
    int distance;       // Distance returned by Ping (Used by Scan_Ahead)
    int ping_dist[5]; 	// Array for measurements from each Sector (cm)
                        // Set by Scan_Ahead and used by Eval_Dist
    int position[] = {625, 1050, 1450, 1900, 2250};   // Servo Position Table (us)
        // Sector 1 - 90 to the right = 625 us   [500 = 0 deg (90 to the Right)]
        // Sector 2 - 45 to the right = 1050 us
        // Sector 3 - Straight Ahead (0) = 1450 us   [1400 us = 90 deg (Center)]
        // Sector 4 - 45 to the Left = 1900 us
        // Sector 5 - 90 to the Left = 2250 us   [2300 = 180 deg (90 to the Left)]
    /******************************************************************************/
    /******************************************************************************/
    /*                                   MAIN                                     */
    /******************************************************************************/
    /******************************************************************************/
    /* Main()
      Center the Ping servo and keep moving forward until something is detected.
      Scan all 5 sectors for the largest distance and then move in that direction */
    /******************************************************************************/
    int main()
    							// Here we go!
    {
      freqout(piezoPin, resetDuration, frequency);            // Reset Indicator
      while(1)                          // repeats forever!
      {
        ctr_ping();				           // Center Ping Servo
        drive_speed(64, 64);			      // Turn servos @ 64 TPS (Forward - 60 RPM)
        while(ping_cm(pingPin) >= det_rng) pause(5);
          // Wait until object is in range of Ping (keep moving)
        drive_speed(0, 0);			        // Stop servos when an object is detected
    	 // Scan all 5 sectors, determine the largest, and move in	that direction
          scan_ahead();      // Scan all 5 sectors
          eval_dist();       // Determine largest sector and move in that direction
        // Placeholder for more nerdy stuff!	:)
        signal(1);           // Alert the Piezo 
      }
    	return 0;						// Okay, we've had enough (will never get here!)
    }
    /******************************************************************************/
    /******************************************************************************/
    /*                                 FUNCTIONS                                  */
    /******************************************************************************/
    /******************************************************************************/
    /* Center_Ping()
      Centers the Ping servo in the forward position.                             */
    /******************************************************************************/
    int ctr_ping()					// Center Ping sensor
    {
      freqout(piezoPin, duration, beepFrequency);       // Beep (500 Hz for 1 Sec)
      servo_set(servoPin, servoCenter);
        // deg posit = 500 + (number of tenths of a degree)					
              // > 500 = 1/10 of a degree CW of 0 degrees
              // Pin, Time in us
              // 1400 = Center
      for (counter = 0; counter < 7; counter++ )
        // Loop 7 times to allow Ping servo to align to the center position (2 sec.)
      {
        pause(delay);					                      // 1/2 sec.
      }
      servo_stop();					                      // Stop servo process
      return(0);										              // Back to main 
    }
    /******************************************************************************/
    /******************************************************************************/
    /* Scan_Ahead()
      Scans each Sector by using a Position table to move the Ping servo into that
        Sector, and then scanning the Sector with the Ping and storing the
        measured distance in the Distance array.                                  */
    /******************************************************************************/
    int scan_ahead()			       // Five sector scan
    {
      for(counter = 0; counter < sizeof(ping_dist)/sizeof(int); counter++)
        // Count from 0 to the size of the Ping_Distance array
      {
        servo_set(servoPin, position[counter]);
          // Turn the servo to the position given by the Position Table 
        pause(servoDelay);        // Wait for Servo to get into position (3 sec.)
        distance = ping_cm(pingPin);         // Get cm distance from Ping)))
        ping_dist[counter] = distance;
          // Store the measured distance in the slot for that Sector
      }
    	return 0;
    }
    /******************************************************************************/
    /******************************************************************************/
    /* Evaluate_Distances()
        Evaluates all the recorded distances and finds the one with the largest
          value.
        That sector number is then fed to Turn_Bob through the global variable
          Array_Position.                                                         */
    /******************************************************************************/
    int eval_dist()                        // Determine longest distance using Ping
    {
    	// Declare Local Variable
        int max_val = ping_dist[0];        // Largest distance
          // Start with 1st position (90 to the Right)
      // Set Global Variable    
    	 array_pos = 0;		                 // Start with the 1st position
      // Loop through Ping Distance array (SizeOf returns the size of a variable)
        for(counter = 0; counter < sizeof(ping_dist)/sizeof(int); counter++)
    	 {
          if (ping_dist[counter] > max_val)
          {
            // If new position is further than current position
            // then make it the new maximum.
              max_val = ping_dist[counter];
              array_pos = counter;
          }
        }
      turn_Bob();		// Turn Bob towards largest distance, using array_pos
      return 0;
    }
    /******************************************************************************/
    /******************************************************************************/
    /* Turn_Bob()
        Turns Bob, the ActivityBot, in the direction of the sector that gave the
          largest distance measurement.
        Eval_Dist determines which sector is largest and stored that in a global
          variable called Array_Position that this routine used.                 */
    /******************************************************************************/
    int turn_Bob()							// Function to change Bob's direction of travel
    {
      switch(array_pos)           // Go to the sector with the largest distance
    	{
    		case 0:		// Sector 1 - 90 to the Right (Note the colon, not a semicolon)
    			signal(1);                     // Alert the Piezo once
    			drive_goto(-28, 27);           // 90-degree
    			drive_speed(0, 0);             // Stop servos
    			break;
    
    		case 1:		// Sector 2 - 45 to the Right
    			signal(2);                     // Alert the Piezo Twice
    			drive_goto(14, -14);           // Rotate Right
    			drive_speed(0, 0);			      // Stop servos
    			break;
        
    		case 2:		// Sector 3 - Straight Ahead (Forward)
    			signal(3);                     // Alert the Piezo 3 Times
    			drive_goto(64, 64);            // 1 Revolution of each wheel
    			drive_speed(0, 0);			      // Stop servos
    			break;
        
    		case 3:		// Sector 4 - 45 to the Left
    			signal(4);                     // Alert the Piezo 4 Times
    			drive_goto(14, -14);
    			drive_speed(0, 0);			      // Stop servos
    			break;    
    
    		case 4:		// Sector 5 - 90 to the Right
    			signal(5);                     // Alert the Piezo 5 Times
    			drive_goto(28, -27);           // 90-degree Right Turn
    			drive_speed(0, 0);			      // Stop servos
    			break;
    
    		default:
    			signal(6);                     // Alert the Piezo 6 Times
    			drive_goto(53, -57);           // Rotate Right - Other way???
    			drive_speed(0, 0);			// Stop servos
    			break;
    	}
    	return 0;    
    }
    /******************************************************************************/
    /******************************************************************************/
    /* signal()
      Signal the PiezoSpeaker by the number of times it's given.                  */
    /******************************************************************************/
    int signal(number)
    {
      for(counter = number; counter == 0; counter--)      // Loop from Number to 0
      {
        freqout(piezoPin, duration, frequency);   // Piezo Alert (3KHz for 1 Sec.)
        pause(delay);                             // 1/2 sec. 
      }
      	return 0;
    }
    /******************************************************************************/
    /******************************************************************************/
    /*                               END OF PROGRAM                               */
    /******************************************************************************/
    /******************************************************************************/
    
  • ajwardajward Posts: 1,130
    edited 2015-03-19 17:53
    @idbruce - Just tried it and it doesn't do any turning. Need to go through the code the check the changes.

    @genetix - I did build all the functions separately and they all did their thing. Problem is, when glued together they start mis-behaving. :-(

    Amanda
  • twm47099twm47099 Posts: 867
    edited 2015-03-19 18:46
    ajward wrote: »
    @idbruce - Just tried it and it doesn't do any turning. Need to go through the code the check the changes.

    @genetix - I did build all the functions separately and they all did their thing. Problem is, when glued together they start mis-behaving. :-(

    Amanda

    Amanda,
    One of the things I do when I'm trying to debug my robot software when I have put all the functions together is to put the robot on a stand so the wheels aren't touching anything, add some print statements at various points in the program, run with terminal, and hold an object at different distances from the ping. Then see what values I get from ping, what value I get for the case statement, and make sure they make sense. If so then do the wheels turn in the right direction? If the ping and case results don't make sense, you will be able to start to isolate the problem.

    Tom
  • twm47099twm47099 Posts: 867
    edited 2015-03-19 21:05
    Amanda,
    I don't have my bot available so I tried to make do with just running the code from post #9 on my quickstart board without the servo and ping functions. I took the code and commented out all of the functions and calls for the servos and ping. I added print statements in each case that just printed "case 0" for case 0, "case 1" for case 1 etc. as shown below.
    printi("case 0",);
    


    I loaded ping_dist[] with 5 values with the maximum a specific array elements. Each time I ran the program, I changed the element that had the max value and the terminal showed the correct case#. So the case statement works.

    Does the ping servo turn the ping to the correct direction? I've not used the servo_set command, but normally use servo_angle. With that command, I have to have a sufficient pause to allow the servo time to reach its desired direction. I would suggest that you put a pause between the servo_set command and the ping_cm command, to make sure that ping is pinging in the correct direction.

    For case 2, the drive_goto command is commented out. That makes sense, since Bob was stopped from moving forward because it was too close to something. But instead of commenting out that command, I'd recommend using the drive_goto(53, -57) to do a 180 (are those numbers correct?).

    I also think that as written the code only goes through main one time? It drives straight ahead until it gets too close to something. It then scans and pings and turns to the furthest distance, then proceeds fwd until it gets too close to something, then stops and ends. If the ping reads the furthest distance as straight ahead, it won't turn, will start to move fwd, but then drop through the while statement and stop before it moves.

    Tom
  • ajwardajward Posts: 1,130
    edited 2015-04-01 01:07
    Many thanks for all the replies. Some much needed time off is coming up and I'll get back to working with this code.

    I'm beginning to think I'm expecting too much from the Ping sensor. In my tests, I'm driving the 'bot into a "T" shaped area not unlike part of a maze. Again, I "think" the distance readings for straight ahead and 90 degrees right and left are correct, But the returns for 45 degrees each way are being artificially extended because the echo doesn't come straight back to the sensor, but bounces around a bit before the Ping reads it. Does this make sense?

    Going to take a look at the "dist_eval" function to see if I can filter out these "erroneous" readings.

    @
  • idbruceidbruce Posts: 6,197
    edited 2015-04-01 01:56
    Amanda

    Sorry I missed your reply of Post #15.

    The eval_dist() function and the case statements should be correct, with all of this being dependant upon the following code:
    if (ping_dist[counter] > max_val)
    {
    	max_val  = ping_dist[counter];
    	array_pos = counter;
    }
    

    as established in the scan_ahead() function.

    Whether or not the intented execution code is workable, I could not verify, but eval_dist() and the case statements were verified as working.
  • twm47099twm47099 Posts: 867
    edited 2015-04-01 10:37
    ajward wrote: »
    Many thanks for all the replies. Some much needed time off is coming up and I'll get back to working with this code.

    I'm beginning to think I'm expecting too much from the Ping sensor. In my tests, I'm driving the 'bot into a "T" shaped area not unlike part of a maze. Again, I "think" the distance readings for straight ahead and 90 degrees right and left are correct, But the returns for 45 degrees each way are being artificially extended because the echo doesn't come straight back to the sensor, but bounces around a bit before the Ping reads it. Does this make sense?

    Going to take a look at the "dist_eval" function to see if I can filter out these "erroneous" readings.

    @

    Amanda,
    I think your concern regarding the 45 degree angle is the issue. The product guide for ping can be downloaded from this link:
    https://www.parallax.com/sites/default/files/downloads/28015-PING-Sensor-Product-Guide-v2.0.pdf

    It shows an example of the sound path for different reflection angles, and 45 degrees (or any too 'shallow' an angle) does not directly return to the ping. Also it shows in "test 2" the acceptance angle for a wall parallel to the backplane of the sensor (perpendicular to the axis of the sound path) to be quite narrow -- at 4 ft it is about 10 degrees to either side, further away it drops to about 5 degrees.

    If you drew a scale sketch of your "maze" and put the virtual 'BOB' at different points, draw a line from Bob to the wall at 45 degrees and trace the reflection as it bounces off the walls, you can get an idea of how long the path is when (and if) it intersects the ping. Try different angles; you may be able to find an angle that isn't too shallow and works well enough.

    If you had a bunch of cylinders as obstacles the 'main beam' of the sound would return directly to ping, but there would also be extraneous reflections that could take multiple paths before returning to the ping. I'm not sure how easy it would be to filter out the extraneous returns.

    For now, I'd recommend staying with 0 and +/- 90 degrees (and if the sketch shows an angle that works, that one also), maybe shortening the 'too close' range so that 90 degrees will show an opening.

    Hope this helps
    Tom
Sign In or Register to comment.