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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
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); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
< ?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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
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); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
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; } } |
1 2 3 4 5 6 |
package logic; public enum Encryption{ ENCRYPT, DECRYPT } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package logic; public class MessageReceivedEventArgs { public MessageReceivedEventArgs(byte[] messageText) { this.messageText = messageText; } byte[] messageText; public byte[] getMessageText() { return messageText; } } |
1 2 3 4 5 6 |
package logic; public interface MessageReceivedListener { public void messageReceived(MessageReceivedEventArgs e); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
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.