Im having trouble concatenating a string with an int - arduino

I have the below code to read a light sensor, convert to lux, concat with "lux." and send it to my SmartThings cloud.
Ultimately I want a value sent to SmartThings formatted like lux.110
void checkLux() {
float logLux = analogRead(lightPIN) * logRange / rawRange;
int luxValue = pow(10, logLux);
String statusUpdate = "lux." + luxValue;
Serial.println(statusUpdate);
smartthing.send(statusUpdate);
delay(1000);
}
This above code spits out some weird combination of characters to the serial monitor and doesnt print lux. or the luxvalue.
If I add this line String luxString = "lux."; and modify the line below, it all works great. Any thoughts on why I need to declare this string separately. According to the documentation either should work fine.
Also if there are any suggestions on variable savings within this block of code. I am not that great at it yet.

As Arduino just uses C++ most C++ functions will also work so dont just limit yourself to Arduino's reference pages.
Apparently the String constructor doesn't support numbers, you must convert them using the String() function first as seen here.
Alternatively I think you can append a string like this:
String statusUpdate = "lux.";
statusUpdate += luxValue;
as seen here,
which is the same as using String's concat function.
statusUpdate.concat(luxValue);

Related

Arduino's string.toDouble() not working

I am trying to convert a string to a double in one of my Arduino projects (specifically using a Teensy 3.5 in the Arduino IDE) using Arduino's string.toDouble() command. When I try to implement the function as shown in the code below, I get the error:
<'class String' has no member named 'toDouble'>.
However, string.toFloat() and string.toInt() work successfully.
Any ideas as to what is going wrong?
String myNumberString = "100";
double myNumber = 0;
void setup() {
Serial.begin(9600);
}
void loop() {
myNumber = myNumberString.toDouble()+1;
Serial.println(myNumber);
myNumberString = String(myNumber);
delay(1000);
}
The problem you are having is that arduino declares myNumberString as a String Object, so you can't use toDouble() to convert the string into a double because the function is not defined in the String class. You will have to use toFloat to convert your string. Here's the link I used to find this.
Seem like the Teensy's Arduino core is missing that function.
I only see toInt and toFloat inside Teensy's implementation of the String class. While the original Arduino core has it implemented.
Maybe you can use atof directly, like:
myNumber = atof(myNumberString.c_str());

Communication issue printing from Arduino to Qt using QSerialPort

I am having problems communicating FROM the arduino to my Qt application through QSerialPort. I have a listening signal that tells me when there is data ready to be read from the arduino. I expect a value for the number of steps that a stepper motor has undertaken before hitting a limit switch, so only a simple int such as "2005". When the data is available for reading, sometimes I get two separate reads with "200" and "5". Obviously this messes things up when I am parsing the data because it records it as two numbers, both much smaller than the intended number.
How can I fix this without me putting in a Sleep or QTimer to allow for a bit more time for the data to come in from the arduino? Note: my program is not multithreaded.
Example Qt code:
//Get the data from serial, and let MainWindow know it's ready to be collected.
QByteArray direct = arduino->readAll();
data = QString(direct);
emit dataReady();
return 0;
Arduino:
int count = 2005;
Serial.print(count);
You can add line break to synchronize.
Example Qt code:
//Get the data from serial, and let MainWindow know it's ready to be collected.
QByteArray direct = arduino->readLine();
data = QString(direct);
emit dataReady();
return 0;
Arduino:
int count = 2005;
Serial.print(count);
Serial.println();
If you are going to use QSerialPort::readyRead signal, you need to also use the QSerialPort::canReadLine function, see this.
Thank you for your help Arpegius. The println() function was definitely a good choice to use for the newline delimiter. And following that link, I was able to get a listening function that got everything the arduino sent as seperate strings. The extra if statements in the loop handle any cases where the incoming string does not contain the newline character (I am paranoid :D)
My code for anyone that has the same problem in the future.
int control::read()
{
QString characters;
//Get the data from serial, and let MainWindow know it's ready to be collected.
while(arduino->canReadLine())
{
//String for data to go.
bool parsedCorrectly = 0;
//characters = "";
//Loop until we find the newline delimiter.
do
{
//Get the line.
QByteArray direct = arduino->readLine();//Line();
//If we have found a new line character in any line, complete the parse.
if(QString(direct).contains('\n'))
{
if(QString(direct) != "\n")
{
characters += QString(direct);
characters.remove(QRegExp("[\\n\\t\\r]"));
parsedCorrectly = 1;
}
}
//If we don't find the newline straight away, add the string we got to the characters QString and keep going.
else
characters += QString(direct);
}while(!parsedCorrectly);
//Save characters to data and emit signal to collect it.
data = characters;
emit dataReady();
//Reset characters!
characters = "";
}
return 0;
}

Pushing values to a Vector of pointers has garbage values

I am a C++ noob and I have written a method to get text file names including full-paths from a given directory. It gives a garbage value on the vector<wchar_t*> names. I used VS2010 debugger and analysed values. It looks like pointers are going out of scope. On the official C++ reference it says that push_back() copies values and seems like as I am pushing a pointer and it just copies pointers value.
static std::vector<wchar_t*> getFileNames(wchar_t* folder) // ex: c:\\textfiles\\My
{
using namespace std;
vector<wchar_t*> names;
wchar_t search_path[200];
swprintf(search_path, L"%s\\*.txt", folder); // ex: c:\\textfiles\\My\\*.txt
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile((wchar_t*)search_path, &fd);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
if(! (fd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) )
{
std::wstring fullPath(folder);
fullPath += L"\\";
fullPath += std::wstring(fd.cFileName); // cFilename has something like Info.txt
names.push_back((wchar_t*)fullPath.c_str());
}
}while(FindNextFile(hFind, &fd)); //goes out of scope and values become garbage
FindClose(hFind);
}
return names; //vector with garbage values
}
Is it possible to get wchar_t* pushed in to the vector someway, a better work around rather than dynamically allocating memory or using heap variables?
Can I get compiler warning for mistakes like this on VS2010 or any VS version(Now I only get casting warning and errors)?
swprintf might overflow the buffer.
(wchar_t*)search_path is a superfluous cast.
Never break out the big guns if you don't need them.
if(! (fd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) ) means you only ignore directories without interesting attributes.
fullPath += std::wstring(fd.cFileName); another superfluous cast.
names.push_back((wchar_t*)fullPath.c_str()); pushes a pointer to the internal buffer of fullpath into names, even though it will be destroyed at the end of the block.
What you should do is change the signature to
static std::vector<std::wstring> getFileNames(std::wstring folder)
Or at least
static std::vector<std::unique_ptr<wchar_t>> getFileNames(wchar_t* folder)
to take advantage of RAII and reduce the chance for errors.
In any case, you should rewrite the function, and should take advantage of the standard-library internally.
As an example, with the proper prototype:
static std::vector<std::wstring> getFileNames(std::wstring folder) {
std::vector<std::wstring> names;
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile((folder+L"\\*.txt").c_str(), &fd);
if(hFind == INVALID_HANDLE_VALUE)
return names;
auto lam = [](HANDLE* p){FindClose(*p);}
std::unique_ptr<HANDLE, decltype(lam)> guard(&hFind, lam);
folder += L"\\";
do {
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
names.push_back(folder+fd.cFileName);
} while(FindNextFile(hFind, &fd));
return names;
}
Ideally, you should store class objects in your vector, instead of character pointers (or wchar_t pointers). You can do it, but it requires some extra work. That's the part that you're missing.
The issue you're seeing is exactly what you describe: the character arrays that are owned by the wstring objects are being destroyed when the wstring goes out of scope. By using wstring::c_str(), you are not creating an independent copy of the character array, you're just looking at the one that it already has created for its own use.
So you need a way to keep a character array around longer. Either you could use the wstring, or you need to copy the character array into one of your own.
The minimal change would be something like this:
std::wstring fullPath(folder);
fullPath += L"\\";
fullPath += std::wstring(fd.cFileName); // cFilename has something like Info.txt
wchar_t *wsz = new wchar_t[fullPath.size() + 1];
wcsncpy(wsz, fullPath.c_str(), fullPath.size());
names.push_back(wsz);
That's enough to get your strings into the vector, but because you're using character pointers, it's also your responsibility to clean them up. So when you are done using the vector, you will need to iterate through and delete each one of them before you let the vector be destroyed.
As I mention in a comment below, it's much simpler to use a std::vector. There are libraries that can help you with the memory management parts, if you must use wchar_t*. You caould take a look at the Boost Smart Pointer library, for example.
The variable fullPath is a local variable.
It goes out of scope.
That is the reason the pointers are becoming junk.
Allocate a dynamic memory as
vector<wstring*> names;// instead of vector<wchar_t*> names;
std::wstring *fullPath = new wstring(folder);
Never store references of objects into a list if you are not aware of their scope.

Qt Check QString to see if it is a valid hex value

I'm working with Qt on an existing project. I'm trying to send a string over a serial cable to a thermostat to send it a command. I need to make sure the string only contains 0-9, a-f, and is no more or less than 6 characters long. I was trying to use QString.contains, but I'm am currently stuck. Any help would be appreciated.
You have two options:
Use QRegExp
Use the QRegExp class to create a regular expression that finds what you're looking for. In your case, something like the following might do the trick:
QRegExp hexMatcher("^[0-9A-F]{6}$", Qt::CaseInsensitive);
if (hexMatcher.exactMatch(someString))
{
// Found hex string of length 6.
}
Update
Qt 5 users should consider using QRegularExpression instead of QRegExp:
QRegularExpression hexMatcher("^[0-9A-F]{6}$",
QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = hexMatcher.match(someString);
if (match.hasMatch())
{
// Found hex string of length 6.
}
Use QString Only
Check the length of the string and then check to see that you can convert it to an integer successfully (using a base 16 conversion):
bool conversionOk = false;
int value = myString.toInt(&conversionOk, 16);
if (conversionOk && myString.length() == 6)
{
// Found hex string of length 6.
}

Arduino+Ethernet-SD shield, listing all filenames existing files on the SD card

I have:
Arduino MEGA 2560;
Ethernet+SD shield http://www.amazon.com/dp/B0022TWQ22/?tag=stackoverfl08-20 ;
SD card 2GB FAT.
SD contains 400 files with names 00000000; 0000001; 0000002; ... 00000098; 0000099; 0000100; ... 00000398; 00000399.
I need to construct String var which will contain all the Filenames separated by ";" like this:
sdata = "0000001;0000002;0000003 ... 00000398;00000399;";
Code:
#include <SdFat.h>
#include <SPI.h>
const uint16_t chipSelect = SS;
char cnamefile[9];
String sdata="";
SdFat sd;
SdFile file;
void setup() {
Serial.begin(9600);
Serial.println("hi");
sdata="";
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
Serial.println("List files");
while (file.openNext(sd.vwd(), O_READ)) {
file.getFilename(cnamefile);
file.close();
sdata = sdata + String(cnamefile) + ";";
}
Serial.print(sdata);
}
void loop() {
}
Listening to the COM port i see:
hi
List files
00000005;00000006;00000007;00000008;00000009;00000010;00000011;00000012;00000013;00000014;00000015;00000016;00000017;00000018;00000019;00000020;00000021;00000022;00000023;00000024;00000025;00000026;00000027;00000028;00000029;00000030;00000031;00000032;00000033;00000034;00000035;00000036;00000037;00000038;00000039;00000040;00000041;00000042;00000043;00000044;00000045;00000046;00000047;00000048;00000049;00000050;00000051;00000052;00000053;00000054;00000055;00000056;00000057;00000058;00000059;00000060;00000061;00000062;00000063;00000064;00000065;00000066;00000067;00000068;00000069;00000070;00000071;00000072;00000073;00000074;00000075;00000076;00000077;00000078;
How to fix this problem and put all filenames in one variable?
Information for: 400 names and 400 ";" its 3600 bytes. When i try to read any file and put all its contents (more than 3600 bytes) in "String sdata" it works normally. Problem only with listing.
Please help me in sorting out this issue.
This seems about the correct place that your program will fail. This innocent line is your problem:
sdata = sdata + String(cnamefile) + ";";
String concatentation like this will use 2X the memory of the sdata for a short moment. This is the how you should view the sequence of operations in that one line
// compiler has done this for you:
String temp1234 = sdata + String();
// note that at this moment, memory usage is now 2x sdata
String temp1235 = temp1234 + ";";
// now you can have 3x the memory used
// assignment to variable
sdata = temp1235;
// now can delete temporary variable
// compiler will do this
//temp1234.delete()
//temp1235.delete()
You are trying to create strings up to 3k bytes but have only 8k total RAM, so will not be able to do the above.
This demonstrates a couple of points about Strings. Your concatenation above on one line, is not necessarily better than this two line form:
sdata = sdata + String(cnamefile);
sdata = sdata + ";";
In this second form, you are ensured there will only be one temporary variable for the intermediate result.
This leads to the next hint. You should be thinking how am I going to escape the temporary variable. That is why we have += operator. Your best chance is to concatenate like this:
sdata += String(cnamefile);
sdata += ";";
If the += operator is available on the String class, the compiler will use this. That operator may be able to use a more memory efficient way of concatenation. For example, if the String was preallocated with some extra memory, then it could just place the new characters into the existing buffer.
In general this is a great learning method about strings in constrained memory spaces, because you must understand some compiler internals and operator details that are often ignored in large CPU environments.
Given the sizes you are suggesting, you will probably only be able to fit in RAM if you change to an approach of pre-constructing a String buffer and filling it with the file names. In other words: don't use String on a microcontroller.

Resources