Serial communication, automatically detect MCU from Java application. Part 2.
Let's say that we want to create a standalone device that can be connected to computer. There are many applications where this might be needed.

Cover image by SUEZ.
- Part 1 - Set communication rules and define address component.
- Part 2 - Define communication packet component
- Part 3 - Virtual Multi-threading technique.
- Part 4 - Establish simple communication with MCU.
- Part 5 - Java setup.
- Part 6 - Java side Communication.
- Part 7 - Create handshake protocol.
- Part 8 - Swing GUI.
- Part 9 - Testing and creating executable.
What will you need
- MCU with at least 1 UART - I will use an STM32F103VET6 but firmware will be provided for the Arduino dev boards too.
- Basic knowledge of the Arduino Framework.
- Computer with JRE 10+ installed.
- Some IDE for Java development like Eclipse or IntelliJ
- Some environment with Arduino Framework - Like PlatformIO.
My setup
- For programming MCU I am using PlatformIO integration with VSCode that I described in this post.
- For Java programming I’m using Eclipde with openJDK 11 that I described in this post.
- As a MCU I’m using custom dev board for STM32F103VE with STLink-v2 and custom FTDI FT232RQ board.
- As an operating system I’m using Ubuntu 18.
Describing communication packet component
If you can recall from the last part, we have set some rules on how communication packet should be formated and we created Address component.
A B C D E F G
| | | | | | |_ Stop byte
| | | | | |___ n bytes of message
| | | | |_____ Split byte
| | | |_______ Data type byte
| | |_________ 4 byte TO address
| |___________ 4 byte FROM address
|_____________ Start byte
Now we can create an object that will give us some methods and functions to manipulate and work with communication packets. In this way, we will abstract packet handling to the end user. Let’s start by creating Communication Packet class.
CONSTRUCTOR | DESCRIPTION
------------------------------------------------- | -----------
CommunicationPacket() | Create instance without initialization of parameters.
CommunicationPacket(to, data_type, message) | Create instance without initialization of `from` address.
CommunicationPacket(to, from, data_type, message) | Create instance and initialize parameters.
METHOD | DESCRIPTION
----------------------- | -----------
build(packet_as_string) | Convert packet as a string to an object.
clear() | Clear all parameters of the object.
toString() | Convert object to packet as a string.
setFrom(address) | Set packet `from` address.
getFrom() | Return packet `from` address.
setTo(address) | Set packet `to` address.
getTo() | Return packet `to` address.
setDateType(data_type) | Set value of packets data type.
getDataType() | Return value of packets data type.
setMessage(message) | Set packet message.
getMessage() | Return packet message.
isCreated() | Return if packet is created.
Since there are some constant values that we will use in the code, it is always a good idea to define them in a separate file. This is what we will do.
// -------------------------------
// ---- CommunicationConsts.h ----
// -------------------------------
#ifndef CommunicationConsts_h
#define CommunicationConsts_h
#define ADDRESS_BYTE_1 0
#define ADDRESS_BYTE_2 1
#define ADDRESS_BYTE_3 2
#define ADDRESS_BYTE_4 3
#define COMMUNICATION_START_BYTE 0x02
#define COMMUNICATION_END_BYTE 0x17
#define COMMUNICATION_SPLIT_BYTE 0x1D
#define COMMUNICATION_PACKET_DATA_TYPE_STRING 20
#define COMMUNICATION_PACKET_FROM_FIRST_BYTE_POS 0
#define COMMUNICATION_PACKET_TO_FIRST_BYTE_POS 4
#define COMMUNICATION_PACKET_DT_FIRST_BYTE_POS 8
#define COMMUNICATION_PACKET_MESSAGE_FIRST_BYTE_POS 10
#endif
After this we can write Communication Packet class.
// -------------------------------
// ---- CommunicationPacket.h ----
// -------------------------------
#ifndef CommunicationPacket_h
#define CommunicationPacket_h
#include "Address.h"
class CommunicationPacket {
public:
CommunicationPacket();
CommunicationPacket(Address to,
unsigned char data_type, String message);
CommunicationPacket(Address from, Address to,
unsigned char data_type, String message);
// Set object properties from communication
// packet in string form.
bool build(String packetAsString);
// Clear all properties of the object
void clear();
// Convert object properties to string
String toString();
void setFrom(Address address);
Address getFrom();
void setTo(Address address);
Address getTo();
void setDataType(unsigned char data_type);
unsigned char getDataType();
void setMessage(String message);
String getMessage();
bool isCreated();
private:
Address f; // FROM
Address t // TO
unsigned char dt; // DATA_TYPE
String m; // MESSAGE
bool created;
};
#endif
// ---------------------------------
// ---- CommunicationPacket.cpp ----
// ---------------------------------
#include "CommunicationPacket.h"
#include "CommunicationConsts.h"
CommunicationPacket::CommunicationPacket() { created = false; }
CommunicationPacket::CommunicationPacket(Address to,
unsigned char data_type, String message) {
t = to;
dt = data_type;
m = message;
created = true;
}
CommunicationPacket::CommunicationPacket(Address from, Address to,
unsigned char data_type, String message) {
f = from;
t = to;
dt = data_type;
m = message;
created = true;
}
bool CommunicationPacket::build(String packetAsString) {
if(packetAsString.charAt(COMMUNICATION_PACKET_MESSAGE_FIRST_BYTE_POS - 1) ==
char(COMMUNICATION_SPLIT_BYTE)) {
String s = packetAsString.substring(COMMUNICATION_PACKET_FROM_FIRST_BYTE_POS,
COMMUNICATION_PACKET_FROM_FIRST_BYTE_POS + 4);
f.set(s.charAt(0), s.charAt(1), s.charAt(2), s.charAt(3));
s = packetAsString.substring(COMMUNICATION_PACKET_TO_FIRST_BYTE_POS,
COMMUNICATION_PACKET_TO_FIRST_BYTE_POS + 4);
t.set(s.charAt(0), s.charAt(1), s.charAt(2), s.charAt(3));
dt = packetAsString.charAt(COMMUNICATION_PACKET_DT_FIRST_BYTE_POS);
m = packetAsString.substring(COMMUNICATION_PACKET_MESSAGE_FIRST_BYTE_POS);
created = true;
return true;
}
return false;
}
String CommunicationPacket::toString() {
String s("");
s += char (COMMUNICATION_START_BYTE);
unsigned char bytes[4];
f.put(bytes);
s += char (bytes[0]);
s += char (bytes[1]);
s += char (bytes[2]);
s += char (bytes[3]);
t.put(bytes);
s += char (bytes[0]);
s += char (bytes[1]);
s += char (bytes[2]);
s += char (bytes[3]);
s += char (dt);
s += char (COMMUNICATION_SPLIT_BYTE);
s += m;
s += char (COMMUNICATION_END_BYTE);
return s;
}
Address CommunicationPacket::getFrom() { return f; }
void CommunicationPacket::setFrom(Address address) { f = address; }
Address CommunicationPacket::getTo() { return t; }
void CommunicationPacket::setTo(Address address) { t = address; }
unsigned char CommunicationPacket::getDataType() { return dt; }
void CommunicationPacket::setDataType(unsigned char data_type) { dt = data_type; }
String CommunicationPacket::getMessage() { return m; }
void CommunicationPacket::setMessage(String message) { m = message; }
void CommunicationPacket::clear() {
f.set(0);
t.set(0);
dt = 0;
m = "";
created = false;
}
Here we have 2 methods that we can talk about (others are self explained), method build()
and method toString()
. Since we are using UART to transmit packets, all packets will be sent and received as an array of bytes where each byte correspond to value from ASCII table (UART it not limited only to this character set but we will use only characters from this set). Therefore, when we receive packet, we want easy way to convert it from a byte array to communication packet object, this way we will have easy access to the packet content. For this we use build(packetAsString)
method. In other case, toString()
method will convert the communication packet object to a char/byte array with format that we described in last post.
How to use CommunicationPacket object
It might look complex but it is actually very simple, therefore it is best to explain use case with a simple example.
#include "Arduino.h"
#include "CommunicationPacket.h"
#include "CommunicationConsts.h"
void setup() {}
void loop() {
Serial.begin(115200);
CommunicationPacket packet;
packet.setFrom(Address(192,168,0,2));
packet.setTo(Address(192,168,0,1));
packet.setDataType(COMMUNICATION_PACKET_DATA_TYPE_STRING);
packet.setMessage("How are you?");
Serial.println(packet.toString());
Serial.println("---- END ----");
while(1) {
}
}
You can view the output of this example with some serial monitor application that can display HEX values. Now that we described 2 components that we will use in communication, we can move on to defining communication protocol that will be used, which is the main task for the next part.