Hi everyone!
I hope you will not find the question stupid, but here is my problem:
I'm developing PID - controller with AtTiny85. I managed to get together everything in 8K memory, including support of 5 buttons and OLED screen 128x64 with small chart on it. To save memory, all the calculations are in integer so for a temperature, when the actual temperature is, for example [22.07], to convert it to an integer i just multiply it by 100 and i do the calculation with integer [2207](main idea is to avoid float/double to save memory). But to show it on the screen, i need to convert it back to normal(22.07).
Here is what i did:
but when i have 2207 for input i get as a result [22.7] NOT [22.07], so i need to add one '0'', but what ever i do it takes too much memory. Right now i use 8134/8192bytes=> i have 58bytes free to solve it. So my question is:
How to convert string to a float look-like string.(XXXX -->XX.XX) with minimum use of resources/memory?
if you are tight in program memory, don't use the String class at all...
you can just do a
Serial.print(temperature_x_100 / 100.0, 2);
that will create the floating point for the display and display with 2 digits after the decimal point. Are you sure your temperature sensor is precise / reliable to 1% of a degree ?
--> deepending on the quality of your sensor, whatever is after the decimal point might just be garbage.. in which case just print the integral part
Delta_G:
If you can live without the String class and stick to char arrays (which you should be on a memory limited system like this) then you can use dtostrf.
I can not use dtostrf()...it take double as a parameter to convert it to a string(char array). I do not have double. I have only integer(Ex.: 2207). What i have to do is divide by 100 and put a '0' if need in the decimal part. I can not use any float calculation. As soon i use float point the code increases with thousand bytes. For the char array - i agree.
J-M-L:
if you are tight in program memory, don't use the String class at all...
you can just do a
Serial.print(temperature_x_100 / 100.0, 2);
that will create the floating point for the display and display with 2 digits after the decimal point. Are you sure your temperature sensor is precise / reliable to 1% of a degree ?
--> deepending on the quality of your sensor, whatever is after the decimal point might just be garbage.. in which case just print the integral part
Serial.print(temperature_x_100 / 100);
Can not be without the decimal part. The sensor is 3-wire PT100(not perfect but accurate enough). And PID control without of the decimal part......will be disaster(for the current process).
Serial.print(temperature_x_100 / 100.0, 2);
--> When you divide by [100.0] is automatically do floating point math, and i need to avoid that.
KrasiSoft:
I can not use dtostrf()...it take double as a parameter to convert it to a string(char array). I do not have double. I have only integer(Ex.: 2207). What i have to do is divide by 100 and put a '0' if need in the decimal part. I can not use any float calculation. As soon i use float point the code increases with thousand bytes. For the char array - i agree.
If it wants a double, it'll take an integer and like it.
on a UNO the compilation states:
Sketch uses 1786 bytes (5%) of program storage space. Maximum is 32256 bytes.
Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.
and the console displays 23.07 which is correct
Now if you run this (crappy on purpose) code using the String class
Sketch uses 3258 bytes (10%) of program storage space. Maximum is 32256 bytes.
Global variables use 198 bytes (9%) of dynamic memory, leaving 1850 bytes for local variables. Maximum is 2048 bytes.
and console still shows the right answer 23.07
==> 3258 bytes with the String class versus 1786 bytes without ==> 1472 bytes saved and you even save some SRAM too.
if you want something in between, this plain version (going to floating point)
will compile with the following size info:
Sketch uses 2540 bytes (7%) of program storage space. Maximum is 32256 bytes.
Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.
So going to floating point adds 2540-1786 = 754 bytes of flash memory... still better than using the String class.
hammy:
Break the temp value into two halves print the top two digits , then print a decimal point , then print the low two digits.
2207 becomes “22””.””07”
Your PID can work just as well in deg C X100 .
I know how to break it into pieces....There is thousands different ways to accomplish that simple task, but with minimum use of memory...that is the goal! I think(as was suggested earlier) i have to avoid the String class at all in entire program. That will save me a lot of memory(actually i'm using only 4 strings, but still simple manipulation with them eats too much memory). Yes, the PID works perfectly. This issue is only for the visualization(how appear on the screen).
Thanks!
J-M-L:
on a UNO the compilation states:
Sketch uses 1786 bytes (5%) of program storage space. Maximum is 32256 bytes.
Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.
So going to floating point adds 2540-1786 = 754 bytes of flash memory... still better than using the String class.
Thank you for effort, but i have only 58bytes to do it. After i did all the PID-calculation without using float..... I'm not going to use it now for this small issue. Going to chart array will solve the problem(i think).
KrasiSoft:
Thank you for effort, but i have only 58bytes to do it.
the 1786 is for the FULL sketch and does not use float at all... obviously what you need is just this function
void printWithDecimal(int16_t t)
{
int16_t tDec = t % 100;
int16_t tInt = t / 100;
Serial.print(tInt);
Serial.write('.');
if (tDec < 10) Serial.write('0');
Serial.print(tDec);
}
which will take not much space... (don't use Serial, use whatever output device you have)
You don't need the char array, it will require memory you don't really need, just print the various parts separately, no intermediary buffer needed was the point.
and you don't even need two int16_t to store the integral and decimal part upfront, you can first calculate the integral part, print it and then reuse the local variable (on the stack) to calculate with modulo the decimal part and print it. (if you are short in SRAM)
//DecimalPointPos[position for the decimal point] can be 1 or 2
unsigned char * Str2Float(int val, byte DecPoinPos)
{
char * Res = malloc(7+1);
itoa(val, Res, 10);
unsigned char len = strlen(Res);
Res[len] = Res[len-1];
if (DecPoinPos == 2)
Res[len-1] = Res[len-2 ];
Res[len - DecPoinPos] = '.';
Res[len + 1] = '\0';
return(Res);
}
I do not think is possible to make it shorter.... Of course need to call free() to free the memory allocated by malloc() every time this function is used(can not be avoid).
Again if this is just for printing right away, why do you need to build a buffer? Just print or display directly as explained above.
Your function messes up with the trailing NULL char - will likely fail in real life when malloc does not give you a zero initialized buffer.
What happens with your code if it’s zero degree ? Or 0.01 degree (and you shifted x100)?
And if you really need the buffer, as you need the memory anyway - make it a static 8 char buffer in the function - you’ll save the alloc and free cost. Of course calling it a second time will override the previous value
Again if this is just for printing right away, why do you need to build a buffer? Just print or display directly as explained above.
Your function messes up with the trailing NULL char - will likely fail in real life when malloc does not give you a zero initialized buffer.
What happens with your code if it’s zero degree ? Or 0.01 degree (and you shifted x100)?
And if you really need the buffer, as you need the memory anyway - make it a static 8 char buffer in the function - you’ll save the alloc and free cost. Of course calling it a second time will override the previous value
I need the buffer. I need to pass a char* buffer to the LCD screen. For the static buffer inside the function... something goes wrong...it works in VC++, but with Arduino did NOT! Arduino ID did not give any error, but the result wasn't as expected. I should check why, when i have time. It is not important that it overrides the buffer. The buffer is not important after the LCD received it.
Yes! Thanks for the point. The current process will never go below 12.00'C[int: 1200] and over 35.00'C[int: 3500], so i have nothing to worry for now. You are right, that is not perfect, but as soon i add more universality to the function, it fly out of the 58 free bytes i have. For now will save the day. I will post update of the universal function. Thanks again.
What kind of LCD library do you have that does not deal with / implement the print architecture ?
Are you saying that If you do a lcd.print(22); it does not print a number ?
malloc() wont create a zero-ed buffer so when you move the last digit one position to make space for the dot, you overwrite the ‘\0’ you got from the itoa() function and rely on luck that the next byte in memory is null. Won’t happen all the time.... so you should put a null there yourself
Good if you are between 12 and 35 - that removes one issue with small numbers and you know then you need only 6 bytes (including the trailing null) to represent your buffer and you know exactly where the decimal point and trailing null char should be
If you are not using itoa() elsewhere in your code then you can save memory by manually computing the digits
This is the universal one will work with all the range(Up to 7 digits), but.....it takes too much of the memory:
char * Str2Float(int val, byte dvd)
{
char * Res = malloc(7+1);
itoa(val, Res, 10);
unsigned char len = strlen(Res);
int shiftBy = dvd+1 - len;
if (val < 0)
shiftBy++;
if (shiftBy > 0)
{
for (int i = len + shiftBy; i >= 0; i--)
if ((i < shiftBy) && (Res[i] != '-'))
Res[i] = '0';
else
if (Res[i - shiftBy] == '-')
Res[i] = '0';
else
{
if (Res[i] == '-')
break;
Res[i] = Res[i - shiftBy];
}
len = strlen(Res);
}
Res[len] = Res[len-1];
if (dvd == 2)
Res[len-1] = Res[len-2 ];
Res[len - dvd] = '.';
Res[len + 1] = '\0';
return(Res);
}
<<<What kind of LCD library do you have that does not deal with / implement the print architecture ?
Are you saying that If you do a lcd.print(22); it does not print a number ?>>> --> The LCD screen is SSD1306 128x64 dots. To show anything on it you need to program this dot-matrix(128x64). I did rewrite the library to limit the size for the font(Presently i use only 5 capital letters and the digits from 0..9 plus the "-" sign) - that modification limited significantly the size of the library. In my first post, i said that i have a small chart on the LCD - it can not be done without graphical LCD.
J-M-L:
But did you fundamentally modify the library so that it does not recognize a number? I would be surprised...
I am not sure what exactly asking me...
that is the representation of the letters 'C', 'D'. 'E' with size 5x7. The SSD1306 support vertical addressing, so each byte is actually 1 column from the symbol[5 bytes making the complete symbol]:
So what i did...i excluded all the symbols, i do not use, and redone the indexing in the array, so i can visualize the symbol by the ASCCI code. The same idea is when you need to show image on the screen...just have to light the pixels you need-[ 1 ] pixel is shining [ 0 ] pixel is dark. There is no ting like "recognize a number". There is no preload fonts inside the LCD - will show only what you set to light with bit matrix 128x64. Hope i understood your question.