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.

4
  • This class is not a “stream” in the C++ sense, it's a Stream in the Arduino sense, i.e. it provides an interface similar to Serial. You can for instance print() and println() to it. Jan 27, 2017 at 19:31
  • @EdgarBonet so in that case how can I use sqlQuery method of storing to a variable in arduino without having to use print(), println or sprintf ? Jan 27, 2017 at 19:50
  • The whole purpose if this StringStream is to let you print() and println() into it. Jan 27, 2017 at 20:29
  • 1
    The reference to StringStream is to "#include <sstream>" which is very much a stream in the C++ sense, not in the Arduino sense. The OP is looking to use std C++ in Arduino. The answer is to use the GNU tool chains used by (and installed with) the Arduino IDE, but without the ino preprocessor. There is no class StringStream. The class is ostringstream, and its purpose is to provide appendage methods via the << operator. Println is never used. The \n, \r\n, or preferably std::endl is used to terminate a line and sprintf may or may not be used underneath the ostringstream methods. Jan 30, 2017 at 17:01

2 Answers 2

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.

4
  • well, I do not have Arduino Yun, but I was rather going to buy [Arduino Yun Shield][1], I'd assume it should not make difference , correct me if I am wrong please ! [1]: arduino.cc/en/Main/ArduinoYunShield Jan 29, 2017 at 4:58
  • Yun board != Yun shield Jan 29, 2017 at 9:42
  • 1
    Thanks Douglas, best answer I have accepted however just little bit more trouble I would appreciate if in your answer you can link up the text "cross compile" , "link using the GNU tool" and "set that targets the Atmel CPU" example on web. It will seed the process of learning. However I would refrain from directly jumping to higher step. Jan 29, 2017 at 11:20
  • 1
    This answer is almost exactly the road we take here at the lab, and it works very well. Most of the serious embedded programmers use microemacs to develop. We tried using the std:: namespace in sketches for containers and strings, but the advantage to moving to g++ and a makefile became obvious in the first week of writing actual programs. The way outlined here has worked for three years now and triples our development speed. Jan 30, 2017 at 16:54
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& str() const {
            return s;
        }

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

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

        // Single char: 'a', '\n', ...
        ostringstream& operator<<(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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.