Skip to content

Commit 3485f6d

Browse files
committed
Add serial monitor send/receive encoding options
- Add "send as <encoding>" dropdown menu. - Add "receive as <encoding>" dropdown menu. Sending and receiving can be done in any encoding specified in StandardCharsets with the additional option to send comma-separated bytes and receive newline-separated bytes directly. This fixes #4452 and offers an easy implementation for issue #4632.
1 parent a87024d commit 3485f6d

File tree

4 files changed

+255
-49
lines changed

4 files changed

+255
-49
lines changed

Diff for: app/src/processing/app/AbstractTextMonitor.java

+51-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
4040
protected JButton clearButton;
4141
protected JCheckBox autoscrollBox;
4242
protected JCheckBox addTimeStampBox;
43+
protected JComboBox<String> sendEncoding;
44+
protected JComboBox<String> receiveEncoding;
4345
protected JComboBox<String> lineEndings;
4446
protected JComboBox<String> serialRates;
4547

@@ -105,6 +107,44 @@ public void windowGainedFocus(WindowEvent e) {
105107
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
106108
noLineEndingAlert.setMinimumSize(minimumSize);
107109

110+
String sendAs = tr("Send as") + " ";
111+
sendEncoding = new JComboBox<>(new String[] {
112+
sendAs + EncodingOption.SYSTEM_DEFAULT,
113+
sendAs + EncodingOption.BYTES,
114+
sendAs + EncodingOption.UTF_8,
115+
sendAs + EncodingOption.UTF_16,
116+
sendAs + EncodingOption.UTF_16BE,
117+
sendAs + EncodingOption.UTF_16LE,
118+
sendAs + EncodingOption.ISO_8859_1,
119+
sendAs + EncodingOption.US_ASCII});
120+
sendEncoding.addActionListener(new ActionListener() {
121+
public void actionPerformed(ActionEvent event) {
122+
PreferencesData.set("serial.send_encoding", sendEncoding.getItemAt(
123+
sendEncoding.getSelectedIndex()).substring(sendAs.length()));
124+
}
125+
});
126+
String sendEncodingStr = PreferencesData.get("serial.send_encoding");
127+
if (sendEncodingStr != null) {
128+
sendEncoding.setSelectedItem(sendAs + sendEncodingStr);
129+
}
130+
sendEncoding.setMaximumSize(sendEncoding.getMinimumSize());
131+
132+
String receiveAs = tr("Receive as") + " ";
133+
receiveEncoding = new JComboBox<>(new String[] {
134+
receiveAs + EncodingOption.SYSTEM_DEFAULT,
135+
receiveAs + EncodingOption.BYTES,
136+
receiveAs + EncodingOption.UTF_8,
137+
receiveAs + EncodingOption.UTF_16,
138+
receiveAs + EncodingOption.UTF_16BE,
139+
receiveAs + EncodingOption.UTF_16LE,
140+
receiveAs + EncodingOption.ISO_8859_1,
141+
receiveAs + EncodingOption.US_ASCII});
142+
String receiveEncodingStr = PreferencesData.get("serial.receive_encoding");
143+
if (receiveEncodingStr != null) {
144+
receiveEncoding.setSelectedItem(receiveAs + receiveEncodingStr);
145+
}
146+
receiveEncoding.setMaximumSize(receiveEncoding.getMinimumSize());
147+
108148
lineEndings = new JComboBox<String>(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
109149
lineEndings.addActionListener((ActionEvent event) -> {
110150
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
@@ -127,6 +167,10 @@ public void windowGainedFocus(WindowEvent e) {
127167
pane.add(Box.createHorizontalGlue());
128168
pane.add(noLineEndingAlert);
129169
pane.add(Box.createRigidArea(new Dimension(8, 0)));
170+
pane.add(sendEncoding);
171+
pane.add(Box.createRigidArea(new Dimension(8, 0)));
172+
pane.add(receiveEncoding);
173+
pane.add(Box.createRigidArea(new Dimension(8, 0)));
130174
pane.add(lineEndings);
131175
pane.add(Box.createRigidArea(new Dimension(8, 0)));
132176
pane.add(serialRates);
@@ -139,15 +183,16 @@ public void windowGainedFocus(WindowEvent e) {
139183
}
140184

141185
@Override
142-
protected void onEnableWindow(boolean enable)
143-
{
186+
protected void onEnableWindow(boolean enable) {
144187
textArea.setEnabled(enable);
145188
clearButton.setEnabled(enable);
146189
scrollPane.setEnabled(enable);
147190
textField.setEnabled(enable);
148191
sendButton.setEnabled(enable);
149192
autoscrollBox.setEnabled(enable);
150193
addTimeStampBox.setEnabled(enable);
194+
sendEncoding.setEnabled(enable);
195+
receiveEncoding.setEnabled(enable);
151196
lineEndings.setEnabled(enable);
152197
serialRates.setEnabled(enable);
153198
}
@@ -165,6 +210,10 @@ public void onSerialRateChange(ActionListener listener) {
165210
serialRates.addActionListener(listener);
166211
}
167212

213+
public void onReceiveEncodingChange(ActionListener listener) {
214+
receiveEncoding.addActionListener(listener);
215+
}
216+
168217
@Override
169218
public void message(String msg) {
170219
SwingUtilities.invokeLater(() -> updateTextArea(msg));

Diff for: app/src/processing/app/EncodingOption.java

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package processing.app;
2+
3+
import java.nio.charset.Charset;
4+
import java.nio.charset.StandardCharsets;
5+
6+
/**
7+
* Represents the encoding option for decoding or encoding bytes that are
8+
* read from or written to a stream.
9+
*/
10+
public enum EncodingOption {
11+
12+
/**
13+
* The system default character set as returned by
14+
* {@link Charset#defaultCharset()}.
15+
*/
16+
SYSTEM_DEFAULT(Charset.defaultCharset()),
17+
18+
/**
19+
* Comma separated unsigned byte representation.
20+
*/
21+
BYTES(null),
22+
23+
UTF_8(StandardCharsets.UTF_8),
24+
UTF_16(StandardCharsets.UTF_16),
25+
UTF_16BE(StandardCharsets.UTF_16BE),
26+
UTF_16LE(StandardCharsets.UTF_16LE),
27+
ISO_8859_1(StandardCharsets.ISO_8859_1),
28+
US_ASCII(StandardCharsets.US_ASCII);
29+
30+
private final Charset charset;
31+
32+
private EncodingOption(Charset charset) {
33+
this.charset = charset;
34+
}
35+
36+
public Charset getCharset() {
37+
return this.charset;
38+
}
39+
40+
@Override
41+
public String toString() {
42+
switch (this) {
43+
case SYSTEM_DEFAULT:
44+
case BYTES:
45+
return this.name().replace('_', ' ').toLowerCase();
46+
default:
47+
return this.charset.name();
48+
}
49+
}
50+
51+
/**
52+
* Gets the {@link EncodingOption} with the given name.
53+
* The name match is case-insensitive and
54+
* whitespaces/dashes are interpreted as '_'.
55+
* @param name - The name of the {@link EncodingOption}.
56+
* @return The matching {@link EncodingOption}
57+
* or null when no such option exists.
58+
*/
59+
public static EncodingOption forName(String name) {
60+
if (name == null) {
61+
return null;
62+
}
63+
try {
64+
return EncodingOption.valueOf(
65+
name.replace(' ', '_').replace('-', '_').toUpperCase());
66+
} catch (IllegalArgumentException e) {
67+
return null;
68+
}
69+
// name = name.replace(' ', '_').replace('-', '_');
70+
// for (EncodingOption option : EncodingOption.values()) {
71+
// if (option.name().equalsIgnoreCase(name)) {
72+
// return option;
73+
// }
74+
// }
75+
// return null;
76+
}
77+
}

Diff for: app/src/processing/app/SerialMonitor.java

+77-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import java.awt.Color;
2525
import java.awt.event.ActionEvent;
26+
import java.awt.event.ActionListener;
27+
import java.nio.charset.Charset;
2628

2729
import static processing.app.I18n.tr;
2830

@@ -32,6 +34,11 @@ public class SerialMonitor extends AbstractTextMonitor {
3234
private Serial serial;
3335
private int serialRate;
3436

37+
private static final EncodingOption DEFAULT_SEND_ENCODING =
38+
EncodingOption.UTF_8;
39+
private static final EncodingOption DEFAULT_RECEIVE_ENCODING =
40+
EncodingOption.UTF_8;
41+
3542
public SerialMonitor(Base base, BoardPort port) {
3643
super(base, port);
3744

@@ -53,6 +60,18 @@ public SerialMonitor(Base base, BoardPort port) {
5360
}
5461
});
5562

63+
onReceiveEncodingChange((ActionEvent event) -> {
64+
String receiveAs = tr("Receive as") + " ";
65+
String selectedEncodingStr = receiveEncoding.getItemAt(
66+
receiveEncoding.getSelectedIndex()).substring(receiveAs.length());
67+
Charset selectedCharset =
68+
EncodingOption.forName(selectedEncodingStr).getCharset();
69+
if (serial.getCharset() != selectedCharset) {
70+
serial.resetDecoding(selectedCharset);
71+
PreferencesData.set("serial.receive_encoding", selectedEncodingStr);
72+
}
73+
});
74+
5675
onSendCommand((ActionEvent event) -> {
5776
send(textField.getText());
5877
textField.setText("");
@@ -76,11 +95,48 @@ private void send(String s) {
7695
default:
7796
break;
7897
}
79-
if ("".equals(s) && lineEndings.getSelectedIndex() == 0 && !PreferencesData.has("runtime.line.ending.alert.notified")) {
98+
if ("".equals(s) && lineEndings.getSelectedIndex() == 0
99+
&& !PreferencesData.has("runtime.line.ending.alert.notified")) {
80100
noLineEndingAlert.setForeground(Color.RED);
81101
PreferencesData.set("runtime.line.ending.alert.notified", "true");
82102
}
83-
serial.write(s);
103+
EncodingOption encodingOption =
104+
EncodingOption.forName(PreferencesData.get("serial.send_encoding"));
105+
if (encodingOption == null) {
106+
encodingOption = DEFAULT_SEND_ENCODING;
107+
}
108+
Charset charSet = encodingOption.getCharset();
109+
byte[] bytes;
110+
if (charSet != null) {
111+
bytes = s.getBytes(encodingOption.getCharset());
112+
} else {
113+
switch (encodingOption) {
114+
case BYTES:
115+
String[] split = s.split(",");
116+
bytes = new byte[split.length];
117+
for (int i = 0; i < split.length; i++) {
118+
String valStr = split[i].trim();
119+
try {
120+
int val = Integer.parseInt(valStr);
121+
if (val < 0x00 || val > 0xFF) {
122+
this.message("\n[ERROR] Invalid byte value given: "
123+
+ val + ". Byte values are in range [0-255].\n");
124+
return;
125+
}
126+
bytes[i] = (byte) val;
127+
} catch (NumberFormatException e) {
128+
this.message("\n[ERROR] Invalid byte value given: " + valStr
129+
+ ". Byte values are numbers in range [0-255].\n");
130+
return;
131+
}
132+
}
133+
break;
134+
default:
135+
throw new Error(
136+
"Unsupported 'send as' encoding option: " + encodingOption);
137+
}
138+
}
139+
serial.write(bytes);
84140
}
85141
}
86142

@@ -90,10 +146,27 @@ public void open() throws Exception {
90146

91147
if (serial != null) return;
92148

93-
serial = new Serial(getBoardPort().getAddress(), serialRate) {
149+
EncodingOption encodingOption =
150+
EncodingOption.forName(PreferencesData.get("serial.receive_encoding"));
151+
if (encodingOption == null) {
152+
encodingOption = DEFAULT_RECEIVE_ENCODING;
153+
}
154+
serial = new Serial(
155+
getBoardPort().getAddress(), serialRate, encodingOption.getCharset()) {
94156
@Override
95157
protected void message(char buff[], int n) {
96-
addToUpdateBuffer(buff, n);
158+
if (serial.getCharset() == null) {
159+
if(buff.length != 0) {
160+
StringBuilder strBuilder = new StringBuilder();
161+
for (int i = 0; i < n; i++) {
162+
strBuilder.append(buff[i] & 0xFF).append("\n");
163+
}
164+
addToUpdateBuffer(
165+
strBuilder.toString().toCharArray(), strBuilder.length());
166+
}
167+
} else {
168+
addToUpdateBuffer(buff, n);
169+
}
97170
}
98171
};
99172
}

0 commit comments

Comments
 (0)