These are chat archives for esp8266/Arduino

20th
Jan 2017
Ash
@ashthespy
Jan 20 2017 02:32 UTC

Question for people with C/C++ knowhow!
I am trying to return a formatted char array from one of my classses, and and wondering if I am going about it right.

const char*  sensor::printsensors()
{
        char * buf;   // But is there enough space allocated with this pointer?
        snprintf(buf, sizeof(buf), "Ti: %u, To: %u, Hi: %u, Ho: %u\n", _Ti,_To,_Hi,_Ho);
        return buf;
}

Would this be sub optimal? I would like to use this function directly in a printf statement - Serial.printf("%s", sensor.printsensors());

Ash
@ashthespy
Jan 20 2017 02:47 UTC
And as always, some Googlefu + reading leads to
const char*  sensor::printsensors()
{
        char * buf = (char*)malloc(30);   // Should be enough space now!
        sprintf(buf, "Ti: %u, To: %u, Hi: %u, Ho: %u\n", _Ti,_To,_Hi,_Ho);
        return buf;
}
Michael Miller
@Makuna
Jan 20 2017 10:29 UTC
@ashthespy Generally I believe most routines should not allocate and return, the caller needs to remember to free the buffer and its not obvious. I subscribe to the model of passing a buffer into the function that it will fill, so...
int sensor::printsensors(char* buff, size_t buffSize)
{
    return snprintf(buff, buffSize, "blah blah", blah blah);   
}
This way the caller could use one stack buffer for several things in series; no heap fragmentation can happen.
Ash
@ashthespy
Jan 20 2017 13:28 UTC

@Makuna I see, but it would mean also it would require a function call prior to use of the buff like so.

#define BUFF_SIZE size_t(10)
char* buff[BUFF_SIZE];

// Update buff
sensor.printsensors(buff,BUFF_SIZE);
// Print buff
Serial.printf("Sensor Vals: %s",buff);

or is there a way to make it a one liner?

efess
@efess
Jan 20 2017 13:38 UTC
it could always return the buffer pointer as well. You would want to do:
char buff[BUFF_SIZE];
and pass the buff in using &buff
otherwise you're crating an array of char pointers
er nm on the &buff part
Ash
@ashthespy
Jan 20 2017 13:43 UTC
Could I ask why do I need to explicitly pass the buffer to the function?
If I have already defined my buffer size during creation, I don't see why I need to explicitly pass the buffer each time?
efess
@efess
Jan 20 2017 13:45 UTC
it depends on the scope of buff. If it's global, you don't need to pass it, it would be accessible by any scope. If you allocate in a function, you'd need to pass it since it's scope is limited to that callstack.
If you're using that string many times in your app, you can always allocate a permanent "sensorString" char array in your sensor class which can be updated and returned to anyone calling "printsensors"
Ash
@ashthespy
Jan 20 2017 13:59 UTC
@efess that is what I am doing now :-)
efess
@efess
Jan 20 2017 14:03 UTC
Then yeah, you're fine - you can just return the pointer to that internal char array to any caller
char sensorString[STR_SIZE];

char * sensor::printsensors()
{
    snprintf(sensorString, STR_SIZE, "blah blah", blah blah);
    return sensorString; 
}
Ash
@ashthespy
Jan 20 2017 14:06 UTC
Yep, thanks!
Michael Miller
@Makuna
Jan 20 2017 18:54 UTC

p.s. Another way to handle array sizes...

char sensorString[34];

...
snprintf(sensorString, sizeof(sensorString)/sizeof(sensorString[0]), "blah blah", blah blah);

often you will find this already define into a macro called _countof() so it just becomes

snprintf(sensorString, _countof(sensorString), "blah blah", blah blah);
AND, BTW, you now are taking up memory the total time of your app running of STR_SIZE. If you used a scoped variable and pass it, it only takes the memory within the scope as the variable is on the stack. Generally I avoid globals if I can.
efess
@efess
Jan 20 2017 19:13 UTC
@Makuna is _counter computed at compile time?
For small projects, 34 bytes shouldn't be much of a deal for the ESP. I agree it is bad practice ..
brutzler
@brutzler
Jan 20 2017 19:15 UTC
Hi,
asked some time before if there is any experience with the watchdog function.
My NodeMCU with actual sketch is sometimes "freezing". Means I can not access it any more over WiFi.
I have a "watchdog ping"-function (thx to everslick) that should cause a reboot if the ESP looses connection to wifi. But I think, that the ESP doesn't process normal code any more.
I tried to simulate the internal watchdog with an endless loop for (int i = 0; i < 100000000; i++) { __asm__ volatile ("nop"); }. In that case the ESP is rebooting properly.
How can I do more debug, to find out what is causing the ESP to stuck?
Any hint is appreciated.
Michael Miller
@Makuna
Jan 20 2017 19:43 UTC
Yes, it's done at compile time
Michael Miller
@Makuna
Jan 20 2017 22:30 UTC
@brutzler Are you familiar with "Divide and Conquer" debugging? Find half the functionality you can easily disable in your sketch; see if the problem reproduces. If not, disable the other part and enable the part that could cause the issue. Continue until you find the problem.
Ash
@ashthespy
Jan 20 2017 23:05 UTC
@Makuna Won't I be using the memory when the variable is allocated? So wouldn't it be better of using one global, instead of defining a buff variable each time I need to print this compiled string?
Michael Miller
@Makuna
Jan 20 2017 23:12 UTC
@ashthespy Local variables allocate on the stack not the heap, which is quick. When the variable goes out of scope (return from a function call) then the stack is popped and the memory just goes away. Stack = cheap memory allocation and limited life. For large allocations you should be concerned (over 128 bytes or so) but otherwise your stack is already taking up memory just in case its needed for the deepest function call.
void MyFunctionThatDoesSomething()
{ // <- at function start, the stack pointer is stored for popping when it leaves the function
    char strPrint[34]; // <- strPrint allocated on stack, which is just check stack max and increment a stack pointer
    snprintf(strPrint, _coundof(strPrint), blah, blah);
} // <-- strPrint just disappears at this point due to the stack being popped for the function
Memory allocations by using new are realitively heavy and if new and delete to often you end up with memory fragmentation which limits allocations after that.
Ash
@ashthespy
Jan 20 2017 23:16 UTC
@Makuna I follow that variables defined in the local scope of the function will be cleared( or as I learned the proper term popped of the stack). However, I am confused as what the best practice is when it comes to passing the said char array pointer out!
Michael Miller
@Makuna
Jan 20 2017 23:17 UTC
General practice is NEVER return an allocated pointer; let the caller allocate and provide the pointer to be filled. The caller then can decided if they allocate on stack or heap.
Ash
@ashthespy
Jan 20 2017 23:18 UTC
Ah, but wouldn't that lead then to redundant pointers being created for each function call?
Michael Miller
@Makuna
Jan 20 2017 23:19 UTC
What? Give an example in code, what you stated doesn't make sense to me.
variables are also passed on the stack to the function
Ash
@ashthespy
Jan 20 2017 23:21 UTC
void someSmallFunction()
{ blah;
blah;
char  buff[34];
sensor::printsensors(buff);
Serial.printf("Sensor value in someSmallFunction: %s",buff);
someOtherFunction();
}


void someOtherFunction()
{
bleh;
//do something that changes the sensor data
bleh;
char  buff[34]; // Isn't this now using more memory compared to a single global?
sensor::printsensors(buff);
Serial.printf("Sensor value in someOtherFunction: %s",buff);
}
Edited :-)
Michael Miller
@Makuna
Jan 20 2017 23:22 UTC
annotate the code with a comment as to what you define as to be redundant pointers.
p.s. That code is wrong. its char buff[34] not char* buff[34]
Ash
@ashthespy
Jan 20 2017 23:24 UTC
If I follow, then buff is added to the stack for each of the functions, instead of a single global deceleration?
Michael Miller
@Makuna
Jan 20 2017 23:24 UTC
yes, and each goes away at the end of their function call.
Ash
@ashthespy
Jan 20 2017 23:26 UTC
Edited the example a bit, does my doubt make more sense now?
Michael Miller
@Makuna
Jan 20 2017 23:26 UTC
By the time you hit one of the buff, the other doesn't exists. It also uses stack memory (which is already set aside) rather than heap.
Ash
@ashthespy
Jan 20 2017 23:28 UTC
Even for such a nested function call? I need to read more into optimising code!
Michael Miller
@Makuna
Jan 20 2017 23:29 UTC
If you called someOtherFunction within someSmallFunction, you could use more; but it would only be more stack. And again, stack is already set aside as a reasonable large value (2K is common)
but if they are both called by the same outer, then no.
Ash
@ashthespy
Jan 20 2017 23:31 UTC
So if would it be right to say, lesson of the day: Generally speaking stack == good, heap == bad?
Michael Miller
@Makuna
Jan 20 2017 23:32 UTC
I hate to be that "rigorous" in the definition. There are cases where heap is the right solution; I just don't believe this is one of them.
To back up my reasoning, look a the function you are calling snprintf, it takes a pointer to a buffer and a size the same as I am suggesting.
Ash
@ashthespy
Jan 20 2017 23:35 UTC
The idea behind that was then that I could directly use it in my Seral.printf() all over the place
with a single global buffer pointer that gets overwritten each time
not the pointer, but the char array gets overwritten
Michael Miller
@Makuna
Jan 20 2017 23:37 UTC
I wish I could copy your code above, but imagine one of those functions passing a string "This sensor value is of the greatest importance and should not be ignored:%s", the callsite it would be obvious you need a larger buffer for this one case; but if you did it globablly internally, it would be harder to catch.
Ash
@ashthespy
Jan 20 2017 23:40 UTC
True, there is a loss in flexibility, but if its a standard string each time (for debug actually) with only the values updating (who's values I know the max value, so the max buffer size), then is it still frowned upon?
Sorry for all the questions, I am trying to relearn C++ with a focus on embedded systems, but using proper object oriented code, rather than than all my sledgehammer-if-it-works-I-am-happy code so far!
Michael Miller
@Makuna
Jan 20 2017 23:42 UTC
its standard right now, you might, someone else might copy your code and also might change in that in the future. Its also about writing robust code. In two months, will you remember details of why? But if you follow good patterns they won't come back and bite you.
AND, I am talking from a point of embedded. Some of this has other arguments against it on desktop machines.
P.S. Have you looked into templates? The old C++ OOD of base classes and derived classes is actually rather memory intensive. Not a subject to step into lightly though ;-)
Ash
@ashthespy
Jan 20 2017 23:46 UTC
Yes, I am used to working with more scientific computing languages on clusters with quite a few gigabytes of RAM, so this is.. fun! :-)
Actually, yes! I was looking an your NeoHueBlend method just this evening and was wondering what a template was. I have a few tabs open for further reading tonight!
Michael Miller
@Makuna
Jan 20 2017 23:48 UTC
My NeoPixelBus library uses templates to shrink the code and memory footprint dramatically if you want something to chew on (and chew, and chew)
Ash
@ashthespy
Jan 20 2017 23:50 UTC
I have been going through your code quite extensively, I have been making a small "analog" RGB + CCT strip controller :-)
Michael Miller
@Makuna
Jan 20 2017 23:50 UTC
The actual NeoPixelBus object takes both a Feature (color order and presense of white in the color values) and a Method (Esp, Avr, Speed, etc).
This model saved HUGE amounts of code for a Arduino. see NeoPixelBus Wiki On Smaller code
I also helped the PJON creator take on templates for his code base to shrink it.
good luck
Ash
@ashthespy
Jan 20 2017 23:55 UTC
More reading! ;-) Oh, I am truly grateful for the internet!