This project allows you to control a MeArm Robot with your smartphone, as if you had many potentiometers in your hands.
Things used in this project
Hardware components |
||||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 |
Story
At first, I wanted to train myself to the wifi network thanks to Arduino … to quickly put myself in mind to control a robot.
In the set of projects I’ve found that already allow to do it, it is asked to appeal to the apps, the shields, or to click on links in web pages ….
I wanted to create something more functional, which allows to easily control the motors on the robot, to the nearest step.
in addition, not wanting to overload the small mkr1000 card in code or wiring, I decided to split the code into 2 parts: a master part (mkr1000) receiving the instructions from the web page, and a slave part (arduino Uno) controlling the motors and the LCD display. This allows you to display text, as well as to display the requested motor and angle commands as a relay.
The communication between the 2 cards uses the I2C protocol, since I could not find a way to use RT / TX.
Finally, the LED located on the original mkr1000 card will be used to indicate whether or not a wifi client is connected.
You will notice that the Serial link with the MKR1000 module must be started live on a PC so that the whole assembly can work properly. Moreover, once the wifi client disconnected, it is better to restart the module mkr1000, otherwise the web page will not be displayed in case of loss of it? I have not found the reason yet.
The motion ranges of the motors can be changed in the Inputs Range of the HTML code.
All of these 2 codes can be used in larger projects using the same technologies.
Code
Master Code (MKR1000)
Arduino
/*
WiFi Web Server Contrôle de robot meArm, affichage LCD et led
created 18 Mars 2018
par Fabrice bonne
*/
#include <SPI.h>
#include <WiFi101.h>
#include <Wire.h>
// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ 90
// size of buffer that stores the incoming string
#define TXT_BUF_SZ 50
char ssid[] = "****"; // your network SSID (name)
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0; // index into HTTP_req buffer
char txt_buf[TXT_BUF_SZ] = {0}; // buffer to save text to
int led = LED_BUILTIN;
int status = WL_IDLE_STATUS;
WiFiServer server(80);
void setup() {
//Initialize serial and wait for port to open:
Wire.begin(); // Rejoindre le bus I2C (Pas besoin d adresse pour le maitre)
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Access Point Web Server");
pinMode(led, OUTPUT); // set the LED pin mode
digitalWrite(led, LOW);
// print the network name (SSID);
Serial.print("Creating access point named: ");
Serial.println(ssid);
// Create open network. Change this line if you want to create an WEP network:
status = WiFi.beginAP(ssid);
if (status != WL_AP_LISTENING) {
Serial.println("Creating access point failed");
// don't continue
while (true);
}
// wait 10 seconds for connection:
delay(10000);
// start the web server on port 80
server.begin();
// you're connected now, so print out the status
printWiFiStatus();
}
void loop() {
// compare the previous status to the current status
if (status != WiFi.status()) {
// it has changed update the variable
status = WiFi.status();
if (status == WL_AP_CONNECTED) {
byte remoteMac[6];
// a device has connected to the AP
Serial.print("Device connected to AP, MAC address: ");
WiFi.APClientMacAddress(remoteMac);
Serial.print(remoteMac[5], HEX);
Serial.print(":");
Serial.print(remoteMac[4], HEX);
Serial.print(":");
Serial.print(remoteMac[3], HEX);
Serial.print(":");
Serial.print(remoteMac[2], HEX);
Serial.print(":");
Serial.print(remoteMac[1], HEX);
Serial.print(":");
Serial.println(remoteMac[0], HEX);
digitalWrite(led, HIGH);
} else {
// a device has disconnected from the AP, and we are back in listening mode
Serial.println("Device disconnected from AP");
digitalWrite(led, LOW);
}
}
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
Serial.println("new client"); // print a message out the serial port
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) { // client data available to read
char c = client.read(); // read 1 byte (character) from client
// limit the size of the stored received HTTP request
// buffer first part of HTTP request in HTTP_req array (string)
// leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
if (req_index < (REQ_BUF_SZ - 1)) {
HTTP_req[req_index] = c; // save HTTP request character
req_index++;
}
// last line of client request is blank and ends with \n
// respond to client only after last line received
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
// remainder of header follows below, depending on if
// web page or XML page is requested
// Ajax request - send XML file
if (StrContains(HTTP_req, "ajax_inputs")) {
// send rest of HTTP header
client.println("Content-Type: text/xml");
client.println("Connection: keep-alive");
client.println();
// print the received text to the Serial Monitor window
// if received with the incoming HTTP GET string
if (GetText(txt_buf, TXT_BUF_SZ)) {
Serial.println("\r\nReceived Text:");
Serial.println(txt_buf);
Wire.beginTransmission(4);
Wire.write(txt_buf);
Wire.endTransmission();
}
}
else { // web page request
// send rest of HTTP header
client.println("Content-Type: text/html");
client.println("Connection: keep-alive");
client.println();
// send web page
client.println("<!DOCTYPE html>");
client.println("<head>");
client.println("<title>Control meArm</title>");
client.println("<meta name='viewport' content='width=device-width, initial-scale=1.0'>");
client.println("<script type='text/javascript'>");
//--fonctions javascript
//fonction texte
client.println("function SendText(){");
client.println("nocache = \"&nocache=\" + Math.random() * 1000000;");
client.println("var request = new XMLHttpRequest();");
client.println("strText = \"&txt=\" + document.getElementById('txt_form').form_text.value + \"&end=end\";");
client.println("request.open(\"GET\", \"ajax_inputs\" + strText + nocache, true);");
client.println("request.send(null);");
client.println("}");
//fonction verticale
client.println("function Verticale(){");
client.println("nocache = \"&nocache=\" + Math.random() * 1000000;");
client.println("var request = new XMLHttpRequest();");
client.println("strText = \"&txt=\" + \"Vertical=\" + document.getElementById('pos_v').posiv_range.value + \"&end=end\";");
client.println("positiv = document.getElementById('pos_v').posiv_range.value;");
client.println("document.getElementById('reportv').value=positiv;");
client.println("request.open(\"GET\", \"ajax_inputs\" + strText + nocache, true);");
client.println("request.send(null);");
client.println("}");
//fonction horizontale
client.println("function Horizontale(){");
client.println("nocache = \"&nocache=\" + Math.random() * 1000000;");
client.println("var request = new XMLHttpRequest();");
client.println("strText = \"&txt=\" + \"Horizontal=\" + document.getElementById('pos_h').posih_range.value + \"&end=end\";");
client.println("positih = document.getElementById('pos_h').posih_range.value;");
client.println("document.getElementById('reporth').value=positih;");
client.println("request.open(\"GET\", \"ajax_inputs\" + strText + nocache, true);");
client.println("request.send(null);");
client.println("}");
//fonction angulaire
client.println("function Angulaire(){");
client.println("nocache = \"&nocache=\" + Math.random() * 1000000;");
client.println("var request = new XMLHttpRequest();");
client.println("strText = \"&txt=\" + \"Angle=\" + document.getElementById('pos_a').posia_range.value + \"&end=end\";");
client.println("positia = document.getElementById('pos_a').posia_range.value;");
client.println("document.getElementById('reporta').value=positia;");
client.println("request.open(\"GET\", \"ajax_inputs\" + strText + nocache, true);");
client.println("request.send(null);");
client.println("}");
//fonction pince
client.println("function Pince(){");
client.println("nocache = \"&nocache=\" + Math.random() * 1000000;");
client.println("var request = new XMLHttpRequest();");
client.println("strText = \"&txt=\" + \"Ouverture=\" + document.getElementById('pos_p').posip_range.value + \"&end=end\";");
client.println("positip = document.getElementById('pos_p').posip_range.value;");
client.println("document.getElementById('reportp').value=positip;");
client.println("request.open(\"GET\", \"ajax_inputs\" + strText + nocache, true);");
client.println("request.send(null);");
client.println("}");
client.println("</script>");
client.println("</head>");
client.println("<div>");
//--Input texte et Input Range
//Envoi de texte
client.println("<form id='txt_form' name='frmText'>");
client.println("<center><textarea name='form_text' rows='2' cols='16'></textarea></center>");
client.println("</form>");
client.println("<center><input type=submit value='Send Text' onclick=SendText() /></center></div>");
client.println("<div>");
client.println("<center>Bouger le robot</center><BR><BR>");
//Verticalement
client.println("<form id='pos_v' name='posText'>");
client.println("<center>Verticalement</center><BR>");
client.println("<center>Bas<input type=range name='posiv_range' min='20' max='90' value='45' step='1' onchange=Verticale() />Haut</center>");
client.println("</form>");
client.println("<center><input type=text id='reportv' size='3'/></center><BR>");
//Horizontalement
client.println("<form id='pos_h' name='posText'>");
client.println("<center>Horizontalement</center><BR>");
client.println("<center>Proche<input type=range name='posih_range' min='0' max='80' value='45' step='1' onchange=Horizontale() />Loin</center>");
client.println("</form>");
client.println("<center><input type=text id='reporth' size='3'/></center><BR>");
//Angulaire
client.println("<form id='pos_a' name='posText'>");
client.println("<center>Tourner le robot</center><BR>");
client.println("<center>Gauche<input type=range name='posia_range' min='0' max='180' value='90' step='1' onchange=Angulaire() />Droite</center>");
client.println("</form>");
client.println("<center><input type=text id='reporta' size='3'/></center><BR>");
//Pince
client.println("<form id='pos_p' name='posText'>");
client.println("<center>Pince</center><BR>");
client.println("<center>Ouvert<input type=range name='posip_range' min='0' max='90' value='45' step='1' onchange=Pince() />Fermee</center>");
client.println("</form>");
client.println("<center><input type=text id='reportp' size='3'/></center><BR>");
client.println("</div>");
client.println("</body>");
client.println("</html>");
}
// reset buffer index and all buffer elements to 0
req_index = 0;
StrClear(HTTP_req, REQ_BUF_SZ);
break;
}
// every line of text received from the client ends with \r\n
if (c == '\n') {
// last character on line of received text
// starting new line with next character read
currentLineIsBlank = true;
}
else if (c != '\r') {
// a text character was received from client
currentLineIsBlank = false;
}
} // end if (client.available())
} // end while (client.connected())
delay(1); // give the web browser time to receive the data
client.stop(); // close the connection
} // end if (client)
}
// extract text from the incoming HTTP GET data string
// returns true only if text was received
// the string must start with "&txt=" and end with "&end"
// if the string is too long for the HTTP_req buffer and
// "&end" is cut off, then the function returns false
boolean GetText(char *txt, int len)
{
boolean got_text = false; // text received flag
char *str_begin; // pointer to start of text
char *str_end; // pointer to end of text
int str_len = 0;
int txt_index = 0;
// get pointer to the beginning of the text
str_begin = strstr(HTTP_req, "&txt=");
if (str_begin != NULL) {
str_begin = strstr(str_begin, "="); // skip to the =
str_begin += 1; // skip over the =
str_end = strstr(str_begin, "&end");
if (str_end != NULL) {
str_end[0] = 0; // terminate the string
str_len = strlen(str_begin);
// copy the string to the txt buffer and replace %20 with space ' '
for (int i = 0; i < str_len; i++) {
if (str_begin[i] != '%') {
if (str_begin[i] == 0) {
// end of string
break;
}
else {
txt[txt_index++] = str_begin[i];
if (txt_index >= (len - 1)) {
// keep the output string within bounds
break;
}
}
}
else {
// replace %20 with a space
if ((str_begin[i + 1] == '2') && (str_begin[i + 2] == '0')) {
txt[txt_index++] = ' ';
i += 2;
if (txt_index >= (len - 1)) {
// keep the output string within bounds
break;
}
}
}
}
// terminate the string
txt[txt_index] = 0;
got_text = true;
}
}
return got_text;
}
// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
{
for (int i = 0; i < length; i++) {
str[i] = 0;
}
}
// searches for the string sfind in the string str
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
{
char found = 0;
char index = 0;
char len;
len = strlen(str);
if (strlen(sfind) > len) {
return 0;
}
while (index < len) {
if (str[index] == sfind[found]) {
found++;
if (strlen(sfind) == found) {
return 1;
}
}
else {
found = 0;
}
index++;
}
return 0;
}
void printWiFiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
// print where to go in a browser:
Serial.print("To see this page in action, open a browser to http://");
Serial.println(ip);
}
Slave Code (Arduino UNO)
Arduino
/*
WiFi Web Server Contrôle de moteur, affichage LCD et led (Slave)
created 18 Mars 2018
par Fabrice bonne
*/
#include <Servo.h>
#include <LiquidCrystal.h>
#include <Wire.h> // Librairie pour la communication I2C
LiquidCrystal lcd(7,6,5,4,3,2); //lcd en bornes 2 à 7
Servo MVertical;
Servo MHorizontal;
Servo MAngle;
Servo MPince;
void setup() {
Wire.begin(4); // Rejoindre le bus à l'adresse #4
Wire.onReceive(receiveEvent); // Preparer une fonction spécifique a la reception de donnee
Serial.begin(9600);
lcd.begin(16, 2); //démarrage du module LCD
lcd.clear();
lcd.setCursor(0,0);
MVertical.attach(10);
MHorizontal.attach(8);
MAngle.attach(9);
MPince.attach(11);
MVertical.write(45);
MHorizontal.write(45);
MAngle.write(90);
MPince.write(45);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("EN ATTENTE");
lcd.setCursor(0,1);
lcd.print("D'ENVOI");
}
void loop()
{
delay(500);
}
// Fonction qui s execute si quelque chose est present sur l interface
void receiveEvent(int howMany)
{
String data = "";
String aff1 = "";
String aff2 = "";
String recherche = "";
int dl;
bool Mot = false;
while (Wire.available()) { //lecture des ordres envoyés
data += (char)Wire.read();
}
Serial.println(data);
recherche = "Vertical="; //Recherche des ordres moteurs
if (data.indexOf(recherche)!=-1){
aff1 = "Vertical=";
aff2 = data.substring(9,12);
Serial.print("Nouvelle valeur V=");
Serial.println(aff2);
MVertical.write(aff2.toInt());
Mot = true ;
}
recherche = "Horizontal=";
if (data.indexOf(recherche)!=-1){
aff1 = "Horizontal=";
aff2 = data.substring(11,14);
Serial.print("Nouvelle valeur H=");
Serial.println(aff2);
MHorizontal.write(aff2.toInt());
Mot = true ;
}
recherche = "Angle=";
if (data.indexOf(recherche)!=-1){
aff1 = "Angle=";
aff2 = data.substring(6,9);
Serial.print("Nouvelle valeur A=");
Serial.println(aff2);
MAngle.write(aff2.toInt());
Mot = true ;
}
recherche = "Ouverture=";
if (data.indexOf(recherche)!=-1){
aff1 = "Ouverture=";
aff2 = data.substring(10,13);
Serial.print("Nouvelle valeur O=");
Serial.println(aff2);
MPince.write(aff2.toInt());
Mot = true ;
}
if (Mot == false) { //Si c'est un texte, découpage et affichage
dl = data.length();
Serial.print("longueur :");
Serial.println(dl);
if (dl > 16) {
aff1 = data.substring(0, 16);
aff2 = data.substring(16, dl);
Serial.print("découpage :");
Serial.println(aff1);
Serial.println(aff2);
}
else {
aff1 = data;
Serial.println("découpage non requis");
}
}
Serial.println();
lcd.clear(); //Affichage des infos moteur et texte en Relay
lcd.setCursor(0,0);
lcd.print(aff1);
lcd.setCursor(0,1);
lcd.print(aff2);
}
Source : Control MeArm Robot With MKR1000 And Your Smartphone