Thread Tools
Jun 20, 2019, 11:59 AM
Registered User
vollrathd's Avatar
Thread OP

Minor software upgrade


I got rid of that "-10 uS" tweek, and moved it to the pulse generator sketch

Code:
/*This Sketch creates a servo pulse from 1.00 to 2.00 milliseconds wide
 * and sends the pulse out on port D13.
 * One issue with the program is the very slow writing time to the OLED
 * Updating the OLED takes around 40 milliseconds, much longer than the
 * normal servo cycle time.
 * In this regard, the bypass OLED function was added that only updates
 * the OLED if the pulse width has changed.
 * Added turn off and turn on interrupts per bearded flier.
 * added 20 uS tweek to allow pulse generator to match pulse reader
 */
//initiate the variables

int val = 0;
int old_val=1200;
int time = 0;
int flag = 0Xff;
int Servo_out = 13;      // select the pin for the Servo_out
#define OLED_RESET 4
#include <Adafruit_SH1106.h>
Adafruit_SH1106 display(OLED_RESET); 
void setup() 
{
  Serial.begin(9600);             //initate the ability to send data to the PC USB port
  display.begin(SH1106_SWITCHCAPVCC, 0x3C); // initialize 128/64 with the I2C addr 0x3C 
  display.clearDisplay();
  pinMode(Servo_out,OUTPUT);
  display.setTextSize(3); 
  display.setTextColor(WHITE);
  analogReference(EXTERNAL); //
  display.setCursor(0,30);
  display.print("PulseGn");
  display.display();  //update the OLED
  delay(2000);
}
//program start
void loop()
{
   val = analogRead(0);                               //get A/D from port A0 (0 to 1024)
   Serial.println(val);
   time = 1000 + val;                               // uS time is 1004 uS plus A/D value
   noInterrupts (); 
   digitalWrite(Servo_out,HIGH);                                      //port D13 = high
   delayMicroseconds(time);
   interrupts ();
   digitalWrite(Servo_out,LOW);                                         //port 13 = low
 if ((val>old_val+1)||(val<old_val-1))      //only update OLED if change by more than 2
   {
    display.clearDisplay();                    //updating OLED drops frequency to 21 Hz
    display.setCursor(0,20);
    time = time+20;                                      //tweek to match pulse reader!
    display.print(time);
    display.print(" uS");
    display.display();
    flag = 0;                                                 //set flag for OLED update
   }
 if (flag!=0)                                       //if flag not zero, do 20 ms delay
   { 
    delay(20);
   }
  flag = 0xff;                                                             //reset flag
  old_val = val;                                                     //update old value
 }

Code:
/* 
 *  This is a simple program to use the Arduino Nano and OLED 
 *  to measure the pulse width of a servo signal on our radios
 *  Complete date 06/12/2019
 *  Added "Servo Meter" to opening screen on OLED
 *  Re-installed the USB to Termite software
 *  Download Termite software from here:
 *  https://www.compuphase.com/software_termite.htm
 *  Now the pulse width can be monitored by either the OLED, USB or both
 *  Added interupt off and interupt on per Bearded Flyer
 */

#define OLED_RESET 4
#include <Adafruit_SH1106.h>
Adafruit_SH1106 display(OLED_RESET); 
   const byte INPIN = 13;  //Initiate which pin for servo input (D7)
int Signal;     //res is variable

 void setup()   
{   
   pinMode(INPIN, INPUT);
   display.begin(SH1106_SWITCHCAPVCC, 0x3C);            // initialize 128/64 with the I2C addr 0x3D  
   display.clearDisplay();
   display.display();  
   Serial.begin(9600);                                              //Initiate Serial Communication
}
//main program starts here
  void loop()
{   
   display.setTextSize(3);
   display.setTextColor(WHITE);
   display.clearDisplay();
   display.setCursor(0,0);
   display.print ("Servo");                                             //Write Servo Meter to OLED
   display.setCursor(0,35);
   display.print ("PulseIn");
   display.display();                                           //routine update of the OLED display
   delay(2000);
  for(;;)
  {
   display.clearDisplay();
   display.setTextSize(3);
   noInterrupts ();
   Signal = pulseIn(INPIN,HIGH,500000UL);       //This is an Arduino command to get HIGH pulse width
   interrupts();
   display.setCursor(92,20);
   display.print("uS");
   display.setCursor(0,20);
   display.print(Signal);
   display.display();                                              //routine command to update OLED
//   Serial.print (Signal);                                   //Send signal pulse width through USB 
//   Serial.println ("   MilliSeconds");                //Print "MilliSeconds" after the pulse data
     delay(100);
  }
}
Sign up now
to remove ads between posts
Jun 20, 2019, 01:56 PM
Registered User
Vollthrad, I have ben looking into why you have to add a "tweak" or "Fudge Factor/FiddleFactor" as I call it over this side of the pond of 20us to your pulse generation sketch. I think it is because you are dealing with small time pulses and the "digitalWrite" command is relatively slow so the pulse is longer than the "time" value as the "digitalWrite(Servo_out,LOW)" command will take time to execute. I have found this discussion https://forum.arduino.cc/index.php/topic,4324.0.html which suggests that using the "PORTD" command is a lot faster however as I am still a learner in Arduino coding I may be barking up the wrong tree!
Last edited by Bearded Flyer; Jun 20, 2019 at 01:57 PM. Reason: corrections
Jun 20, 2019, 11:54 PM
Registered User
vollrathd's Avatar
Thread OP

Thanks !


Quote:
Originally Posted by Bearded Flyer
Vollthrad, I have ben looking into why you have to add a "tweak" or "Fudge Factor/FiddleFactor" as I call it over this side of the pond of 20us to your pulse generation sketch. I think it is because you are dealing with small time pulses and the "digitalWrite" command is relatively slow so the pulse is longer than the "time" value as the "digitalWrite(Servo_out,LOW)" command will take time to execute. I have found this discussion https://forum.arduino.cc/index.php/topic,4324.0.html which suggests that using the "PORTD" command is a lot faster however as I am still a learner in Arduino coding I may be barking up the wrong tree!
I'm used to the MicroChip line with ports A, B, C, D, E and so on, but could not find an equivalent for the Arduinos. Based on your suggestions, I found those links.

And, the command to drive the Arduino pin #13 high or low has been added to the scheme. Now, the Arduino Nano pulse driving sketch matches the Arduino Nano pulse reading sketch within one digit.
Jun 20, 2019, 11:58 PM
Registered User
vollrathd's Avatar
Thread OP

Latest Arduino Nano for Servo Pulse Driver


Thanks to Bearded Flyer for this pointer. I've been learning new things every day on these Arduino's.

Code:
/*This Sketch creates a servo pulse from 1.00 to 2.00 milliseconds wide
 * and sends the pulse out on port D13.
 * One issue with the program is the very slow writing time to the OLED
 * Updating the OLED takes around 40 milliseconds, much longer than the
 * normal servo cycle time.
 * In this regard, the bypass OLED function was added that only updates
 * the OLED if the pulse width has changed.
 * Added turn off and turn on interrupts per bearded flier.
 * added 20 uS tweek to allow pulse generator to match pulse reader
 * Added direct port manipulation per BeardedFlyer.  
 * Result, pulse driver matches pulse reader exactly from 1ms to 2ms
 */
//initiate the variables

int val = 0;
int old_val=1200;
int time = 0;
int flag = 0Xff;
//int Servo_out = 13;      // select the pin for the Servo_out
#define OLED_RESET 4
#include <Adafruit_SH1106.h>
Adafruit_SH1106 display(OLED_RESET); 
void setup() 
{
  Serial.begin(9600);             //initate the ability to send data to the PC USB port
  display.begin(SH1106_SWITCHCAPVCC, 0x3C); // initialize 128/64 with the I2C addr 0x3C 
  display.clearDisplay();
//  pinMode(Servo_out,OUTPUT);
  display.setTextSize(3); 
  display.setTextColor(WHITE);
  analogReference(EXTERNAL); //
  display.setCursor(0,30);
  display.print("PulseGn");
  display.display();                                                  //update the OLED
  DDRB = B11111111;                                //added to set port B to all outputs
  delay(2000);
}
//program start
void loop()
{
   val = analogRead(0);                               //get A/D from port A0 (0 to 1024)
   Serial.println(val);
   time = 1000 + val;                               // uS time is 1004 uS plus A/D value
   noInterrupts (); 
   PORTB = B00100000;                                               //pin 13 equals high
   delayMicroseconds(time);
   interrupts ();
   PORTB = 0X00;                                                    //pin 13 equals low
 if ((val>old_val+1)||(val<old_val-1))      //only update OLED if change by more than 1
   {
    display.clearDisplay();                    //updating OLED drops frequency to 21 Hz
    display.setCursor(0,20);
    time = time+0;                                      //tweek to match pulse reader!
    display.print(time);
    display.print(" uS");
    display.display();
    flag = 0;                                                 //set flag for OLED update
   }
 if (flag!=0)                                       //if flag not zero, do 20 ms delay
   { 
    delay(20);
   }
  flag = 0xff;                                                             //reset flag
  old_val = val;                                                     //update old value
 }
Last edited by vollrathd; Jun 21, 2019 at 12:05 AM.
Jul 05, 2019, 03:51 PM
Registered User
Over the past couple of weeks I have been, with vollrathd's permission, combining his reader and pulse generator sketches into one unit. After a few false starts, 2 dead Arduino Nano's and a problem with the Nano re-booting when connected to a servo I think that I have solved most of the problems. However my original code did not seem to produce accurate pulse lengths. I measured them using one of vollrathd's Pulse Reader designs, a G.T. Power servo tester and a FMA Direct servo tester (which only reads to 3 significant figures. I found that all of them indicated that the produced pulse length was about 9us too long throughout the range of 1000us to 2000us therefore I deduced that the pulse generator code was to blame. I have therefore include a 'corr' variable in the code which is a "fiddle factor" to get the pulse length correct. This now gives me a reading on the pulse length readers that is within 2us throughout the range which is good enough for me. If somebody wants to modify the code to get better accuracy then please do.

The unit decides which mode to start in by checking the position of the potentiometer at power up. If it is below 1/4 turn then the Arduino starts in pulse reading mode. If the potentiometer is above 3/4 turn then it starts in a fixed pulse length mode of producing only pulse lengths of 1000us, 1500us and 2000us. In this mode the initial pulse length is 1500us, to avoid damaging the servo or control surface, and control of the pulse length only occurs when the potentiometer is turned back towards the centre. If the ptentiometer is over 1/4 turn but below 3/4 turn at power up then the Arduino starts in pulse generation mode where the pulse length is proportional to the position of the potentiometer but is capped at 2000us. I also fitted the unit with 2 male servo leads. Either one can be used when reading servo pulses and both are used, one for the power source and one for the servo, when acting as a pulse generator.

I found that whilst this all worked when powering a pulse reader when a 7g servo was attached and moved quickly the Nano would brown out and reset. After much experimenting which involved trying an optoisolator and some resistors to reduce current draw I tacked the problem down to the servo motor causing ripples on the powewr line. This was cured easily by installing a 100uF electrolytic capacitor between the input live and ground. The value is arbitary and works!

I also 3D printed a box for the unit in bright orange so that I am less likely to lose it.

The current sketch is copied below along with a couple of photos of the finished product and a circuit diagram.

Code:
/*This Sketch is based on the work by vollthrad in creating a servo pulse generator and reader and combines the two projects.
 * Many thanks to vollthrad for allowing me to use and modify his original code.
 * 
 * All serial commands have been removed to save memory.
 * The generator produces a pulse from 1.00 to 2.00 milliseconds wide and sends the pulse out on port D13.
 * The reader reads the pulse on port D13 and displays the pulse length on the OLED.
 * The position of the potentiometer at startup determines the useage.
 *              Pot up to 25% from fully anticlockwise = Servo Reader
 *              Pot between 25% and 75% from full anticlockwise = pulse length controlled by potentiometer.
 *              Pot over 75% from fully anticlockwise = fixed length pulses of 1000/1500/2000us produced depending on position of potentiometer 
 *              (useful for setting up neutral and limit postions of servos)
 * Version 1.00 by Bearded Flyer (with due appreciation to Vollthrad for all his hard work) 29th June 2019
 */
//initiate the variables

int start_val = 512;                                             // default position of potentiometer at startup
int val = 0;
int old_val=1200;
float pulse = 0;
float old_pulse = 0;
float start_pulse = 1;
float realpulse = 1;
int corr = 9;                                                    // Correction for length errors in producing pulses
int flag = 0;
const byte INPIN = 12;                                           // Initiate which pin for servo input (D12)
#define OLED_RESET 4
#include <Adafruit_SH1106.h>
Adafruit_SH1106 display(OLED_RESET); 

void setup() 
{
   display.begin(SH1106_SWITCHCAPVCC, 0x3C);                     // initialize 128/64 with the I2C addr 0x3C 
   display.clearDisplay();
   display.setTextSize(3);
   display.setTextColor(WHITE);
   display.clearDisplay();
   display.setCursor(0,0);
   display.print ("Pulse");                                      //Write Servo Meter to OLED
   display.setCursor(0,35);
   start_val = analogRead(0);                                    // get position of potentiometer at startup
   if (start_val <= 256) display.print ("Reader");               // display current meter usage of pulse reader
   else if (start_val >= 768) display.print ("Fixed");           // display current meter usage of a fixed 1500us pulse generator
   else display.print ("Output");                                // display current meter usage of a variable pulse length generator
   display.display();                                            // routine update of the OLED display
   delay(2000);                                                  // update the OLED
   if (start_val > 256) DDRB = B11111111;
   else  pinMode(INPIN, INPUT);                                  // added to set port B to all outputs or pin D13 to input
}


//program start
void loop()
{
   // Pulse Generator
   while ((start_val >256) && (start_val <768)) {
   val = analogRead(0);                                         // get A/D from port A0 (0 to 1024)
   pulse = 1000 + val;                                          // uS time is 1000 uS plus A/D value
   if (pulse > 2000) pulse = 2000;                              // set max pulse length to 2000 us
   realpulse = pulse-corr;                                      // pulse length correction
   noInterrupts ();                                             // added to stabilise reading
   PORTB = B00010000;                                           // pin 12 equals high
   delayMicroseconds(realpulse);
   PORTB = 0X00;                                                // pin 12 equals low
   interrupts ();                                               // restart interrupts
   if (flag == 0)                                               // only update OLED every 200us (10 x 20us loops)
   {
   display.clearDisplay();
   display.setTextSize(5);                                      // Use full height of OLED
   if (pulse < 1000) display.setCursor(30,20);                  // alter display position for 3 figure readings
   else display.setCursor(0,20);
   display.print(pulse,0);
   display.display();  
   flag = 10;                                                   // set flag for OLED update to stabilise display. 10 loops of 20us
   }
  if (flag<10)                                                  // either update display or delay 20us
   { 
    delay(20);
   }
  flag = flag-1;                                                 // decrease flag
  }

  // Pulse Reader
  while (start_val <= 256) {
  for(;;)
  {
   display.clearDisplay();
   display.setTextSize(5);                                     // Use full height of OLED with no units shown
   noInterrupts ();                                            // added to stabilise reading
   pulse = pulseIn(INPIN, HIGH, 3000000UL);                    // This is an Arduino command to get HIGH pulse width
   interrupts ();                                              // restart interrupts
   if ((pulse < 1000) && (pulse > 99)) display.setCursor(30,20);   // alter display position for 3 figure readings
   if ((pulse < 100) && (pulse > 9)) display.setCursor(60,20);   // alter display position for 3 figure readings
   else if (pulse <10) display.setCursor(90,20);               // alter display position for 0 reading
   else display.setCursor(0,20);
   display.print(pulse,0);                                     // display pulse length in us
   display.display();  
   delay(125); //routine command to update OLED
  }
  }

  // Fixed Pulse Length
   while (start_val > 768) {
   val = analogRead(0);                                       // get A/D from port A0 (0 to 1024)
   if (val <= 256) pulse = 1000;                              // potentiometer up to 25% from fully anticlockwise =  1000 us 
   else if (start_pulse!=0) pulse = 1500;                     // set startup pulse to 1500us regardless of potentiometer position
   else if (val >= 768) pulse = 2000;                         // potentiometer over 75% from full anticlockwise = 2000us
   else pulse = 1500;                                         // potentiometer between 25% and 75% from fully anticlockwise = 1500us
   realpulse = pulse-corr;                                    // pulse length correction                 
   noInterrupts ();                                           // added to stabilise reading 
   PORTB = B00010000;                                         // pin 12 equals high
   delayMicroseconds(realpulse);
   PORTB = 0X00;                                              // pin 12 equals low
   interrupts ();                                             // restart interrupts
   if (pulse != old_pulse) {                                  //only update OLED when pulse length changes
   display.clearDisplay();
   display.setTextSize(5);                                    // Use full height of OLED
   if (pulse < 1000) display.setCursor(30,20);                // alter display position for 3 figure readings
   else display.setCursor(0,20);
   display.print(pulse,0);                                    // display pulse length in us 
   display.display();
   flag = 0;                                                  // set flag for OLED update
   }
  if (flag!=0)                                                // if flag not zero, do 20 ms delay
   { 
    delay(20);
   }
  flag = 1;                                                   // reset flag
  old_pulse = pulse;                                          // update old value
  if (val < 768) start_pulse = 0;                             // if potentiometer has been turned below 3/4 then pot determines pulse length
  }
  
}
Jul 05, 2019, 06:21 PM
Registered User
vollrathd's Avatar
Thread OP

A million ways!


This is the great thing about these microcontrollers. The same exact project board can be used in a number of different ways by just changing the programming. I go back to the mid 1960's where if you wanted to change what a transistor powered project did, you had to start over by rewiring it.

As indicated, there is a wide variety of ways to build up a pulse generator scheme for driving our servos. And, I've saved Bearded Flyers coding for potential use in other projects I'm working on.

Also like the locking tab used on the 3D printed case, will have to try that for some of my 3D printed cases.

Right now, I'm working on an Arduino controlled milliampere hour meter for keeping tract of how many milliampere hours is pulled out of a given receiver battery during a days flying. One of the issues is how to initiate the Arduino for the start of the day by zeroing the milliampere hour counter. A switch will do it, but it would be nice to be able to avoid that.
Last edited by vollrathd; Jul 05, 2019 at 06:32 PM.


Quick Reply
Message:

Thread Tools

Similar Threads
Category Thread Thread Starter Forum Replies Last Post
Discussion No output signals from output headers of SP F3 FC ShoC Beginner Multirotor Drones 4 Aug 03, 2018 11:32 PM
Cool Dual input/output 5A electronic switchjack failsafe system for RX/servos Inflexo Batteries and Chargers 1 Jan 09, 2011 03:02 AM
New Product Dual input/output 5A electronic switchjack failsafe system for RX/servos Inflexo Sailplane Talk 2 Jan 09, 2011 03:01 AM
Discussion Spektrum receiver servo output signals? foxflier DIY Electronics 11 Aug 20, 2008 06:29 PM
Question 10-bit analog output from servo input, anybody have a canned solution available? EddieB DIY Electronics 33 Mar 11, 2005 02:47 AM