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.

What will you need

My setup

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.

Share on: