0

I learned about StringStream today at work.

#include <iostream>
#include <sstream>
#include <stdio.h>
#include <string.h>

using namespace std;

char* getSql() {
    float outdoorTempInC = 15;
    int outoorHumidity = 23;
    float indorTempinC = 20;
    int humidity = 22;
    int val = 400; // soil sensor reading
    int avgVal = 38; // avaerage of 10 values of soils sensor
    char statusMsg[50] = {"Hello World"} ;
    std::stringstream sqlQuery;
    sqlQuery  << "INSERT INTO arduinoSensorData.sensorLog (out_temperature,  " 
        << "out_humidity,  drwngRoom_temperature, drwngRoom_humidity, "
        << "pot1_soilMoisture, pot1_avg_SoilMoisture, wateringPot1) VALUES ('" 
        << outdoorTempInC << "', '" << outoorHumidity << "', '" << indorTempinC
        << "', '" << humidity << "', '" << val << "', '" << avgVal << "', '"
        << statusMsg << "');";

    char *mychar = new char[sqlQuery.str().length() + 1];
    strcpy(mychar, sqlQuery.str().c_str());
    return mychar;
}

string getSqlStream() {
    float outdoorTempInC = 15;
    int outoorHumidity = 23;
    float indorTempinC = 20;
    int humidity = 22;
    int val = 400; // soil sensor reading
    int avgVal = 38; // avaerage of 10 values of soils sensor
    char statusMsg[50] = {"Hello World"} ;
    std::stringstream sqlQuery;
    sqlQuery  << "INSERT INTO arduinoSensorData.sensorLog (out_temperature,  " 
        << "out_humidity,  drwngRoom_temperature, drwngRoom_humidity, "
        << "pot1_soilMoisture, pot1_avg_SoilMoisture, wateringPot1) VALUES ('" 
        << outdoorTempInC << "', '" << outoorHumidity << "', '" << indorTempinC
        << "', '" << humidity << "', '" << val << "', '" << avgVal << "', '"
        << statusMsg << "');";
    return sqlQuery.str();
}

int main() {
    string sql = getSqlStream();
    cout << getSql() << "\n";
    cout << sql.c_str() << "\n";
    return  0;
}

I was very happy until I discovered I cannot use StringStream in Arduino, so I dug in the internet and came across with this.

So if I try to use the above linked gist StringSteam like I have used in my example code, I am getting this error:

error: no match for 'operator<<' (operand types are 'StringStream' and 'const char [60]')
     sqlQuery  << "INSERT INTO arduinoSensorData.sensorLog (out_temperature,  " 
               ^
webServer_Displaying_everything_v01:24: error: 'outdoorTempInC' was not declared in this scope
         << outdoorTempInC << "', '" << outoorHumidity << "', '" << indorTempinC
            ^
webServer_Displaying_everything_v01:24: error: 'outoorHumidity' was not declared in this scope
         << outdoorTempInC << "', '" << outoorHumidity << "', '" << indorTempinC
                                        ^
webServer_Displaying_everything_v01:24: error: 'indorTempinC' was not declared in this scope
         << outdoorTempInC << "', '" << outoorHumidity << "', '" << indorTempinC
                                                                    ^
webServer_Displaying_everything_v01:25: error: 'humidity' was not declared in this scope
         << "', '" << humidity << "', '" << val << "', '" << avgVal << "', '"
                      ^
webServer_Displaying_everything_v01:25: error: 'val' was not declared in this scope
         << "', '" << humidity << "', '" << val << "', '" << avgVal << "', '"
                                            ^
webServer_Displaying_everything_v01:25: error: 'avgVal' was not declared in this scope
         << "', '" << humidity << "', '" << val << "', '" << avgVal << "', '"
                                                             ^
webServer_Displaying_everything_v01:28: error: 'class StringStream' has no member named 'str'
     char *mychar = new char[sqlQuery.str().length() + 1];
                                      ^
webServer_Displaying_everything_v01:29: error: 'class StringStream' has no member named 'str'
     strcpy(mychar, sqlQuery.str().c_str());

Or I am doing it completely the wrong way? It feels I still cannot use it the way I have done in the example. Please help.

dda
  • 1,595
  • 1
  • 12
  • 17
Ciasto piekarz
  • 575
  • 3
  • 12
  • 28

2 Answers2

3

Direct Answer

Program in C++ and cross compile to the Arduino Yún. The Arduino Sketch language is a thin layer over C++, but the std:: namespace is not light weight to the degree it would need to be to fit the things you are appreciating from the standard C++ library into the memory footprint of a small embedded CPU board. See Arduino Yun C++ environment? Bridge + Cross-Compiler.

Serious Programming on Arduino

If you wish to open the door to C++ elegance on smaller embedded CPU boards like the simpler Arduinos, you will need to move beyond the Arduino sketch language. You may find it both fun and useful to learn how to cross compile and link using the GNU tool set that targets the Atmel CPU you have on your board.

This more professional approach is not for the faint at heart, but it is done all the time. We do it here in TranSeed Labs every day.

To get started, you don't need to download or install anything other than the Arduino software. The tool set that targets Atmel's AVR architecture (used by several Arduino boards) is in a sub-directory under the Arduino IDE's home directory. The tool set that targets Atmel's SAM Architecture for the Arduino Due is placed in a sub-directory of your home directory when you set up the IDE to upload sketches to the Due.

Returning to the Question in This C++ Context

Once you learn how to compile and link using g++ from the GNU tool chain1 and use the upload tools2 your program via the USB port, you can create a template class to get the elegance available in C++ like the operator overloading that you like.

The issue with trying to employ the C++ library with embedded targets is that the allocation system and other dependencies will probably exhaust much if not all of your program memory. The ostringstream class from the C++ library, with all its dependencies included, is not extremely lean.

An approach that consumes very little memory and does not require the CPU cycles of dynamic allocation is to create a C++ template that wraps a static array. Its buffer size can be one of the template parameters.

Then you can overload the << operator of the class using these examples as a guide. You get your elegance and drastically reduce your computing resource requirements.


[1] Both cross compiling and cross linking are done with g++ (gcc for the Aruino and Atmel C files in the library). For Arduino boards using the AVR based Atmel CPUs, use the AVR GNU Tool Chain. For the Due, which uses the SAM based Atmel CPUs, use the SAM5X GNU Tool Chain.

[2] The program bossac is used to upload to the Due. The program avrdude is used to upload to the Mega 2560.

Douglas Daseeco
  • 274
  • 1
  • 7
1

It has nothing to do with cross compiling or linkage using the GNU tool. IO streams do present on Arduino but attempt to include it result in too big binary. It simply does not fit on chip's memory.

There are a couple of solutions to this problem. One is to use a library called ard-streams. Another one is to use a lightweight, simplified version of stringstream just enough to compile your code.

#include <string>

namespace ard { struct ostringstream { ostringstream() = default; ostringstream(std::string arg) : s(std::move(arg)) {}

    const std::string&amp; str() const {
        return s;
    }

    // Arithmetic types: int, float, ...
    template &lt;class T&gt;
    auto operator&lt;&lt;(T arg) -&gt; decltype(std::to_string(arg), *this) {
        s.append(std::to_string(arg));
        return *this;
    }

    // String types: std::string, const char*
    template &lt;class T&gt;
    auto operator&lt;&lt;(const T&amp; arg) -&gt; decltype(std::string().append(arg), *this) {
        s.append(arg);
        return *this;
    }

    // Single char: 'a', '\n', ...
    ostringstream&amp; operator&lt;&lt;(char arg) {
        s.append(1, arg);
        return *this;
    }

private:
    std::string s;
};

}

Now just replace std::stringstream with ard::ostringstream and it will work as expected.