Dtostrf float to char im Code unklar

N'Abend zusammen :grinning:

Ich komme bei diesem Projekt

(einfache Temperaturanzeige 2 Stellen vor dem Punkt, 2 danach) nicht hinter die verwendete Schreibweise von

dtostrf in der void readTemperature. Da steht

dtostrf(temp, 2, 2, TemperatureStr);

Code Snippet

char TemperatureStr[6];

void readTemperature() {
    float temp;
    int cnt = 3; //retry counter
    do {
       if (cnt <= 0) {
          String nanStr = "NaN";
          nanStr.toCharArray(TemperatureStr, 6);
          break;
       }
       DS18B20.requestTemperatures(); 
       temp = DS18B20.getTempCByIndex(0);
       
       **dtostrf(temp, 2, 2, TemperatureStr);**
       delay(100);
       cnt--;
    } while (temp == 85.0 || temp == (-127.0));
    SecureCounter++;
    digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off
}

void getApi() {
    readTemperature();
    StaticJsonBuffer<200> jsonBuffer;
    JsonObject& jsonObj = jsonBuffer.createObject();
    char JSONmessageBuffer[200];
  
    jsonObj["secure_counter"] = SecureCounter;
    jsonObj["symbol"] = "°C";
    jsonObj["temperature"] = TemperatureStr;
    jsonObj["unit"] = "Celsius";
    jsonObj.prettyPrintTo(JSONmessageBuffer, sizeof(JSONmessageBuffer));
    http_rest_server.send(200, "application/json", JSONmessageBuffer);
}

void getIndex() {
    readTemperature();
    String html = web_ui_html;
    html.replace("%T%", String(TemperatureStr));
    html.replace("%SC%", String(SecureCounter));
    http_rest_server.send(200, "text/html", html);
}

Es wird ein char [6] deklariert (5 Stellen mit Komma + /0 denke ich?)

Und die erste 2 bei
dtostrf(temp, 2, 2, TemperatureStr);

gibt doch in der Syntax die Gesamtanzahl der Stellen vom TemperatureStr an dachte ich?

Müsste es nicht
dtostrf(temp, 5, 2, TemperatureStr); heissen? 5 Stellen und davon 2 nach dem Komma?

Ich habe den Code getestet und er funktioniert... aber warum? :crazy_face:

Danke für eure Hinweise :grinning:

Nein. Das ist die Mindestbreite

https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga060c998e77fb5fc0d3168b3ce8771d42

Mijn SafeString-bibliotheek V4.1.0 +
als een afdrukmethode (waarde, prec, breedte) om af te drukken naar een SafeString die precies de breedte afdwingt en beschermt tegen bufferoverloop
zie Afdrukken naar een SafeString voor de details (in English :frowning: )

Ich danke euch :grinning:

@drmpf
Very insteresting links you posted! Thanks!
As I'm new to ESP8266 I read also your link about Arduino Strings vs Chars.
And I wonder if it makes any sense to put a rawstring literal in PROGMEM (because of layout purpose) and then copy it to
a SafeString of your lib?
Thanks for any advise, regards :slightly_smiling_face:

PROGMEM is usually used on small memory devices like UNO etc to save RAM
Serial.print(F("test")); uses less RAM then Serial.print("test")
With SafeStrings you can do
cSF(sfStr,10); // createSafeString( .. )
sfStr = F("test');

Not sure what you mean by "because of layout".

Because ESP8266 has so much more memory use F( ) is not so important unless you have a lot of text

You can also cast PROGMEM pointers to __FlashStringHelper
F(string_literal) macro is actually
(reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
so you can use PROGMEM string pointes where you can use F(string)
Can you post a small example sketch?

Thank you and sorry for beeing unclear :grimacing:
I want to put HTML code in a RAW String literal because of easier coding than e.g.
massage += "HTML code"; and escaping all " with /"

I'm sticking to this code:

String web_ui_html = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Temp2IoT</title>
    <style type="text/css">
      html
      {
        font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
        -webkit-text-size-adjust:100%;
        background: #1e87f0;
        color:#666;
      }

    @media all and (min-width:600px) {
        .card {
          box-shadow: 0 5px 15px rgba(0,0,0,.2);
      }
    }

      body
      {
        margin:0;
      }
    .container {
        right: 0%;
        top: 50%;
        position: fixed;
        -webkit-transform: translateY(-50%);
           -moz-transform: translateY(-50%);
            -ms-transform: translateY(-50%);
             -o-transform: translateY(-50%);
                transform: translateY(-50%);
        width: 100%;
    }
    .card {
        margin: auto;
        background: #1e87f0;
        color: #fff;
        width: 400px;
    }
    .card-header {
      padding: 20px 40px;
      margin: 0 0 20px 0;
    }
    .card-header h2,
    .card-body h1 {
      font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
      font-weight: 400;
      text-transform: none;
    }
    .card-header h2 {
      font-size: 2.0rem;
      line-height: 1.4;
    }
    .card-body h1 {
      font-size: 5rem;
      line-height: 0.8;
      margin: 0 0 15px 0;
    }
    .card-body {
      padding: 40px 40px;
    }
    .card-footer {
      padding: 20px 40px;
      color: rgba(255,255,255,.7);
    }
    .card-footer a{
      color: #fff;
      text-decoration: none;
      cursor: pointer;
    }
    .icon {
      fill: currentColor;
    }
    .description {
      margin: 0px;
      font-size: 1.5rem;
      line-height: 1.4;
    }
    .units {
      font-size: 2.5rem;
      line-height: 1.4;
    }
    </style>
</head>
  <body>
  <div class="container">
      <div class="card">
        <div class="card-header">
          <h2>
            <span class="icon" style="position: relative; bottom: -7px;">
              <svg vwidth="32" height="32" viewBox="0 0 512 512">
              <path d="M227.89,331.58V77.15c0-31.56-25.59-57.15-57.15-57.15s-57.15,25.59-57.15,57.15v254.43
                c-11.85,13.43-19.05,31.06-19.05,50.37c0,42.08,34.12,76.2,76.2,76.2s76.2-34.12,76.2-76.2
                C246.94,362.63,239.74,345.01,227.89,331.58z M170.74,439.1c-31.56,0-57.15-25.59-57.15-57.15c0-16.92,7.36-32.12,19.05-42.59V77.15
                c0-21.04,17.06-38.1,38.1-38.1s38.1,17.06,38.1,38.1v262.21c11.69,10.46,19.05,25.66,19.05,42.59
                C227.89,413.51,202.3,439.1,170.74,439.1z"/>
              <path d="M285.04,381.95c0,18.38-4.36,35.73-12.07,51.11l17.04,8.52c8.99-17.95,14.08-38.19,14.08-59.63
                c0-21.44-5.09-41.69-14.08-59.63l-17.04,8.52C280.68,346.22,285.04,363.57,285.04,381.95z"/>
              <path d="M342.19,381.95c0,27.57-6.54,53.59-18.11,76.67l17.07,8.54c12.85-25.64,20.09-54.58,20.09-85.21s-7.24-59.57-20.09-85.21
                l-17.07,8.53C335.65,328.36,342.19,354.38,342.19,381.95z"/>
              <path d="M399.34,381.95c0,36.76-8.69,71.48-24.1,102.25l17.04,8.52c16.7-33.33,26.11-70.95,26.11-110.77s-9.41-77.44-26.11-110.77
                l-17.04,8.52C390.65,310.47,399.34,345.19,399.34,381.95z"/>
              <path d="M189.79,348.99V229.55v-76.2c0-10.52-8.53-19.05-19.05-19.05c-10.52,0-19.05,8.53-19.05,19.05v76.2v119.44
                c-11.38,6.59-19.05,18.87-19.05,32.96c0,21.04,17.06,38.1,38.1,38.1s38.1-17.06,38.1-38.1
                C208.84,367.86,201.17,355.58,189.79,348.99z"/>
            </svg>
            </span>
            Temp2IoT
          </h2>
        </div>
        <div class="card-body">
          <h1><span id="temperature">%T%</span><sup class="units">&deg;C</sup></h1>
          <p class="description">Water Temperature</p>
        </div>
        <div class="card-footer">
          <p>Secure Counter: <span id="secure-counter">%SC%</span><br><span class="icon" style="line-height: 2; position: relative; bottom: -2px;"><svg width="16" height="16" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" data-svg="github"><path d="M10,1 C5.03,1 1,5.03 1,10 C1,13.98 3.58,17.35 7.16,18.54 C7.61,18.62 7.77,18.34 7.77,18.11 C7.77,17.9 7.76,17.33 7.76,16.58 C5.26,17.12 4.73,15.37 4.73,15.37 C4.32,14.33 3.73,14.05 3.73,14.05 C2.91,13.5 3.79,13.5 3.79,13.5 C4.69,13.56 5.17,14.43 5.17,14.43 C5.97,15.8 7.28,15.41 7.79,15.18 C7.87,14.6 8.1,14.2 8.36,13.98 C6.36,13.75 4.26,12.98 4.26,9.53 C4.26,8.55 4.61,7.74 5.19,7.11 C5.1,6.88 4.79,5.97 5.28,4.73 C5.28,4.73 6.04,4.49 7.75,5.65 C8.47,5.45 9.24,5.35 10,5.35 C10.76,5.35 11.53,5.45 12.25,5.65 C13.97,4.48 14.72,4.73 14.72,4.73 C15.21,5.97 14.9,6.88 14.81,7.11 C15.39,7.74 15.73,8.54 15.73,9.53 C15.73,12.99 13.63,13.75 11.62,13.97 C11.94,14.25 12.23,14.8 12.23,15.64 C12.23,16.84 12.22,17.81 12.22,18.11 C12.22,18.35 12.38,18.63 12.84,18.54 C16.42,17.35 19,13.98 19,10 C19,5.03 14.97,1 10,1 L10,1 Z"></path></svg></span> Find the <a href="https://github.com/100prznt/Temp2IoT" target="_blank">Temp2IoT</a> project on GitHub.</p>
      </div>
    </div>
  </div>
  </body>

  <script>
  setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("temperature").innerHTML = JSON.parse(this.responseText).temperature;
        document.getElementById("secure-counter").innerHTML = JSON.parse(this.responseText).secure_counter;
      }
    };
    xhttp.open("GET", "/api", true);
    xhttp.send();
  }, 10000 ) ;
  </script>
</html>
)rawliteral";

And then put it in a String to handle placeholders:

void getIndex() {
    readTemperature();
    String html = web_ui_html;
    html.replace("%T%", String(TemperatureStr));
    html.replace("%SC%", String(SecureCounter));
    http_rest_server.send(200, "text/html", html);
}

Yes that is a good example of using PROGMEM (or SPIFFS storage)
And the replacement code is simple.

String web_ui_html = ... copies all the PROGMEM into a String and then
String html = web_ui_html; copies it all again.
Not a problem if you have the memory available, which on the ESP8266 you probably do.

Here is a sample program that reads directly from the PROGMEM and skips the first copy

//https://forum.arduino.cc/t/dtostrf-float-to-char-im-code-unklar/860614/6

//  download and install the SafeString library V4.0.3+ from Arduino library manager
//  or from www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
#include "SafeString.h"
const char web_ui_html[] PROGMEM = "|%T%C|"; //%T% expands to exactly 7 chars and no more
String html;

void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(i); Serial.print(' ');
    delay(500);
  }
  Serial.println();
  html.reserve(50); 
  
  float temp = 55.5;
  cSF(sfTemp, 20);
  sfTemp.print(temp,1,7); // format in width
  
  String html = (reinterpret_cast<const __FlashStringHelper *>(web_ui_html));
  html.replace("%T%", sfTemp.c_str()); // don't need String(TemperatureStr);
  Serial.println(html);
}

void loop() {
}

The output is

|   55.5C|

Also check the guidelines for Taming Arduino Strings particularly the auto reboot of the ESP to clean up memory leaks.

Thanks for your support on this :+1:

Read your link befor you posted it and it was very very interesting :grinning:

Everybody else should read it
STRINGS VS CHAR ESP8266

Two more questions:

You mention „put Strings in a method“
Do you mean FUNCTIONS in C++?

Would it be more efficient to put a HTML page in a SafeString directly instead of a CHAR before?
I would think so… as a beginner :wink:

Yes methods don't have to return a result.

Memory (SRAM) wise, storing in PROGRAM memory is more efficient.
You want to take one copy so that you can do the replacement again, so loading either a String or a SafeString from PROGMEM is a good way to do the copy.

In this case I would use String so I don't have to keep track of how large the html is and can modify the html without having to go back and change the SafeString size.

Make the String html a global, skip the reserve( ) and let the code sort that out. Being a global it will hold the memory once allocated to the correct size for your html and avoid extra fragmentation due to this String changing size

Thank for clearing this up!

My Last question would be :innocent:

Does a rawstring affect fragmentation in any way? I could not imagine how and why… as I understand it is just another way to define a String.

Global:

String raw_html;

In a function:

String raw_html=R"(html Code with placeholders)";

Thank you so much :grinning:

No, fragmentation depends on when you allocate/re-allocate the space in memory and when you release it.

Having the in function String raw_html will not cause additional fragmentation as its memory will be recovered on the method exit, so in your case you can ignore my previous advice and just use a local String initialized each function call.

Great, thanks!

As it is in a function I also will combine it with F( macro :slightly_smiling_face:

Rather then using F( ) in the function you can have an #include "Page.h" at the top of your sketch
and then in the Page.h file on the same dir as the sketch have

const char web_ui_html[] PROGMEM = R"=====(
<!DOCTYPE html>
. . . 
</html>
)=====";

Then in your function you can just have

  String raw_html = (reinterpret_cast<const __FlashStringHelper *>(web_ui_html));

This keeps your sketch.ino file cleaner.

So then I have a Char AND a String containing the HTML :thinking:

I have sorted out functions to Tabs :grinning:
And would go like:

In a funtion tab

String raw_html;

raw_html=F(R"=====(html Code with placeholders)=====");

If there are no disadvantages :+1:

A String is a wrapper around a char[..] that restricts access and re-allocates a larger char[..] when you add too many chars for the existing char[..].

[quote]If there are no disadvantages[\quote]
That will work. Only suggested the const char web_ui_html PROGMEM in separate tab all by itself, with no code, as I found that easier to edit just the html.

Thank you again for teaching me :grinning:
Very helpfull :+1:

Is a good way, too, but found out

String raw_html= FPSTR(web_ui_html);

is short form of your code if I'm right?

That is neat (for ESP) but not portable. The FPSTR define does not exist in Uno/Meg2560 (AVR) boads
but you could always add at the top of the sketch, below all the includes

#ifndef FPSTR
##define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#endif

to make the code 'portable' to other boards

Well, will do that when I‘m an expert :partying_face: