Pointers, pointers to pointers, and reading information at the memory address pointed to by the pointers. These are topics that I haven’t seen addressed in the Arduino environment yet.
I was having a problem getting a variable to pass globally from one function to another. I declared the variables that I wanted to use in multiple functions globally outside all the functions and setup(). I tried declaring the variable in every function, and passing it using the return function, and putting the variable to be passed in the function call, i.e. function(variable). No success, I kept getting errors about scope, or the program will compile and upload, but the variable will be empty in the other function where I am trying to read the value.
I talked to David at uCHobby about the problem, and he pointed me to an article about using the volatile modifier on variables. It is a modifier that is rarely taught in depth but can make a difference in the programming especially when there is an optimizer in use (the Arduino uses an optimizer to make the code more efficient or take up less memory, I am not sure which, David could tell you). [David: the volatile modifier tells the compiler not to keep a local copy of a variable in a register. Its important when an interrupt could update the value making a saved copy invalid.]
I built a set of code from a few examples to try and quickly make something work. I was trying to use a PING sensor, some servos, some LEDs, and the serial monitor. I built the system I was working with one piece at a time, starting with the PING. Compile example, upload and voila. Simple. I can put my hand in front of the sensor and the serial monitor reads the distance my hand is from the sensor.
Next lets make the LEDs dim and brighten based on how far away the PING is from an object. Something like:
analogWrite(dimLED, inches);
where dimLED is a LED on a PWM pin that is dimming as the variable inches gets smaller. Success. I can make a sensor affect the LED.
Now let’s make the servo move. The Servo knob example works great for this. It lets us use an analog input to move a servo through its range. You will note that later in the actual example I put together, I did not use the servo library, or this example, but rather used an example from Robotgrrl.
Now lets make the ping sensor talk to the servo. Should be simple but the first attempt I had of this made things very jittery. The refresh rate on the servo and the PING sensor made it necessary for these to be set up as separate functions. Separate functions ended up meaning that we needed a timer to call them at timed intervals so that the refresh rates did not make the servo jittery. Separate functions and library calls eventually made it difficult to have the variables working correctly. The inches variable from the PING example was not passing to the servo function. Leaving me with a servo that did not move, but the PING was getting data. I moved the variable to make it global, and that didn’t fix the problem.
Scope errors, and undefined in this type messages drove me nuts. I consulted C++ tutorials on passing variables and global variables, and then tried the volatile modifier David at uC Hobby suggested. I talked to a computer programmer and he suggested I use pointers. Here is a good reference that helped me realize how to use pointers. I vaguely remember using pointers in my C programming class, but that was 8 years ago. I then set up the variable globalInches and a pointer to it *getInches:
int globalInches;
int* getInches=&globalInches;
To read the variables in and write the variables, as they are needed you have to reference them:
int inches1 = *getInches;
*getInches = inches;
The article about pointers goes into pointers to pointers, to pointers… and how to point to a memory address of a variable as opposed to a variable. The reason for pointing to the memory address instead of trying to pass the variable is that likely the program will try making a new variable rather than checking for the existence of a variable already. Whereas pointing to the memory address will get you the data that is in that position always. The last example they use is like this one:
int main()
{
int instruments(12);
int* p_instruments = &instruments;
int** p_p_instruments(&p_instruments);
int*** p_p_p_instruments = &p_p_instruments;
***p_p_p_instruments = 6;
}
Table for current information from above code
Variable Name | Variable Type | Memory Address | Stored Value |
instruments | int | 1279 | 6 |
p_instruments | int* | 1377 | 1279 |
p_p_instruments | int** | 1477 | 1377 |
p_p_p_instruments | int*** | 1000 | 1477 |
I put the last memory address lower than the first to show that a computer doesn’t care when a variable is declared, it will put it into memory at the first available location and during a program run, memory addresses can and will be emptied at any moment.
This makes instruments = 12, then points p_instruments to the address of instruments, then points p_p_instruments to the address of p_instruments, then points p_p_p_instruments to the address of p_p_instruments, then lastly dereferences p_p_p_instruments to put 6 in the instruments variable. Each * is a “dereference operator”, and takes the location that the variable has stored and uses it to look at the data in the memory location. The & symbol is “the address of operator”. It points the variable to the address of the variable it modifies. Reading the operators like they are called makes the line of code make more sense.
int* p_instruments = &instruments;
Reads like “int* p_instruments equals the address of instruments”.
*p_instruments = 6;
Reads like “dereferenced p_instruments equals 6”, meaning that the memory location that p_instruments is pointing to will now have 6 as it’s value. It just so happens that that memory location is the variable instruments, so now instruments = 6.
After getting it all working and tweaking the values I have a program that can make the servo with a flag on it move closer to the sensor till the servo is at or almost at the sensor, and then move it away as soon as it gets to an inch or so away. The action to start the servo to the sensor will be initiated by a button push.
Note to programmers on keeping track of problem versions of code. In doing this example, and making it work from multiple source code parts and pieces, I ended up with many versions of the same file that all work in different fashions, or not at all. I apparently never saved any of the ones that had numerous errors in them, and I should have made a separate sketch folder for failed code, so that I could go back and see what didn’t work in doing this example. As it is I have only the memory of variable failures, and not the code to show and repeat the errors for this article. I wish I had a library of the bad code to be able to give the exact errors that came up.
The physical pin outs for the example is as follows using a Bare Bones Board kit:
Bare Bones Board Arduino in the breadboard
4 pin momentary on push-button with one pin at pin 2, one on the opposite side to 5+, and one to
ground with a pull down resistor
Servo with power and ground to the respective power lines, and the signal to pin 6
PING sensor with power and ground to the respective power lines, and the signal to pin 7
LED to pin 13 and ground
Here is the breadboard and Bare Bones Board with the sensor, servo, pushbutton and LED hooked up ready to go for this example.
/* || || @file highfivePINGaervo.pde || @version 1.0 || @author Charles Stutzman || @contact charlesd.stutzmansr@gmail.com || || @description || | This sketch takes a button push and initiates a high five style motion from a servo to a PING || | sensor. || | based off of the TimedAction example ThreeExamplesAtOnce.pde by Alexander Brevig || | alexanderbrevig@gmail.com || | has three TimedActions, that interact and initiate a high five motion, and one that is a || | heartbeat to show activity on the board || | May contain parts and pieces from Ardiuno Examples Library (PING, Blink LED, Button || | digital Input, and http://robotics.learnhub.com/lesson/1766-arduino-and-servos || # || */ #include//TimedAction initialization of the circuits heartbeat TimedAction heartbeatAction = TimedAction(1000,heartbeat); //TimedAction initialization of the Servo position function TimedAction servoAction = TimedAction(20,servo); //TimedAction initialization of the PING sensor read function TimedAction pingAction = TimedAction(200,ping); //TimedAction initialization of the button push for the start of a "high five" between the servo flag //and the PING sensor TimedAction buttonAction = TimedAction(50,button); // globalvariables for ping and servo functions int globalInches; int* getInches=&globalInches; //heartbeat variables and pins const int ledPin = 13; boolean ledState = false; //servo variables and pins const int servo1 = 6; int servoAngle1; int pulseWidth1; //ping variables and pins const int pingPin = 7; //button pins and variables const int buttonPin= 2; int buttonState = 0; //set up for servo position setting void servoPulse1 (int servo1, int servoAngle1) { pulseWidth1 = (servoAngle1 * 11) + 500; // Converts angle to microseconds digitalWrite(servo1, HIGH); // Set servo high (turns it on) delayMicroseconds(pulseWidth1); // Wait a very very small amount digitalWrite(servo1, LOW); // Set servo low (turns it off) // Typical Refresh cycle of servo (20 ms) normally a delay would be here, but the delay is in the //TimedAction calls } void setup() { pinMode(ledPin,OUTPUT); digitalWrite(ledPin,ledState); Serial.begin(9600);// there for debugging and watching the sensor values pinMode(servo1, OUTPUT); pinMode(buttonPin, INPUT); } void loop() { heartbeatAction.check(); //trigger every second servoAction.check(); //trigger every 20 millisecond pingAction.check();//trigger every 200 millieconds buttonAction.check();//trigger every 50 milliseconds } //checks for a button push to initiate the "high five" void button() { buttonState = digitalRead(buttonPin); if (buttonState == HIGH){ *getInches = 6;// makes globalInches go to a value that the servo will move when the servo checks //the value } } //beats the LED at pin 13 at 1 beat per second as a heartbeat void heartbeat() { ledState ? ledState=false : ledState=true; digitalWrite(ledPin,ledState); } //moves the servo according to data in *getInches void servo() { int inches1 = *getInches; if (inches1 > 2) { if (inches1 < 7) { servoAngle1 = inches1*3; } } else { servoAngle1 = 60; } servoPulse1(servo1, servoAngle1); Serial.print(inches1); Serial.println(" inches from sensor."); //datalogging for debugging } //gets data from the ping sensor void ping() { // establish variables for duration of the ping, // and the distance result in inches and centimeters: long duration, inches; // The PING))) is triggered by a HIGH pulse of 2 or more microseconds. // Give a short LOW pulse beforehand to ensure a clean HIGH pulse: pinMode(pingPin, OUTPUT); digitalWrite(pingPin, LOW); delayMicroseconds(2); digitalWrite(pingPin, HIGH); delayMicroseconds(5); digitalWrite(pingPin, LOW); // The same pin is used to read the signal from the PING))): a HIGH // pulse whose duration is the time (in microseconds) from the sending // of the ping to the reception of its echo off of an object. pinMode(pingPin, INPUT); duration = pulseIn(pingPin, HIGH); // convert the time into a distance inches = microsecondsToInches(duration); Serial.print(inches); Serial.print(" inches is what the sensor just read.");//there to help identify the number Serial.println(); *getInches = inches; } long microsecondsToInches(long microseconds) { // According to Parallax's datasheet for the PING))), there are // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per // second). This gives the distance travelled by the ping, outbound // and return, so we divide by 2 to get the distance of the obstacle. // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf return microseconds / 74 / 2; }
Here is the sequenced pictures of the action:
Have fun and enjoy making the Arduino interact with others and its environment.
Charles Stutzman
Article continued after the jump...