I code a program for encode and decode a morse code, this code is for decode a morse code.
i use 2 arduino board, 1st Arduino is for encode the text into morse and 2nd is for receiving the morse and decode into text.Connection of 2 arduino board is using IR sensors.
in 1st arduino board
This ARDU INO is a input of my code,then convert like this .-|.-.|-..|..-| |..|-.|---|, in this | is a end of word. Then transmit this morse code
in 2nd Arduino board
it receives .-|.-.|-..|..-| |..|-.|---|, but its Decode like this "ARDUU INOO ",it print the same word twice which is before the space
why this happen? please help me
#define SIZE 26
const int btnPin=7;
String morseCode="";
String text="";
int characterAscii=0;
int startPos=0, endPos=0;
int startPos1=0, endPos1=0;
String characterCode="";
int btnState=0;
unsigned long int duration = 0;
//Array of MorseCode for letters of English Language A to Z
String letters[SIZE]={
// A to I
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..",
// J to R
".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.",
// S to Z
"...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."
};
void setup() {
pinMode(btnPin, INPUT_PULLUP);
Serial.begin(9600);
//Serial.println("*********************");
Serial.println(" Demonstration of Morse Code ");
Serial.println("********************* ");
//Serial.println("\nInstructions");
Serial.println("1. First Write Your Morse code");
Serial.println("2. When you are done Write 1 on above input box and Press Enter or click Send Button ");
Serial.println("3. For Space between letters write 2 and Press Enter ");
Serial.println("4. For Space between words write 3 and Press Enter ");
Serial.println("5. Thats all Translation of Morse Code will then be Shown ");
Serial.println("\n\nEnter Your Morse Code Here ");
}
void loop() {
while(Serial.available() > 0 ) {
int ascii=Serial.read();
switch(ascii)
{
case 49: // 49 is Ascii value of 1
Serial.print("\n");
morseCode.concat('#');// Placeing # at the end of morseCode to simplify further processing
Serial.print("\nYour Morse code Translation : ");
endPos1=morseCode.indexOf('#');
while(endPos1 < morseCode.length() ){
extractLetters(morseCode.substring(startPos1, endPos1)); // This function would extract Letter as name suggest and would convert code to text SIMPLE!
startPos1=endPos1+1;
if(startPos1 == morseCode.length() ){
break;
}
endPos1= morseCode.indexOf('#', startPos1);
}
startPos1=0;
endPos1=0;
text=""; // For New Translation
morseCode="";
Serial.println("\n\nEnter Your Morse Code Here ");
break;
}
process();
}
void process(){
while(digitalRead(btnPin) == LOW ) {
delay(150); //if you want more resolution, lower this number
duration = duration + 150;
}
switch(duration){
case 450:
morseCode.concat("-"); // Storing code in variable morseCode with the help of concatenation function
Serial.print("-");//Prints User entered Code
//Serial.print(duration);
break;
case 150:
morseCode.concat(".");
Serial.print(".");
//Serial.print(duration);
break;
case 300:
morseCode.concat("#");
Serial.print("|");
//Serial.println(duration);
break;
case 1050:
morseCode.concat("#");
Serial.print(" |");
//Serial.println(duration);
break;
}
duration = 0;
}
char convertIntoText(String characterCode)
{
characterAscii=65;
for(int index=0; index<SIZE; index++)
{
if(characterCode == letters[index])
{
return characterAscii;
}
characterAscii++;
}
}
void extractLetters(String words)
{
words.concat('#'); // Placeing # at the end of word to simplify further processing
endPos=words.indexOf('#');
//Loop to extracting single character morse Code from string of word
while( endPos<words.length() )
{
characterCode=words.substring(startPos, endPos);
//Now CharacterCode will now convert in text
text.concat(convertIntoText(characterCode));
startPos=endPos+1;
characterCode="";
// if condition is just to terminate loop when our extracting single character code is complete thats all
if(startPos == words.length() )
{
break;
}
endPos=words.indexOf('#', startPos);
}
Serial.print(text);
Serial.print(" ");
startPos=0;
endPos=0;
text="";
}
The issue seems to be in your extracting logic, there is no accounting for the # to break the words, then because convertIntoText does not have a default return path, when the input character is NOT in the expected 26 characters the response from the function used in the sketch is actually the previous response from that function call (this is an error state)
First rule, make sure all logic paths in your functions return a value, either inject a known error value, or deliberately raise an error. In this case a non-alpha character is good enough:
char convertIntoText(String characterCode)
{
characterAscii=65;
for(int index=0; index<SIZE; index++)
{
if(characterCode == letters[index])
{
return characterAscii;
}
characterAscii++;
}
return '!'; // error code
}
If you see any ! characters in your output, then you know that there is an issue mapping the input... but that doesn't help debugging, you should probably output the entire characterCode value, so maybe try this:
return "(" + characterCode + ")";
But you would also need to change the response type of this function to a String as well.
The actual issue, make sure you take # into account in extractLetters, there are a number of ways to do this, the easiest to fit into this current logic might be at the start of the loop, just check the next character:
//Loop to extracting single character morse Code from string of word
while( endPos<words.length() )
{
// check for word break
if(words.substring(startPos,startPos+1) == '#')
{
text.concat(' ');
// advance 1 char
startPos++;
}
characterCode=words.substring(startPos, endPos);
//Now CharacterCode will now convert in text
text.concat(convertIntoText(characterCode));
startPos=endPos+1;
characterCode="";
// if condition is just to terminate loop when our extracting single character code is complete thats all
if(startPos == words.length() )
{
break;
}
endPos=words.indexOf('#', startPos);
}
Some general code feedback:
When building morseCode you use # as a delimiter, but you output to the serial stream |. Having two meanings for the same thing makes parsing code and explaining to colleagues much more complicated than it needs to be. Try to avoid having an internal representation as well as a debugging or external one. Pick pipe or at and be consistent.
When testing this, it wasn't obvious at first, but your input to extractLetters is actually:
.-#.-.#-..#..-##..#-.#---#
(Actually this is more of the same) In process(), to simplify debugging, you should write the same characters that you are interpreting and processing to the serial out as you are recording internally.
Related
When running the following program, it works as expected but adds an additional output for -38 as an input. It doesn't matter what input I use, it will print out for -38 as well.
int number;
void setup()
{
Serial.begin(9600);
}
void loop()
{
number=0;
Serial.flush();
while(Serial.available() == 0)
{
//just waiting while nothing entered
}
while (Serial.available()>0)
{
number = Serial.read() - '0';
// read the number in buffer and
//remove ASCII text for "0"
}
Serial.print("You entered: ");
Serial.println(number);
Serial.print(number);
Serial.print(" multiplied by two is ");
number = number * 2;
Serial.println(number);
}
Output looks like:
>You entered: 4
>4 multiplied by two is 8
>You entered: -38
>-38 multiplied by two is -76
Your problem might be the Serial.flush. As the documentation states, it
Waits for the transmission of outgoing serial data to complete. (Prior to Arduino 1.0, this instead removed any buffered incoming serial data.)
You seem to try to achieve the pre 1.0 behavior of flushing the incoming buffer. So basically Serial.flush does nothing in your sketch, which leads to the second run reading and calculating with the line feed character (which is 10 in ASCII).
You could clear the buffer like so:
while ( Serial.available() > 0 ) {
Serial.read();
}
But please note that this part
while (Serial.available()>0)
{
number = Serial.read() - '0';
// read the number in buffer and
//remove ASCII text for "0"
}
may not do exactly what you intend it to to. If for example you would send 12 via serial, it may only print the result for the last character (the 2). It would be equally possible for your program to only output the -38 result, because the last character always is a line feed. You basically just got lucky and the serial data wasn't sent fast enough for this to happen, but as soon as your loop gets longer it likely will happen.
I would rather suggest you moving the calculation part inside the loop too and checking the input data instead of flushing the buffer after the first character. Consider this:
while ( Serial.available() > 0 )
{
// read the number in buffer and
number = Serial.read();
// make sure the character is 0 - 9 in ASCII
if( number < '0' || number > '9' ){
// invalid character, skip
continue;
}
//remove ASCII text for "0"
number = number - '0';
Serial.print("You entered: ");
Serial.println(number);
Serial.print(number);
Serial.print(" multiplied by two is ");
number = number * 2;
Serial.println(number);
}
This would read your buffer byte by byte and would apply the multiplication to each (valid) character.
Im try to add to my sketch a dynamic way to setup the ethernet info (mac, ip, gateway, subnet) from a configuration file (config.txt). So running a webserver and serving htm files from sd card, user can go to setting page, fill a form with these info and when posted , the webserver parse the http form and save (update) the config.txt file. After that system do a restart, in order to start with the new settings (by read the config.txt file)
I have create succesfully all the parts (sd, ethernet, webserver, webclient, create the config file from posted form data) except the get params by reading the config.txt file.
I can read line by line the config, I can split the line to param & value, and now I need to fill some byte variables with the readed data. I can (after a month of google searching) to read IPs (decimal values) to byte array. Im stack to read the MAC ADDRESS hex into byte array. The config file contains the:
mac=8f:2c:2b:19:e0:b7;
ip=192.168.1.200;
netmask=255.255.255.0;
gateway=192.168.1.254;
dns=8.8.8.8;
posturl=192.168.1.157;
postport=8080;
postscript=/itherm/update.php;
interval=60000;
and the code that I use to read is:
byte myMAC[6];
byte myIP[4];
File fset;
fset = SD.open("config.txt");
if (fset){
char ff[40];
while (fset.available()>1){
bool eol=false;
for (int i=0; !eol;i++){
ff[i]=fset.read();
if (ff[i]=='\n'){
eol=true;
}
}
String par="";
bool DONE=false;
for (int i=0; !DONE;i++){
par+=ff[i];
if (ff[i]== '='){DONE=true;}
}
String pval="";
DONE=false;
//------------------------
if (par=="ip=" ){
int x=0;
while(!DONE){
for(int i=3;i<=i+21;i++){
if(ff[i]=='.'){
myIP[x]=pval.toInt();
x++;
i++;
pval="";
}
else if(ff[i]==';' || i>20){
myIP[x]=pval.toInt();
DONE=true;
break;
}
pval+=ff[i];
}
}
}
} //while (fset.available()>1)
} //if (fset)
I will appreciate any help. Please no answers with simple use of Serial.print(). I have found hundreds of suggestions but none, that work properly to read all the parameters (dec, hex, strings). After a month of effort & searching, I wonder why something so necessary and useful does not exist as an example in the community, completely functional !!
Best regards
Okay so here is a complete set of routines to do what you want -I think you misunderstood the concept of char arrays vs a single char[0] The routines are documented and self explanatory. I recomend not to finish lines with ; but with '\n' which in your example is there anyway (also you can not see the new line terminator) To get the mac address I need three lines:
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
line one compares the first 4 characters and if it is 0 (meaning its a fit)
line two copies the chars from the fifth to the last char from the lineBuffer to the target array, which can actually be used as param for functions.
The file structure should be with no ; as you would have to parse ; and \n
mac=8f:2c:2b:19:e0:b7
ip=192.168.1.200
....
postport=8080
To convert a char array to eg int we use atoi(), to convert a single char[0] to a single number we use int singleDigit = char[0]-48;
const char configurationFilePath [] = "/someconfig.txt";
char cfgLine[128] = {'\0'}; // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
char numBuffer[16] = {'\0'}; // this is a global temo char array to help to convert char to number
char macAddr [18] = {'\0'}; // this is a global char array to hold the mac address
char ipAddr [16] = {'\0'}; // this is a global char array to hold the IP address - max xxx.xxx.xxx.xxx
int postport=0;
// .... you can easyly implement for all other data you want to store/retrieve
// Counts the lines of a file
uint16_t countLines() {
uint16_t currentLineCount = 0;
File cfgFile = SD.open(configurationFilePath, "r");
if (!cfgFile) {
Serial.println(F("Config file open failed on read"));
} else {
while (cfgFile.available()) {
/** Lets read line by line from the file */
if (cfgFile.read() == '\n') currentLineCount ++; // Lines are delimited by '\n'
}
cfgFile.close();
}
return currentLineCount;
}
//Load the config file from SD/SPIFFS/LittleFS
bool loadConfigFile() {
uint16_t lineCounter = countLines();
if (lineCounter <= 0) {
Serial.print(F("No config data stored in file ")); Serial.println(configurationFilePath);
return false;
}
else {
File cfgFile = SD.open(configurationFilePath, "r");
while (cfgFile.available()) {
strcpy (cfgLine, (cfgFile.readStringUntil('\n').c_str())); // normaly you use new line, we copy one line at a time
// Serial.println(cfgLine); /** Printing for debuging purpose */
while (cfgLine[0] != '\0') { /* Block refilling of cfgLine till processed */
loadSingleCfgLine();
}
}
cfgFile.close();
Serial.println(F("[Success] Loaded config !"));
return true;
}
}
//Load the data of a single line into a char array
void loadSingleCfgLine() {
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
if (strncmp(cfgLine, "ip=", 3) == 0) {
strcpy (ipAddr, cfgLine + 3);
}
if (strncmp(cfgLine, "postport=", 9) == 0) {
strcpy (numBuffer, cfgLine + 9);
postport = atoi(numBuffer); // One extra step to convert to int
}
// ... easy to implement for all other data
}
I divided the routines into small independend functions, so its easy adaptable for different uses. I'm sorry for not digging into your code as it is hard to follow and unclear what you want todo.As an added bonus we do not use the String class. These Strings tend to fragment heap - causing resets/crashes while the global char arrays are compiled to flash and don't show this behavior.
I am writing an ESP32 project which receives an UDP packet and based on it some action should be carried out. There are those commands:
FON
FOFF
MSC <INT> <INT>
TC <INT>
I receive the message like this:
void receiveUdpMessages(){
int udpMsgLength = Udp.parsePacket();
if(udpMsgLength != 0){
byte udpPacket[udpMsgLength+1];
IPAddress senderIp = Udp.remoteIP();
Udp.read(udpPacket, udpMsgLength);
udpPacket[udpMsgLength] = 0;
Udp.flush();
Serial.println("Received UDP Message from : " + String(senderIp[0]) + "." + String(senderIp[1]) + "." + String(senderIp[2])+ "."+ String(senderIp[3]));
processReceivedMessage((char *)udpPacket);
}
}
and this is the processReceivedMessage method:
void processReceivedMessage(char *message){
if(strncmp("FON",message,3)==0){
setParameters(ct, 100);
}else if(strncmp("FOFF",message,4)==0){
setParameters(ct, 0);
}else if(strncmp("MSC",message,3)==0){
}else if(strncmp("TC",message,2)==0){
}
}
My question is what is the best way to split both of the ints for the parameterized commands? Also if you notice any sort of issue with above code please tell me I did not have an opportunity to test it yet.
So there's actually two seperate steps here:
Find where the integers substrings start in your message
Convert those substrings to actual ints
For step 1, there are many ways to do this, but using strchr is probably the easiest for your purposes.
For step 2, either use atoi or the safer-but-harder-to-use strtol
Here's an example for the MSC message, the one for processing the TC message will be very similar.
I've kept it as one function for clarity, but there's scope for refactoring it.
void process_msc_message(char * message)
{
int integers[2];
// strchr returns a pointer to the space character
char* substring = strchr(message, ' ');
if (substring)
{
// atoi will convert the first number it finds in the given string
integers[0] = atoi(substring);
}
// Jump forward to the next number
substring = strchr(substring+1, ' ');
if (substring)
{
integers[1] = atoi(substring);
}
// Do something with the integers...
}
I am trying to create a software lighting desk by using Qt and Arduino with a DMX Shield. I've been able to establish communication between these two and can send commands over to Arduino Mega (at the moment the communication goes only one way). I am periodically (every 200 ms) sending values of 11 faders to Mega as a String.
eg.: A123 B234 C050 ... J222 M255
The values in the string above are variables based on the position of the sliders and should be used to adjust the values of light intensities saved into each fader on the Mega side. The Letters in each section identify corresponding fader. A = fader1, B = fader2, ... Just for clarity: I can bring up a light/s at a specific intensity -> these intensities are then assigned to a fader and when that fader is moved I want these values to adjust and be sent out to the actual lights/dimmers. The calculations work fine but my Mega would eventually become unresponsive.
I think my problem is parsing the incoming string. I have tried the strtok() method and readStringUntil() to no avail. It is also difficult to monitor the incoming strings in Serial Monitor as this is used for the communication with Qt.
Would be happy for any kind of help. Please ask questions if anything is unclear.
Edit:
This is one of my attempts at solutions
const char delim[2] = " ";
char *token;
if(Serial.available())
{
//incomingMessage = Serial.readString();
incomingMessage = Serial.readStringUntil("\n"); // read the whole string until newline
//Serial.println(incomingMessage);
const char* str = incomingMessage.c_str(); // convert it to a C String terminated by a null character "\0"
//Serial.println(str);
token = strtok(str, delim); // first part is a first section until delimiter occurs "-space- "
//Serial.println(token);
LX_Rated.commandLineResolve(token); // resolve it
while( token != NULL ) { // continue splitting and resolving the incoming message until it reaches the end
token = strtok(NULL, delim);
LX_Rated.commandLineResolve(token);
}
}
Edit2:
I have confirmed that I receive the whole string sent by Qt. When I try to tokenise it using the strtok() function and print out the first token I get back the whole string, the other tokens are empty. I don't see any mistake in my code here. I even tried to slow down the sending of the string from Qt to one per 5 sec. Does anybody have any idea what is going on? I don't see why this standard function doesn't work as expected. Please see the amended code below.
if(Serial.available()) {
incomingMessage = Serial.readStringUntil("\n");
Serial.println("ok");
Serial.flush();
char* nullTerminatedIncomingMessage = incomingMessage.c_str();
const char delimiter = " ";
char* token;
char* token1;
char* token2;
//char* secondToken;
token = strtok(nullTerminatedIncomingMessage, delimiter);
token1 = strtok(NULL, delimiter);
token2 = strtok(NULL, delimiter);
Serial.println(token); // print the first section
//Serial.println(incomingMessage);
Serial.flush();
Serial.println(token1);
Serial.flush();
Serial.println(token2);
Serial.flush();
//while(token != NULL)
// secondToken = strtok(NULL, delimiter);
//Serial.println(secondToken);
//Serial.flush();
incomingMessage = "";
}
Your mistake - at the very least - is in assuming that all the input is available when you expect it. You need to defer processing until an entire line has been assembled. Serial.readStringUntil blocks until an entire line is available, and that's not what you expect. You essentially need to replace Serial.available() with Serial.lineAvailable(), except the latter is not implemented.
This answer contains a complete solution to your issue - including both Qt and Arduino code - and an Arudino emulation layer. It might be a good starting point, especially that you can easily co-debug both Qt and Arduino projects from within one application and using one debugger!
As for difficulty in monitoring communication, you can(in Qt) dump everything you read into console and do the same for everything you write into the serial port. It will show in the console tab of QtCreator
#include <QDebug>
...
qDebug() << "whatever" << endl;
Aso for parsing the data you read from to serial port, take a look at this to see how to easily split the sliders info into individual strings(with QRegExp)
How Can I Split a String According To Delimiters in Qt?
I can't possibly guess why your arduino would be unresponsive without the code.
EDIT:
Is it possible, when you generate the string in Qt, that you separate the tokens by something other than space? Maybe tab("\t") or something? strtok accepts multiple delimiters in the delimiter string, may be something to try.
If that is not the case, there is the unlikely possibility that something's wrong with the strtok(...) function(btw. it modifies the original string, that in itself could be a problem). Also, strtok could return a NULL pointer, you don't seem to handle that case(some wrong input - print a message). You could try this as an alternative to normal strtok:
/**
* #brief custom strtok replacement with the same interface
* It does not modify the original string
* Token length is limited to 63 characters
* #param ptr pointer to the string or NULL
* #param delim delimiting character(only the first character will be used)
*/
const char * my_strtok(const char * ptr, const char * delim) {
// Persistent variables, it will remember pointer to the processed string
static const char * src;
static char buffer[64]; // Token is limited to 63 characters
if(ptr) { // Remember the pointer, if a new one was supplied
src = ptr;
}
if(src == NULL || *src == '\0')// Invalid / empty string / no next token - return NULL
return NULL;
char i = 0;
for(i = 0; i < 63 && *src != delim[0]; i++) {// Copy token until delimiter or end of buffer
buffer[i] = *(src++);
}
if(*src == delim[0]) // Skip over the delimiter to the begining of the next token
++src;
buffer[i] = '\0'; // Any returned string must be terminated
return buffer;
}
#include <cstdlib>
#include <cstring>
#include <cassert>
void test() {
const char * str1 = "123 456 asdf jkl;";
assert(strcmp("123", my_strtok(str1, " ")) == 0);
assert(strcmp("456", my_strtok(NULL, " ")) == 0);
assert(strcmp("asdf", my_strtok(NULL, " ")) == 0);
assert(strcmp("jkl;", my_strtok(NULL, " ")) == 0);
assert(NULL == my_strtok(NULL, " "));
assert(NULL == my_strtok(NULL, " "));
assert(strcmp("123", my_strtok(str1, " ")) == 0);
}
I want to turn ON and OFF, three LEDS connected to arduino, using a python GUI, thus I use pyserial. The state of the LEDs is described by a tuple
RGB = (Red On/Off, Green On/Off, Blue On/Off), e.g. (1,0,1)
Since pyserial.write() works only with strings, let's say that RGB = "101"
I want to sent to arduino the RGB string, split it to three values 1, 0 and 1, and set LOW or HIGH three output pins, depending on the incoming values.
The python code works fine:
import serial
ser = serial.Serial('/dev/ttyACM4', 9600)
ser.write('101')
This is the arduino code:
void setup() {
Serial.begin(9600);
Serial.println("Ready");
}
void loop() {
char input_data = ' ';
if(Serial.available()){
char input_data = {Serial.read()};
Serial.println(input_data);
}
delay(100);
}
The print line is only for inspection purpose.
Can I somehow split the input_data string, and retrieve its values, like:
int R = input_data[0];
int G = input_data[1];
int B = input_data[2];
Any suggestion would be appreciated.
OK, so one quick clarification here (as verified by your comment): input_data is not a string, it is a single char (one byte). Also, serial.read() returns a single byte (the next in the serial stream) and does so as an int, so you're implicitly casting int to char in your code. This is fine, just wanted to make that clear.
What you can do is make input_data an array of chars, and store each new byte in a new location in the array. Then you'll have all three numbers you're looking for. Then, all that's left is to turn input_data[i] into a LOW or HIGH value to be passed to each of your LED pins. What I would do is within your for loop, test each value read from the serial.read() function to see if it's value is '0' or '1' and then storing true or false in another array of bools. Finally, you can test that array and set the corresponding pin 'HIGH' or 'LOW' as necessary. Please keep in mind what I mentioned earlier in the comments, that you're going to want to test the received data as the characters 1 and 0 and not the numbers 1 and 0.
Here's a quick snippet as an example:
int[3] pins; //Initialize this to contain the numbers of the R, G, and B pins.
void setup() {
Serial.begin(9600);
Serial.println("Ready");
}
void loop() {
char[3] input_data;
bool[3] low_high;
if(Serial.available() >= 3){
for (int i=0; i<3; i++) {
input_data[i] = Serial.read();
if(input_data[i] == 48) //ASCII for 0
low_high[i] = false;
if(input_data[i] == 49) //ASCII for 1
low_high[i] = true;
//Serial.println(input_data);
}
}
for (int j=0; j<3; j++) {
if(low_high[j]) {
digital_write(pins[j],'HIGH');
} else {
digital_write(pins[j], 'LOW');
}
}
delay(100);
}
That should get you up and running. Please keep in mind that this example has very limited input checking, so you'll need to beef it up a bit (i.e. checking for more than 3 bytes ready, making sure you're getting char's and not gibberish, etc.).