Thimble Learning Platform
Back to full view

Robotics Kit 1

Introduction

This is the first in a series of a WiFi Controlled Robot projects. In this kit you'll be putting together the base of the robot and connecting it to WiFi to control it remotely.

Objectives

  • Learn about infrared waves.
  • Create your own robot and webserver using HTML and CSS.
  • Have fun!
  • I've Never Used an Arduino

    Then this is a great place to start! If you're already familiar with setting up the Arduino and the IDE, you can skip to the next section.

    Terms 

    When we talk about Arduino, the Arduino IDE, and coding, there are a lot of words being thrown around. This section will clear all of those up and get you ready to start building!

    Arduino 

    The Arduino is a electronics prototyping platform. That means it's a flexible platform for building, testing, and, of course, prototyping electronics. That could be simple LEDs that light up at night, or advanced robots. When we talk about the Arduino we are talking about the physical board itself. The one that comes in this kit is red, but they come in many different shapes and colors. The Arduino brand and original board were created by arduino.cc and you can read more about the details of its history here.

    Arduino IDE 

    The Arduino IDE, or Integrated Development Environment, is not hardware, but a piece of software. If you wanted to write an essay, you might use Microsoft Word or Google Docs. If you wanted to edit pictures, you'd probably use Photoshop or Paint.net. If you want to program code for the Arduino, you'd use the Arduino IDE. Its a computer program (tool) that let's you develop and test out code. Once written, the code can be upload to the physical Arduino where it will stay until you upload new code.

    Download

    Since it is a program, you'll need to install it.

    For Windows computers use this link Windows Installer

    For Mac computers use this link Mac Installer

    After following each link, press the Just Download button to begin downloading the Arduino IDE. The Mac version has a few more steps and we have a video just for that.

    Mac Installation
    For Mac users

    Installation

    Open the executeable file you downloaded. You'll see a screen asking you to agree to their license. You can press I Agree.

    LicenseLicense

    Next up are the installation options. The Install USB Drivers lets the Arduino board communicate with the IDE. We definitely want this. The Associate .ino files makes any Arduino file on your computer open up to the Arduino IDE. Very helpful, so we'll keep that too. Press Next >.

    OptionsOptions

    Now you are being asked where you'd like the Arduino IDE program installed. I don't mind it in the standard Program Files so I just press Install. You can change where it will be installed, just remember what you chose. I recommend not changing it.

    FolderFolder

    Now the IDE is installing.

    InstallingInstalling

    Once completed proceed to the next section.

    CompletedCompleted

    Arduino IDE 101

    Open up the Arduino IDE for the first time. So, let's figure out what's what.

    Arduino IDEArduino IDE

    There is a mostly empty space in the middle that has this text:

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void setup() {
      // put your setup code here, to run once:
    
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    
    }
    

    This is a simple Arduino Sketch. A Sketch is code you write that will later be uploaded to the Arduino board.

    There are few steps to go from code written in a sketch to code that runs on the Arduino board. Those are Verify > Compile > Upload.

    All of these you can get to by going under Sketch on the top bar of the program. Or using the Checkmark and Arrow icons right below that. The Checkmark verifies your code will compile, and the arrow button uploads the code to the Arduino.

    Sketch
    Icons

    Verify - looks at your code and makes sure it can actually run. It checks that there are no errors or things out of place. If your code will work, it goes to the Compile stage.

    Compile - The code you've written is great for human. It is readable and hopefully understandable. But this isn't great for a machine ie the Arduino. The Compile stage takes your written code and translates into something that the Arduino can understand and run, called machine code. It saves that compiled file and tried to Upload it to the board.

    Upload - This takes the compiled files and tries to send it to a plugged in Arduino. The IDE doesn't know where you've plugged in your Arduino or what kind it is. You have to tell it. I'll show you how to set that up in the next section.

    Arduino Board Setup

    For this section, make sure your Arduino Board that came with your kit is plugged in. Use the included micro USB cable to plug your Arduino into any free USB port on you computer.

    Navigate to the Tools section at the top of the Arduino IDE.

    You'll be greeted with a scary drop down. To setup your Arduino to accept uploads, do the following: Go to Board: and make sure Arduino/Genuino Uno is selected.

    BoardBoard

    Next is the Port. For Mac users refer to the video for these instructions. With your Arduino plugged in, you'll see some options. Mine says COM 15. This is the channel that the IDE will use to communicate with your Arduino. If you have a lot of COM # devices and don't know which is your Arduino, unplug your Arduino and take note of that numbers are there. Then plug it back in. Whichever is the new one in the list is your Arduino. Select that port.

    PortPort

    With the board and port selected and ready to go, let's upload our first sketch.

    Sketches

    We are going to upload a simple sketch to make an LED on the board blink. Head to File > Examples > Basics > Blink . You should see this sketch.

    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
    /*
      Blink
    
      Turns an LED on for one second, then off for one second, repeatedly.
    
      Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
      it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
      the correct LED pin independent of which board is used.
      If you want to know what pin the on-board LED is connected to on your Arduino
      model, check the Technical Specs of your board at:
      https://www.arduino.cc/en/Main/Products
    
      modified 8 May 2014
      by Scott Fitzgerald
      modified 2 Sep 2016
      by Arturo Guadalupi
      modified 8 Sep 2016
      by Colby Newman
    
      This example code is in the public domain.
    
      http://www.arduino.cc/en/Tutorial/Blink
    */
    
    // the setup function runs once when you press reset or power the board
    void setup() {
      // initialize digital pin LED_BUILTIN as an output.
      pinMode(LED_BUILTIN, OUTPUT);
    }
    
    // the loop function runs over and over again forever
    void loop() {
      digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
      delay(1000);                       // wait for a second
      digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
      delay(1000);                       // wait for a second
    }
    

    Don't worry about the coding for now. Let's upload this sketch. You can press the Arrow icon, go to Sketch then Upload, or you can use the shortcut Ctrl+U in order to upload the code. Your sketch will auto-verify, compile, then upload to your Arduino board.

    Upload the SketchUpload the Sketch

    You will see the message Done uploading and your board should now have a blinking light.

    Uploading Sketches and Code From Thimble 

    We provide example code for all our projects. To use those you'll need to copy them from our website. Then, go into the Arduino IDE, File > New. It's best to then delete all the code already there in the sketch before you paste our code in, as that will avoid any errors from copying over the old code.

    Serial Plotter

    You'll be asked to use the Serial Plotter on some projects. This reads any information coming from the Arduino and displays it on a graph. It can only be accessed after you've uploaded your code. It is in the Tools menu. Once opened, make sure the Baud rate is set to 9600.

    Where to find the Serial Plotter
    Baud rate

    Troubleshooting 

    The most common issues are solved by double checking your Board and Port settings.

    Libraries

    What are Libraries? 

    Libraries are a collection of code that has already been written. We use them when we want to add functionality without have to write everything from scratch. The advantage of using a library is code re-use.

    Some of the projects in this set need libraries so this section will show you how to install those.

    Libraries to be downloaded 

    Our libraries come in the form of zip files that need to be downloaded but NOT unzipped.

    I2C Motor Driver - This is for the motor driver.

    Average - This is for statistical functions used in RPM measurement.

    Adding Them to the Arduino IDE 

    Open up the Arduino IDE and go to Sketch > Include Library > Add .ZIP Library.... After that navigate to the zip files and add them one by one. And that's it!

    Add a LibraryAdd a Library

    What Should I Have Received?

    List of Parts 

    This is a list of all the parts you should have received in your Robotics Kit 1 box. Your WiFi module might look different and you wrapper colors may vary.

    Parts

    All Parts x Qty
    DC Motor with Gear Box x 2
    Tire x 2
    I2C Motor Driver x 1
    Cables x 3
    9v AA Battery Holder x 1
    Robotics Kit Chassis x 1
    Motor Mounts x 4
    Line Finder Module x 2
    Line Finder Mount x 2
    Encoder Wheel x 2
    Steel Omni Wheel x 1
    Bag of Hardware x 1

    Additional parts

    You should also have received a sheet of 4 stickers and a postcard with instructions.

    Infrared

    What is Infrared? 

    Infrared is a type of electromagnetic wave, similar to x-rays and microwaves. Electromagnetic (EM) waves are also a form of light. EM waves bounces off objects and into our eyes, allowing us to see colors. Each color we see has a different wavelength where wavelength is the distance between two crests (the highest point) on a wave. All of the different EM waves have different wavelengths with shorter wavelengths having higher energy and longer wavelengths having less energy. This broad range of waves makes up the Electromagnetic Spectrum.

    Electromagnetic SpectrumElectromagnetic Spectrum

    The EM waves humans can see are only a small fraction of all the EM waves in the universe; the wavelengths we can see are between 400 - 700 nanometers (about nine times smaller than a human hair) while the entire EM spectrum goes from Gamma rays with wavelengths of about 0.0001 nanometers to Radio waves with wavelengths of about 100 meters.

    Infrared (IR) light has a longer wavelength than the light visible to humans. It ranges from about 700 nanometers to .1 millimeters. This means we can't see infrared, but it's there and you can feel it as heat! Because humans can't see IR, it is commonly used in remote controls. In a TV remote for example, when you click the button to turn the volume up the microprocessor in the remote converts that action into a binary code that is then emitted from an IR emitting LED to and IR receiver on the TV. This code is sent out in pulses of light that the TV's own microprocessor can then detect and decode, resulting in the volume increasing.

    Infrared radiation is also a way of transferring heat and makes up almost half of the sun's energy. While some energy from the sun bounces off objects and allows us to see colors, most of the energy is absorbed by objects around us.

    This occurrence allows for one of the most important uses for infrared light - bolometers. A device that measures IR energy named after the Greek word for a ray of light, bolē. The IR light emitted from objects can be detected by electronic sensors such as a temperature-sensitive resistor called a thermistor. An increase in heat or IR energy lowers the resistance of a thermistor. This change in resistance causes a change in voltage. Much like the sensors in past Thimble kits, we can measure this change and covert it to a temperature reading.

    You can learn more about IR and its multitude of uses here.

    Infrared Encoder 

    So how are we going to use infrared light? A common way to accurately measure how far a motor has turned is an infrared encoder. The line finder module in the WiFi Robot 1 kit has one IR emitting LED (the clear LED) and an IR sensitive phototransistor (the dark bulb). The clear bulb emits an IR beam that can bounce back and be detected by the dark bulb. The emitter constantly sends an IR signal. When that signal hits a reflective surface or light colored surface, it bounces back and into the phototransistor. High causes a measurable voltage change and HIGH signal output from the sensor. If the IR signal doesn't hit anything or hits a dark surface, the signal doesn't make it to the phototransistor and the sensor outputs a LOW signal. We use this property in the encoding wheel which has patches of black and white. When the sensor goes from LOW to HIGH to LOW, we know that the wheel is turning. How quickly the LOW to HIGH to LOW changes can be directly corelated to the speed of the wheel.

    As the encoding wheel turns the IR ray bounces back and results in a square wave inputAs the encoding wheel turns the IR ray bounces back and results in a square wave input

    Using IR for Measurements 

    Once we are able to count the number of turns in the motor using the IR sensor and encoder wheels, we can also calculate the velocity using a little bit of geometry.

    The circumference of a circle is pi times diameter. When the wheel fully turns once, it goes a distance thats equal to its circumference.

    Wheel circumferenceWheel circumference

    This means that the distance the robot travels is equal to the number of times the wheel completes one full rotation times the circumference.

    To figure out how many ticks will get us a specific distance we can divide the 360 degrees in a circle by the circumference to see how far the wheel will travel per tick.

    Multiplying this by the distance we want to go will tell us how many ticks to count to go that distance.

    Velocity

    In order to calculate velocity we need to know the RPM of the motor. RPM stands for "Rotations Per Minute" and is a number that indicates how many times the motor turns one full rotation in one minute. By knowing how many times the motor rotates per minute we can use the distance it travels in one rotation (its circumference) to find its velocity with the formula velocity = circumference * (rpm / 60) .

    We divide by 60 to get rotations per seconds (rps) which gives us the velocity in a standard unit such as meters per second (m/s).

    Wheel speed illustrationWheel speed illustration

    Now that you know what IR is and how we can use it for many calculations... Let's get on to building your robot!

    Robotics Kit 1 - Hardware

    Start 

    This is the Main Project for this kit. We'll be using the WiFi module to create a web server that will allow us to wirelessly control a robot. We'll use HTML and CSS to format and customize your web server and an I2C motor driver to control the motors.

    Modules 

    Gather the following parts to complete this project.

    Parts

    All Parts x Qty
    Robotics Kit Chassis x 1
    DC Motor with Gear Box x 2
    Tire x 2
    I2C Motor Driver x 1
    Cables x 3
    9v AA Battery Holder x 1
    Robot Body Level 1 x 1
    Motor Mounts x 4
    Line Finder Module x 2
    Line Finder Mount x 2
    Encoder Wheel x 2
    Steel Omni Wheel x 1
    M3 20mm Standoff x 2
    M3 30mm Machine Screw x 4
    M3 10mm Machine Screw x 6
    M3 Hex Nuts x 10
    M3 Nylon Locking Nuts x 6
    M2 10mm Machine Screw x 10
    M2 Hex Nuts x 10
    Sharpie x 1
    Screwdriver (phillips and flathead) x 1

    Note:

    In your kit there is a 9v AA battery holder to run the motors with. If you would like your robot to move faster (or you would just like to use a different battery) then you can use any battery between 6 to 15 volts. Do not use any battery greater than 15v or you may damage the motor driver. Do not use any battery lower than 6v because the motors will not have enough power to turn.

    Important:

    Be sure to follow the instructions below in the order they are given in order to make construction of the robot as easy as possible.

    Calibrating Line Finder modules 

    First you'll need two line finder modules, two cables, a piece of paper, and a sharpie or a black marker.

    1. Take a cable, unwrap it, and plug one side into one of the line finders and the other into any Digital socket.

    2. Then take your black marker and the sheet of white printing paper. Draw a long thick rectangle. The size doesn't have to be exact but approximately 1/2" wide and 2" long.

    Parts Needed
    Line finder modules with cables
    Take one end of one of the cables
    Plug into one of the line finders
    Repeat for other line finder
    Plug line finder modules into any Digital pins
    Take paper and Sharpie
    Draw black rectangle (approximately 1/2" x 2")
    Fill in the rectangle

    Upload

    Upload the following code. The example below uses the D3 digital pin.

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // digital pin of Line Finder being calibrated
    int signalPin =  3;
    
    void setup()   
    {
      // initialize the digital pin as an output
      pinMode(signalPin, INPUT);
    
      // initialize serial to display what line finder reads
      Serial.begin(9600);
    }
    void loop()
    {
      // if a color is detected print 1, else print 0
      if (HIGH == digitalRead(signalPin))
        Serial.println(1);
      else  Serial.println(0);
    }
    

    Observe

    Open Tools -> Serial Plotter to see the inputs. This will display the value the line finder detects. You'll also notice the red LED on the module lights up when you put your hand in front of the sensor. The goal is to adjust when this LED turns on so that the sensor detects when it is passed from black to white.

    Calibrate the Modules 

    Hold the module about eighth an inch from the black rectangle you drew so that both IR LEDs are over the black. Take a small screw driver turn the adjustable resistor clockwise slowly until you see that that red LED turns off. While holding the module half an inch away from the paper move it until both IR LEDs are over white and then turn the adjustable resistor slowly in either direction but just until the LED turns back on.

    When the line finder is calibrated correctly the LED will be off when over the black and on over the white. If if didn't work that first time don't be discouraged! This part can be quite tricky. Keep moving the module over the black, turning the resistor until the LED turns off, moving it over the white, slowly turning the resistor just until the LED turns on, and checking the Serial Monitor / the red LED to see if the module is correctly calibrated.

    Repeat this process for both line finder modules making sure to change the code provided to match the digital pin of the module you're calibrating. It is important that the line finders are calibrated half an inch away because they will be reading the encoder wheels which will be approximately half an inch away from the line finders.

    Observe indicator LED on line finder turn on
    Hold line finder module 1/2" above black rectangle
    Use screwdriver to turn potentiometer clockwise until LED turns off
    Turn potentiometer counterclockwise until red LED comes back on
    LED should turn on when held 1/2" away from white part of paper
    LED should turn off when held 1/2" away from black part of paper

    Arrow Means Up 

    There is an arrow engraved into the chassis. This shows the top or front of the chassis and kit. Any directions saying left, right, or bottom, will assuming this arrow is pointing ahead.

    The ArrowThe Arrow

    Peeling the Acrylic 

    All the acrylic parts in Robotics Kit 1 have a protective film. Before assembling the kit, remove it.

    A piece with the film
    Peel off the film
    Peel off the film
    One side done...
    ...and the other

    Mounting the Line Finders 

    Now that the line finders are calibrated you can mount them.

    1. Begin by grabbing the line finder modules, four M2 10mm screws, four M2 hex nuts, and two line finder mounts.

    2. Place the black part of the line finder module through the center cutout of the mount. The holes in the module should line up with the holes in the mount.

    3. Insert a M2 screw into both holes then flip over the piece. Attach a M2 hex nut to both screws and hand tighten.

    4. Repeat this for the second module. Note that you want the pieces to mirror each other and not be copies.

    All the parts you need
    The socket
    Should be over here when mounted
    Take the line finder module
    Place through slot
    Take screw
    Place in hole
    Place in hole
    Place in hole
    Place in hole
    Flip over
    Take hex nut
    Place on screw
    Repeat for other screw and nut
    Repeat for other screw and nut
    Tighten down
    This one is done
    Repeat for other side
    Repeat for other side
    Socket should be up here

    Securing Line Finders to Chassis 

    Now grab two M3 10mm screws, two M3 hex nuts, and the chassis.

    Insert into SLOT CInsert into SLOT C

    1. Now insert the mounts into slot C. One on each side. You want the black part of the line finder module facing out with the socket facing in.

    2. With the mounts pushed into the chassis, place a nut into the T shaped hole on the line finder mount. The nut should be parallel to the chassis.

    3. Using your fingers to holder the nut in place, screw a M3 10mm screw into the hole from the other side of the chassis. The screw should go through the hex nut. Make sure not to tighten the screw too much or you can damage the acrylic.

    4. Repeat this for the other mount. When you're done both mounts and modules should be securely attached so they don't wiggle.

    All the parts
    Here is the C-Slot
    Take a module
    Line up with the slots
    Place in slot
    Socket pointing inward
    A hex nut
    It will go in this tiny slot
    Take a hex slot
    Slide into slot
    Zoomed in
    Flip over and take a screw
    Insert from the top through the chassis
    Screw into nut
    Zoomed in
    Repeat with other module
    Both sockets are inward

    Mounting the Motors and Encoder Wheel 

    In order to mount the motors you'll need to mount the encoder wheel at the same time. For this you'll need the chassis, four motor mount pieces, the two encoder wheels, two of the stickers, four M3 30mm screws, four M3 nylon locking nuts, the two tires, and the two DC motors.

    1. Grab the stickers and encoder wheels. Peel off one of the stickers and carefully stick it to the encoding wheel. Try and line it up as neatly as possible with the edge of the wheel so that there is no excess hanging over the edge of the wheel. If any is over the edge, use scissors to trim it. Do this for both wheels using one sticker per wheel.
    Stickers and wheels
    These are the encoder stickers
    They go on the wheels
    Peel a sticker
    Peel a sticker
    The center cutout is removed
    Placed over a wheel
    Placed over both wheels
    1. Insert two motor mount pieces into SLOT A so that it pushes through the chassis and the longer part is one the same side as the line finder mount. The largest hole should be pointed towards the double slits in the chassis or towards the back. This hole is where the motor axle is going, so orienting it this way ensures the axle is centered with the tire slot.

    2. Slide the motors into the mounts so that the axle goes into the largest hole and the screw holes line up with the screw holes on the mount. The axle should be centered with the tire slot.

    3. SLOT B is for the encoding wheel. Place the encoder wheel in the slot by lining up the hole on the encoder wheel with the axle of the motor. Adjust the position of the encoder wheel so that it may turn freely when the motor runs - meaning it should not be up against the motor mount or the edge of SLOT B. The sticker side of the wheel should face the line finder module.

    4. Take the other two motor mounts and place them on the outside of the motor. Place one screw through the hole above the chassis and another screw through the hole diagonal across. If you have the tools, tighten down a nylon locking nut. If not, a normal M3 hex nut will do just fine.

    Do not over tighten. There should be no bends in the acrylic parts.

    1. You can put on the tires now.
    Flipped over chassis and two mounts
    This mount
    Will go in this slot
    These holes
    Should be pointed up
    Take the mount
    Insert from the bottom
    Inserted
    Both inserted
    This is the motor orientation
    The motor holes line up with the mount holes
    Adding the other motor
    Grab the encoder wheels
    They should be like this
    Place on axis
    The sticker should face the encoder
    Grabbing the other mounts
    Match up to motor
    Match up to motor
    Repeat on other side
    Hardware parts needed
    Take the screw
    Insert into bottom hole
    Insert into bottom hole
    Grab locking nut
    Tighten to the screw
    Tighten to the screw
    Tighten to the screw
    You might need pliars
    So using a hex nut instead is fine
    Repeat for other side
    Now flip over the chassis
    Now flip over the chassis
    Here are the other holes
    Here are the other holes
    Hardware needed
    Take a screw
    Insert into mount
    Insert into mount
    Take locking nut
    Screw onto screw
    Screw onto screw
    Screw onto screw
    Watch out for the tightness
    This is perfect
    This is too tight
    Repeat for the otherside
    Get the tires
    Line them up
    Tires on

    Installing the Front wheel 

    In order to balance your robot you'll need to install the front wheel. To do this grab the omni wheel, two M3 10mm standoffs, two M3 nylon locking nuts, and two M3 10mm screws.

    1. First screw the two M3 10mm screws into the two front holes of the chassis.

    2. Screw the standoffs onto these screws.

    3. Place the standoffs into the holes of the omni wheel, place hex nuts on the screws, and tighten. You want the wheel to be securely attached so consider using a wrench or pliers to really tighten the nylon locking nuts.

    Parts needed
    Take a screw
    Place through chassis hole
    Place through chassis hole
    Take standoff
    Tighten from under the chassis
    Tightened standoff
    Repeat for only side
    Flipped over
    Take wheel
    Place over standoffs
    Take a locking nut
    And the other
    Tighten
    You need pliers for this. Substitute a hex nut if you don't have
    Omni wheel setup
    done

    Removing the Jumper 

    1. First you need to remove a jumper on the motor driver to use the external battery holder. A jumper is just a piece of metal that connects two wires. The jumper on the driver is located in between the white cable port and the green screw terminal labeled VS and GND.

    2. Using your fingers or a screw driver pull it off. Place this with your Creator Set so it doesn't get lost.

    Locate jumper on motor driver
    Use a flathead screwdriver to remove the jumper by pulling it up and off the board
    Remove the jumper
    Jumper is removed

    Mounting the Battery Holder 

    Grab 2 M2 10 mm screws, two M2 hex nuts, and the battery holder.

    1. Place the battery holder on the front of the chassis so that the holes in the chassis are visible through the battery holder.

    2. Place both M2 screws through the battery holder then through the chassis.

    3. Hand tighten a M2 hex nut on each screw.

    Parts needed
    These holes
    Will line up with these
    Take the battery holder
    Line up with chassis holes
    Line up with chassis holes
    Take a screw
    Place into hole
    Take a screw
    Place into hole
    Flip over
    Take a nut
    Tighten
    Take the other nut
    Tighten
    Screws tightened down
    Done

    Mounting the I2C Motor Driver 

    Grab 2 M2 10 mm screws and two M2 hex nuts. On the I2C Motor Driver there are a total of 3 green screw terminal blocks. The one by the socket is for the battery while the other two are for the motor.

    1. Unscrew the screw inside the battery screw terminal. The screws will NOT comes out. Just a few turns are fine.

    2. Place the RED wire coming from the battery holder into the slot marked VS on the screw terminal. Tighten that screw.

    3. Place the BLACK wire coming from the battery holder into the slot marked GND on the screw terminal. Tighten that screw.

    4. Place the I2C Motor Driver, with the socket facing forward, in the middle of the chassis.

    5. Place one screw in the mounting hole in the middle right of the board and one in the top left.

    6. Flip the board and chassis over then hand tighten a nut to both screws.

    I2C Motor Driver
    Battery terminals
    Loosen
    Loosen
    Red wire from battery holder
    Into VS terminal
    Tighten
    All tightened down
    Black wire from battery holder
    Into GND terminal
    Tighten
    Ready to mount
    On the chassis
    This socket
    Is towards the front
    These are the mounting holes
    Add screws into these
    Hold down
    Flip over
    Add a nut
    Add a nut
    Tighten both
    Flip back over
    Done

    Connecting the Motors 

    Each motor should have a red and black jumper wire coming off of it.

    1. Loosen the two motor screw terminal blocks. The screws will NOT comes out. Just a few turns are fine.

    2. Place the RED wires into M1+ and M2+. Which ever is closest. Then tighten those screws.

    3. Place the BLACK wires into M1- and M2-. Each wire pair should go to its own screw terminal block. Then tighten those screws.

    Motor terminals
    Loosen all terminals
    Loosen all terminals
    Take red motor wires
    Insert into + terminals
    Tighten both
    Tighten both
    Take black motor wires
    Insert into - terminals
    Tighten both
    Tighten both
    Done

    Mounting the Arduino 

    For this part we need the Arduino and the black expansion shield separated. We will also need two M3 10mm screws and two M3 hex nuts.

    1. Place the Arduino with its USB port and power jack facing towards the left on the chassis. The bottom right Arduino mounting hole should match up to the outer slot in the chassis. The top left Arduino mounting hole should line up inside the center bottom slot.

    2. Place a screw in each of the holes described previously. The top left one will be slightly crooked.

    3. Flip over the chassis and hand tighten a hex nut to both screws.

    4. Flip the chassis over again. The black expansion board can be put back on top of the Arduino.

    Arduino and shield
    These must be separated
    Separated
    Bottom of chassis
    Mounting points
    Arduino orientation
    Insert screw
    Insert other screw
    Hold down screws
    Flip over
    Add a nut
    Add a nut
    Add a nut
    Tighten both
    Flip back over
    Take shield
    Line up with Arduino headers
    Press down
    Done

    Attaching the Cables 

    We will need 3 standard cables to get the robot up and running.

    1. Take a cable and plug one end into the motor driver socket and the other into any I2C port.

    2. Use two cables and gently plug them into the line finder modules if they aren't already attached. The long slots on the chassis have little notches in them. These allow you to thread the cables through the board to the other side.

    3. Take each of the line finder cables and push them through to the top of the robot. To do this take a close look at the clips at the ends of the cables. On one side of the clips there's a notch that fits into the notch in the slots on the chassis. Align these to push the cables through.

    4. Plug the left line finder into Digital socket D3 and the right line finder in to Digital socket D2.

    Chassis and cables
    Take a cable
    Unwrap
    Place one end in the Motor Driver socket
    The other into any I2C Socket
    Flip over
    Flip over
    Table both cables
    Unwrap them
    One side into a line finder socket
    Then feed through the chassis slot to the top side
    Like that
    One side into a line finder socket
    Then feed through the chassis slot to the top side
    Like that
    Flip back over
    Flip back over
    Right side line finder
    To D2
    Plugged in
    Right side to D3
    Right side to D3

    Next section 

    Now all the hardware is mounted. It is time to test the motors and learn how to control the WiFi Robot.

    Find the Motor Offset

    Start 

    Calibration in Progress

    Powering the Robotics Kit 

    The motor and Arduino use different power sources. They both must be on for the kit to work. The I2C Motor Driver uses 6 AAs and the Arduino uses a 9 volt.

    Kit and batteries
    9 volt for Arduino
    AAs for motors
    Take AAs
    Place in holder
    Like this
    Use tape
    To secure the 9 volt
    To secure the 9 volt
    I like to place it next to the holder
    Plug in the barrel jack
    Plug in the barrel jack
    A fully powered kit
    These lights show each part is on

    Automagic Calibration Program 

    This program uses a feedback loop with the two line finders and the two motors. First we'll upload it and use then we'll talk about how it works. We need to calibrate because there are many small differences in everyone's robot. Motors,

    Upload

    Upload the following program. Make sure your left line finder is plugged into D3 and the right line finder is plugged into D2. If for any reason the tires are not on the motor axle, place them on now.

    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
    //Libraries used in this sketch
    #include <Average.h>
    #include "Grove_I2C_Motor_Driver.h"
    
    //Address needed to comunicate with the driver
    #define I2C_ADDRESS 0x0f
    
    //Variable names for the motors
    unsigned char left = MOTOR2;
    unsigned char right = MOTOR1;
    
    //Amount of samples to take
    int sampleAmount = 16;
    
    //Arrays to hold the samples
    Average<float> leftTimeDiff(sampleAmount);
    Average<float> rightTimeDiff(sampleAmount);
    
    //Variables used for counting time
    unsigned long previousLeftTick;
    unsigned long previousRightTick;
    unsigned long previousChange;
    
    //Waiting amount for comparisons
    int interval = 2000;
    
    //Motor variables
    int motorRevUpTime = 2000;
    int motorPower = 60;
    
    //Offset variables
    int offset = 0;
    bool offsetFound = false;
    
    //Error variables
    float deltaError = 2;
    float error = 100;
    
    void setup() {
    //Start Serial communications
      Serial.begin(9600);
    
      // start motor driver
      Motor.begin(I2C_ADDRESS);
      // correct pwm cycles
      Motor.frequence(F_490Hz);
    
      attachInterrupt(1, leftTick, CHANGE);
      attachInterrupt(0, rightTick, CHANGE);
    
    }
    
    void loop() {
    
      Serial.println("Starting Calibration");
      Serial.println("____________________");
      Serial.println("Spinning up Motors");
    
      Motor.speed(left, -motorPower - offset);
      Motor.speed(right, motorPower - offset);
    
      delay(motorRevUpTime);
    
      Serial.println("Motors up to Speed");
      Serial.println("__________________");
      Serial.println("Starting Sampling");
    
      error = abs(leftTimeDiff.mean() - rightTimeDiff.mean());
    
      while (!offsetFound) {
        if (millis() - previousChange > interval) {
          error = abs(leftTimeDiff.mean() - rightTimeDiff.mean());
    
          Serial.println("Left Mean : " + String(leftTimeDiff.mean()));
          Serial.println("Left Dev : " + String(leftTimeDiff.stddev()));
    
          Serial.println("Right Mean : " + String(rightTimeDiff.mean()));
          Serial.println("Right Dev : " + String(rightTimeDiff.stddev()));
    
          Serial.println("Current offset of : " + String(offset));
          Serial.println("Current error of : " + String(error));
          Serial.println("Must resolve to : " + String(deltaError));
          Serial.println();
    
          if (error <= deltaError) {
            offsetFound = true;
            break;
          }
    
          if (leftTimeDiff.mean() > rightTimeDiff.mean()) {
            offset++;
          } else {
            offset--;
          }
    
          previousChange = millis();
    
          Motor.speed(left, -motorPower - offset);
          Motor.speed(right, motorPower - offset);
        }
      }
    
      Serial.println("Offset found");
      Serial.println("_____________");
      Serial.println("Offset = " + String(offset));
    
      Motor.stop(left);
      Motor.stop(right);
    
      while (1) {
      }
    
    
    }
    
    void leftTick() {
      leftTimeDiff.push(millis() - previousLeftTick);
      previousLeftTick = millis();
    }
    
    void rightTick() {
      rightTimeDiff.push(millis() - previousRightTick);
      previousRightTick = millis();
    }
    
    

    Observe

    Flip the robot upside-down so it doesn't drive off your workspace. We want the tires on when calibrating the motor because they'll be on when the motor is used. Removed them would change the motor's response and give us less accurate data. Open up the Serial Monitor and check out the output. If your line finders are properly calibrated you should see an output like this.

    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
    Starting Calibration
    ____________________
    Spinning up Motors
    Motors up to Speed
    __________________
    Starting Sampling
    Left Mean : 22.56
    Left Dev : 8.39
    Right Mean : 19.06
    Right Dev : 2.15
    Current offset of : 0
    Current error of : 3.56
    Must resolve to : 1.00
    
    ...
    
    Left Mean : 19.94
    Left Dev : 8.14
    Right Mean : 19.38
    Right Dev : 2.23
    Current offset of : 5
    Current error of : 0.56
    Must resolve to : 1.00
    
    Offset found
    _____________
    Offset = 5
    
    

    The offset at the end is the one you should use.

    Is the motors seem to be spinning slow try resetting the I2C Motor Driver.

    Reset ButtonReset Button

    So What is Happening? 

    First the motors are spun up before we take measurements. The motors don't immediately spin at the speed we want them to. We use a small delay to let them get to speed before starting our sampling measurements. After the interval the initial measurements are taken. We are measuring how long it takes to go from one slice on the encoder wheel sticker to the next. After taking 2 seconds of measurements, we compare the average of the two encoder. We want the two means to be under the value specified in the deltaError . The lower that deltaError more inaccurate the offset with be. If the means are over the deltaError value then the offset in increased. The motors spin without measurements to get the motors to the right speed. And the whole process repeats.

    Diagram

    How the Calibration WorksHow the Calibration Works

    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
    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.

    How to Turn Off the Kit

    Unplugged 

    To turn the Arduino all you have to do is unplug the connector. The Motor Driver also needs to be turned off. A battery from the holder must be removed.

    Batteries in use
    Choose one
    Pop it out
    Now the lights are off
    Customizing Your Web Server

    Start 

    Want to add more buttons or change the color of your web server? Using HTML and CSS you can do just that!

    Upload 

    Copy and paste the following code (also found in the main tutorial of this kit) into a new Arduino sketch so you can experiment with your web server. Make sure the pins in the example match the pins that your robot are connected to.

    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
    #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 = 3;
    
    // 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;
      }
    }
    
    

    Modify

    Try changing the heading of the web server from "My WiFi Robot Controls" to "[YOUR NAME]'s Super Duper One-of-a-Kind Robot Control Panel".

    You can also change the color, font, and labeling of the buttons.

    To change the font

    If you'd like to change the font of your buttons, locate the line that sets the "font-family" as Helvetica. Replace Helvetica with the font you wish to use. You cannot use any font you'd like. This is because your web server can be used by any smart device with a web browser so you need to use a default font that the device already has downloaded. If you use a font that not all devices have then the user will see a default font such as Times New Roman - even if this is not the font you intended to use.

    Here are some fonts you can safely use:

    1. Comic SansComic Sans
    2. Courier newCourier new
    3. ImpactImpact
    4. PalatinoPalatino
    5. VerdanaVerdana

    For more fonts go here

    To change the color

    If you'd like to change the font of your buttons, locate the lines that set the "background-color" for each button. The colors are in the form of a code that specifies a RGB color. Since there are three colors in RGB (red, green, blue) that are combined to make each color, and 255 different shades for each color there are 16,581,375 colors to pick from!

    Go here to pick a color and change the hue. After you pick your color the RGB code will be in bold, located under the box that displays your color.

    Color pickerColor picker

    You can also change the hue, saturation, and lightness further down the page.

    HueHue

    To change the labels and functions of the buttons

    If you'd like to change the label and function of your buttons, locate the following lines:

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    // 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"
    

    As well as...

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    6
    7
    // 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"));
    

    The first part gives a name to the button so that when it's referenced the server knows which button to display and what the formatting of that specific button looks like.

    The second piece of the code displays the buttons, and tells the server that when a certain button is pressed it should append the corresponding command to the url of the server. Together

    and

    indicate a new paragraph. When a button is enclosed by them it's displayed on a new line. If you want your buttons all in a row, then delete these. On the other hand, if you want your buttons in a column then add

    to the beginning of each button to indicate a new paragraph and

    to indicate the end of that paragraph. You'll notice that when you click the forward button the url of your server changes from https://[YOUR I.P ADDRESS] to https://[YOUR I.P ADDRESS]/fwd. If you want to change the functionality of these buttons and the label follow this format to edit the buttons accordingly:

    Download file Copy to clipboard
    1
    <button class=\"button [THE NAME OF THE BUTTON YOU'RE USING]\" onClick=location.href='/[YOUR COMMAND SHORTCUT]'>[THE LABEL OF YOUR BUTTON]</button>\r\n"
    

    In the next tutorial you'll see how to add a button that turns an LED on your robot ON and OFF from the web server!

    Customizing Your Robot

    Start 

    Now that your robot is fully constructed the possibilities for customization are endless! In this tutorial we'll show you how to use your RGB lights and buzzer modules to turn your robot into a car!

    Forward and Reverse Lights in Action!

    Parts

    All Parts x Qty
    RGB LED Module x 2
    Cables x 3
    Buzzer Module x 1
    1x1 Wrapper x 1
    2x1 Wrapper x 1

    Adding the Taillights 

    Begin by grabbing 2 cables and 2 RGB LED modules.

    1. Plug one side of one cable into the IN side of one RGB module and the other into any Digital pin.

    2. Take the other cable and plug it into the OUT side of the first RGB and the other end of the cable into the IN side of the second RGB.

    3. Take your buzzer module and one more cable. Plug one side into the module and the other into any Digital pin.

    Use a smaller wrapper to attach the buzzer to the front of the robot. Use tape for the RGB LED modules

    Parts for adding LEDs
    Take both cables
    Unwrap them
    Plug one into D6
    The other into the IN side of an LED
    A new cable into the OUT side
    And in the IN side of the other LED
    And in the IN side of the other LED
    We will mount them here
    Like this
    With tape
    Sides are taped in place
    Parts to add the buzzer
    Take a wrapper screw
    Place into wrapper
    Then onto chassis
    Tighten down
    Take buzzer module
    Place into wrapper
    Place into wrapper
    Take remaining cable
    Unwrap it
    Plug one side into the buzzer socket
    The other into D5
    Ready for upload

    Upload

    Upload the following sketch. The example below uses:

    • The left motor as MOTOR1
    • The right motor as MOTOR2
    • The left encoder in D2
    • The right encoder in D3
    • RGB modules in D6
    • Buzzer in D4
    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
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    #include "WiFiEsp.h"
    #include "SoftwareSerial.h"
    #include "Grove_I2C_Motor_Driver.h"
    #include <ChainableLED.h>
    
    // default I2C address is 0x0f
    #define I2C_ADDRESS 0x0f
    // Amount of chained LEDs
    #define NUM_LEDS  2
    //Change here if you're using a different socket
    #define buzzerSocket 4
    
    // 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);
    
    //Create led object
    ChainableLED leds(5, 6, NUM_LEDS);
    
    //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 = 3;
    
    // 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;
    bool buzzerSounds = false;
    int buzzerState = LOW;
    
    // Variables to time and stop moving
    int interval;
    unsigned long startTime;
    int buzzerInterval = 50;
    unsigned long buzzerStartTime;
    
    // 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);
    
      // initialize LEDs
      leds.init();
    
      // initialize buzzer
      pinMode(buzzerSocket, OUTPUT);
    
      // 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();
    
      leds.setColorRGB(0, 128, 128, 0);
      leds.setColorRGB(1, 128, 128, 0);
    }
    
    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);
              leds.setColorRGB(0, 0, 255, 0);
              leds.setColorRGB(1, 0, 255, 0);
              moveTime(2, power);
            }
            // if the string "/bwd" is found,
            //then call the moveDistance function
            if (request.indexOf("/bwd") != -1) {
              //moveDistance(35, -power);
              leds.setColorRGB(0, 255, 0, 0);
              leds.setColorRGB(1, 255, 0, 0);
              moveTime(2, -power);
              buzzerSounds = true;
              buzzerStartTime = millis();
            }
            // 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');
            }
            //Check to see if the buzzer should be active
            checkBuzzer();
    
            // Return the response
            client.print(
              "HTTP/1.1 200 OK\r\n"
              "Content-Type: text/html\r\n");
            //Check to see if the buzzer should be active
            checkBuzzer();
    
            client.println();
    
            //Check to see if the buzzer should be active
            checkBuzzer();
    
            // 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"));
    
            //Check to see if the buzzer should be active
            checkBuzzer();
    
            // 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"));
    
            //Check to see if the buzzer should be active
            checkBuzzer();
    
            client.println("</body></html>");
    
            //Check to see if the buzzer should be active
            checkBuzzer();
    
            break;
          }
    
        }
        //Check to see if the buzzer should be active
        checkBuzzer();
    
        // close the connection
        client.stop();
        Serial.println("Client disconnected");
        //Check to see if the buzzer should be active
        checkBuzzer();
      }
    
      //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;
          //Check to see if the buzzer should be active
          buzzerState = HIGH;
          checkBuzzer();
          buzzerSounds = false;
          leds.setColorRGB(0, 128, 128, 0);
          leds.setColorRGB(1, 128, 128, 0);
        }
      }
    }
    
    
    // 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;
      }
    }
    
    void checkBuzzer() {
      if (buzzerSounds) {
        if (millis() - buzzerStartTime > buzzerInterval) {
          buzzerStartTime = millis();
          // if the buzzer is off turn it on and vice-versa:
          if (buzzerState == LOW) {
            buzzerState = HIGH;
          } else {
            buzzerState = LOW;
          }
          // set the state of the buzzer
          digitalWrite(buzzerSocket, buzzerState);
        }
      }
    }
    
    

    Observe

    Open the Serial Monitor and make sure your motor driver is connected to your battery. Your WiFi module should be initialized and should create an access point. Follow the link displayed (http://[ your WiFi module's IP address]) to your web server to see your controls. Disconnect your robot from the computer and insert the 9v battery into the barrel jack on the Seeeduino. Using any smart device (a phone or computer) connect to your robot by selecting My WiFi Robot in your WiFi settings. Open an Internet browser and navigate to your web server.

    When your robot drives forward the RGB lights should turn green. When your robot reverses the RGB lights should turn red and the buzzer should make a sound similar to a truck reversing.

    Lights are yellow when not moving
    Green when going forward
    Red for reverse

    Experiment

    Change the color of your lights or make your little robot car play a song when it reverses! You can add more sensors and buttons to customize your robot to do all sorts of things.

    Adding Buttons to Webserver

    Start 

    Working LED Button

    Parts

    All Parts x Qty
    LED Module x 1
    Cable x 1

    Setup 

    Take any color LED and insert it into the LED module. The longer leg is positive and should therefore be inserted into the hole labeled "+" on the module. Next, take the cable and plug one side into the LED module and the other into any Digital socket.

    Parts needed
    Take a wrapper screw
    Place in wrapper
    Tighten to chassis
    Take led module
    Place into wrapper
    Place into wrapper
    Take a cable
    Unwrap
    Place one end into LED socket
    Place into D7 even though D6 is shown here

    Upload 

    Upload the following code. The example below uses D7 for the LED module.

    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
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    #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 = 3;
    
    // 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;
    
    // Pin for LED module
    int ledPin = 7;
    
    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);
    
      // Initialize LED module as output and turn LED OFF
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW);
    
      // 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');
            }
    
            // Set LED value as LOW and check to see if LED is currently ON or OFF
            int value = LOW;
            if (request.indexOf("/LED=ON") != -1) {
              digitalWrite(ledPin, HIGH);
              value = HIGH;
            }
            if (request.indexOf("/LED=OFF") != -1) {
              digitalWrite(ledPin, LOW);
              value = LOW;
            }
    
            // 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
    
                ".led {background-color: #0077b3;}\r\n"
                ".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"));
    
                // If the LED is already off, display ON button.
                if (value == LOW) {
                  client.print(F("<p><button class=\"button led\" onClick=location.href='/LED=ON'>Turn LED ON</button><p>\r\n"));
                }
                // else, display OFF button
                else {
                  client.print(F("<p><button class=\"button led\" onClick=location.href='/LED=OFF'>Turn LED OFF</button><p>\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

    Connect to the web server. You should now see a blue button. Your LED should turn ON and OFF when you click the button. If it doesn't, try a different LED. Also make sure your code matches the Digital pin your LED module is connected to. If your LED is dim you can use a screw driver to adjust the potentiometer on the module to adjust its resistance and dim or brighten the LED.

    LED off
    LED on

    Experiment

    Add more buttons! Now that you know how to add new buttons as well as change their font, color, and function the possibilities are endless!

    Some additional projects to try:

    • Add the LCD to your robot to display different messages with various buttons.
    • Add the sound module to have your robot sing a song at the click of a button.
    • Use the LCD and weather station sensors to display real time updates of room temperatures, pressures, and humidities.
    Interrupts

    What is an interrupt? 

    Interrupts are an extremely useful mechanism built into Arduinos. They can be used to have your microcontroller react to execute a function when a specified event occurs and then return to where it left off in the code.

    In the context of this project we use interrupts, to calibrate the motor offset. We need to take a rolling average of the wheel's rotation and do some comparisons on that. The most important thing to getting a correct offset is good data. Our use of interrupts ensures that we always have the most up to date data as soon as possible.

    So why can't we just use a simple function to count the ticks? An interrupt frees up the processor to continue doing other important things like run the motors. If we didn't use an interrupt then the processor would have to stop running the motors in order to count the ticks which might result in a tick being missed, thus throwing off the counts.

    How to use interrupts 

    Only certain pins can be used as interrupts. On your microcontroller these pins are Digital pins 2 and 3. These specific pins can be set to trigger on RISING or FALLING signal edges. This means that when you open your Serial Plotter and see a graph of the signal inputs you can have a interrupt triggered and your function executed when the signal goes from LOW to HIGH (RISING) or from HIGH to LOW (FALLING). This also means that the event that triggers your interrupt has to be connected to either pin 2 or 3.

    Here we have a simple button sketch included in the Arduino IDE (Files -> Examples -> Digital -> Button):

    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
    const int buttonPin = 6;     // the number of the pushbutton pin
    const int ledPin =  13;      // the number of the LED pin
    
    // variables will change:
    int buttonState = 0;         // variable for reading the pushbutton status
    
    void setup() {
      // initialize the LED pin as an output:
      pinMode(ledPin, OUTPUT);
      // initialize the pushbutton pin as an input:
      pinMode(buttonPin, INPUT);
    }
    
    void loop() {
      // read the state of the pushbutton value:
      buttonState = digitalRead(buttonPin);
    
      // check if the pushbutton is pressed.
      // if it is, the buttonState is HIGH:
      if (buttonState == HIGH) {
        // turn LED on:
        digitalWrite(ledPin, HIGH);
      }
      else {
        // turn LED off:
        digitalWrite(ledPin, LOW);
      }
    }
    

    Say instead you want your program to do something else and only blink the LED when a certain event occurs. You would need to attach your LED to an interrupt pin first (pins 2 or 3) and then initialize or "attach" and interrupt in your setup. When your code executes it runs the main code and when an event occurs (the button being pressed) the processor stops running the main code and jumps to the interrupt function and runs that. When it finishes it jumps back into the main code right where it left off!

    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
    const int buttonPin = 2;     // the number of the pushbutton pin
    const int ledPin =  13;      // the number of the LED pin
    
    // variables will change:
    volatile int buttonState = 0;         // variable for reading the pushbutton status
    
    void setup() {
      // initialize the LED pin as an output:
      pinMode(ledPin, OUTPUT);
      // initialize the pushbutton pin as an input:
      pinMode(buttonPin, INPUT);
      // Attach an interrupt to the ISR vector
      attachInterrupt(0, pin_ISR, CHANGE);
    }
    
    void loop() {
      // your main code goes here!
    }
    
    void pin_ISR() {
      buttonState = digitalRead(buttonPin);
      digitalWrite(ledPin, buttonState);
    }
    

    The syntax for the attachInterrupt function is as follows: attachInterrupt(digitalPinToInterrupt, ISR, mode).

    digitalPinToInterrupt This is the interrupt vector which is a reference to where in the processor should look to see if an interrupt should be triggered. On your Seeeduino pins 2 and 3 correspond to interrupt vectors 0 and 1 respectively.

    ISR An ISR is an Interrupt Service Routine. These functions are unique because they cannot have an parameters and should not return anything. They should be short so that the main code function is not delayed too long and if multiple ISR's are called, they execute in an order that depends on the priority they have. The delay() and millis() functions will not work inside of an ISR because both functions require interrupts to work! In short, what goes here is the name of the function you want executed when the interrupt is triggered.

    mode This defines what type of event should trigger the interrupt. There are four predefined constants listed below:

    • LOW to trigger the interrupt whenever the pin is low,

    • CHANGE to trigger the interrupt whenever the pin changes value

    • RISING to trigger when the pin goes from low to high,

    • FALLING for when the pin goes from high to low.

    Quick note on volatile variables

    When you compile code the compiler not only turns your code into something a processor can read but it also gets rid of unused variables. When using an interrupt it might seen as if the variable does not change because its state is determined by an external event such as user input. By declaring the buttonState to be a volatile int, like in the above example, you are telling the processor that even though it seems as though the buttonState variable doesn't change and isn't used, it actually is.

    Wrapping up Interrupts 

    Interrupts are an extremely useful tool that allow you to free up your microcontroller's processor and react to real time events. After reading through this tutorial we encourage you to take a closer look at the main code of this kit and see how interrupts are used with your robot. For more information on interrupts, how to use them, and more examples follow this link.

    First Time WiFi Module Setup

    Start 

    You must follow this guide the first time you want to use the WiFi module. The module comes with a different Baud rate than what our libraries use. If you'd like to know more about Baud rates and why we have to change it, check out the page after this one.

    Note: If you have already set up your WiFi Module from the Weather Station Series then skip to here

    My module is different from the pictures 

    In the middle of the production of this kit, our manufacturer changed what the WiFi module looks like. There is one that has a silver rectangle and one button. The other version has a black square and two buttons. The hardware is identical. They have the same hardware specifications and function the same.

    Both WiFi modules side by sideBoth WiFi modules side by side

    Modules 

    Gather the following parts to complete this project.

    Parts

    All Parts x Qty
    WiFi x 1
    Cable x 1

    WiFi Module 

    Take a cable and unwrap it. Plug one side into the WiFi socket and the other into any Digital socket.

    Parts needed
    Take a cable
    Unwrap it
    Plug one side into the WiFi socket
    The other into any Digital socket

    Upload

    Upload the following code. The example below uses the D8 digital 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
    #include <SoftwareSerial.h>
    #define TIMEOUT 10000
    
    //Creates serial communication object
    SoftwareSerial espSerial(8, 9); // RX, TX
    
    String response = "";
    
    int baudCount = 0;
    
    bool timedout = false;
    bool set = false;
    bool confirmed = false;
    
    unsigned long timestamp = 0;
    
    void setup() {
      // Open serial communications
      Serial.begin(9600);
      espSerial.begin(115200);
      Serial.println("Testing WiFi Module...");
      espSerial.write("AT\r\n");
    }
    
    
    
    void loop() { // run over and over
      while (response == "" && !timedout) {
        if (espSerial.available()) {
          delay(200);
          espSerial.write("AT\r\n");
          //Display in the Serial Monitor
          response = espSerial.readString();
        }
        if(millis() > (TIMEOUT + timestamp)){
          timedout = true;
          timestamp = millis();
        }
      }
      if(timedout && !set && !confirmed){
        Serial.println("Timeout communicating with WiFi Module at 115200 Baud");
      }
    
      if (response.endsWith("OK\r\n") && !set && !timedout) {
        Serial.println("Found WiFi Module. Setting Baudrate to 9600...");
        espSerial.write("AT+UART_DEF=9600,8,1,0,0\r\n");
        set = true;
        response = "";
        espSerial.end();
        espSerial.begin(9600);
        delay(200);
        espSerial.write("AT\r\n");
      }
      if (!set && timedout) {
        Serial.println("Setting Baudrate to 9600...");
        espSerial.write("AT+UART_DEF=9600,8,1,0,0\r\n");
        set = true;
        response = "";
        espSerial.end();
        espSerial.begin(9600);
        delay(200);
        espSerial.write("AT\r\n");
        timestamp = millis();
        timedout = false;
      }
    
      if (response.endsWith("OK\r\n") && set && !confirmed) {
        Serial.println("WiFi Module configured to 9600 baud");
        delay(200);
        Serial.println("Ready for Thimble use");
        confirmed = true;
      }
    }
    

    Did it work? 

    Open the Serial Monitor after uploading to see if it worked. You want to see this output text...

    Download file Copy to clipboard
    1
    2
    3
    4
    Testing WiFi Module...
    Found WiFi Module. Setting Baudrate to 9600...
    WiFi Module configured to 9600 baud
    Ready for Thimble use
    

    I only see Testing WiFi Module

    Press the reset button on your Arduino. If still only see Testing ESP... run the code below.

    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
    #include <SoftwareSerial.h>
    
    //Creates serial communication object
    SoftwareSerial espSerial(8, 9); // RX, TX
    
    String response = "";
    
    bool confirmed = false;
    
    void setup() {
      // Open serial communications
      Serial.begin(9600);
      espSerial.begin(9600);
      Serial.println("Testing WiFi Module...");
      espSerial.write("at\r\n");
    }
    
    
    void loop() { // run over and over
      while (response == "") {
        if (espSerial.available()) {
          delay(200);
          espSerial.write("at\r\n");
          //Display in the Serial Monitor
          response = espSerial.readString();
        }
      }
    
      if (response.endsWith("OK\r\n") && !confirmed) {
        Serial.println("Your WiFi Module is working!");
        Serial.println("You are done with this step");
        confirmed = true;
      }
    }
    

    Did it work?

    Open the Serial Monitor after uploading to see if it worked. If you see this...

    Download file Copy to clipboard
    1
    2
    3
    Testing ESP...
    Your WiFi Module is working!
    You are done with this step
    

    ...then you are ready to go. If not, then double check you are using D8 as your socket and run the first code again.