Platform Protocol

Protocol

The Platform controller uses an ASCII protocol for communication with the host. Using this form of protocol allows better cross communication with varying host platforms.

The protocol consists of sending text packets in the form:

<CMD ARG1 ARG2 ARG3 ... ARGx>

For example the ping instruction looks like:

<P>

Creating the Packets

Creating these packets is pretty trival in a high level language like Java or C++.

In my implementations I used a Builder design pattern to hide the underlying protocol details and also prevent arbitrary data from being sent to the platform.

Below is the packet building code I used in the Platform Controller App:

First I created the Packet class:

/**
	Packet Data Wrapper
*/
public class Packet
{
	private String contents = "";

	/**
		Note the private constructor
	*/
	private Packet(String contents)
	{
		this.contents = contents;
	}

	/**
		Accessor for the packet data
	*/
	public String getContents()
	{
		return this.contents;
	}
}

Pretty simple. The actual data is stored in a string and is accessed with a getter.

So why the private constructor? This is used so that the packet cannot be directly instantiated preventing random data from being used in the packet.

So how is the packet created? I used a Packet Builder class, which is a nested class in the packet (therefore has access to its private constructor) to build the packets.

public class Packet
{
	...

	/**
		Constructs Packets
	*/
	public static class Builder
	{
		// the command of the packet
		private String command;
		// a list of packet arguments
		private ArrayList<String> arguments;

		public Builder()
		{
			this.arguments = new ArrayList<String>();
		}

		/**
			Set the packet command

			return this builder for chaining
		*/
		public Builder setCommand(String cmd)
		{
			this.command = cmd;
			return this;
		}

		/**
			Add a integer argument

			return this builder for chaining
		*/
		public Builder addArgument(int arg)
		{
			this.argument.add(String.valueOf(arg));
			return this;
		}
	}
}

Ok, so now we have a builder class. Notice that setCommand() and addArgument() return a reference to this object for chaining. This is just for convience.

For example:

Packet.Builder builder = new Packet.Builder();

builder.setCommand("S").addArgument(90);

But we have a problem. The point of the builder is to prevent random data from being added to the packet. The String argument to setCommand() can be anything so it’s not safe!

What to do? I used an enum to solve this problem.

public class Packet
{
	...

	public enum Command
	{
		PING("P"),
		ECHO("E"),
		SERVO("S"),
		STEPPER("ST"),
		MTR_SPEED("MS"),
		MTR_DIR("MD"),
		SYNC("Z");

		private String value;

		Command(String v)
		{
		    value = v;
		}

		public String getValue() {
		    return value;
		}
	}

	public static class Builder
	{
		// the command of the packet
		private String command;
		// a list of packet arguments
		private ArrayList<String> arguments;

		public Builder()
		{
			this.arguments = new ArrayList<String>();
		}

		/**
			Set the packet command

			return this builder for chaining
		*/
		public Builder setCommand(Command cmd)
		{
			this.command = cmd.getValue();
			return this;
		}

		/**
			Add a integer argument

			return this builder for chaining
		*/
		public Builder addArgument(int arg)
		{
			this.argument.add(String.valueOf(arg));
			return this;
		}
	}
}

This way only the valid commands can be used!

Packet.Builder builder = new Packet.Builder();

builder.setCommand(Packet.Command.SERVO).addArgument(90);

Finally the method for building the packets:

public class Packet
{
	...

	public static class Builder
	{
		...

		public Packet build()
		{
			StringBuilder builder = new StringBuilder();

			builder
				.append("<")
				.append(this.command);

			for(final String s : this.arguments)
			{
				builder
					.append(" ")
					.append(s);
			}

			builder.append(">");

			return new Packet(builder.toString());
		}
	}
}

And to use it:

Packet.Builder builder = new Packet.Builder();

builder.setCommand(Packet.Command.SERVO).addArgument(90);

Packet packet = builder.build();