Including slabs of inary data in a sketch

Is there an easy way to include binaries in a sketch? That is: I would like a PROGMEM byte array to be initialised with some stuff in a file in my compile dirtectory. Something like

PROGMEM byte theImage[] = /* compiler, read "foo.png" and put it here in this byte array */ ;

The alternative is to generate C code from the data, which would work, but bleh.

-- EDIT --

Hmm, there's the xxd utility. That would work ok.

$ xxd -i foo.png > foo.png.h
PROGMEM byte theImage[] = {
#include "foo.png.h"
};

You know, these binary resources are mainly test web pages. … no, C++ doesn't appear to have multi-line string literals. And in any case, the "#include" would just be treated as text.

Including slabs of inary data in a sketch

inary data? What is that?

Something like

PROGMEM byte theImage = /* compiler, read "foo.png" and put it here in this byte array */ ;

The compiler does not read anything but the c or cpp file it is given. The preprocessor has read any #include statements, and read the files, and placed the contents in the place of the #include statement.

If you have a file that contains the data,

PROGMEM byte theImage[] = {
#include "TheDataFile.ext"
};

will cause whatever is in TheDataFile.ext to be in-lined in place of the #include directive.

EVERYTHING in the file, so you must make sure that the file contains ONLY data that makes sense where the #include directive is.

If TheDataFile.ext contains

0x00, 0x01, 0x02, 0x03

What the compiler will see, when it runs is:

PROGMEM byte theImage[] = {
0x00, 0x01, 0x02, 0x03
};

If you are embedding web pages, happily for you, the following statement is incorrect :

PaulMurrayCbr:
. . .
You know, these binary resources are mainly test web pages. … no, C++ doesn’t appear to have multi-line string literals.
. . .

There are “super quotes” available, for example . . .

const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
   <head>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <style>
         body {font-family: helvetica,arial,sans-serif;}
         table {border-collapse: collapse; border: 1px solid black;}
         /*table {border: 1px solid black;}*/
         td {padding: 0.25em;}
         .title { font-size: 2em; font-weight: bold;}
         .name {padding: 0.5em;}
         .heading {font-weight: bold; background: #c0c0c0; padding: 0.5em;}
         .update {color: #dd3333; font-size: 0.75em;}
         output {font-size: 0.75em; color: #696969;}
      </style>
   </head>
   <script>
      function myInit() {
      }
      
      function isValidField( fieldName , fieldMaxLength ) {
      //
      // returns boolean
      //  
      var testField = document.getElementById(fieldName) ;
      var isValid = false  ;
      
      if (  testField ) 
      {
        if (  testField.value.search(/\S+/) == -1  ||  testField.value.length >= fieldMaxLength ) {
           testField.style.backgroundColor="LightCoral";
        }
        else {
           testField.style.backgroundColor="transparent";
           isValid = true ;
        }
      }
      return ( isValid ) ;
      }
      
      
      function check() {
      //
      //
      //
      if ( 
         isValidField( "config_ssid" , 32  ) 
         &&  isValidField( "config_psk" , 64  ) 
         &&  isValidField( "config_remoteHost" , 32  ) 
         &&  isValidField( "config_ruleIntlFrom" , 6  ) 
         &&  isValidField( "config_ruleIntlTo" , 6  ) 
         &&  isValidField( "config_ruleLocalFrom" , 6  ) 
         &&  isValidField( "config_ruleLocalTo" , 6  ) 
         &&  isValidField( "config_phpRetrievePath" , 32  ) 
         &&  isValidField( "config_phpStorePath" , 32  ) 
      
       )
      { 
         document.getElementById("callerIdConfigForm").submit(); 
         document.getElementById("updateResponse").value = "configuration sent to server" ;
      } 
      else {
      document.getElementById("updateResponse").value = "error in marked field(s)" ;
      }
      }
      
   </script>
   <body onload="myInit()">
      <form method="post" action="/form"  id="callerIdConfigForm">
      <div class=title>CallerID</div>
      <button type="button" onclick="check()">update</button>
      <p>
         <input class=update size=40 type=text name="updateResponse" id="updateResponse" value="$@updateResponse@$" >  
      <p>
      <div id=divPage1>
         <table>
            <tr>
               <td colspan=2 class=heading>Configuration</td>
            </tr>
            <tr>
               <td>SSID:</td>
               <td><input type="text" id="config_ssid" name="config_ssid" value="$@config_ssid@$" size=30 ></td>
            </tr>
            <tr>
               <td>PSK:</td>
               <td><input type="password" id="config_psk"  name="config_psk"  value="$@config_psk@$" size=30 ></td>
            </tr>
            <tr>
               <td>Host:</td>
               <td><input type="text" id="config_remoteHost"  name="config_remoteHost"  value="$@config_remoteHost@$" size=30 ></td>
            </tr>
            <tr>
               <td>Intl Rule 1:</td>
               <td><input type="text" id="config_ruleIntlFrom"  name="config_ruleIntlFrom"  value="$@config_ruleIntlFrom@$" size=30 ></td>
            </tr>
            <tr>
               <td>Intl Rule 2:</td>
               <td><input type="text" id="config_ruleIntlTo"  name="config_ruleIntlTo"  value="$@config_ruleIntlTo@$" size=30 ></td>
            </tr>
            <tr>
               <td>Local Rule 1:</td>
               <td><input type="text" id="config_ruleLocalFrom"  name="config_ruleLocalFrom"  value="$@config_ruleLocalFrom@$" size=30 ></td>
            </tr>
            <tr>
               <td>Local Rule 2:</td>
               <td><input type="text" id="config_ruleLocalTo"  name="config_ruleLocalTo"  value="$@config_ruleLocalTo@$" size=30 ></td>
            </tr>
            <tr>
               <td>Get path:</td>
               <td><input type="text" id="config_phpRetrievePath"  name="config_phpRetrievePath"  value="$@config_phpRetrievePath@$" size=30 ></td>
            </tr>
            <tr>
               <td>Store path:</td>
               <td><input type="text" id="config_phpStorePath"  name="config_phpStorePath"  value="$@config_phpStorePath@$" size=30 ></td>
            </tr>
         </table>
      </div>
   </body>
</html>

)=====";

6v6gt:
There are "super quotes" available, for example . . .

Thanks, that's what I needed. the inary data (favicon.ico, mainly) wiil be processed with xxd

byte PROGMEM favicon_ico[] = {
  0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, 0x00,
  0x20, 0x00, 0x68, 0x04, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
  0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x12, 0x0b,
(etc)

I may even gzip it and use the HTTP 'Content-Encoding' header - save a little space.

It's looking good. Now to hook up some neopixels, an RTC, and smoke this puppy.

If you are using an ESP8266, you may be aware that you have also other options for handling such files using the spiffs files system. I haven't used it myself like that but here is a short description: A Beginner's Guide to the ESP8266.