Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiple definition of `targzFreeBytesFn()' #39

Open
sharandac opened this issue May 5, 2021 · 15 comments
Open

multiple definition of `targzFreeBytesFn()' #39

sharandac opened this issue May 5, 2021 · 15 comments

Comments

@sharandac
Copy link

Hey hey. I tried today to use ESP32-targz in several places in the same project. This results in the following error ...

Any Idea?

.pio/build/ttgo-t-watch-v1/src/utils/http_ota/http_ota.cpp.o: In function `targzFreeBytesFn()':
/home/sharan/Schreibtisch/My-TTGO-Watch/.pio/libdeps/ttgo-t-watch-v1/ESP32-targz/src/ESP32-targz.h:100: multiple definition of `targzFreeBytesFn()'
.pio/build/ttgo-t-watch-v1/src/app/watchface/watchface_app_tile.cpp.o:/home/sharan/Schreibtisch/My-TTGO-Watch/.pio/libdeps/ttgo-t-watch-v1/ESP32-targz/src/ESP32-targz.h:100: first defined here
.pio/build/ttgo-t-watch-v1/src/utils/http_ota/http_ota.cpp.o: In function `targzTotalBytesFn()':
http_ota.cpp:(.text._Z17targzTotalBytesFnv+0x0): multiple definition of `targzTotalBytesFn()'
.pio/build/ttgo-t-watch-v1/src/app/watchface/watchface_app_tile.cpp.o:watchface_app_tile.cpp:(.text._Z17targzTotalBytesFnv+0x0): first defined here
@tobozo
Copy link
Owner

tobozo commented May 5, 2021

hey thanks for your feedback :-)

just don't use setupFSCallbacks() with OTA Update and it should be fine

@sharandac
Copy link
Author

Thank you for your answer. I have not yet used setFSCallbacks(). I have only used the following in two different places with

#define DEST_FS_USES_SPIFFS
#include <ESP32-targz.h>

Without any further code.

@tobozo
Copy link
Owner

tobozo commented May 5, 2021

two different places 🤔 maybe it doesn't like multiple instances ?

What does the targz setup code look like, can you share a snippet with all the statements calling this library ?

@sharandac
Copy link
Author

Here is the first snippet with the function that use your lib

#define DEST_FS_USES_SPIFFS
#include <ESP32-targz.h>
/*
// some other code
*/
bool http_ota_start_compressed( const char* url, const char* md5, int32_t firmwaresize ) {
    bool retval = false;
    int32_t size = UPDATE_SIZE_UNKNOWN;

    HTTPClient http;
    /**
     * start get firmware file
     */
    http.setUserAgent( "ESP32-UPDATE-" __FIRMWARE__ );
    http.begin( url );
    int httpCode = http.GET();

    if ( httpCode > 0 && httpCode == HTTP_CODE_OK ) {
        /**
         * send http_ota_start event
         */
        http_ota_send_event_cb( HTTP_OTA_START, (void *)NULL );
        /**
         * start an unpacker instance, reister progress callback and put the stream in
         */
        GzUnpacker *GZUnpacker = new GzUnpacker();
        GZUnpacker->setGzProgressCallback( http_ota_progress_cb );
        GZUnpacker->setPsram( true );
        http_ota_send_event_cb( HTTP_OTA_START, (void*)"start flashing ..." );
        /**
         * if firmware size known set the right value
         */
        if ( firmwaresize != 0 )
            size = firmwaresize;
        /**
         * progress the stream
         */
        if( !GZUnpacker->gzStreamUpdater( http.getStreamPtr(), size, 0, false ) ) {
            log_e("gzStreamUpdater failed with return code #%d\n", GZUnpacker->tarGzGetError() );
            http_ota_send_event_cb( HTTP_OTA_ERROR, (void*)"error ... weak wifi?" );
        }
        else {
            http_ota_send_event_cb( HTTP_OTA_FINISH, (void*)"Flashing ... done!" );
            retval = true;
        }
    }
    else {
        http_ota_send_event_cb( HTTP_OTA_ERROR, (void*)"http error ... weak wifi?" );        
    }
    http.end();

    return( retval );
}

At this point it works perfect. But if I add in a second place, it fails. I have reduce it to only

#define DEST_FS_USES_SPIFFS
#include <ESP32-targz.h>

@tobozo
Copy link
Owner

tobozo commented May 5, 2021

interesting, this makes me realize there's no test suite for using gzStreamUpdater() with HTTP stream

Can you try to add this:

GZUnpacker->setupFSCallbacks( nullprt, nullptr );

then try to compile, if it still fails comment this out:

    // GZUnpacker->setGzProgressCallback( http_ota_progress_cb );

and see if it improves ?

Meanwhile I'll see if I can create an example sketch out of your snippet.

@sharandac
Copy link
Author

With both changes comes the same compiler error. I am a bit confused, because actually the

#ifndef _TGZ_FSFOOLS_

should prevent this. But I don't see any reason why it does.

@tobozo
Copy link
Owner

tobozo commented May 5, 2021

idea: separate the gathering of the http stream pointer from the gz logic

Here's a function I use for that, it can even follow redirects:

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
HTTPClient http;


WiFiClient *getFirmwareClientPtr( WiFiClientSecure *client, const char* url, const char *cert = NULL )
{
  client->setCACert( cert );
  const char* UserAgent = "ESP32-HTTP-TarGzUpdater-Client";
  http.setReuse(true); // handle 301 redirects gracefully
  http.setUserAgent( UserAgent );
  http.setConnectTimeout( 10000 ); // 10s timeout = 10000
  if( ! http.begin(*client, url ) ) {
    log_e("Can't open url %s", url );
    return nullptr;
  }
  const char * headerKeys[] = {"location", "redirect", "Content-Type", "Content-Length", "Content-Disposition" };
  const size_t numberOfHeaders = 5;
  http.collectHeaders(headerKeys, numberOfHeaders);
  int httpCode = http.GET();
  // file found at server
  if (httpCode == HTTP_CODE_FOUND || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
    String newlocation = "";
    String headerLocation = http.header("location");
    String headerRedirect = http.header("redirect");
    if( headerLocation !="" ) {
      newlocation = headerLocation;
      Serial.printf("302 (location): %s => %s\n", url, headerLocation.c_str());
    } else if ( headerRedirect != "" ) {
      Serial.printf("301 (redirect): %s => %s\n", url, headerLocation.c_str());
      newlocation = headerRedirect;
    }
    http.end();
    if( newlocation != "" ) {
      log_w("Found 302/301 location header: %s", newlocation.c_str() );
      // delete client;
      return getFirmwareClientPtr( client, newlocation.c_str(), cert );
    } else {
      log_e("Empty redirect !!");
      return nullptr;
    }
  }
  if( httpCode != 200 ) return nullptr;
  return http.getStreamPtr();
}

So later I can do this:

  WiFiClientSecure *client = new WiFiClientSecure;
  Stream *streamptr = getFirmwareClientPtr( client, URL, certificate );
  Unpacker->gzStreamUpdater( streamptr, UPDATE_SIZE_UNKNOWN, 0, false );

also I don't include <ESP32-targz> from multiple places, maybe gather all unpacking logic into a single document for simplicity ?

@sharandac
Copy link
Author

That will probably be the solution, that I will pack the functions into a file. Many thanks for your help!

@tobozo
Copy link
Owner

tobozo commented May 5, 2021

I've updated the example with a gzStreamUpdater test:

https://github.com/tobozo/ESP32-targz/tree/1.0.5-beta/examples/ESP32/Update_from_gz_stream

I don't see a compilation error but I've only tested with Arduino IDE, does it compile in your platformio environment ?

@tibotix
Copy link

tibotix commented Jun 8, 2021

Hey,

only wanted to mention, that it can be also resolved simply by putting the declarations of targzFreeBytesFn and targzTotalBytesFn in the ESP32-targz-lib.h file, and the definitions in the ESP32-targz-lib.cpp file. The FSInfo fsinfo declaration should also be moved to the ESP32-targz-lib.h header so it can be used by the targzFreeBytesFn function.

At least this worked for me :-)

@tobozo
Copy link
Owner

tobozo commented Jun 9, 2021

@tibotix doing this breaks esp8266 compatibility, but I'm not sure I'll keep on maintaining the esp8266 part of the library so I'll keep your suggestion in mind

@tibotix
Copy link

tibotix commented Jun 9, 2021

@tobozo oh okay I see. That was not intended.

Anyway, for others that might face this issue: if you have to include the ESP32-targz.h header multiple times, you could define the tarGzFS and FS_NAME Macros as well as the targzFreeBytesFn and targzTotalBytesFn functions and if needed the FSInfo fsinfo variable yourself and include the ESP32-targz-lib.h directly instead of the ESP32-targz.h header.

@monte-monte
Copy link

monte-monte commented Aug 27, 2022

Strange thing is thatI have included ESP32-targz.h only once, but still have this error. I ended up declaring targzFreeBytesFn and targzTotalBytesFn locally.

@tobozo
Copy link
Owner

tobozo commented Aug 29, 2022

@monte-monte this was solved a year ago, what core and library versions are you using?

@monte-monte
Copy link

@tobozo version=1.1.4, esp8266 with latest core in platformio.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants