Robotics Kit 1 - Software

Start 

Check out this video of the webserver controlling the Robotics Kit robot!

Wowzers!

Connecting to WiFi 

Note:

Your WiFi module should already be configured. If it's not, go HERE.

Similar to the Weather Station 2 project, we're going to use your WiFi module as an Access Point to host a webserver that will allow you to remotely control your robot. For this part of the project you'll need your WiFi module and a cable. We'll also use a wrapper and a large wrapper screw. Take your cable and plug one side into the WiFi module and the other into Digital pin 8.

All the parts needed
All the parts needed
Take larger screw from wrapper
Place it through a hole in the wrapper
Place wrapper on chassis
Tighten into chassis
Take WiFi Module
Place into wrapper
Place into wrapper
Take cable
Unwrap
Plug on side into the WiFi socket
The other into D8
Ready for code

Upload

Change the error variable in the code below to the value you found in the previous section. Upload the following code. The example below uses the D8 digital pin for the WiFi module.

Remember to reset the I2C Motor Driver after upload

Do not change the ssid and pass variables to your home's WiFi ssid and password because in this tutorial we're creating an access point instead of connecting to a WiFi network. If your WiFi module is working than you should see "My WiFi Robot" appear in your list of WiFi networks on your computer or smart device.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#include "WiFiEsp.h"
#include "SoftwareSerial.h"
#include "Grove_I2C_Motor_Driver.h"

// default I2C address is 0x0f
#define I2C_ADDRESS 0x0f

//Create Serial communication Object
SoftwareSerial Serial1(8, 9); // RX, TX
//Wifi server object
WiFiEspServer server(80);

// use a ring buffer to increase speed and reduce memory allocation
RingBuffer buf(8);

//Wifi settings
char ssid[] = "My WiFi Robot";    // the name of your access point
char pass[] = "password";        // the password for your access point
int status = WL_IDLE_STATUS;     // the Wifi radio's status

// CHANGE THIS TO YOUR OFFSET VALUE
int offset = 5;

// variable for power -> from -100 to 100 (negative numbers are backwards)
int power = 70;

//Increase for larger turns. Decrease for smaller turns
int turnAmount = 180;

// Variable for running the moving code
bool moving = false;

// Variables to time and stop moving
int interval;
unsigned long startTime;

// change these values to match your motor orientation
unsigned char left = MOTOR2;
unsigned char right = MOTOR1;

void setup()
{
  // Start Serial Communication with computer and WiFi module
  Serial.begin(9600);
  // initialize serial for ESP module
  Serial1.begin(9600);
  // initialize ESP module
  WiFi.init(&Serial1);

  // start motor driver
  Motor.begin(I2C_ADDRESS);
  // correct pwm cycles
  Motor.frequence(F_490Hz);

  // Print out debugging messages
  Serial.print("Attempting to start AP ");
  Serial.println(ssid);

  // start access point
  status = WiFi.beginAP(ssid);

  // start the web server on port 80
  server.begin();
  IPAddress ip = WiFi.localIP();
  Serial.println("Server started");
  Serial.print("To see this page in action, connect to ");
  Serial.print(ssid);
  Serial.print(" and open a browser to http://");
  Serial.println(ip);
  Serial.println();
}

void loop()
{

  // Wait for a device to connect to the WiFi
  WiFiEspClient client = server.available();  // listen for incoming clients

  // if you get a client,
  if (client) {
    // print a message out the serial port
    Serial.println("New client");
    // initialize the circular buffer
    buf.init();

    // loop while the client's connected
    while (client.connected()) {
      // if there's bytes to read from the client,
      if (client.available()) {
        // read a byte, then
        char c = client.read();
        // push it to the ring buffer
        buf.push(c);

        String request = client.readStringUntil('\r');
        client.flush();

        // Match the request
        // if the string "/fwd" is found,
        //then call the moveDistance() function
        if (request.indexOf("/fwd") != -1) {
          //moveDistance(35, power);
          moveTime(2, power);
        }
        // if the string "/bwd" is found,
        //then call the moveDistance function
        if (request.indexOf("/bwd") != -1) {
          //moveDistance(35, -power);
          moveTime(2, -power);
        }
        // if the string "/left" is found,
        //then call the turn() function
        if (request.indexOf("/left") != -1) {
          turn('l');
        }
        // if the string "/right" is found,
        // then call the turn() function
        if (request.indexOf("/right") != -1) {
          turn('r');
        }

        // Return the response
        client.print(
          "HTTP/1.1 200 OK\r\n"
          "Content-Type: text/html\r\n");
        client.println();

        // CSS to style the on/off buttons
        client.print(F(
                       "<!DOCTYPE HTML>\r\n"
                       "<html>\r\n"
                       "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n"
                       "<link rel=\"icon\" href=\"data:,\">\r\n"

                       // change the font and size of each button here
                       "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\r\n"
                       ".button { background-color: #ff6666; border: black; color: black; padding: 16px 40px;\r\n"
                       "text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}\r\n"
                       ".button:hover {background: #eee;}\r\n"

                       // change the background color of each button here
                       ".forward {background-color: #ff6666;}\r\n"
                       ".backward {background-color: #aaff80;}\r\n"
                       ".left {background-color: #ff9900;}\r\n"
                       ".right {background-color: #ffff66;}</style></head>\r\n"

                       // change the heading of the web server here
                       "<h2>My WiFi Robot Controls</h2>\r\n"));

        // change the function and label of the buttons here
        client.print(F(
                       "<button class=\"button forward\" onClick=location.href='/fwd'>forward</button>\r\n"
                       "<p><button class=\"button left\" onClick=location.href='/left'>left</button>\r\n"
                       "<button class=\"button right\" onClick=location.href='/right'>right</button></p>\r\n"
                       "<button class=\"button backward\" onClick=location.href='/bwd'>backward</button>\r\n"));


        client.println("</body></html>");
        break;
      }

      //Run if moving
      if (moving) {
        //Checks the interval timing
        if (millis() - startTime > interval) {
          //Stop the motors
          Motor.stop(left);
          Motor.stop(right);
          //Change moving variable
          moving = false;
        }
      }
    }

    // close the connection
    client.stop();
    Serial.println("Client disconnected");
  }

  //Run if moving
  if (moving) {
    //Checks the interval timing
    if (millis() - startTime > interval) {
      //Stop the motors
      Motor.stop(left);
      Motor.stop(right);
      //Change moving variable
      moving = false;
    }
  }
}


// function to move robot forward or backward
void moveTime(int seconds, int motorPower) {
  //Set the starting time
  startTime = millis();
  //Set this true to run the move code
  moving = true;
  //Set the time to run
  interval = seconds * 1000;

  //Set correct motor polarity
  int leftPower = motorPower;
  int rightPower = -motorPower;

  //Set motor speed
  Motor.speed(left, leftPower - offset);
  Motor.speed(right, rightPower - offset);
}


// function to turn robot left or right
void turn(char Dir) {
  //Set this true to run the move code
  moving = true;
  //Set the time to run
  interval = turnAmount;

//Switch case for the two directions
  switch (Dir) {
    // if the direction (Dir) is left, turn on the right motor
    case 'l':
      Motor.speed(right, - power - offset);
      break;

    // if the direction is right, turn on the left motor
    case 'r':
      Motor.speed(left, power - offset);
      break;
  }
}

Observe

Open the Serial Monitor. You should see this:

Download file Copy to clipboard
1
2
3
4
5
6
7
[WiFiEsp] Initializing ESP module
[WiFiEsp] Initilization successful - 2.2.1
Attempting to start AP My WiFi Robot
[WiFiEsp] Access point started My WiFi Robot
[WiFiEsp] Server started on port 80
Server started
To see this page in action, connect to My WiFi Robot and open a browser to http://192.168.4.1

If you do, navigate to the WiFi menu on any Internet enabled device and select My WiFi Robot from the menu to connect. Then follow the link displayed in the Serial Monitor (it should be http://[ your WiFi module's IP address] ) to see the web server.

If your module isn't connected, first make sure that the WiFi module is connect to D8. Then make sure that your motor driver has power (the AA battery holder should be connected to the Seeeduino) and is connected to the Seeeduino. The WiFi module will not connect if the motor driver is not detected. If it is still not connecting, follow this link to reconfigure your module.

The web server should be a web page that looks like this:

Web Server ControlsWeb Server Controls

You are now ready to wirelessly control your robot!

Disconnect your robot from your computer and make sure your battery holder is connected to your Seeeduino. The LEDs on your Seeeduino and motor driver should be on. You may also choose to mount for WiFi module onto your robot to prevent it from disconnecting. The unused slots on your chassis are for securing the wrappers to the robot to add on various modules. If you'd like to mount a module follow the steps below:

Now place your robot on the ground - shaggy carpet and uneven floors will make it difficult for your robot to move. Concrete, hard wood floors, or tiles are the best option. Using the web server remote, try and move your robot forward, backwards, left and right. If your robot does not move very much then you may need to increase the power you're supplying to your motors. Change this is the code above and re-upload to your Seeeduino.

Note:

The web server will need a few seconds to refresh. Click on one of the buttons and wait for the page to refresh before clicking another button.

If your robot does not move as expected take a look at this list of possible problems and solutions:

  • Does your robot not move or drive slowly Your wheels may be rubbing up against the screws securing the motors. Check your robot to see if this is the case. If it is use super glue or hot glue to secure the wheels to the motor axles in a position that does not rub against the screws.

  • Does your robot not move? First make sure that the green LED on your WiFi module is turned on indicating power and is connected. Next make sure that the AA batteries you're using are new and fully charged. If you have one and know how to use it, use a multimeter to check this. Next unplug the 9v battery from your Seeeduino and plug it back into the computer. Re-upload the code and make sure your WiFi module is connected to the Internet and the code properly complies.

  • Does your robot turn in the wrong direction or turn too much? Make sure your left motor is plugged into the M1 terminals and the right motor is plugged into the M2 terminals. If your robots turn too much then go into the turn() function in the code and reduce the power to the motors.

  • Do your motors hum but don't turn? The speed you set your motors to in the code may be too low. Make sure the value for the power variable is above 30. This number correlates to the voltage the motors get from the driver. If it's too low then the motors don't have enough voltage to turn, hence the humming.

  • Does your robot curve when it goes forward? This problem is why we account for differences in motor power in the code. Make sure you changed the error value you got in the previous section in the code above. Also check that your wheels aren't sliding off - if they are then drop some super glue onto the axle of the motor and reattach the tire. If your robot still curves, return to THIS section to experiment with your error value.

Experiment

If you are familiar with html and css programming languages then play around with the font, size, and color of the buttons. If you are not familiar go [here](## Further_reading_HTML_and_CSS) to get an overview. To get the code for RGB colors use this link. Once you pick a color, copy and paste it into the code. In the Customizing Your Web Server section, you can learn how to customize this page to add more buttons. You can also speed up the motors by increasing the power variable and change how far the robot turns by changing the delay value in the turn() function.

This is the end of the main project for this kit. Play around with your robot, learn more about RPM below, and send us pictures of your customized web server and robot!

Learn More About RPM and Tweak Your Motors 

If you'd like to look more into your motor's performance and how fast they can go then follow the tutorials below:

Calculating RPM 

Not all motors are created equal

One of your motors may be slower than the other one and you'll notice it when you make your robot move forward and it curves slightly in either direction. Do not despair! Either you are lucky and your motors are in fact equal in power or your motors are not equal but you're just as lucky because you have Thimble to teach you how to fix it! In order to figure out any disparities in motor power, precisely control the robot, or estimate its velocity we need to know the motors RPM. This stands for "Rotations Per Minute" and is a number that tells you how many times your motor turns one full rotation in one minute.

Upload

Plug your Seeeduino into your computer and upload the following code and make sure your robot is upside down so it doesn't drive off your workspace! The example below uses the D2 digital pin. You'll need to check both motors so be sure to record the RPM of MOTOR1 and MOTOR2 and check that that motor's line finder is plugged into the correct pin.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "Grove_I2C_Motor_Driver.h"
#include <Average.h>

//IR sensor INPUT
const int dataIN = 2;

// To store time
unsigned long prevmillis;

// To store time difference
unsigned long duration;

int rpm;

// CHANGE THIS VALUE TO EITHER MOTOR1 OR MOTOR2
unsigned char motor = MOTOR1;

// initalizes array to get mode of 50 rpm values
Average<int> RPMavg(50);

// initalize counting of RPM values
int i = 0;

// current state of IR input scan
boolean currentstate;

// state of IR sensor in previous scan
boolean prevstate;

// default I2C address is 0x0f
#define I2C_ADDRESS 0x0f

void setup()
{
  // start motor driver
  Motor.begin(I2C_ADDRESS);
  // correct pwm cycles
  Motor.frequence(F_490Hz);

  // initialize line finder as input
  pinMode(dataIN,INPUT);

  // start timer off at 0 and previous state at LOW
  prevmillis = 0;
  prevstate = LOW;

  // start Serial Communication
  Serial.begin(9600);
}

void loop()
{
  // read IR sensor state
  currentstate = digitalRead(dataIN);

  // begin motor
  Motor.speed(motor, 60);

  // check to see if there is change in input
 if( prevstate != currentstate)
   {
     // if input only changes from LOW to HIGH
     if( currentstate == HIGH )
       {
         // time difference between wedges times eight
         // because the detection goes from HIGH to LOW eight times per rotation
         duration = ( micros() - prevmillis ) * 8;

         // rpm = (1/ time millis)*1000*1000*60;
         rpm = (60000000/duration);

         // store time for next revolution calculation
         prevmillis = micros();

         // add rpm to array
         RPMavg.push(rpm);

         // increase count for each rpm value added
         i++;
       }
   }

  // store this new data for next scan
  prevstate = currentstate;

  // if 50 values are collected, print the mode of the array of values
  if (i == 50) {
    Serial.print("RPM for Motor "); Serial.print(motor); Serial.print(" : ");
    Serial.println(RPMavg.mode()); //display mode of rpm values

    // reset count
    i = 0;

    // clear saved rpm values
    RPMavg.clear();
  }
}

Observe

Open up the Serial Monitor and wait for the program to collect data. You should see the approximate RPM for the motor you're testing appear every 15 seconds or so. Let it run a few times. You may see very different values.

Record which value occurs most frequently. This value should between 50 - 300 if you're using the 9v battery holder included in the kit. If you choose to use a higher voltage your RPM will be higher but should be under 600. If you get negative numbers or numbers outside of the expected range then first check that the line finder module is plugged in and you're using the correct one for each motor. If this doesn't work, reset the Seeduino as the timers that run in the code can fill up its memory quickly. If your motors do not start after you upload the code try hitting the reset button on the motor driver.

Note:

if you do not see any values appear on the Serial monitor than first check to make sure your Baud rate is set to 9600 and then make sure that the LED on the line finder module you're testing is blinking when the motor turns. If its not then it's not calibrated correctly and you can either remove it from the chassis and recalibrate using the black rectangle you drew or you can use a screw driver to adjust it while its still mounted. The LED should blink very quickly as the motor turns. This will indicate that it is working correctly. You can also move the encoder wheel closer or further away on the motor axle to see if this allows for a better reading.

Calculating Velocity 

Now that we have the RPM of the motors we can calculate the velocity. Make sure to read the Infrared section to get an in-depth breakdown of these calculations.

Upload

Upload the following code to calculate the velocity in meters per second (m/s) and feet per second (ft/s). Change the RPM values to the values you got.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Diameter of wheel in millimeters
const float diameter = 65;

// RPM of MOTOR1 - change to your value
const float rpmMotor1 = 250;

// RPM of MOTOR2 - change to your value
const float rpmMotor2 = 250;

const float pi = 3.14159;

void setup()
{
  // start serial communication
  Serial.begin(9600);

  // divide by 100 to get diameter in meters
  float perimeter = pi * (diameter / 100);

  // divide by 60 to get rps (Revolutions per Second)
  float velocity1 = perimeter * (rpmMotor1 / 60);
  float velocity2 = perimeter * (rpmMotor2 / 60);

  // print speed of MOTOR1
  Serial.print("Speed of MOTOR1 in m/s: "); Serial.println(velocity1);

  // conversion from meters to feet
  Serial.print("Speed of MOTOR1 in ft/s: "); Serial.println(velocity1 * 3.28084);

  // print a new line
  Serial.println();

  // print speed of MOTOR2
  Serial.print("Speed of MOTOR2 in m/s: "); Serial.println(velocity2);
  Serial.print("Speed of MOTOR2 in ft/s: "); Serial.println(velocity2 * 3.28084);
}

void loop() {
  // empty so data is displayed only once
}

Observe

Open the Serial Monitor. You should see the speed of MOTOR1 and MOTOR2 displayed in both m/s and ft/s as shown below. Your values will be different based off of your RPM values.

Download file Copy to clipboard
1
2
3
4
5
Speed of MOTOR1 in m/s: 8.51
Speed of MOTOR1 in ft/s: 27.91

Speed of MOTOR2 in m/s: 8.51
Speed of MOTOR2 in ft/s: 27.91

Notice the difference between the speeds of each motor. This will give you an idea of how much of a difference there is in the motor power for both of your motors.

You're done! You should now have a better idea of how different your motors are which will allow you to better calibrate them.