英文:
How do Serial Monitors interpret the ASCII base 10 data produced from Serial.print messages?
问题
背景:
我试图创建一个系统,该系统能够从传入的Serial.print、Serial.println或Serial.write消息中读取串行数据。然后,在解析传入消息后,将文本保存为变量,然后将其打印到LCD或不同的串行端口。我成功地使Serial.write消息正常工作,但在尝试解析Serial.print消息时遇到了问题。由于Serial.print消息在发送之前将二进制ASCII字符转换为Base 10 ASCII,并且由于串行通信在通信时一次发送一个字符,因此我无法在接收后将消息分隔成其各个ASCII字符。例如,当发送文本“red”时,接收到的是'11410110010'。类似地,“test”返回'11610111511610'。(请注意,它们都以换行['\n']字符结尾的'10')
为了解决这个问题,我试图通过查看GitHub上串行监视器的源代码来看看Arduino IDE是如何做的。以下是我在有限的Java理解下发现的情况:
- 我首先查看了https://github.com/arduino/Arduino/blob/master/app/src/processing/app/SerialMonitor.java,该文件创建了串行监视器GUI。它扩展了“Abstract Monitor”,因此我去查看它的功能。
- 在https://github.com/arduino/Arduino/blob/master/app/src/processing/app/AbstractTextMonitor.java中,它处理了串行监视器的监听器和其他附加功能(如时间戳、自动滚动和行尾处理)。它也扩展了“Abstract Monitor”,所以我去查看它的功能。
- 在https://github.com/arduino/Arduino/blob/master/app/src/processing/app/AbstractMonitor.java中,我有点迷失。从我能看到的内容来看,它似乎处理了串行通信的关键功能(如打开和关闭通信、设置波特率等),但我可能错过了或无法理解我正在寻找的功能的部分。
以下是我使用的代码和硬件,如果有帮助的话。除此之外,如果需要其他信息,请告诉我。
发送设备:Sparkfun Pro Micro
接收设备:Arduino Duemilonove
Pro Micro的引脚14连接到Duemilonove的引脚9
Pro Micro的引脚16连接到Duemilonove的引脚8
LCD已正确配置到Duemilonove,使用接收器代码中找到的引脚
发送代码(Pro Micro):
#include <SoftwareSerial.h>
SoftwareSerial mySerial(14, 16);
void setup() {
Serial.begin(115200);
mySerial.begin(115200);
}
void loop() {
if (Serial.available()) { // If anything comes in Serial (USB),
mySerial.print(Serial.read()); // read it and send it out mySerial (Software Serial)
}
}
接收代码(Duemilonove):
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
SoftwareSerial mySerial(8, 9); //for Uno or Duemilanove
char lineOne[16];
char lineTwo[16];
char tempChar;
bool change = false;
bool newLineBuffer = false;
void setup() {
Serial.begin(115200);
mySerial.begin(115200);
lcd.begin(16, 2);
}
void loop() {
SerialCheck();
newLineShift();
updateLCD();
}
void SerialCheck() {
if (mySerial.available()) { // If anything comes in mySerial (Software Serial Port),
tempChar=mySerial.read();
change=true;
}
if (Serial.available()) { // If anything comes in Serial (USB port)
tempChar=Serial.read();
change=true;
}
}
void newLineShift(){
if(change){
if(tempChar=='\n'){
if(newLineBuffer){
lineChange();
}else{
newLineBuffer=true;
}
}else{
if(newLineBuffer){
lineChange();
}
for (byte i = 0; i < 15; i = i + 1) {
if(!lineOne[i]=='\0'){
//Do nothing lol
}else{
lineOne[i]=tempChar;
break;
}
}
newLineBuffer=false;
}
}
}
void updateLCD(){
if(change){
lcd.clear();
Serial.println("Line Two is: " + (String)lineTwo);
lcd.setCursor(0, 0);
lcd.print(lineTwo);
Serial.println("Line One is: " + (String)lineOne);
lcd.setCursor(0, 1);
lcd.print(lineOne);
}
change=false;
tempChar='\0';
}
void lineChange(){
for (byte i = 0; i < 15; i = i + 1) {
lineTwo[i]=lineOne[i];
lineOne[i]='\0';
}
}
如果您有任何进一步的问题,请告诉我。
英文:
Background:
I was trying to create a system that would be able to read serial data from an incoming Serial.print, Serial.println, or Serial.write message. Then, after parsing the incoming message, save the text as a variable which would then be printed onto an LCD or a different Serial port. I was able to get Serial.write messages working successfully, but I ran into issues when trying to parse Serial.print messages. Since Serial.print messages convert the binary ASCII characters to Base 10 ASCII prior to sending it, and since Serial communication sends one character at a time when communicating, I was unable to separate a message into its individual ASCII characters after receiving it. For example, when the text "red" was sent, '11410110010' was received. Similarly "test" returned '11610111511610'. (please note that the '10' appended to the end of both of them are the newline ['\n'] characters)
In search of a solution to this problem, I tried to see how the Arduino IDE does it via viewing the source code of the Serial Monitor on GitHub. This is what I found with my limited understanding of Java:
- I first started at https://github.com/arduino/Arduino/blob/master/app/src/processing/app/SerialMonitor.java which creates the Serial Monitor GUI. It extended "Abstract Monitor", so I went to look at what that did.
- In https://github.com/arduino/Arduino/blob/master/app/src/processing/app/AbstractTextMonitor.java it handles the listeners and other add-ons that the Serial Monitor does (such as timestamps, auto-scroll, and line endings). It extended "Abstract Monitor", so I went to look at what that did.
- At https://github.com/arduino/Arduino/blob/master/app/src/processing/app/AbstractMonitor.java, I was mostly lost. From what I can see, it appears to handle the critical functionality of the serial communication (Such as opening and closing the communication, setting the baud rate, etc), but I likely missed/ could not understand sections that deal with the functionality that I was looking for.
Here is the code and hardware that I was using if it is helpful. Aside from that, if any other information is needed, please tell me.
Sender Device: Sparkfun Pro Micro
Receiver Device: Arduino Duemilonove
Pin 14 of the Pro Micro is connected to Pin 9 of the Duemilonove
Pin 16 of the Pro Micro is connected to Pin 8 of the Duemilonove
The LCD is configured properly to the Duemilonove with the pins found in the Receiver Code
Sender Code (Pro Micro):
//If anything is sent from the Arduino IDE's Serial Monitor to the Device,
//foreword that message to the device connected to its Software Serial pins (The Receiver/ The Duemilonove)
#include <SoftwareSerial.h>
SoftwareSerial mySerial(14, 16);
void setup() {
Serial.begin(115200);
mySerial.begin(115200);
}
void loop() {
if (Serial.available()) { // If anything comes in Serial (USB),
mySerial.print(Serial.read()); // read it and send it out mySerial (Software Serial)
}
}
Receiver Code (Duemilonove):
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
//SoftwareSerial mySerial(14, 16); for pro micro
SoftwareSerial mySerial(8, 9); //for Uno or Duemilanove
char lineOne[16];
char lineTwo[16];
char tempChar;
bool change = false;
bool newLineBuffer = false;
void setup() {
Serial.begin(115200);
mySerial.begin(115200);
lcd.begin(16, 2);
}
void loop() {
SerialCheck();
newLineShift();
updateLCD();
}
void SerialCheck() {
if (mySerial.available()) { // If anything comes in mySerial (Software Serial Port),
tempChar=mySerial.read();
change=true;
}
if (Serial.available()) { // If anything comes in Serial (USB port)
tempChar=Serial.read();
change=true;
}
}
void newLineShift(){
if(change){
if(tempChar=='\n'){
if(newLineBuffer){
lineChange();
}else{
newLineBuffer=true;
}
}else{
if(newLineBuffer){
lineChange();
}
for (byte i = 0; i < 15; i = i + 1) {
if(!lineOne[i]=='\0'){
//Do nothing lol
}else{
lineOne[i]=tempChar;
break;
}
}
newLineBuffer=false;
}
}
}
void updateLCD(){
if(change){
lcd.clear();
Serial.println("Line Two is: "+ (String)lineTwo);
lcd.setCursor(0, 0);
lcd.print(lineTwo);
Serial.println("Line One is: "+ (String)lineOne);
lcd.setCursor(0, 1);
lcd.print(lineOne);
}
change=false;
tempChar='\0';
}
void lineChange(){
for (byte i = 0; i < 15; i = i + 1) {
lineTwo[i]=lineOne[i];
lineOne[i]='\0';
}
}
答案1
得分: 0
read()
返回一个整数。他们这样做是为了在没有可读内容时返回 -1。通常,我们只关心已读取的字节,它位于低 8 位中。
所以当你写下这个代码:
mySerial.print(Serial.read());
调用的是打印整数变量的那个版本。那个版本会将数字拆分成 ASCII 码,从 "red" 中的 'r' 变成 '1'、'1' 和 '4'。
相反,你应该调用打印字符的版本。你只关心已读取的字符变量。所以尝试这个:
char c = Serial.read();
mySerial.print(c);
或者甚至:
mySerial.print((char)Serial.read());
这将使用你想要的打印版本。
或者,你也可以使用 write
直接写回你读取的字节。由于 write
将字节原封不动地发送出去,它将发送 114 字节,就像它读取的那样,你将在另一端看到 'r'。
mySerial.write(Serial.read());
英文:
read() returns an int. They do this so it can return -1 when there is nothing there to read. Normally we're only interested in the byte that was read which is in the low 8 bits.
So when you write this:
mySerial.print(Serial.read());
the version of print that gets called is the one for printing an int variable. That version breaks the number down into ascii and the '114' from the r in red becomes a '1', a '1', and a '4'.
Instead you want to call the version for printing a char. You're only worried about the char variable that was read anyway. So try this:
char c = Serial.read();
mySerial.print(c);
or even:
mySerial.print((char)Serial.read());
and that will get you the version of print that you want.
Alternatively you can just write the byte that you read back directly by using write instead of print. Since write sends the byte out exactly as is, it will send the 114 byte just like it read it and you'll see 'r' on the other end.
mySerial.write(Serial.read());
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论