Skip to content

Adding encoding support to serial monitor #4801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions app/src/processing/app/AbstractMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.ByteArrayOutputStream;

@SuppressWarnings("serial")
public abstract class AbstractMonitor extends JFrame implements ActionListener {

private boolean closed;

private StringBuffer updateBuffer;
private ByteArrayOutputStream updateBuffer;

private Timer updateTimer;

private BoardPort boardPort;
Expand Down Expand Up @@ -73,13 +75,14 @@ public void actionPerformed(ActionEvent event) {
}
}

updateBuffer = new StringBuffer(1048576);
updateBuffer = new ByteArrayOutputStream(1048576);
updateTimer = new Timer(33, this); // redraw serial monitor at 30 Hz
updateTimer.start();

closed = false;
}


protected abstract void onCreateWindow(Container mainPane);

public void enableWindow(boolean enable) {
Expand Down Expand Up @@ -127,7 +130,7 @@ protected int[] getPlacement() {
return location;
}

public abstract void message(final String s);
public abstract void message(final byte[] buf);

public boolean requiresAuthorization() {
return false;
Expand Down Expand Up @@ -161,19 +164,19 @@ public void setBoardPort(BoardPort boardPort) {
this.boardPort = boardPort;
}

public synchronized void addToUpdateBuffer(char buff[], int n) {
updateBuffer.append(buff, 0, n);
public synchronized void addToUpdateBuffer(byte buff[], int n) {
updateBuffer.write(buff, 0, n);
}

private synchronized String consumeUpdateBuffer() {
String s = updateBuffer.toString();
updateBuffer.setLength(0);
return s;
private synchronized byte[] consumeUpdateBuffer() {
byte[] contents = updateBuffer.toByteArray();
updateBuffer.reset();
return contents;
}

public void actionPerformed(ActionEvent e) {
String s = consumeUpdateBuffer();
if (s.isEmpty()) {
byte[] s = consumeUpdateBuffer();
if (s.length == 0) {
return;
} else {
message(s);
Expand Down
76 changes: 70 additions & 6 deletions app/src/processing/app/AbstractTextMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

import javax.swing.Box;
import javax.swing.BoxLayout;
Expand Down Expand Up @@ -35,7 +38,12 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
protected JCheckBox autoscrollBox;
protected JComboBox lineEndings;
protected JComboBox serialRates;

protected byte[][] lineEndingSelection;

protected JComboBox<Charset> txCharsets;
protected JComboBox<Charset> rxCharsets;

public AbstractTextMonitor(BoardPort boardPort) {
super(boardPort);
}
Expand Down Expand Up @@ -83,11 +91,10 @@ protected void onCreateWindow(Container mainPane) {
noLineEndingAlert = new JLabel(I18n.format(tr("You've pressed {0} but nothing was sent. Should you select a line ending?"), tr("Send")));
noLineEndingAlert.setToolTipText(noLineEndingAlert.getText());
noLineEndingAlert.setForeground(pane.getBackground());
Dimension minimumSize = new Dimension(noLineEndingAlert.getMinimumSize());
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
noLineEndingAlert.setMinimumSize(minimumSize);
noLineEndingAlert.setMinimumSize(new Dimension(0, noLineEndingAlert.getPreferredSize().height));

lineEndings = new JComboBox(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
lineEndingSelection = new byte[][] { {}, {'\n'}, {'\r'}, {'\r', '\n'}};
lineEndings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
Expand All @@ -105,10 +112,49 @@ public void actionPerformed(ActionEvent event) {
}

serialRates.setMaximumSize(serialRates.getMinimumSize());


java.util.List<Charset> list = new java.util.ArrayList<Charset>();

// All JVMs are required to support these standard charsets. I will add them to the top
// the list

list.add(Charset.forName("US-ASCII"));
list.add(Charset.forName("ISO-8859-1"));
list.add(Charset.forName("UTF-8"));
list.add(Charset.forName("UTF-16"));
list.add(Charset.forName("UTF-16BE"));
list.add(Charset.forName("UTF-16LE"));

list.addAll(Charset.availableCharsets().values());

rxCharsets = new JComboBox<Charset>(list.toArray(new Charset[list.size()]));
for(java.util.Iterator<Charset> i = list.iterator(); i.hasNext();) {
if(!i.next().canEncode()) {
i.remove();
}
}
txCharsets = new JComboBox<Charset>(list.toArray(new Charset[list.size()]));

txCharsets.setSelectedItem(Charset.forName("US-ASCII")); // US-ASCII is always available
rxCharsets.setSelectedItem(Charset.defaultCharset()); // assume that the arduino is being tested against the current platform

// some of these encoding names are a little long, so I'll knock some size off the fonts
Font compressed = txCharsets.getFont().deriveFont(new AffineTransform(.8, 0, 0, 1, 0, 0));
txCharsets.setFont(compressed);
rxCharsets.setFont(compressed);
txCharsets.setMaximumSize(txCharsets.getMinimumSize());
rxCharsets.setMaximumSize(rxCharsets.getMinimumSize());

pane.add(autoscrollBox);
pane.add(Box.createHorizontalGlue());
pane.add(noLineEndingAlert);
pane.add(new JLabel("Tx"));
pane.add(Box.createRigidArea(new Dimension(2, 0)));
pane.add(txCharsets);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(new JLabel("Rx"));
pane.add(Box.createRigidArea(new Dimension(2, 0)));
pane.add(rxCharsets);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(lineEndings);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
Expand Down Expand Up @@ -137,10 +183,28 @@ public void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}

public void message(final String s) {
public void onTxCharsetChange(ActionListener listener) {
txCharsets.addActionListener(listener);
}

public void onRxCharsetChange(ActionListener listener) {
rxCharsets.addActionListener(listener);
}

public void message(final byte[] msg) {
String s;

try {
s = new String(msg, ((Charset)rxCharsets.getSelectedItem()).name());
} catch (UnsupportedEncodingException e) {
s = new String(msg);
}

final String final_s = s;

SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.append(s);
textArea.append(final_s);
if (autoscrollBox.isSelected()) {
textArea.setCaretPosition(textArea.getDocument().getLength());
}
Expand Down
12 changes: 10 additions & 2 deletions app/src/processing/app/NetworkMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;

import static processing.app.I18n.tr;

Expand Down Expand Up @@ -120,7 +121,14 @@ public void run() {
}

@Override
public synchronized void message(String s) {
public synchronized void message(String msg) {
message(msg.getBytes((Charset)rxCharsets.getSelectedItem()));
}

@Override
public synchronized void message(byte[] msg) {
String s = new String(msg);

if (s.contains("can't connect")) {
while (!channel.isClosed()) {
try {
Expand Down Expand Up @@ -148,7 +156,7 @@ public void run() {
s = "\n" + tr("Unable to connect: is the sketch using the bridge?");
}
}
super.message(s);
super.message(msg);
}

@Override
Expand Down
20 changes: 6 additions & 14 deletions app/src/processing/app/SerialMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.nio.charset.Charset;

import static processing.app.I18n.tr;

Expand Down Expand Up @@ -65,23 +66,14 @@ public void actionPerformed(ActionEvent e) {
}

private void send(String s) {

if (serial != null) {
switch (lineEndings.getSelectedIndex()) {
case 1:
s += "\n";
break;
case 2:
s += "\r";
break;
case 3:
s += "\r\n";
break;
}
serial.write(s.getBytes((Charset)txCharsets.getSelectedItem()));
serial.write(lineEndingSelection[lineEndings.getSelectedIndex()]);
if ("".equals(s) && lineEndings.getSelectedIndex() == 0 && !PreferencesData.has("runtime.line.ending.alert.notified")) {
noLineEndingAlert.setForeground(Color.RED);
PreferencesData.set("runtime.line.ending.alert.notified", "true");
}
serial.write(s);
}
}

Expand All @@ -92,8 +84,8 @@ public void open() throws Exception {

serial = new Serial(getBoardPort().getAddress(), serialRate) {
@Override
protected void message(char buff[], int n) {
addToUpdateBuffer(buff, n);
protected void message(byte[] bytes, int length) {
addToUpdateBuffer(bytes, length);
}
};
}
Expand Down
6 changes: 3 additions & 3 deletions app/src/processing/app/SerialPlotter.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ private void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}

public void message(final String s) {
messageBuffer.append(s);
public void message(final byte[] buf) {
messageBuffer.append(new String(buf));
while (true) {
int linebreak = messageBuffer.indexOf("\n");
if (linebreak == -1) {
Expand Down Expand Up @@ -260,7 +260,7 @@ public void open() throws Exception {

serial = new Serial(getBoardPort().getAddress(), serialRate) {
@Override
protected void message(char buff[], int n) {
protected void message(byte buff[], int n) {
addToUpdateBuffer(buff, n);
}
};
Expand Down
38 changes: 30 additions & 8 deletions arduino-core/src/processing/app/Serial.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ public synchronized void serialEvent(SerialPortEvent serialEvent) {
try {
byte[] buf = port.readBytes(serialEvent.getEventValue());
if (buf.length > 0) {
String msg = new String(buf);
char[] chars = msg.toCharArray();
message(chars, chars.length);
message(buf, buf.length);
}
} catch (SerialPortException e) {
errorMessage("serialEvent", e);
Expand All @@ -164,8 +162,27 @@ public synchronized void serialEvent(SerialPortEvent serialEvent) {
}

/**
* This method is intented to be extended to receive messages
* This method is intended to be overridden to receive messages
* coming from serial port.
*
* For backward compatibility, the default implementation decodes
* the incoming bytes using the default charset, and calls
* message(char[], int).
*/
protected void message(byte[] bytes, int length) {
String msg = new String(bytes, 0, length);
char chars[] = msg.toCharArray();
message(chars, chars.length);
}

/**
* This method is intended to be overridden to receive messages
* coming from serial port.
*
* This method is deprecated. You should override message(byte[], int)
* and handle decoding explicitly.
*
* @deprecated override message(byte[], int)
*/
protected void message(char[] chars, int length) {
// Empty
Expand All @@ -184,9 +201,9 @@ public void write(int what) { // will also cover char
}


private void write(byte bytes[]) {
public void write(byte what[]) {
try {
port.writeBytes(bytes);
port.writeBytes(what);
} catch (SerialPortException e) {
errorMessage("write", e);
}
Expand All @@ -203,10 +220,15 @@ private void write(byte bytes[]) {
* <p>
* If you want to move Unicode data, you can first convert the
* String to a byte stream in the representation of your choice
* (i.e. UTF8 or two-byte Unicode data), and send it as a byte array.
* (i.e. UTF8 or two-byte Unicode data), and use write(byte[]).
*
* @deprecated use write(byte[])
*/
public void write(String what) {
write(what.getBytes());
byte[] buf = new byte[what.length()];
for(int i = 0; i<what.length();i++)
buf[i] = (byte) (what.charAt(i) & 0xff);
write(buf);
}

public void setDTR(boolean state) {
Expand Down