I'm working with a project that consist on sending data that I receive from the console terminal from Arduino IDE. I have been able to get the data that I have typed inside the console and store on a variable. My variable is a String, when I get the data that I have typed from the console, it comes like this: This is text. I would need to concat one quote to the start and one to the end, so it will stay like: "This is text". I would need that because to write the data inside esp32 SPIFFS it has to be inside quotes.
String wifi_name;
if (Serial.available()){
wifi_name = Serial.readString();
}
if (wifi_name == NULL){
Serial.print("File size: ");
Serial.print(file.size());
Serial.println(", O valor dentro da string é nulo, nada será adicionado ao arquivo");
delay(2500);
}else{
write_file_info(wifi_name);
}
void write_file_info(String message) {
file = SPIFFS.open("/wifi.txt", FILE_WRITE);
if (!file){
Serial.println("Error opening file");
return;
}else{
Serial.println("Success opening file");
}
if (file.print("\message\")){ //This line I would need to have quotes in the end and start
Serial.println("File was written");
}else{
Serial.println("File was not written");
}
file.close();
}
I would need that the message part if (file.print("message")) stayed between quotes, but since it is a String, I can't apply the quotes.
https://www.arduino.cc/en/Tutorial/BuiltInExamples/StringAdditionOperator
And to concat special character like " you can use escape character like this "\"" + text + "\"" .
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.
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);
}
I have the following code
global
File logFile
and inside loop()
DateTime now = rtc.now();
String stringFilename = "";
stringFilename = String(now.year())+"_"+String(now.month())+"_"+String(now.day());
stringFilename += ".csv";
Serial.println(stringFilename);
char filename[stringFilename.length() + 1];
stringFilename.toCharArray(filename, sizeof(filename));
String dataString = "";
dataString += String(now.year());
Serial.print("Filename: ");
Serial.println(filename);
if (!SD.exists(filename)){
Serial.println("File does not exist");
logFile = SD.open(filename, FILE_WRITE);
}
if (logFile){
logFile.println(dataString);
}else{
Serial.println("Could not open file");
}
Filename is something like 2014_5_26.csv. SD empty. The above code won't open file for writing. If I substitute filename with a literal like "datalog.txt" it works. What am I doing wrong?
EDIT: I got what my problem was. The name was to long. It can be only up to 8 characters. Can I ask something different? Can I call SD.begin(chipSelect) in loop to determine if SD is present or is this called always on setup?
You can use it in loop but the program speed will be reduce.
Don't forget to close your file (SD.close()) before to be sure that your data are correctly written to the SD card
8.3 filename rules may apply. Your Date derived ("2014_5_26") filename appears to have 9 characters. When you use "datalog", that has 8 characters, it works.
How can i find a specific character in a QFile which has a text in it?
for example i have ' $5000 ' written somewhere in my file. in want to find the "$" sign so i will realize that I've reached the number.
I tried using QString QTextStream::read(qint64 maxlen) by putting 1 as the maxlen :
QFile myfile("myfile.txt");
myfile.open(QIODevice::ReadWrite | QIODevice::Text);
QTextStream myfile_stream(&myfile);
while(! myfile_stream.atEnd())
{
if( myfile_stream.read(1) == '$')
{
qDebug()<<"found";
break;
}
}
and i get "error: invalid conversion from 'char' to 'const char* "
i also tried using the operator[] but apparently it can't be used for files.
Read in a line at a time and search the text that you've read in
QTextStream stream(&myFile);
QString line;
do
{
line = stream.readLine();
if(line.contains("$"))
{
qDebug()<<"found";
break;
}
} while (!line.isNull());
The error message you've posted doesn't match the issue in your code. Possibly the error was caused by something else.
QTextStream::read returns QString. You can't compare QString and const char* directly, but operator[] can help:
QString s = stream.read(1);
if (s.count() == 1) {
if (s[0] == '$') {
//...
}
}
However reading a file by too small pieces will be very slow. If your file is small enough, you can read it all at once:
QString s = stream.readAll();
int index = s.indexOf('$');
If your file is large, it's better to read file by small chunks (1024 bytes for example) and calculate the index of found character using indexOf result and count of already read chunks.
a single char could be read with
QTextStream myfile_stream(&myfile);
QChar c;
while (!myfile_stream.atEnd())
myfile_stream >> c;
if (c == '$') {
...
}
myfile_stream.read(1) - this is not good practice, you should not read from file one byte at a time. Either read the entire file, or buffered/line by line if there is a risk for the file to be too big to fit in memory.
The error you get is because you compare a QString for equality with a character literal - needless to say that is not going to work as expected. A string is a string even if there is only one character in it. As advised - use either the [] operator or better off for reading - QString::at() const which is guaranteed to create no extra copy. You don't use it on the QFile, nor on the QTextStream, but on the QString that is returned from the read() method of the text stream targeted at the file.
Once you have the text in memory, you can either use the regular QString methods like indexOf() to search for the index of a contained character.
in want to find the "$" sign so i will realize that I've reached the
number.
It sounds to me that you're searching for the '$' symbol because you're more interested in the dollar value that follows it. In this case, I suggest reading the files line by line and running them through a QRegExp to extract any values you're looking for.
QRegExp dollarFind("\\$(\\d+)");
while(!myfile_stream.atEnd()){
QString line = myfile_stream.readLine();
if (dollarFind.exactMatch(line)){
QStringList dollars = dollarFind.capturedTexts();
qDebug() << "Dollar values found: " << dollars.join(", ");
}
}