Skip to content

Commit ba302ee

Browse files
Properly decode UTF-8 characters comming in from serial one byte at a time.
This fixes #2430.
1 parent 7434443 commit ba302ee

File tree

1 file changed

+44
-5
lines changed

1 file changed

+44
-5
lines changed

Diff for: arduino-core/src/processing/app/Serial.java

+44-5
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,17 @@
2828
import jssc.SerialPortException;
2929

3030
import java.io.IOException;
31+
import java.nio.ByteBuffer;
32+
import java.nio.CharBuffer;
33+
import java.nio.charset.Charset;
34+
import java.nio.charset.CharsetDecoder;
35+
import java.nio.charset.CodingErrorAction;
36+
import java.nio.charset.StandardCharsets;
3137
import java.util.Arrays;
3238
import java.util.List;
3339

34-
import static processing.app.I18n.tr;
3540
import static processing.app.I18n.format;
41+
import static processing.app.I18n.tr;
3642

3743
public class Serial implements SerialPortEventListener {
3844

@@ -47,6 +53,12 @@ public class Serial implements SerialPortEventListener {
4753

4854
private SerialPort port;
4955

56+
private CharsetDecoder bytesToStrings;
57+
private static final int IN_BUFFER_CAPACITY = 128;
58+
private static final int OUT_BUFFER_CAPACITY = 128;
59+
private ByteBuffer inFromSerial = ByteBuffer.allocate(IN_BUFFER_CAPACITY);
60+
private CharBuffer outToMessage = CharBuffer.allocate(OUT_BUFFER_CAPACITY);
61+
5062
public Serial() throws SerialException {
5163
this(PreferencesData.get("serial.port"),
5264
PreferencesData.getInteger("serial.debug_rate", 9600),
@@ -101,6 +113,8 @@ private Serial(String iname, int irate, char iparity, int idatabits, float istop
101113
//this.parent = parent;
102114
//parent.attach(this);
103115

116+
resetDecoding(StandardCharsets.UTF_8);
117+
104118
int parity = SerialPort.PARITY_NONE;
105119
if (iparity == 'E') parity = SerialPort.PARITY_EVEN;
106120
if (iparity == 'O') parity = SerialPort.PARITY_ODD;
@@ -153,10 +167,24 @@ public synchronized void serialEvent(SerialPortEvent serialEvent) {
153167
if (serialEvent.isRXCHAR()) {
154168
try {
155169
byte[] buf = port.readBytes(serialEvent.getEventValue());
156-
if (buf.length > 0) {
157-
String msg = new String(buf);
158-
char[] chars = msg.toCharArray();
159-
message(chars, chars.length);
170+
int next = 0;
171+
while(next < buf.length) {
172+
while(next < buf.length && outToMessage.hasRemaining()) {
173+
int spaceInIn = inFromSerial.remaining();
174+
int copyNow = buf.length - next < spaceInIn ? buf.length - next : spaceInIn;
175+
inFromSerial.put(buf, next, copyNow);
176+
next += copyNow;
177+
inFromSerial.flip();
178+
bytesToStrings.decode(inFromSerial, outToMessage, false);
179+
inFromSerial.compact();
180+
}
181+
outToMessage.flip();
182+
if(outToMessage.hasRemaining()) {
183+
char[] chars = new char[outToMessage.remaining()];
184+
outToMessage.get(chars);
185+
message(chars, chars.length);
186+
}
187+
outToMessage.clear();
160188
}
161189
} catch (SerialPortException e) {
162190
errorMessage("serialEvent", e);
@@ -226,6 +254,17 @@ public void setRTS(boolean state) {
226254
}
227255
}
228256

257+
/**
258+
* Reset the encoding used to convert the bytes coming in
259+
* before they are handed as Strings to {@Link #message(char[], int)}.
260+
*/
261+
public synchronized void resetDecoding(Charset charset) {
262+
bytesToStrings = charset.newDecoder()
263+
.onMalformedInput(CodingErrorAction.REPLACE)
264+
.onUnmappableCharacter(CodingErrorAction.REPLACE)
265+
.replaceWith("\u2e2e");
266+
}
267+
229268
static public List<String> list() {
230269
return Arrays.asList(SerialPortList.getPortNames());
231270
}

0 commit comments

Comments
 (0)