FINAL PROJECT - PROGRAMMING


Home About me

CONTENTS


Click on any of the links below to see further details about the final project.

  1. Project management: Checklist of the project's gradual development.
  2. Concept: General draft of the first conceived idea.
  3. 2D/3D design: Summary and steps of the design process.
  4. Electronic production and design: Details, design and production of the main board for the project.
  5. 3D printing, laser cutting and prototyping: Further details about the production of the robot's parts.
  6. Embedded, application and interface programming: Networking, motor control and web interface.
  7. Final project summary: Video demonstration, bill of materials, files of the final robot and general summary.

SUMMARY


MOTOR CONTROL


MOTOR CONTROL V0.1 - MASTER/SLAVE WITH TWO JC-WATCHOBOT-V2.0 BOARDS

Note: It was discovered later that the jc-watchobot-v2.0 board needs two vmots and two grounds so the power supply of the pololu drivers can operate two motors at the same time.

Web motor control experimented in week 17 is improved to control two motors.

Wiring diagram is as follows:

The ESP8266 is connected directly to hardware serial in the jc-watchobotv2.0 board, a pololu A4988 stepper motor driver to the carrier and the motor coils to their respective pins 1A, 1B, 2A, 2B respectively. In the image below, 1A, 2A are in magenta and 1B and 2B are in brown.

After 2 all-nighters troubleshooting and head-scratching, my board's second pololu driver was showing strange behaviour on both of my boards; perhaps due to a faulty pololu driver or soldered joint. After trying to drive two motors from the same board, I decided to control the two motors using two boards, each controlling one stepper and instructions done with I2C communication protocol from master to slave.

Code below shows the first attempt to improve the code; also with a I2C instruction for the slave board

The master code:

/*
 * ****************************
 * Copyright (c) J. C. Mariscal
 * MIT License
 * *****************************
 */

 /*
  * *****************************
  * Program: motor control master v0.1
  * ****************************
  */
#include <Arduino.h>
#include "BasicStepperDriver.h"
#include <SoftwareSerial.h>
#include <Wire.h>

/*
 * /////////////////
 * MASTER STEPPER
 * /////////////////
 */
#define DIR PD5
#define STEP PD6
// using a 200-step motor.
#define STEPMOTOR 200
BasicStepperDriver masterStepper(STEPMOTOR, DIR, STEP);
int state =5; //non-movement state
int i = 0;

/*
 * ////////////
 * ESP8266
 * ////////////
 */
 #define DEBUG true
String ssid =  "WLANID";
String pass = "pass";
SoftwareSerial debugSerial(PD3,PD4); //for debugging serial
//SoftwareSerial espSerial(PD3, PD4);

// functions init
String sendData(String command, const int timeout, boolean debug);
void startServer();
void MasterStepperInit(unsigned int rpm, unsigned int microstep);
void moveMotor();

// SETUP
void setup() {
  //init stepper
  MasterStepperInit(1, 1);

  //init serial
  Serial.begin(115200);
  debugSerial.begin(9600);
  debugSerial.println("serial starting");

  // Start esp8266 server
  startServer();
  // Start master
  Wire.begin();        // join i2c bus (address optional for master)
  
  
}


// MAIN LOOP
void loop() {
  if ( Serial.available() ) {
    if (Serial.find("+IPD,") ) {
      String msg;
      Serial.find("?");
      msg = Serial.readStringUntil(' '); //read the whole text
      String command = msg.substring(0, 2);
      String valueStr = msg.substring(4);
      
      if (DEBUG){
        debugSerial.println(command);
        debugSerial.println(valueStr);
      }

      //move forward
      if (command == "01"){
        i = 0;
        state = 1;
      }
      // move backwards
      if (command == "02"){
        i = 0;
        state = 2;
      }
      // stop
      if (command == "03"){
        i = 0;
        state = 3;
      }
    }
  }
  moveMotor();
}
/*
 * //////////////
 * MOTOR FUNCTIONS
 * //////////////
 */

/*
 * ***********************
 * Init for master stepper
 * ***********************
 */
void MasterStepperInit(unsigned int rpm, unsigned int microstep){
  DDRB &= ~(1 << PB0);
  PORTB &= ~(1 << PB0);

  masterStepper.setRPM(rpm);
  // 1 full microstep, 2 half, etc...
  masterStepper.setMicrostep(microstep);
  //test rotation
  masterStepper.rotate(60);
  masterStepper.rotate(-60);
  masterStepper.rotate(60);
}

/*
 * ****************
 * Motor movement: uses global variable state
 * ****************
 */
void moveMotor(){
  // move motor forward
  if (state ==1) {
    Wire.beginTransmission(2); //begin transmission to device 2 (i.e. slave board)
    Wire.write(1);
    Wire.endTransmission(); 
    masterStepper.move(1);
    masterStepper.setRPM(i);
    /*
    stepper2.move(1);
    stepper2.setRPM(i);
    */
    // set acceleration
    if (i < 50){
      i++;
    }
  }

  // move motor backward
  if (state == 2) {
    Wire.beginTransmission(2); //begin transmission to device 2 (i.e. slave board)
    Wire.write(2);
    Wire.endTransmission();
    masterStepper.move(-1);
    masterStepper.setRPM(i);
    // set acceleration
    if (i < 50){
      i++;
    }
  }

  // stop
  if (state == 3){
    Wire.beginTransmission(2); //begin transmission to device 2 (i.e. slave board)
    Wire.write(3);
    Wire.endTransmission();
    masterStepper.move(0);
  }
}

/*
 * ///////////////////
 * ESP8266 FUNCTIONS
 * ///////////////////
 */
/* 

/*
 * ***************
 * sendData function: Sends command to the ESP8266 and returns response.
 * *****************
 */
String sendData(String command, const int timeout, boolean debug)
{
    debugSerial.println("sending data");
    String response = "";
    
    Serial.print(command); // send the read character to the mySerial
    
    long int time = millis();
    
    while( (time+timeout) > millis())
    {
      while(Serial.available())
      {
        
        // The esp has data so display its output to the serial window 
        char c = Serial.read(); // read the next character.
        response+=c;
      }  
    }
    
    if(debug)
    {
      debugSerial.print(response);
    }
    
    return response;
}

 /*  ****************
 *  esp8266 server: uses global variable ssid, pass.
 *  ****************
 */
void startServer(){
  debugSerial.println("starting server");
  // reset module
  sendData("AT+RST\r\n",2000,DEBUG);
  // configure as access point
  sendData("AT+CWMODE=1\r\n",1000,DEBUG);
  // connect to wifi
  sendData("AT+CWJAP=\"" + ssid +"\",\"" + pass + "\"", 1000, DEBUG);
  // get ip address
  sendData("AT+CIFSR\r\n",1000,DEBUG);
  // configure multiple connections
  sendData("AT+CIPMUX=1\r\n",1000,DEBUG);
  // use port 80 and turn on the server
  sendData("AT+CIPSERVER=1,80\r\n",1000,DEBUG);
}


/*
 * ****************************
 * Read web request as string after character ? and change state of motor
 * ****************************
 */
 String changeMotorState(){

 }
  

The slave code:

/*
 * ***********************
 * Copyright (c) J. C. Mariscal
 * MIT License
 * ***********************
 */
 /*
  * *****************************
  * Program: motor control slave v0.1
  * ****************************
  */
#include <Arduino.h>
#include "BasicStepperDriver.h"
#include <SoftwareSerial.h>
#include <Wire.h>
/*
 * /////////////////
 * SLAVE STEPPER
 * /////////////////
 */
#define DIR PD5
#define STEP PD6
// using a 200-step motor.
#define STEPMOTOR 200
BasicStepperDriver slaveStepper(STEPMOTOR, DIR, STEP);
int x = 0; // i2c byte sent from master
int i = 0;

void setup() {
  //init stepper
  slaveStepperInit(10, 1);

  // Start slave
  Wire.begin(2);        // join i2c bus address 2
  Wire.onReceive(receiveEvent);
}

void loop () {
  if (x == '1'){
    slaveStepper.move(1);
    slaveStepper.setRPM(i);
    // set acceleration
    if (i < 50) {
      i++;
    }
  }

  if (x == '2'){
    slaveStepper.move(-1);
    slaveStepper.setRPM(i);
    // set acceleration
    if (i < 50) {
      i++;
    }
  }

  if (x == '3'){
    slaveStepper.move(0);
  }
}

void receiveEvent(int bytes) {
  x = Wire.read();
  slaveStepper.rotate(360);
}

void slaveStepperInit(unsigned int rpm, unsigned int microstep){
  DDRB &= ~(1 << PB0);
  PORTB &= ~(1 << PB0);

  slaveStepper.setRPM(rpm);
  // 1 full microstep, 2 half, etc...
  slaveStepper.setMicrostep(microstep);
  //test rotation
  slaveStepper.rotate(60);
  slaveStepper.rotate(-60);
  slaveStepper.rotate(60);
}
  

MOTOR CONTROL V0.2 - MOTOR CONTROL WITH JC-WATCHOBOT V3.0


jc-watchobotv2.0 board was improved to handle 2 stepper motors, hence, jc-watchobot v3.0 board is used.

Wiring diagram is as follows:

Code is improved to drive two stepper motors from one board at the same time.

/*
 * ----------------------------------
 * Copyright (c) J. C. Mariscal
 * MIT License
 * ----------------------------------
 */

/*
* ----------------------------------
* Program: watchobot motor control
* ---------------------------------
*/
#include <Arduino.h>
#include "BasicStepperDriver.h"
#include <SoftwareSerial.h>

/** ******************
 * STEPPERS
 * *******************/
#define DIR 5 // PD5
#define STEP 6 // PD6
#define DIR2 9         // PB1
#define STEP2 10       // PB2
// using a 200-step motor.
#define STEPMOTOR 200
int g_RPM = 30;
int g_microstep = 16;

BasicStepperDriver masterStepper(STEPMOTOR, DIR, STEP);
BasicStepperDriver stepper2(STEPMOTOR, DIR2, STEP2);
int g_motorState =3; //non-movement state
int i = 0;
int g_j = 0;

/* *************
 * ESP8266
 * *************/
#define DEBUG true
String ssid =  "WLANID";
String pass = "pass";
SoftwareSerial debugSerial(PD3,PD4); //for debugging serial

/********************
 * FUNCTIONS INITS
 *********************/
String sendData(String command, const int timeout, boolean debug);
void startServer();
void stepperInit(unsigned int rpm, unsigned int microstep);
void moveMotor();
void checkUserCommand(String command);

/***************************************************************************/

// SETUP
void setup() {
  //init stepper
  stepperInit(g_RPM, g_microstep);

  //init serial
  Serial.begin(115200);
  debugSerial.begin(9600);
  debugSerial.println("serial starting");

  // Start esp8266 server
  startServer();
}

/***************************************************************************/

// MAIN LOOP
void loop() {
  if ( Serial.available() ) {
    if (Serial.find("+IPD,") ) {
      String msg;
      Serial.find("?");
      msg = Serial.readStringUntil(' '); //read the whole text
      String commandReceived = msg.substring(0, 2);
      String valueStr = msg.substring(4);
      
      if (DEBUG){
        debugSerial.println(commandReceived);
        debugSerial.println(valueStr);
      }
      /*
      String newRPM = msg.substring(0, 3);
      if (newRPM.charAt(0) == "a"){
        newRPM = newRPM.substring(1,3);
        g_RPM = newRPM.toInt();
        stepper2.setRPM(g_RPM);
        masterStepper.setRPM(g_RPM);
      }
      */
      
      checkUserCommand(commandReceived);
      }

    }
  moveMotor(g_motorState);
}

/****************************************************************************/
/*
 * //////////////
 * MOTOR FUNCTIONS
 * //////////////
 */

/*
 * ***********************
 * Init for master stepper
 * ***********************
 */
void stepperInit(unsigned int rpm, unsigned int microstep){
  DDRB &= ~(1 << PB0);
  PORTB &= ~(1 << PB0);
  DDRB &= ~(1 << PD7);
  PORTB &= ~(1 << PD7);

  masterStepper.setRPM(rpm);
  // 1 full microstep, 2 half, etc...
  masterStepper.setMicrostep(microstep);
  //test rotation

  stepper2.setRPM(rpm);
  stepper2.setMicrostep(microstep);
  masterStepper.rotate(10);
  masterStepper.rotate(-10);
  stepper2.rotate(10);
  stepper2.rotate(-10);
}

/*
 * ****************
 * Motor movement: uses global variable state
 * ****************
 */
void moveMotor(int motorState){
  // move motor forward
  if (motorState ==1) {
    masterStepper.move(1);
    masterStepper.setRPM(i);
    //stepper2 -1 is forward
    stepper2.move(-1);
    stepper2.setRPM(i);
    ++g_j;
    
    // set acceleration
    if (i < g_RPM && (g_j % 100 == 0)){
      i++;
    }
  }

  // move motor backward
  if (motorState == 2) {
    masterStepper.move(-1);
    masterStepper.setRPM(i);
    // stepper2, move 1 is backwards
    stepper2.move(1);
    stepper2.setRPM(i);
    ++g_j;
    // set acceleration
    if (i < g_RPM && (g_j % 100 == 0)){
      i++;
    }
  }

  // stop
  if (motorState == 3){
    masterStepper.move(0);
  }

  // move right
  if (motorState == 4){
    masterStepper.move(1);
    masterStepper.setRPM(2);
    stepper2.move(-1);
    stepper2.setRPM(i);
    // set acceleration
    if (i < g_RPM){
      i++;
    }
  }

  // move left
  if (motorState == 5){
    masterStepper.move(1);
    masterStepper.setRPM(i);
    stepper2.move(-1);
    stepper2.setRPM(2);

    // set acceleration
    if (i < g_RPM){
      i++;
    }
  }
  // pivot right
  if (motorState == 6){
    masterStepper.move(-1);
    masterStepper.setRPM(1);
    stepper2.move(-1);
    stepper2.setRPM(i);
    // set acceleration
    if (i < g_RPM){
      i++;
    }
  }

  // pivot left
  if (motorState == 7){
    masterStepper.move(1);
    masterStepper.setRPM(i);
    stepper2.move(1);
    stepper2.setRPM(1);
    // set acceleration
    if (i < g_RPM){
      i++;
    }
  }

}

/*
 * ///////////////////
 * ESP8266 FUNCTIONS
 * ///////////////////
 */
/* 

/*
 * *****************
 * sendData function: Sends command to the ESP8266 and returns response.
 * *****************
 */
String sendData(String command, const int timeout, boolean debug)
{
    debugSerial.println("sending data");
    String response = "";
    
    Serial.print(command); // send the read character to the mySerial
    
    long int time = millis();
    
    while( (time+timeout) > millis())
    {
      while(Serial.available())
      {
        
        // The esp has data so display its output to the serial window 
        char c = Serial.read(); // read the next character.
        response+=c;
      }  
    }
    
    if(debug)
    {
      debugSerial.print(response);
    }
    
    return response;
}

 /*  ****************
 *  esp8266 server: uses global variable ssid, pass.
 *  ****************
 */
void startServer(){
  debugSerial.println("starting server");
  // reset module
  sendData("AT+RST\r\n",2000,DEBUG);
  // configure as access point
  sendData("AT+CWMODE=1\r\n",1000,DEBUG);
  // connect to wifi
  sendData("AT+CWJAP=\"" + ssid +"\",\"" + pass + "\"", 1000, DEBUG);
  // get ip address
  sendData("AT+CIFSR\r\n",1000,DEBUG);
  // configure multiple connections
  sendData("AT+CIPMUX=1\r\n",1000,DEBUG);
  // use port 80 and turn on the server
  sendData("AT+CIPSERVER=1,80\r\n",1000,DEBUG);
}


/*
 * ****************************
 * WATCHOBOT COMMANDS FUNCTION: set event according to string command.
 * ****************************
 */
void checkUserCommand(String command){
  //move forward
  if (command == "01"){
    i = 1;
    g_motorState = 1;
    g_j = 0;
  }
  // move backwards
  if (command == "02"){
    i = 1;
    g_motorState = 2;
    g_j = 0;
  }
  // stop
  if (command == "03"){
    i = 1;
    g_motorState = 3;
  }
  // move right
  if (command == "04"){
    i = 1;
    g_motorState = 4;
  }
  // move left
  if (command == "05"){
    i = 1;
    g_motorState = 5;
  }
  // increase RPM
  if (command == "06"){
    ++g_RPM;
    if (g_RPM == 4000){
      g_RPM = 3;
    }
  }

  // decrease RPM
  if (command == "07"){
    --g_RPM;
    if (g_RPM == 1){
      g_RPM = 1;
    }
  }

  // reset RPM default
  if (command == "08"){
    g_RPM = 3;
    masterStepper.setMicrostep(16);
    stepper2.setMicrostep(16);
    
  }
  // full step mode
  if (command == "09"){
    masterStepper.setMicrostep(1);
    stepper2.setMicrostep(1);
    
  }
  // 1/2 microstep
  if (command == "10"){
    masterStepper.setMicrostep(2);
    stepper2.setMicrostep(2);
  }
  // 1/4 microstep
  if (command == "11"){
    masterStepper.setMicrostep(4);
    stepper2.setMicrostep(4);
  }
  // 1/8 microstep
  if (command == "12"){
    masterStepper.setMicrostep(8);
    stepper2.setMicrostep(8);
  }

  if (command == "13"){
    g_motorState = 6;
    i = 0;
  }
  
  if (command == "14"){
    g_motorState = 7;
    i = 0;
  }
}
  

The board is connected similarly as with the jc-watchobot-v2.0.

Board communicates over serial with the ESP8266 board; which is also initialised with AT commands and connected to a local wifi.

CAMERA CONTROL


PIXY CAMERA TEST CODE


Pixy CMUcam5 module is used for this project.

Before using, camera must be set to communicate through ICSP by using the PixyMon software. Instructions on how to install in a linux system can be found here.

The following code is uploaded to test the functionality of the camera; two servos are attached to the 3D printed pan and tilt mechanism (more info here).

The two servos are connected straight to the Pixy board.

As the pixy board uses ICSP to interface with the microcontroller, the connections to the jc-watchobot-v3.0 are as follows:

The following test code moves the servo motors and it worked as expected.

#include <SPI.h>  
#include <Pixy.h>

Pixy pixy;

void setup()
{

  Serial.begin(9600);
  Serial.print("Starting...\n");

  pixy.init();
}

void loop() 
{ 
  pixy.setServos(PIXY_RCS_MAX_POS, PIXY_RCS_MAX_POS);
  delay(1000);
  pixy.setServos(PIXY_RCS_MIN_POS, PIXY_RCS_MIN_POS);
  delay(1000);
}
  

PIXY PAN AND TILT CODE


Next code uses same libraries as previously, available here.

The code is connected to another board and run.

Camera follows the object and pans and tilts accordingly.

Code is partially modified from the example file in charmedlabs's github to function with my pan and tilt mechanism.

//
// begin license header
//
// This file is part of Pixy CMUcam5 or "Pixy" for short
//
// All Pixy source code is provided under the terms of the
// GNU General Public License v2 (http://www.gnu.org/licenses/gpl-2.0.html).
// Those wishing to use Pixy source code, software and/or
// technologies under different licensing terms should contact us at
// cmucam@cs.cmu.edu. Such licensing terms are available for
// all portions of the Pixy codebase presented here.
//
// end license header
//
// This sketch is a simple tracking demo that uses the pan/tilt unit.  For
// more information, go here:
//
// http://cmucam.org/projects/cmucam5/wiki/Run_the_Pantilt_Demo
//

#include <SPI.h>  
#include <Pixy.h>

Pixy pixy;

#define X_CENTER        ((PIXY_MAX_X-PIXY_MIN_X)/2)       
#define Y_CENTER        ((PIXY_MAX_Y-PIXY_MIN_Y)/2)
bool g_setHomePosition = false;

class ServoLoop
{
public:
  ServoLoop(int32_t pgain, int32_t dgain);

  void update(int32_t error);
   
  int32_t m_pos;
  int32_t m_prevError;
  int32_t m_pgain;
  int32_t m_dgain;
};


ServoLoop panLoop(300, 500);
ServoLoop tiltLoop(-500, -700);

ServoLoop::ServoLoop(int32_t pgain, int32_t dgain)
{
  m_pos = PIXY_RCS_CENTER_POS;
  m_pgain = pgain;
  m_dgain = dgain;
  m_prevError = 0x80000000L;
}

void ServoLoop::update(int32_t error)
{
  long int vel;
  char buf[32];
  if (m_prevError!=0x80000000)
  {  
    vel = (error*m_pgain + (error - m_prevError)*m_dgain)>>10;
    //sprintf(buf, "%ld\n", vel);
    //Serial.print(buf);
    m_pos += vel;
    if (m_pos>PIXY_RCS_MAX_POS) 
      m_pos = PIXY_RCS_MAX_POS; 
    else if (m_pos<PIXY_RCS_MIN_POS) 
      m_pos = PIXY_RCS_MIN_POS;
  }
  m_prevError = error;
}



void setup()
{
  Serial.begin(9600);
  Serial.print("Starting...\n");
  pixy.init();
  delay(1000);

}

void loop()
{ 
  if (g_setHomePosition == false){
    pixy.setServos(0, PIXY_RCS_MAX_POS);
    g_setHomePosition = true;
  }
  static int i = 0;
  int j;
  uint16_t blocks;
  char buf[32]; 
  int32_t panError, tiltError;
  
  blocks = pixy.getBlocks();
  
  if (blocks)
  {
    panError = X_CENTER-pixy.blocks[0].x;
    tiltError = pixy.blocks[0].y-Y_CENTER;
    
    panLoop.update(panError);
    tiltLoop.update(tiltError);
    
    pixy.setServos(panLoop.m_pos, tiltLoop.m_pos);
    
    i++;
    
    // do this (print) every 50 frames because printing every
    // frame would bog down the Arduino
    if (i%50==0) 
    {
      sprintf(buf, "Detected %d:\n", blocks);
      Serial.print(buf);
      for (j=0; j<blocks; j++)
      {
        sprintf(buf, "  block %d: ", j);
        Serial.print(buf); 
        pixy.blocks[j].print();
      }
    }
  }  
} 
  

WEB CONTROL


jsquery is used to create a set of functions to control the robot movement over the web.

There is a input form, where the IP address of the ESP8266 needs to be filled.

Function uses get method to send data over to the ESP8266, which in turn sets the appropriate command of movement for the steppers.

$.ajaxSetup({timeout:100});

var latch = false;

function readUrlAV (form) {
    TextVar = form.inputbox.value;
	VideoVar = "http://"+TextVar+":8080/video";
	AudioVar = "http://"+TextVar+":8080/audio.opus";
	document.getElementById("video").setAttribute('data', VideoVar);
	document.getElementById("audio").setAttribute('data', AudioVar);
}
function testarArduino (form) {
    TextVar = myform2.inputbox.value;
	ArduinoVar = "http://" + TextVar + ":80";
	$.get( ArduinoVar, { "cm3": 7000 })	;
	{Connection: close};
}

function setRPM (form) {
    TextVar = myform2.inputbox.value;
    ArduinoVar = "http://" + TextVar + ":80";
    formRpmVar = formRPM.inputbox.value;
    rpmSet = "a" + formRpmVar;
    $.get( ArduinoVar, {number: rpmSet});
	{Connection: close};
}


document.onkeydown = checkKeyDown;
document.onkeyup = checkKeyUp;

function checkKeyDown(e) {

    e = e || window.event;

    if (e.keyCode == '38') {
        // up arrow
		if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "01": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }
    else if (e.keyCode == '40') {
        // down arrow
		if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "02": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }
    else if (e.keyCode == '37') {
       // left arrow
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "05": 1000 })	;
			{Connection: close};
			latch = true;
		}

	}
    else if (e.keyCode == '39') {
       // right arrow
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "04": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }

    // q: pivot right
    else if (e.keyCode == '81') {
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "13": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }
    // w: pivot left
    else if (e.keyCode == '87') {
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "14": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }

    // i: increase speed 
    else if (e.keyCode == '73') {
       // i character
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "06": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }
    // d: decrease speed
    else if (e.keyCode == '68') {
       // d character
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "07": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }

    // r: reset speed/microstep
    else if (e.keyCode == '82') {
       // r character
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "08": 1000 })	;
			{Connection: close};
			latch = true;
		}
	}
    // 1: full speed/microstep
    else if (e.keyCode == '49') {
       // 1 character
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "09": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }    // 2: 1/2 speed/microstep
    else if (e.keyCode == '50') {
       // 2 character
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "10": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }    // 4: 1/4 speed/microstep
    else if (e.keyCode == '52') {
       // r character
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "11": 1000 })	;
			{Connection: close};
			latch = true;
		}
    }    // 8: 1/8 speed/microstep
    else if (e.keyCode == '56') {
       // r character
	   if (latch == false) {
			TextVar = myform2.inputbox.value;
			ArduinoVar = "http://" + TextVar + ":80";
			$.get( ArduinoVar, { "12": 1000 })	;
			{Connection: close};
			latch = true;
		}
	}

}

function checkKeyUp(e) {
    e = e || window.event;

    if ((e.keyCode == '38')||(e.keyCode == '40')||(e.keyCode == '37')||(e.keyCode == '39') || (e.keyCode == '56') || (e.keyCode == '52')
        || (e.keyCode == '50') || (e.keyCode == '49') || (e.keyCode == '82') || (e.keyCode == '68') || (e.keyCode =='73') || (e.keyCode =='87') || (e.keyCode == '81') ){ 

		setTimeout(doNothing, 200);
    }
}

function doNothing(){
	TextVar = myform2.inputbox.value;
		ArduinoVar = "http://" + TextVar + ":80";
		$.get( ArduinoVar, { "03": 1000 })	;
		{Connection: close};
		latch = false;
} 
  

Having the functions ready, an html file is created to host the interface program for the watchobot. Code is as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>WATCHOBOT CONTROL</title>
	<script src="jquery.js"></script>
	<script src="myscript.js"></script>

</head>

<body>

	<div id="main">
		<div id="commands" style="position: absolute; top: 50px; left:35%; width:65%">
	
			<form name="myform2" action="" method="GET">
				Watchobot IP Address:
				<input type="text" name="inputbox" Value="192.168.2.106">
				<input type="button" name="button3" Value="Test" onClick="testarArduino(this.form)">
			</form>

			<img src="images/keyboard.png" style="margin-left:100px; margin-top:50px;">
            <p>Keys for the robot:</p>
            <ul>
                <li><b>Up arrow:</b> move forward.</li>
                <li><b>Down arrow:</b> move backwards.</li>
                <li><b>Right arrow:</b> move right.</li>
                <li><b>Left arrow:</b> move left.</li>
                <li><b>q:</b> pivot right.</li>
                <li><b>w:</b> pivot left.</li>
                <li><b>i:</b> increase speed.</li>
                <li><b>1:</b> set micro-step to full step.</li>
                <li><b>2:</b> set 1/2 micro-step.</li>
                <li><b>4:</b> set 1/4 micro-step.</li>
                <li><b>8:</b> set 1/8 micro-step.</li>
                <li><b>r:</b> reset to default values.</li>
            </ul>
			<form name="formRPM" action="" method="GET">
				Watchobot speed in RPM
				<input type="text" name="inputbox" Value="30">
				<input type="button" name="button4" Value="Set RPM" onClick="setRPM(this.form)">
			
			
	
	</div>
</body>
</html>
  

Final look of the interface is:

FILES


Motor control v0.1 code for master and slave

Motor control v0.2 - 2 stepper web control

Web control files

Pixy pan and tilt code.


The content of this page is licensed under Attribution-NonCommercial 4.0 International CC BY-NC 4.0. Any source code, if otherwise unspecified, is licensed under the MIT License