DESIGNED & ENGINEERED IN LONDON, UK

© 2019 Creoqode

As explained in the first chapter, Nova Joystick Shield has two thumb joysticks consisting of 4 potentiometers and 2 switches in total. We will be using these controllers to move Nova in 5 degrees of freedom by controlling all 5 of its servo motors. During the assembly, we have already completed the wiring, so there is nothing to worry about with the connections. Just to make sure, remember that tactile switches that comes with the joysticks are connected to digital pins 43 and 45 on Creoqode Mini Mega, and the potentiometers are connected to analog pins A9, A11, A13 and A15. Let's start coding by opening Arduino Software (IDE).

Let's start by including the servo library and defining the servo motors as we did in the calibration chapter. Then we need to define the potentiometers and tactile switches on the joystick shield. Remember there will be 6 variables in total: X axis, Y axis, and tactile switch of the joystick on the left, and X axis, Y axis, and tactile switch of the joystick on the right. Finally, we will be defining a variable called ACTIVATED for the case when tactile switches are pressed (when pressed, they are connected to ground), thus we will be assigning LOW to ACTIVATED.

You can see the code below to understand the above paragraph better:

#include <Servo.h>

#define ACTIVATED LOW

Servo NovaServo_1; //Head Movement - Front and Back
Servo NovaServo_2; //Head Rotation - Clockwise and Anticlockwise
Servo NovaServo_3; //Head Rotation - Up and Down
Servo NovaServo_4; //Whole Body Rotation - Z axis
Servo NovaServo_5; //Head Movement - Up and Down

int joystick1_x = A9;
int joystick1_y = A11;
int joystick2_x = A13;
int joystick2_y = A15;


int joystick1_sw = 43;
int joystick2_sw = 45;

We will now create 6 new variables to assign the current values of the potentiometers and tactile switches. You can see the code lines below:

int value1_x;
int value1_y;
int value2_x;
int value2_y;

int value1_sw;
int value2_sw;

When servo motors are directly connected to potentiometers like joysticks, it is common to have servo jitter and unsteady movements which does not look nice. For this purpose we will be using a method called "filter". All the data we are measuring and assigning to the variables above will be filtered, and then passed on to the servo motors. For this purpose, we will create 4 new variables for potentiometers:

int value1_xF = 90;
int value1_yF = 110;
int value2_xF = 90;
int value2_yF = 100;

As you will notice, shaft angles are assigned to each variable above. These are the initial positions of the servos when no joysticks are moved at all. You can change them as you want, considering the movement range of Nova.

By pressing the tactile switches, we will be increasing and decreasing one of the servo shaft angles. For this purpose, we will create another variable called "degree" and assign an initial value of 90.

int degree = 90;

Finally, we will be creating 4 more variables for the filtering process of potentiometer readings. These 4 are the filter constants that will be used inside the filtering formula that we will see in detail in loop() function soon. The values you will see below work fine for this project, but you can always change them to receive faster or smoother movements.

float filter1_x = 0.05;
float filter1_y = 0.05;
float filter2_x = 0.08;
float filter2_y = 0.08;

We can now start building the setup() function. Let's start by setting up the serial communication at 9600 baud rate. We might want to display the servo positions or the values read by the potentiometers through the Serial Monitor in Arduino Software (IDE).

Following this, we will set the pins that the tactile switches are connected to as INPUT_PULLUP, by using pinMode() function. When connecting the tactile switches, we have not used any resistors and connected one of their terminals to digital pins, and the other terminals directly to ground. By setting them as INPUT_PULLUP, we are enabling the internal resistors of ATmega2560 for the digital pins 43 and 45. Otherwise, if we assign just INPUT to those pins, they would cause short circuit in the system as we would be connecting a 5V pin directly to GND. In that case, we would have used an external resistor for each switch to prevent short circuits in the system. INPUT_PULLUP is the easiest method to connect a switch with no additional component requirement.

Finally, we will assign the pin number of each servo motor that they are attached to, and initialise them with the values determined before. Please see the code lines below:

Serial.begin(9600);

pinMode(joystick1_sw, INPUT_PULLUP);
pinMode(joystick2_sw, INPUT_PULLUP);

NovaServo_1.attach(32);
NovaServo_2.attach(34);
NovaServo_3.attach(36);
NovaServo_4.attach(38);
NovaServo_5.attach(40);

NovaServo_1.write(110);
NovaServo_2.write(90);
NovaServo_3.write(100);
NovaServo_4.write(90);
NovaServo_5.write(95);

Now, it is time to build our loop() function. We will start by reading values from potentiometers and tactile switches, by using analogRead and digitalRead functions, respectively.

As explained earlier, the analog pins on Creoqode Mini Mega can provide 10 bits of resolution, which is 1024 different values. They measure from ground to 5 volts, and return a value between 0 to 1024, where 512 would be the neutral position of each potentiometer. We will read the values from potentiometers, and map them to a range where we define the minimum and maximum correspondances of 0 and 1024. With this method, we will be bringing the values read from potentiometers suitable for the servos shaft angle ranges. For example, a potentiometer returns a value from 0 to 1024. When we map this range from 20 to 160, 0 value will be equal to 20, 1024 will be equal to 160, and any value between will be mapped within the same range. The value 512, which is the ideal value returned from joystick at steady state, will be equal to 90. This method allows us to use the values from potentiometers directly for servos shaft angles.

value1_x = analogRead(joystick1_x);
value1_y = analogRead(joystick1_y);
value2_x = analogRead(joystick2_x);
value2_y = analogRead(joystick2_y);

value1_sw = digitalRead(joystick1_sw);
value2_sw = digitalRead(joystick2_sw);

value1_x = map(value1_x, 0, 1024, 170, 10);
value1_y = map(value1_y, 0, 1024, 50, 170);
value2_x = map(value2_x, 0, 1024, 40, 140);
value2_y = map(value2_y, 0, 1024, 40, 160);

Now, we will be adding the filtering functions we have discussed in the beginning of this chapter. These functions slows down the rate of change and smoothens the movements of the servos. If we would be placing directly the values read from potentiometers, we would experience lots of small oscillations and servo jitters in the system. This method allows us to control Nova with the joysticks in a smoother way. Please find below the code lines:

value1_xF = value1_xF * (1 - filter1_x) + value1_x * filter1_x;
value1_yF = value1_yF * (1 - filter1_y) + value1_y * filter1_y;
value2_xF = value2_xF * (1 - filter2_x) + value2_x * filter2_x;
value2_yF = value2_yF * (1 - filter2_y) + value2_y * filter2_y;

Following this step, we will be increasing and decreasing the shaft angle of the servo motor based on whether the tactile switches are pressed or not. First "if" condition we will be creating checks whether the switch is "ACTIVATED" or not. Second "if" condition checks the minimum and maximum angles the servo shaft is allowed to turn. These values are determined considering the movement range of NovaServo_5. If both conditions are met, depending on which switch is activated, the value of the variable "degree" is either increased by 2 degrees at each iteration, or decreased by 2 degrees.

if (value1_sw == ACTIVATED) {
    if(degree < 141) degree = degree + 2;
}

if (value2_sw == ACTIVATED) {
    if(degree > 89) degree = degree - 2;

Then, to slow down the loop() function, we are increasing the time interval between each iteration of loop() by adding a simple code line. Delay() function pauses the sketch for a given period of time in milliseconds. For this project, 30 milliseconds of delay between each iteration of loop() function works fine.

 

delay(30);

Finally, we need to place all the variables inside the servo.write() functions in order to move Nova accordingly. For NovaServo_1 and NovaServo_3, we have placed "if" conditions to limit their movement considering the safe movement range of Nova.

if(value1_yF > 80)      NovaServo_1.write(value1_yF);
                                  NovaServo_2.write(value2_xF);
if(value2_yF > 65)      NovaServo_3.write(value2_yF);
                                  NovaServo_4.write(value1_xF);
                                  NovaServo_5.write(degree);

That's it! You have completed the sketch to be able to control Nova with the joysticks. You can now upload your sketch to Nova by clicking the upload symbol on top left corner of Arduino Software (IDE). Nova should now move according to your movements with joysticks.