This is the second part of the STM32 series. For your convenience you can find other parts in the table of contents in Part 1 – DES implementation

Today we are going to write very simple Java application to be able to see the traffic and communicate with our STM32. Let’s begin.

Implementation

We assume that our computer has an address 192.168.1.1 and that the STM32 uses 192.168.1.125. For encryption we use port 50000, for decryption 50001. Below are all required files:

package chat;

import gui.MainWindow;
import logic.Communicator;
import logic.Converter;
import logic.Encryption;
import logic.MessageReceivedEventArgs;
import logic.MessageReceivedListener;

public class Chat {

    private final int PORT_FOR_ENCRYPTION = 50000;
    private final int PORT_FOR_DECRYPTION = 50001;
    private final MainWindow window;
    private final Communicator communicator;
    private final Encryption mode;

    public Chat(String address, Encryption encryption) {
        this.mode = encryption;
        window = new MainWindow(this);
        window.setVisible(true);
        switch (encryption) {
            case ENCRYPT:
                communicator = new Communicator(address, PORT_FOR_ENCRYPTION, PORT_FOR_ENCRYPTION);
                break;
            case DECRYPT:
                communicator = new Communicator(address, PORT_FOR_DECRYPTION, PORT_FOR_DECRYPTION);
                break;
            default:
                throw new AssertionError(encryption.name());
        }

        communicator.addMessageReceivedListener(new MessageReceivedListener() {
            @Override
            public void messageReceived(MessageReceivedEventArgs e) {
                receiveMessage(e.getMessageText());
            }
        });
    }

    public static void main(String[] args) {
        if (args.length > 1 && args[1].equals("-d")) {
            new Chat(args[0], Encryption.DECRYPT);
        } else {
            new Chat(args[0], Encryption.ENCRYPT);
        }
    }

    public void sendMessage(String text) {
    	byte[] buffer = null;
        if (mode == Encryption.DECRYPT) {
            buffer = Converter.convertFromHexToBytes(text);
            for(int i=0; i < buffer.length; ++i)
        		System.out.print(String.format("%x", buffer[i]));
        }
        else {
        	buffer = text.getBytes();
        }
        System.out.println(text);
        communicator.sendMessage(buffer);
    }

    private void receiveMessage(byte[] text) {
    	for(int i=0; i < text.length; ++i)
    		System.out.print(String.format("%x", text[i]));
    	System.out.println("");
    	String message = new String(text);
        if (mode == Encryption.DECRYPT) {
        	message = Converter.convertFromTextToHex(text);
        }
        window.showNewMessage("Friend: " + message);
    }
}
package gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import chat.Chat;

public class MainWindow extends javax.swing.JFrame {

    private Chat chat;

    public MainWindow(Chat chat) {
        initComponents();
        this.chat = chat;
        
        inputField.addActionListener(new ActionListener(){
        	@Override
        	public void actionPerformed(ActionEvent e){
        		sendButtonActionPerformed(e);
        	}
        });
    }

    @SuppressWarnings("unchecked")
    // < editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        chatField = new javax.swing.JTextArea();
        sendButton = new javax.swing.JButton();
        inputField = new javax.swing.JTextField();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        chatField.setEditable(false);
        chatField.setColumns(20);
        chatField.setRows(5);
        jScrollPane1.setViewportView(chatField);

        sendButton.setText("SEND");
        sendButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sendButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(inputField)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(sendButton))
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 271, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(sendButton)
                    .addComponent(inputField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
        );

        pack();
    }// < /editor-fold>//GEN-END:initComponents

    private void sendButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendButtonActionPerformed
        chat.sendMessage(inputField.getText());
        showNewMessage("You: " + inputField.getText());
        inputField.setText("");
    }//GEN-LAST:event_sendButtonActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextArea chatField;
    private javax.swing.JTextField inputField;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton sendButton;
    // End of variables declaration//GEN-END:variables

    public void showNewMessage(String text) {
        chatField.append(text + "\n");
    }
} 

MainWindow.form designer:

< ?xml version="1.0" encoding="UTF-8" ?>

< Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
  < Properties>
    < Property name="defaultCloseOperation" type="int" value="3"/>
  < /Properties>
  < SyntheticProperties>
    < SyntheticProperty name="formSizePolicy" type="int" value="1"/>
  < /SyntheticProperties>
  < AuxValues>
    < AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
    < AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
    < AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
    < AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
    < AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
  < /AuxValues>

  < Layout>
    < DimensionLayout dim="0">
      < Group type="103" groupAlignment="0" attributes="0">
          < Group type="102" alignment="1" attributes="0">
              < Component id="inputField" max="32767" attributes="0"/>
              < EmptySpace max="-2" attributes="0"/>
              < Component id="sendButton" min="-2" max="-2" attributes="0"/>
          < /Group>
          < Component id="jScrollPane1" alignment="0" pref="400" max="32767" attributes="0"/>
      < /Group>
    < /DimensionLayout>
    < DimensionLayout dim="1">
      < Group type="103" groupAlignment="0" attributes="0">
          < Group type="102" alignment="0" attributes="0">
              < Component id="jScrollPane1" pref="271" max="32767" attributes="0"/>
              < EmptySpace max="-2" attributes="0"/>
              < Group type="103" groupAlignment="3" attributes="0">
                  < Component id="sendButton" alignment="3" min="-2" max="-2" attributes="0"/>
                  < Component id="inputField" alignment="3" min="-2" max="-2" attributes="0"/>
              < /Group>
          < /Group>
      < /Group>
    < /DimensionLayout>
  < /Layout>
  < SubComponents>
    < Container class="javax.swing.JScrollPane" name="jScrollPane1">
      < AuxValues>
        < AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
      < /AuxValues>

      < Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
      < SubComponents>
        < Component class="javax.swing.JTextArea" name="chatField">
          < Properties>
            < Property name="editable" type="boolean" value="false"/>
            < Property name="columns" type="int" value="20"/>
            < Property name="rows" type="int" value="5"/>
          < /Properties>
        < /Component>
      < /SubComponents>
    < /Container>
    < Component class="javax.swing.JButton" name="sendButton">
      < Properties>
        < Property name="text" type="java.lang.String" value="SEND"/>
      < /Properties>
      < Events>
        < EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sendButtonActionPerformed"/>
      < /Events>
    < /Component>
    < Component class="javax.swing.JTextField" name="inputField">
    < /Component>
  < /SubComponents>
< /Form>
package logic;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Communicator {

    private int sendPort;
    private String address;
    private DatagramSocket socket;

    public Communicator(String address, int sendPort, int receivePort) {
        try {
            messageReceivedListeners = new ArrayList< MessageReceivedListener>();
            this.address = address;
            this.sendPort = sendPort;
            socket = new DatagramSocket();
            Thread receiver = new Receiver(receivePort, this);
            receiver.start();
        } catch (SocketException ex) {
            Logger.getLogger(Communicator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    private ArrayList< MessageReceivedListener> messageReceivedListeners;

    public void sendMessage(byte[] buffer) {
    	try {
            buffer = prepareMsgToSend(buffer);
            
            InetAddress inetAddress = InetAddress.getByName(address);
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, inetAddress, sendPort);
            socket.send(packet);
        } catch (IOException ex) {
            Logger.getLogger(Communicator.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("ERROR");
        }
    }

	private byte[] prepareMsgToSend(byte[] buffer) {
		int length = buffer.length + (8 - buffer.length % 8) % 8;
		
		byte[] reversedBuffer = new byte[length];
		
		for(int i=0;i < buffer.length;++i){
			reversedBuffer[length-i-1] = buffer[i];
		}
		
		return reversedBuffer;
	}

    public void addMessageReceivedListener(MessageReceivedListener listener) {
        messageReceivedListeners.add(listener);
    }

    public void removeMessageReceivedListener(MessageReceivedListener listener) {
        messageReceivedListeners.remove(listener);
    }

    public void onMessageReceived(byte[] message) {
        MessageReceivedEventArgs args = new MessageReceivedEventArgs(message);

        for (MessageReceivedListener listener : messageReceivedListeners) {
            listener.messageReceived(args);
        }
    }
}
package logic;

public class Converter {

    public static String convertFromTextToHex(byte[] text) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < text.length; ++i) {
            builder.append(String.format("%02x", text[i]));
        }

        return builder.toString().toUpperCase();
    }

    public static String convertFromHexToText(String text) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < text.length() - 1; i += 2) {
            builder.append((char) Integer.parseInt(text.substring(i, i + 2), 16));
        }
    	
    	

        return builder.toString();
    }
    
    public static byte[] convertFromHexToBytes(String text) {
    	byte[] result = new byte[text.length()/2];
    	
        for (int i = 0; i < text.length() - 1; i += 2) {
        	System.out.println(text.substring(i, i+2));
             result[i/2] = (byte) Integer.parseInt(text.substring(i, i + 2), 16);
             System.out.println(result[i/2]);
        }
        
        return result;
    }
}
package logic;

public enum Encryption{
    ENCRYPT,
    DECRYPT
}

package logic;

public class MessageReceivedEventArgs {

    public MessageReceivedEventArgs(byte[] messageText) {
        this.messageText = messageText;
    }
    byte[] messageText;

    public byte[] getMessageText() {
        return messageText;
    }
}

package logic;

public interface MessageReceivedListener {

    public void messageReceived(MessageReceivedEventArgs e);
}
package logic;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

class Receiver extends Thread {

    private int receivePort;
    private Communicator communicator;
    private DatagramSocket socket;
    private BufferedReader reader;

    public Receiver(int receivePort, Communicator communicator) {
        this.receivePort = receivePort;
        this.communicator = communicator;
    }

    @Override
    public void run() {
        super.run();
        try {
			socket = new DatagramSocket(receivePort);
		} catch (SocketException e) {
			e.printStackTrace();
		}

        for (;;) {
            try {
                byte[] buffer = new byte[256];
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                socket.receive(packet);
                onMessageReceived(Arrays.copyOf(buffer, packet.getLength()));
            } catch (IOException ex) {
                Logger.getLogger(Receiver.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void onMessageReceived(byte[] message) {
    	 byte[] reversedBuffer = new byte[message.length];
         for(int i=0;i < message.length;++i){
         	reversedBuffer[message.length - i -1] = message[i];
         }
        communicator.onMessageReceived(reversedBuffer);
    }
}

As a side note: I remember I wrote some unit tests for converting text to bytes and hexes but apparently I lost it. It's been 5 years since I was working on this project.

Summary

Next time we will see very simple code for handling network packets on STM side.