baby0168 program code help for servo and cut off

You can use any pins you want. Note that the example you’re using as a reference is for the Orangutan SVP, which handles servos slightly differently than your Baby Orangutan since it has dedicated servo outputs connected to a demultiplexer. You would want something like:

const unsigned char servoPins[] = {IO_D0, IO_D1, IO_D2}; 
servos_init(servoPins, 3);

Right now the only example programs included with the Pololu AVR library are for the Orangutan SVP, but the next release of the library will include the following two general servo examples that work the same way on all Orangutans (the comments should explain how to use it and why it uses the “B” versions of the servo functions):

#include <pololu/orangutan.h>

/*
 * servos1: for all Orangutans (LV, SV, SVP, X2, Baby) and the 3pi robot.
 *
 * This example uses the OrangutanServos functions to control two servos
 * connected to pins PD0 and PD1.  The servo pulses are generated in the
 * background using interrupts, so program execution is not blocked by
 * the pulse-generation code.  If you are using the Orangutan SV or SVP,
 * you can power the servos directly from the board's 5V bus.  For all
 * other Orangutans (LV, X2, Baby) and the 3pi robot, you need to power
 * the servos from a separate source since the on-board voltage regulators
 * do not have enough power to drive typical servos.  This example uses
 * pushbutton inputs to trigger events, so you will need connect an external
 * pushbutton (or wire) to the correct pin if if you are using a Baby
 * Orangutan.
 * 
 * The program will hold the two servos connected to pins PD0 and PD1 at
 * default positions unless a user pushbutton is pressed.  While the button
 * is held down, the servos will slowly move to new positions.  When the
 * button is released, the servos will quickly go back to their default
 * positions.
 *
 * Note: the "B" version of the servo functions are used in this example
 * so that it behaves the same for the Orangutan SVP as for all other
 * Orangutans.  If you are not using the Orangutan SVP, you can do not
 * need to use the B versions of the functions (e.g. you can use
 * set_servo_target(0, 1300) instead of set_servo_target_b(0, 1300)).
 * If you do this, you should replace the call to servos_init_extended()
 * with servos_init().
 *
 * The OrangutanServo library uses timer 1, so it conflicts with the
 * OrangutanBuzzer and OrangutanPulseIn libraries, which also use timer 1.
 * You should not add calls to OrangutanBuzzer or OrangutanPulseIn
 * functions to this example.
 *
 * https://www.pololu.com/docs/0J20
 * https://www.pololu.com
 * https://forum.pololu.com
 */

int main()
{
	// This array specifies the correspondence between I/O pins and
	// software-PWMed servos.  Servo 0 is on pin D0 and servo 1 is on pin D1.
	const unsigned char servoPinsB[] = {IO_D0, IO_D1};

	servos_init_extended((unsigned char[]){}, 0, servoPinsB, sizeof(servoPinsB));

	while (1)
	{
		set_servo_speed_b(0, 0);	// no speed limit (move as fast as possible)
		set_servo_target_b(0, 1200);
		set_servo_speed_b(1, 0);
		set_servo_target_b(1, 1700);

		// wait for any button to be pressed
		unsigned char button = wait_for_button_press(ANY_BUTTON);

		// set speed of servo 0 to 150, which means that the pulse width
		// will change by at most 15 microseconds every 20 ms, so it will
		// take 800 ms (0.8 s) to go from a position of 1200 to 1800.
		set_servo_speed_b(0, 150);
		set_servo_target_b(0, 1800);	// start servo 0 moving

		// set speed of servo 0 to 60, which means that the pulse width
		// will change by at most 6 microseconds every 20 ms, so it will
		// take 1333 ms (1.3 s) to go from a position of 1300 to 1700.
		set_servo_speed_b(1, 60);
		set_servo_target_b(1, 1300);	// start servo 1 moving

		// wait for pressed button to be released (servos will keep moving)
		wait_for_button_release(button);

		// when button is released, we jump to the top of the loop and the
		// servos positions are reset
	}
}
#include <pololu/orangutan.h>
#include <stdio.h>	// required for printf()

/*
 * servos-and-buzzer: for for the Orangutan LV, SV, SVP, X2, Baby-O and 3pi robot.
 *
 * This example demonstrates how you can have a program that makes sounds
 * and uses the OrangutanServos library.  OrangutanServos and OrangutanBuzzer
 * both use the same hardware timer (timer 1) to operate, so the two
 * libraries are not compatible with each other.  However, they can be
 * alternately used to make sound, then control servos, then make sounds
 * again.  This program also defines a simple note-playing function
 * (play_simple_note()) that uses loop delays to generate a signal with
 * the appropriate frequency on the buzzer pin.  This is a blocking function,
 * but it does not use timer 1 and does not require the OrangutanBuzzer
 * library (other than some #defines from the header file), so it can be
 * used to play notes while servos are active.
 *
 * This example uses the OrangutanServos functions to control two servos
 * connected to pins PD0 and PD1.  The servo pulses are generated in the
 * background using interrupts, so program execution is not blocked by
 * the pulse-generation code.  If you are using the Orangutan SV or SVP,
 * you can power the servos directly from the board's 5V bus.  For all
 * other Orangutans (LV, X2, Baby) and the 3pi robot, you need to power
 * the servos from a separate source since the on-board voltage regulators
 * do not have enough power to drive typical servos.  This example uses
 * pushbutton inputs to trigger events, so you will need connect an external
 * pushbutton (or wire) to the correct pin if if you are using a Baby
 * Orangutan.
 *
 * Note: the "B" version of the servo functions are used in this example
 * so that it behaves the same for the Orangutan SVP as for all other
 * Orangutans.  If you are not using the Orangutan SVP, you can do not
 * need to use the B versions of the functions (e.g. you can use
 * set_servo_target(0, 1200) instead of set_servo_target_b(0, 1200)).
 * If you do this, you should replace the call to servos_init_extended()
 * with servos_init().
 *
 * https://www.pololu.com/docs/0J20
 * https://www.pololu.com
 * https://forum.pololu.com
 */

#include <avr/pgmspace.h>  // this lets us refer to data in program space (i.e. flash)
// store this fugue in program space using the PROGMEM macro.  
// Later we will play it directly from program space, bypassing the need to load it 
// all into RAM first.
const char fugue[] PROGMEM = 
  "! O5 L16 agafaea dac+adaea fa<aa<bac#a dac#adaea f"
  "O6 dcd<b-d<ad<g d<f+d<gd<ad<b- d<dd<ed<f+d<g d<f+d<gd<ad"
  "L8 MS <b-d<b-d MLe-<ge-<g MSc<ac<a ML d<fd<f O5 MS b-gb-g"
  "ML >c#e>c#e MS afaf ML gc#gc# MS fdfd ML e<b-e<b-"
  "O6 L16ragafaea dac#adaea fa<aa<bac#a dac#adaea faeadaca"
  "<b-acadg<b-g egdgcg<b-g <ag<b-gcf<af dfcf<b-f<af"
  "<gf<af<b-e<ge c#e<b-e<ae<ge <fe<ge<ad<fd"
  "O5 e>ee>ef>df>d b->c#b->c#a>df>d e>ee>ef>df>d"
  "e>d>c#>db>d>c#b >c#agaegfe f O6 dc#dfdc#<b c#4";

#ifndef _ORANGUTAN_X2	// this code isn't needed if we're using the Orangutan X2

// frequencies in tenths of a Hertz of lowest 12 allowed notes (stored in program memory)
unsigned int base_freqs[] PROGMEM = {412, 437, 463, 490, 519, 550, 583, 617, 654, 693, 734, 778}; 

// returns the frequency (in Hz) for the specified note.  Note macros are
// defined in OrangutanBuzzer.h.  For example to get the frequency of note
// C sharp in octave 5:
//   get_frequency(C_SHARP(5));
unsigned int get_frequency(unsigned char note)
{
	unsigned int freq = 0;
	unsigned char offset_note = note - 16;

	if (note <= 16)
		offset_note = 0;
	else if (offset_note > 95)
		offset_note = 95;

	unsigned char exponent = offset_note / 12;

	// read the base frequency for the requested note from program memory
	freq = pgm_read_word(base_freqs + (offset_note % 12));

	// in the following calculations we add half of the denominator to the
	// numerator to get a result that is rounded properly (otherwise, the
	// decimal is just truncated and something like 3.9 becomes 3).
	if (exponent < 7)
	{
		freq = ((freq<<exponent) + 5) / 10;	// == (frequency * 2^exponent) / 10
	}
	else
		freq = (freq*64 + 2) / 5;	// == freq * 2^7 / 10 without int overflow

	return freq;
}

#endif


// Play a note without using timer 1 or the OrangutanBuzzer library, which
// means it is safe to use this function while using the OrangutanServo or
// OrangutanPulseIn libraries.
// This is a blocking function; execution does not return until note is done.
// This function will not have an effect if timer 1 is configured for
// buzzer operation.  The argument freq is in Hz and duration is in ms.
void play_simple_note(unsigned char note, unsigned int duration)
{
#ifdef _ORANGUTAN_X2	// buzzer is controlled by aux MCU on X2

	x2_play_note(note, duration);
	delay_ms(duration);

#else	// if using any non-X2 Orangutan or 3pi robot

	unsigned long time = get_ms();
	unsigned int freq = get_frequency(note);
	unsigned int half_us_delay = (500000UL + freq/2) / freq;

	while (get_ms() - time <= duration)
	{
		// BUZZER_IO is buzzer pin (defined in OrangutanBuzzer.h)
		// use loop delays to generate a square wave on buzzer pin at
		// the appropriate frequency for the note we want to play
		set_digital_output(BUZZER_IO, HIGH);
		delay_us(half_us_delay);
		set_digital_output(BUZZER_IO, LOW);
		delay_us(half_us_delay);
	}

#endif
}


int main()
{
	play_from_program_space(fugue);

	clear();
	lcd_init_printf();			// required for using printf()

	printf("Fugue");
	delay_ms(200);
	printf(".");
	delay_ms(200);
	printf(".");
	delay_ms(200);
	printf(".");

	delay_ms(500);

	while (1)
	{
		clear();
		printf("Press a\nbutton");

		wait_for_button_press(ANY_BUTTON);

		// setup pins D0 and D1 as servo outputs and initialize timer 1 for servo pulses
		servos_start_extended(0, 0, (unsigned char[]) {IO_D0, IO_D1}, 2);

		// move servos at full speed to their initial positions
		set_servo_target_b(0, 1200);
		set_servo_target_b(1, 1200);

		delay_ms(50);	// let positions be updated before we change the speed

		// set servo speed to something slower
		set_servo_speed_b(0, 200);
		set_servo_speed_b(1, 100);


		while (1)
		{
			set_servo_target_b(0, 1200);
			// blocking sound functions that works while servos are active
			play_simple_note(C(5), 50);
			play_simple_note(E(4), 50);

			set_servo_target_b(1, 1200);
			delay_ms(400);

			if (button_is_pressed(ANY_BUTTON))
			{
				wait_for_button_release(button_is_pressed(ANY_BUTTON));
				servos_stop();
				break;
			}

			set_servo_target_b(0, 1800);
			delay_ms(200);
			set_servo_target_b(1, 1800);
			delay_ms(400);

			if (button_is_pressed(ANY_BUTTON))
			{
				wait_for_button_release(button_is_pressed(ANY_BUTTON));
				// disable servo signals (allow OrangutanBuzzer code to be used again)
				servos_stop();
				break;
			}
		}

		// since we have called servos_stop(), we can use OrangutanBuzzer code again
		play("cdefgab>c");
		delay(1000);
	}
}

- Ben

thank you Ben got it so far.

thank you

Before I learned about interrupts I used a very basic mechanism for counting the pwm percentage or a digital accellerometer. In my application it is a repeating process so I do not mind that the first reading is wrong. Here is the code, maybe it gives you some ideas. If you perform this loop twice then the second one will be right.

	timh = 0; timl = 0;
	while (1) // count until input changes
	{
	++timl; 	// set 1 more
	if (PINB & (1 << PB2)) // goto next if pin PB2 is high 
	{ break; }
	}

	while (1) // count until input changes
	{
	++timh; // set 1 more
	if ( (PINB & (1 << PB2)) == 0 ) // goto next if pin PB2 is low 
	{ break; }
	}
	avg = ((timl)/(timl+timh)); // percentage output

thank you Eric

David,

Sorry for such a delay but Its only been until now that I have been able to verify
that the code you gave me does not operate properly. Your idea was to eliminate the timer, which is a good way of handling the problem.

Just to refresh, I am activating a switch that controls channel 5 on my transmitter radio.
Down is off, up is on, or Down is low, up is high ( in terms of pin talk ). the pin is PC3.
PC3 is connected to my receiver’s signal lead. the RX ground is not connected ( but i did try it connected with no change). PC3 is suppose to sense the pulse change,
hence weather I have thrown the switch up. The routine should only execute once, the routine should resets only when the switch is thrown down, or pin PC3 low.

Here is the code you gave me with minor alterations

#include <avr/io.h>  
#define F_CPU 20000000  // system clock is 20 MHz  
#include <util/delay.h>  // uses F_CPU to achieve us and ms delays  
#include <pololu/orangutan.h>

// delay for time_ms milliseconds by looping  
//  time_ms is a two-byte value that can range from 0 - 65535
//  a value of 65535 (0xFF) produces an infinite delay  
void delay_ms(unsigned int time_ms)  
  {  
  // _delay_ms() comes from <util/delay.h> and can only  
  //  delay for a max of around 13 ms when the system  
  //  clock is 20 MHz, so we define our own longer delay  
  //  routine based on _delay_ms()  
     
  unsigned int i;  
     
  for (i = 0; i < time_ms; i++)  
  	_delay_ms(1);          
  }  

// flashing LED signals round is packed and ready to fire
void TurnOnLED()
{
int count;
 for (count=0; count < 10; count++)
 	{
	PORTD |= 1 << PD1; 		// LED on
	delay_ms( 150 );
	PORTD &= ~( 1 << PD1 );	// LED off
	delay_ms( 200 );
	}
}

// move servo arm down and back
void S1_MOVE(int delay_amt)
{
// Slowly move the servo to position 1800.  
set_servo_speed(0, 150);
set_servo_target(0, 1800);  
delay_ms(700);  
   
// Disable the speed limit  
// Make the servo move back to position 1300 as fast as possible.  
set_servo_speed(0, 0);  
set_servo_target(0, 1300);  
}

void MoveServos()
{
// 1200 - 1800 limits
// Slowly move the servo to position 1800.  
set_servo_speed(0, 150);
set_servo_target(0, 1800);  
delay_ms(700);  
   
// Disable the speed limit  
// Make the servo move back to position 1300 as fast as possible.  
set_servo_speed(0, 0);  
set_servo_target(0, 1300); 
}
    
int main()  
{
unsigned char PROC_CTL = 1;
const unsigned char ServoArr[] = {IO_D0, IO_C0, IO_C1};
// servo signal pin assignment 		PD0, PC0, PC1

servos_init(ServoArr, sizeof(ServoArr));  
set_servo_target(0, 1300);    		// Make the servo 0 go to a neutral position
set_servo_target(1, 1300);    		// Make the servo 1 go to a neutral position

DDRD |= 1 << PD1;					// set LED pin PD1 to output, PD1 reserved for user LED

while (1)
{

  	while (!(PINC & (1 << PC3)))   // Wait for PC3 to go high.
		{ PROC_CTL = 1; }

	delay_us(1450);					// delay 1.45 ms

	if ((PINC & (1 << PC3)))        // Test to see if PC3 is still high
		{
	    // The pulse is longer than 1.45 ms
		if ( PROC_CTL == 1)
			{
			// MoveServos();
    		TurnOnLED();			 // make LED blink: display armed signal
			PROC_CTL = 0;			 // reset process control flag
			}
		}
	else{ }
}

return 0;
}

Whats happening is the process is being ran continuously regardless of TX switch position.
again, PC3 pin is connected to the signal on the RX and ground is disconnected, but I did try it connected.

Please advise

Dave

Hello Dave,

Maybe David will have something to add, but I wanted to point out some things:

  • Your code can’t possibly even compile as you presented it here - you must have deleted some things within it. If you are copying it incorrectly, who knows what got changed? Please verify that your code compiles, then use copy and paste to put the code into your post, and don’t change it after you paste it in!
  • If you don’t show the looping code that allows MoveServos() to run multiple times, we can’t possibly help you figure out why it runs too many times!
  • Please use the “Code” button to format your code so that it is readable.
  • Please simplify your code as much as possible and post the entire program. For example, remove the Servo code and just debug pulse detection using the LED until it is working.

-Paul

Paul,

Sorry about that, I have edited my previous post with the full program code.

To clarify:
The LED-function should execute only one time then stop; hence PROC_CTL=0.
Then the program should loop, and if pin3 is still high, bypass the LED function
, else if pin3 has been set low again ( tx ch5 switch down ), the the program should loop at pin3 low, until pin3 is set high again ( tx ch5 switch up ). Note:
I am not 100% sure but i do believe the end points for ch5 are set to full extent, 1200pulse (low) to 1800pulse ( high)

Earlier in this post I was doing this by means of a timer, Dave showed this way without a timer. I would prefer to free up the timer if possible.

For this example I have remarked out the servo-Function to simplify the problem, i will
be troubleshooting that soon.

Let me know if you see anything, there.

Thank you

Dave

Hello Dave,

You definitely need to have a ground connection between your Orangutan and your R/C receiver.

Also, it looks like your logic for setting PROC_CTL is wrong. My understanding is that you want PROC_CTL to mean “the last pulse was low”. So you should set it to 1 after every low pulse detected and set it to 0 after every high pulse.

You did set it to 0 in the right place, but you are setting it to 1 while you are waiting for a pulse, instead of in that else { } section.

-Paul

Paul

Yes your right my logic was off, by making that change to LED routine runs only when PIN3 is high, however , if I keep PIN3 high, the routine will continuously execute, the routine stops only when Pin3 is set low. its close
almost there.

thx

Dave

Try adding a loop that waits for the pin to go low before waiting for it to go high. That should fix the case where the pin high for a really long time.

David

Yes A loop waiting for low pin seems logical, it would seem i would no long need a Process Control Flag however, when i run this i get the same results .

while (1)
{
  	while (!(PINC & (1 << PC3)));   // Wait for PC3 to go high. ( channel 5 will activate )
	delay_us(1450);					// delay 1.45 ms
	if ((PINC & (1 << PC3)))        // Test to see if PC3 is still high
		{
	    // The pulse is longer than 1.45 ms
		// MoveServos();
    	       TurnOnLED();			 // make LED blink: display armed signal
		}
	while ((PINC & (1 << PC3)));	// Wait for PC3 to go Low
}

this while statement does not loop during PC3 High for some reason.
while ((PINC & (1 << PC3))); // Wait for PC3 to go Low

Dave

It looks as if you repeating function is creating new orders faster than the execution program can handle.

A Rx signal has 48-49 ms low and 1-2 ms high, in this example you wait for a high signal and look again 1.45 ms later to see what happens, then you call the LED to blink and the servo to move. The next step is to wait until it goes low. In time there is no relation between the high and low period. Binking the LEDs take 10x 350ms and I cannot judge what the servo’s are doing.

It is better to separate the functionality that looks what your input is doing and the main program where you want things to happen.

If you copy-paste my RC capture interrupt the program will continously monitor the status of the input pin PD2. It will only calculate the length of the pulse when the level changes, so inbetween these changes the processor is free to execute the main program.

Replace from LCD init:
if (prevcounts > 450) // previous counts (add prevcounts to uint16_t counts)
{
if ( counts <= 450 ) // actual counts
{
int i;
for (i=0; i <= 20; i++)
   {
   PORTD |= 1 << PD1;       // LED on
   delay_ms( 150 );         // delay 150 ms
   PORTD &= ~( 1 << PD1 );   // LED off
   delay_ms( 200 );         // delay 200 ms
   }
// MOVE YOUR SERVO HERE
}
prevcounts = counts;
//In this way it remembers the previous counts and will not loop if the previous loop was below 450

Erick

I don’t quite follow your code. I don’t see where you are monitoring PD2?

here is the old way I did this and it worked but It requires the use of a Timer which I would
like not to use a timer to keep them free for Servos, or other functions.

unsigned char PROC_CTL = 1;
unsigned char error = 0;  			// ignore this pulse if error is 1
unsigned int pulseWidth;

while (1)
{

  while (!(PINC & (1 << PC3))); 	// wait while pin PC3 is low

  error = 0;
  TCNT1 = 0;  						// effectively start timing now
	
  while (PINC & (1 << PC3)) 		// wait while pin PC3 is high
  	{
    if (TCNT1 > 5000)
      error = 1;  					// identify this as a bad pulse before timer1 overflows
  	}

   if (!error)						 // no error detected
  	{
	pulseWidth = TCNT1 * 0.4;		 // convert to micro Seconds

 	if (pulseWidth < 1450)          // Test to see if pulse is Over 1.5ms
		{
  	 	if ( PROC_CTL == 1)			// confirm switch on channel 5 has been activated
			{
		        // Proform some function Servos maybe LED's
			}
  		}
	else PROC_CTL = 1;				  // Reset process control
  	}
}

Thx

Dave

Eric / Dave

My appologies, it does work.
When i add the 2nd whie (Pin High). i forgot to add my Process_Flag variable.

All is good

Thank you all.

Dave

Ben and Guys

Thanks for all your help

my redone, baby0