Preamble


  • These exercises allow to "recreate" a server that has functionalities similar to the server used for micro-controllers exercises.
  • In order to test the server without implementing the µC part, a client application is also needed.

 

1°/ Server part

 

1.1°/ Setup the project

  • Create a Java project with your favorite IDE (IDEA, vscode, ...)
  • Within the source directory, create a file named Measure.java and copy the following code within.
public class Measure {

    private double value;
    private int type;
    public static final int UNSET = 0;
    public static final int VOLTAGE = 1;
    public static final int DURATION = 2;

    public Measure(double value, int type) {
        this.value = value;
        if ((type != VOLTAGE) && (type != DURATION)) {
            type = UNSET;
        }
        this.type = type;
    }

    public Measure() {
        this.value = 0;
        this.type = UNSET;
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }
}
 
  • Within the source directory, create a file named ServerData.java and copy the following code within. 
  • Fulfill the three last methods.
import java.util.*;

class ServerData {

    // HashMap key is a client id
    HashMap<Integer, ArrayList<Measure> > measures;

	public ServerData() {
		measures = new HashMap<Integer, ArrayList<Measure> >();
	}

	public boolean exists(int id) {
		if (measures.containsKey(id)) {
			return true;
		}
		return false;
	}

	public void registerClient(int id) {
        // create a list for the new client
		ArrayList<Measure> mes = new ArrayList<>();
        // add that list to the map
		measures.put(id,mes);
	}

	public void addMeasure(int id, double value, int type) {
        // get the List for the given client id
		ArrayList<Measure> mes = measures.get(id);
        // add a new Measure to that list
		mes.add(new Measure(value, type));
	}

	public double averageValue(int id, int measureType) {
		/* TO FULFILL:
		  for a given client id, compute the average of all values stored in measures but only for the given type of measure
                  NB: if measureType is = 0 (UNSET), return 0.0
		*/
	}

	public double minMaxValue(int measureType, int minOrMax) {
		/* TO FULFILL:
		  compute the minimum/maximum value stored in measures but only for the given type of measure.
                  If minOrMax = 0, computes the minimum, otherwise the maximum
		*/
	}

	public double valueCloseTo(int id, double v, double epsilon, int measureType  ) {
		/* TO FULFILL:
		  for a given client id, find the first value stored in measures, for the given type, that is close to v, with a precision of epsilon (i.e. (v-found value) < epsilon )
		*/
	}
}

 

  • The above class is used by the server to store data transmitted by clients using some particular requests

1.2°/ Writing the server

  • Thanks to the template given in courses (see client/server basic canvas ), create files named  MainServer.java and ESP8266Server.java.
  • Here is the beginning of the "real" server part, MainServer.java:
import java.io.*;
import java.net.*;
import java.util.*;
 
class MainServer {
 
    ServerSocket sockConn; Socket sockComm;
    PrintStream ps; BufferedReader br;
    ServerData data;
    int idClient;
 
    MainServer(int port) throws IOException {
        sockConn = new ServerSocket(port);
        data = new ServerData();
        idClient = 0; // incremented at each connection
    }
    ...
}
 
  • WARNING: in the canvas presented in courses, the server sends an id to the client and then enters in the requestLoop() method. It is not sufficient in the present case because the server uses an additional object of type ServerData to store measures from the clients. It means that each client must be "registered" in this object so that storing measures operates correctly. This is done by calling the registerClient() method. After that call, the server can enter in requestLoop().

 

  • By default, the server proposes 3 services, relying on quite simple protocols :
    • #1: check the state of a button :
      • client sends a text line with the following format: "BUTTON client_id button_state",
      • if button_state equals to 0 or 1, sever sends back the line "OK"
      • otherwise, server sends back the line "ERR invalid button state"
    • #2: compute a voltage according to a level read on an analog pin of an esp8266 :
      • client sends a text line with the following format: "PIEZO client_id analog_level",
      • if analog_level is >=0 and <1024, server sends back the line "OK"
      • otherwise, server sends back the line "ERR invalid analog level"
      • if OK has been sent, the server converts the level into a voltage (i.e. level*3.3/1024), stores it in data (with type = 1), and sends it back to the client.
    • #3: compute a duration in seconds, according to 2 time values in milliseconds:
      • client sends a text line with the following format: "PRESS client_id time1 time2",
      • if time1 or time2 >=0, or time1 > time2, server sends back the line "OK"
      • otherwise, server sends back the line "ERR invalid times"
      • if OK has been sent, the server get the gap between the two times, divides it by 1000, stores it in data (with type = 2), and sends the result back to the client.

 

2°/ Client part
 
 

Create files named MainClient.java and ESP8266Client.java according to the template of the courses

  • Client's principles: 
    • requests are typed on the keyboard with a relevant format, like:
      • request 1: button state
      • request 2 : piezo level
      • request 3 : press t1 t2
    • NB: note that user has not to type the client id
    • These lines are then translated into real requests to the server.

Remark : the client application may implement a variable number of checks on what is typed by the user. For example, it could check if request parameters have valid values. But in practice, no checks are done by the client, assuming that it is in charge of the server to test everything, and if something is not valid, to send back an error.

 

3°/ Add-ons

 
  • Modify both client and server to add the following services:
    • # 4 : ask the server for the average of all stored values, for a given client id and a given type of measure. Note that the client may ask this average for an id different of its own.
    • # 5 : ask the server for the minimum or maximum stored value, for a given type of measure
    • # 6 : ask the server for the first stored value that is close to another one, for a given client id, a given type of measure and a given precision.
  • For each service, you are free to choose the format of the requests and further communications implied by processing it.

 

4°/ Testing with a µC

  • If you execute your server on a laptop, it is quite easy to test your server with a real µC client.
  • For that, just connect to the Wifi AP (same credentials as for the µC: ssid & passowrd = testingiot)
  • Note the IP address you receive from the AP (something like 192.168.0.X, with X >= 100)
  • Modify your sketch so that the µC connects to that IP, instead of 192.168.0.2.