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...
}
Related
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.
I'm desperatly trying to get arduino to divide a string from processing into two sets of variables. In the code below I've decided to just type the important parts but x and y does of course contain the correct values. Any solution would be appreciated. These are my two attempts so far:
Attempt 1 doesn't work at all.
1.Processing:
myPort.write(x + "," + y + "\n");
1.Arduino:
String tempX = Serial.readStringUntil(44);
String tempY = Serial.readStringUntil(10);
String x = tempX.substring(0,tempX.length() -1);
String y = tempY.substring(0,tempY.length() -1);
Attempt 2 where x works correctly but not y.
2.Processing:
String [] dataToSend = new String [2];
dataToSend [0] = x;
dataToSend [1] = y;
String joinedData = join(dataToSend, ":");
myPort.write(joinedData);
2.Arduino:
String x = Serial.readStringUntil(":");
Serial.read(); //next character is comma, so skip it using this
String y = Serial.readStringUntil('\0');
First, don't worry about combining them on the Processing side. Sending two strings one right after the other is the same as sending one long string. It's all being broken into bytes on the Serial line and nobody can tell where one print line stops and the next starts.
myport.write(x);
myport.write(',');
myport.write(y);
myport.write('\n')
will work just as good.
Then on the Arduino side you most likely want to shy away from the String class. Read the data character by character into a char array.
char myArray[howLongTheStringIs];
char x[howLongXIs];
char y[howLongYIs];
int index = 0;
This gets called over and over in loop and picks up serial data as it comes in:
while (Serial.available()){
char c = Serial.read();
myArray[index] = c; // add c to the string
myArray[++index] = 0; // null terminate our string
if(c == '\n'){ // if we are at the end of the string
handleString();
}
}
Then you have a function to parse your string there are lots of ways to do that:
If you don't know anything about the strings other than the separator use strtok:
void handleString(){
char* ptr = strtok(myArray, ":"); // get up to the ":" from the string
strcpy(x, ptr); // copy into x
ptr = strtok(NULL, "\n"); // get from the separator last time up to the next "\n"
strcpy(y, ptr); // copy into y
index = 0 // reset our index and
myArray[0] = 0; // and clear the string
}
That's all untested and uncompiled and written in the reply box, so if I made a little typo in there please forgive and correct. But something like this should work. If you already know the exact lengths of the strings (or can send them from the processing code) then the handleString method can be simpler. If you've got something short to do with x and y and don't need them after that then maybe you can just keep pointers to where they are in myArray. It all depends on what the larger picture goal of your code is. But something like this should get the job done.
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 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);
}
can some one help me to rectify this error i have am using VS2010 ASP.Net C#3.0 i found this could on net but it is not working as it give me the error as show in the Screen shot. I am not able to understand the error message
Code reference from http://weblogs.asp.net/abdullaabdelhaq/archive/2009/06/27/displaying-arabic-number.aspx
CODE :
protected void Button1_Click(object sender, System.EventArgs e)
{ //Call Function
this.Label1.Text = "Arabic Number : <b>" + TranslateNumerals(this.TextBox1.Text) + "</b>";
}
public static string TranslateNumerals(string sIn)
{
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
System.Text.Decoder utf8Decoder = null;
utf8Decoder = enc.GetDecoder();
dynamic sTranslated = new System.Text.StringBuilder();
char[] cTransChar = new char[2];
byte[] bytes = {217,160 };
// Start Converting characters into Arabic mode.
char[] aChars = sIn.ToCharArray();
foreach (char c in aChars)
{
if (char.IsDigit(c))
{
bytes[1] = 160 + Convert.ToInt32(char.GetNumericValue(c));
utf8Decoder.GetChars(bytes, 0, 2, cTransChar, 0);
sTranslated.Append(cTransChar[0]);
}
else
{
sTranslated.Append(c);
}
}
return sTranslated.ToString();
}
The compiler is complaining about your request to implicitly convert the result of the integer addition (160 + Convert.ToInt32...) to a byte. This is a narrowing conversion; integers have a wider range than bytes do, and the compiler wants you to acknowledge, with an explicit cast operator, that you're aware that this could produce runtime errors.
Assuming that adding 160 actually does something useful to a character, I'd advise using something like this instead.
if (c >= '0' && c <= '9')
{
bytes[i] = (byte)((int)c + 160);
}
...which will properly produce, as the i'th value of the array bytes, a byte with a value 160 greater than the char c, if c represents an ASCII digit between 0 and 9.
Don't use the function char.IsDigit, which will return true even if c is a digit outside of the ASCII digit range. I don't run into this much, but since you're explicitly writing multilingual code, you'll want to handle that case.
I haven't reviewed the rest of the post you link to, but given these two quite obvious errors, I wouldn't put much faith in the correctness of the rest of it, frankly.
You could add a cast like (byte) in front of the 160.
I'm a little confused why such a large function is required to do that.
Updated answer:
Given that there are only 10 possible numerals to convert between, you can do something like this to make it slightly more readable. I think, anyway, I don't usually code in c#, and I'm on a mac right now... feel free to bash my code.
public static string TranslateNumerals(string sIn)
{
var sb = new System.Text.StringBuilder();
char[] arabic = { '٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'};
foreach (char c in sIn) {
int idx;
// if it wasn't a number, just append it, otherwise convert it
if(!Integer.tryParse("" + c, idx)) {
sb.Append(c);
} else {
sb.Append(arabic[idx]);
}
}
return sb.toString();
}
// backwards array (which appears frontwards here)
{ '٩','٨','٧','٦','٥','٤','٣', '٢', '١', '٠'};