Kees Pieters
Published © Apache-2.0

DAY 1: Connecting to the Aquabots Client

Day 1 of the Aquabots Autnomous Vessels Project: Connecting to the Aquabots Client.

IntermediateWork in progress1,273
DAY 1: Connecting to the Aquabots Client

Things used in this project

Hardware components

Arduino Ethernet Shield 2
Arduino Ethernet Shield 2
Include an RJ-45 Ethernet Cable to plug the Ethernet Shield in to a switch or router
×1
Arduino Mega 2560
Arduino Mega 2560
×1
Seeed Studio Grove Shield for Mega
×1

Software apps and online services

Aquabots Client

Story

Read more

Code

Vessels

C/C++
The main programme for the Autonomous Vessels Project
#include <ArduinoJson.h>

#include "WebClient.h"
#include "Registration.h"

WebClient webClient;
Registration registration;

void setup() {
    Serial.begin(9600);
    Serial.println("Initialised");
    webClient.setup();
    registration.setup();
}

void loop() {
  long id = registration.registerVessel( "AquaBoat", "AquaPassphrase", 51.0, 4.2 );
  Serial.print(F("Vessel id: ")); Serial.println( id );
  delay(2000);
}

WebClient.c

C/C++
The implementation of the Web Client
WebClient::WebClient() {}

void WebClient::setup( ) {
  Serial.println(F("SETUP WEB CLIENT..."));
  id = "AquaBoat";
  token = "AquaPassphrase";
  host = "http://www.condast.com";
  port = 10081;
  context = "/arnac/registration/";
  String server_addr = "192.168.8.102"; //Aquabots Client IP Address
  server.fromString( server_addr);
  Serial.print(F("SERVER ADDRESS:")); server.printTo( Serial );
  Serial.println();
  String ip_addr = "192.168.8.102";
  ip.fromString( ip_addr);
  Serial.print(F("IP ADDRESS:")); server.printTo( Serial );

  String mc = "{0x90,0xA2,0xDA,0x11,0x12,0x3A}";
  Serial.print(F("CONNECTING TO ")); Serial.print( host ); Serial.print(F(":")); Serial.print( port ); Serial.println( context );

  // start the Ethernet connection:
  Serial.println(F("SETUP WEB CLIENT "));
  if (Ethernet.begin(mac) == 0) {
    Serial.println(F("Failed to configure Ethernet using DHCP"));
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip);
  }
  connected = false;

  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println( F("done"));
}

bool WebClient::connect() {
  //Serial.print(F("Connecting to: ")); Serial.print( server ); Serial.print(F(":")); Serial.println( port );
  client.setTimeout(3000);
  bool result = client.connect(server, port);
  //Serial.print(F("Connected: ")); Serial.println( result );
  if ( result) {
    connected = result;
    return result;
  }
  //if ( connected )
  //  Serial.print(F("Connection failed: ")); Serial.println( result );
  client.stop();
}

void WebClient::disconnect() {
  client.stop();
  //if ( connected )
  //  Serial.println(F("Disconnecting: Complete "));
  connected = false;
}

void WebClient::requestService( int request ) {
  switch ( request ) {
     case REGISTER_VESSEL:
      client.print(F("register"));
      break;
     case VESSEL_CONFIG:
      client.print(F("config"));
      break;
    case DEBUG:
      client.print(F("debug"));
      break;
    case FIELD:
      client.print(F("field"));
      break;
    case DATA:
      client.print(F("data"));
      break;
    case NMEA:
      client.print(F("nmea"));
      break;
    case OPTIONS:
      client.print(F("options"));
      break;
    case LOG:
      client.print(F("log"));
      break;
    case WAYPOINTS:
      client.print(F("waypoints"));
      break;
    case WAYPOINT:
      client.print(F("waypoint"));
      break;
    case UPDATE:
      client.print(F("update"));
      break;
    default:
      client.print(F("unknown"));
      break;
  }
}

/**
   Is used to transform the int to a String
*/
void WebClient::logRequestStr( int request ) {
  switch ( request ) {
     case REGISTER_VESSEL:
      Serial.print(F("register"));
      break;
     case VESSEL_CONFIG:
      Serial.print(F("config"));
      break;
    case DEBUG:
      Serial.print(F("debug"));
      break;
    case FIELD:
      Serial.print(F("field"));
      break;
    case DATA:
      Serial.print(F("data"));
      break;
    case NMEA:
      Serial.print(F("nmea"));
      break;
    case LOG:
      Serial.print(F("log"));
      break;
    case WAYPOINTS:
      Serial.print(F("waypoints"));
      break;
    case WAYPOINT:
      Serial.print(F("waypoint"));
      break;
    case UPDATE:
      Serial.print(F("update"));
      break;
    case OPTIONS:
      Serial.print(F("options"));
      break;
    default:
      Serial.print(F("unknown (")); Serial.print( request ); Serial.print(F(")"));
      break;
  }
}

boolean WebClient::sendHttp( int request, String message ) {
  String msg = message;
  if ( msg.length() > 0 ) {
    msg = F("&msg=");
    msg += message;
  }
  return sendHttp( request, false, msg );
}

boolean WebClient::sendHttp( int request, boolean post, String attrs ) {
  if ( client.connected()) {
    if( request != NMEA )
      Serial.print(F("REQUEST ")); logRequestStr( request ); Serial.print(F(": ")); Serial.println(attrs );
    //logRequest( request, post, attrs );

    // Make a HTTP request:
    client.print( post ? F("POST ") : F("GET ") );
    client.print( context );
    Serial.print( context );//Serial.print( register);
    requestService( request );
    client.print(F("?id=" ));
    client.print( id );
    client.print(F("&token="));
    client.print( token );
    if ( !post && ( attrs.length() > 0 )) {
      client.print( attrs );
    }
    client.println(F(" HTTP/1.1" ));

    client.print(F("Host: "));
    client.println( host );
    client.println(F("Connection: close\r\n"));
    if ( post && ( attrs.length() > 0 )) {
      client.println( F("Accept: */*"));
      client.println( F("Content-Type: application/x-www-form-urlencoded ; charset=UTF-8" ));
      client.print( F("Content-Length: "));
      client.println( attrs.length() );
      client.println();
      client.println( urlencode( attrs ));
    }
    client.println();
    return processResponse( request );
  }
  return false;
}

/**
   Handle the response, by taking away header info and such
*/
bool WebClient::processResponse( int request ) {
  // Check HTTP status
  char status[32] = {"\0"};
  client.setTimeout(HTTP_TIMEOUT);
  client.readBytesUntil('\r', status, sizeof(status));
  char http_ok[32] = {"\0"};
  strcpy( http_ok, "HTTP/1.1 200 OK");
  if (strcmp(status, http_ok) != 0) {
    Serial.print(F( "Unexpected response (" )); logRequestStr( request); Serial.print(F( "):" ));
    Serial.println(status);
    return false;
  }

  // Skip HTTP headers
  char endOfHeaders[] = "\r\n\r\n";
  if (!client.find(endOfHeaders)) {
    Serial.println( F( "Invalid response (" )); logRequestStr( request); Serial.print(F( "):" ));
    return false;
  }
  return true;
}

void WebClient::logRequest( int request, boolean post, String attrs ) {
  // Make a HTTP request:
  Serial.print( post ? F("POST ") : F("GET "));
  Serial.print( context );
  logRequestStr( request );
  Serial.print(F( "?id=" ));
  Serial.print( id );
  Serial.print(F( "&token=" ));
  Serial.print( token );
  if ( !post && ( attrs.length() > 0 )) {
    Serial.print( attrs );
  }
  Serial.print(F( " HTTP/1.1" ));
  Serial.println();
  Serial.print(F( "Host: "));
  Serial.println( host );
  Serial.println(F( "Connection: close" ));
  if ( post && ( attrs.length() > 0 )) {
    Serial.println(F( "Accept: */*" ));
    Serial.println(F( "Content-Type: application/x-www-form-urlencoded ; charset=UTF-8"));
    Serial.print(F( "Content-Length: "));
    Serial.println( attrs.length() );
    Serial.println();
    Serial.println( attrs );
  }
}

/**
   Creates a String request from the client
*/
String WebClient::printResponse( int request ) {
  Serial.print( F("RESPONSE TO "));
  logRequestStr( request );
  // Serial.print(" PROCESSING: ");
  //Serial.print( client.available() );
  String retval = "";

  //  Serial.println();
  while (client.available()) {
    char c = client.read();
    retval += c;
  }
  Serial.print( F( ": ")); Serial.println( retval );
}

void WebClient::loop() {
  // if there are incoming bytes available
  // from the server, read them and print them:
  if (!client.connected()) {
    connect();
  }
}

/*
  ESP8266 Hello World urlencode by Steve Nelson
  URLEncoding is used all the time with internet urls. This is how urls handle funny characters
  in a URL. For example a space is: %20
  These functions simplify the process of encoding and decoding the urlencoded format.

  It has been tested on an esp12e (NodeMCU development board)
  This example code is in the public domain, use it however you want.
  Prerequisite Examples:
  https://github.com/zenmanenergy/ESP8266-Arduino-Examples/tree/master/helloworld_serial
*/
String WebClient::urldecode(String str) {
  String encodedString = "";
  char c;
  char code0;
  char code1;
  for (int i = 0; i < str.length(); i++) {
    c = str.charAt(i);
    if (c == '+') {
      encodedString += ' ';
    } else if (c == '%') {
      i++;
      code0 = str.charAt(i);
      i++;
      code1 = str.charAt(i);
      c = (h2int(code0) << 4) | h2int(code1);
      encodedString += c;
    } else {

      encodedString += c;
    }
    yield();
  }

  return encodedString;
}

String WebClient::urlencode(String str)
{
  String encodedString = "";
  char c;
  char code0;
  char code1;
  char code2;
  for (int i = 0; i < str.length(); i++) {
    c = str.charAt(i);
    if (c == ' ') {
      encodedString += '+';
    } else if (isalnum(c)) {
      encodedString += c;
    } else {
      code1 = (c & 0xf) + '0';
      if ((c & 0xf) > 9) {
        code1 = (c & 0xf) - 10 + 'A';
      }
      c = (c >> 4) & 0xf;
      code0 = c + '0';
      if (c > 9) {
        code0 = c - 10 + 'A';
      }
      code2 = '\0';
      encodedString += '%';
      encodedString += code0;
      encodedString += code1;
      //encodedString+=code2;
    }
    yield();
  }
  return encodedString;

}

unsigned char WebClient::h2int(char c)
{
  if (c >= '0' && c <= '9') {
    return ((unsigned char)c - '0');
  }
  if (c >= 'a' && c <= 'f') {
    return ((unsigned char)c - 'a' + 10);
  }
  if (c >= 'A' && c <= 'F') {
    return ((unsigned char)c - 'A' + 10);
  }
  return (0);
}

Registration.h

C/C++
This module manages the registration process of a vessel with the Aquabots Client
#ifndef Registration_h
#define Registration_h

#define REGISTRATION "Registration"
#define REGISTRATION_ID "registration"

class Registration {

  private:
    bool enabled; //general purpose flag 
    long vesselId; 

  public: Registration(void);
    void setup();
    long registerVessel( String name, String passphrase, double latitude, double longitude);
    bool getConfig();
};

#endif

Registration.c

C/C++
The Implementation of the Registration Module
Registration::Registration() {};

void Registration::setup( ) {
  enabled = true;
}

long Registration::registerVessel( String name, String passphrase, double latitude, double longitude ) {
  if ( !enabled )
    return -1;
  if ( ! webClient.connect() )
    return -2;

  String url = F("&name=");
  url += String( name );
  url += F("&passphrase=");
  url += String( passphrase);
  url += F("&latitude=");
  url += String( latitude);
  url += F("&longitude=");
  url += String( longitude);

  boolean result = webClient.sendHttp( WebClient::REGISTER_VESSEL, false, url);
  if (!result ) {
    webClient.disconnect();
    return -3;
  }
  String retval = "";
  while (webClient.client.available()) {
    char c = webClient.client.read();
    retval += c;
  }
  Serial.println( retval);
  vesselId = atol( retval.c_str() );
  webClient.disconnect();
  return vesselId;
}

bool Registration::getConfig() {
}

WebClient.h

C/C++
Header files for Communicating with the Aquabots Client
#ifndef WebClient_h
#define WebClient_h

#include <SPI.h>
#include <Ethernet.h>

const unsigned long HTTP_TIMEOUT = 5000;// max respone time from server

/*
  Web client

  This sketch connects to a website
  using an Arduino Wiznet Ethernet shield.

  Circuit:
   Ethernet shield attached to pins 10, 11, 12, 13

  created 18 Dec 2009
  by David A. Mellis
  modified 9 Apr 2012
  by Tom Igoe, based on work by Adrian McEwen

*/

class WebClient {

  public: WebClient();
    enum request {
      UNKNOWN = 0,
      REGISTER_VESSEL = 1,
      VESSEL_CONFIG = 2,
      DEBUG = 3,
      FIELD = 4,
      UPDATE = 5,
      WAYPOINTS = 6,
      DATA = 7,
      NMEA = 8,
      OPTIONS = 9,
      LOG = 10,
      WAYPOINT = 11
     };
    
    EthernetClient client;
    void setup();
    bool connect();
    void disconnect();
    bool requestLog();
    bool logMessage( String message );
    bool getWaypoint();
    bool sendUpdate( String url );
    bool sendHttp( int request, String msg );
    bool sendHttp( int request, boolean post, String attrs );
    String urlencode(String str);
    String printResponse( int request );
    void logRequest( int request, boolean post, String attrs );//for debugging
    void logRequestStr( int request ); //dito
    void loop();

  private:
    IPAddress server;
    IPAddress ip;
    String host;
    int port;
    bool connected;
    String context;
    String id;
    int token;

    // Enter a MAC address for your controller below.
    // Newer Ethernet shields have a MAC address printed on a sticker on the shield
    byte mac[6] = {0};

    // Initialize the Ethernet client library
    // with the IP address and port of the server
    // that you want to connect to (port 80 is default for HTTP):
    void requestService( int request );
    bool processResponse( int request );
    boolean update( JsonObject& root );
    String urldecode(String str);
    unsigned char h2int(char c);
};

#endif

Credits

Kees Pieters
6 projects • 12 followers

Comments