esp8266 / Arduino

ESP8266 core for Arduino
GNU Lesser General Public License v2.1
16.07k stars 13.33k forks source link

New binary does not fit SPI Flash size even if free Flash size is 8x bigger #6752

Closed jbrepogmailcom closed 4 years ago

jbrepogmailcom commented 5 years ago

Basic Infos

Platform

Settings in IDE

Problem Description

Detailed problem description goes here.

binary downloaded over OTA does not fit into available space. bin is 409kB, available space 3.7MB. Other similar sketch that has about 390kb works fine on same hardware.

[httpUpdate] Header read fin. [httpUpdate] Server header: [httpUpdate] - code: 200 [httpUpdate] - len: 459248 [httpUpdate] ESP8266 info: [httpUpdate] - free Space: 3710976 [httpUpdate] - current Sketch Size: 462640 [httpUpdate] runUpdate flash... [httpUpdate] New binary does not fit SPI Flash size

MCVE Sketch that is causing the problem. Compiled about 409000 bytes


char* D_Name = "toshiba_ac";
const long FW_VERSION = 19111000;

//#include <ESP8266WiFi.h>
//#include <ESP8266mDNS.h>
//#include <WiFiUdp.h>
#include <Ticker.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager

//#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include "CTBot.h"
#include "DHTesp.h"

CTBot myBot;
DHTesp dht;
WiFiClient client;

// For DHT
int cycleCount = 50;
int currentCycle = cycleCount + 1;  //overrun for first time so Temp is sent immediately
String APIkey = "...........L6CWR6SQZ";
const char* TSserver = "api.thingspeak.com";
int DHTIO = 13;  // GPIO 15 - D8 right next to 3.3V pin
int DHTGround = 12; // GPIO 5 
String st = ""; // temperature declared as global
unsigned int recipient;

// For Toshiba AC control
#include <Arduino.h>
#include <IRremoteESP8266.h>  //https://github.com/crankyoldgit/IRremoteESP8266
#include <IRsend.h>
#include <ir_Toshiba.h>
#include <string.h>
const uint16_t kIrLed = 4;  // ESP8266 GPIO pin 4 to use. Recommended: 4 (D2).
IRToshibaAC ac(kIrLed);  // Set the GPIO to be used for sending messages.

void printState(unsigned int my_telegram_id) {
  // Display the settings.
  Serial.println("Toshiba A/C remote is in the following state:");
  Serial.printf("  %s\n", ac.toString().c_str());
  myBot.sendMessage(my_telegram_id, ac.toString().c_str());
  myBot.sendMessage(my_telegram_id, "Temp: "+st+" (v."+FW_VERSION+")");
  // Display the encoded IR sequence.
  unsigned char* ir_code = ac.getRaw();
  Serial.print("IR Code: 0x");
  for (uint8_t i = 0; i < kToshibaACStateLength; i++)
    Serial.printf("%02X", ir_code[i]);
  Serial.println();
}
//--------

const char* fwUrlBase = "http:// -IP address-- /files/";
String OldCommand = "";
String newCommand = "";

//String token = "old token....PzHitzWH-Bshe3BWiqEwuqgSs";   // REPLACE myToken WITH YOUR TELEGRAM BOT TOKEN
String token = "new token... spjKaVoOdWHvtcZSNCyM";   // REPLACE myToken WITH YOUR TELEGRAM BOT TOKEN
unsigned int my_telegram_id = 12345678;
unsigned int sleep_time = 600000000;  //600 seconds
rst_info *xyz;
byte reset_reason;

int valid = 0;

Ticker ticker;

//char* ssid = "";
//char* password = "";

String ssid = "";
String password = "";

// ADC_MODE(ADC_VCC);

#define LED_ESP 2
#define PH_VCC 14
#define PH_GND 12
#define THRESHOLD 900
#define POWER_CYCLE 6
#define WD_RESET 5
#define AFTER_OTA 4

// the setup function runs once when you press reset or power the board
void setup() {

  dht.setup(DHTIO, DHTesp::DHT22);
  delay(2000);

  // For Toshiba AC control
  pinMode(LED_BUILTIN, OUTPUT);
  ac.begin();
  Serial.begin(115200);
  delay(200);
  //------------

  // Set up what we want to send. See ir_Toshiba.cpp for all the options.
  Serial.println("Default state of the remote.");
  //printState(my_telegram_id);
  Serial.println("Setting desired state for A/C.");
  ac.on();
  // ac.setFan(1);
  // ac.setMode(kToshibaAcCool);
  ac.setMode(kToshibaAcAuto);
  ac.setTemp(25);

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_ESP, OUTPUT);
  Serial.begin(115200);  
  Serial.println("Starting...");

  // Init DHT ground
  pinMode(DHTGround, OUTPUT);
  digitalWrite(DHTGround, LOW);

  Serial.print("Checking Reset reason...");
  xyz = ESP.getResetInfoPtr();
  reset_reason = (*xyz).reason;
  Serial.println(reset_reason);

  //if (reset_reason == POWER_CYCLE) {
  Serial.print("Connecting to WiFi...");
  connect_to_wifi();
  Serial.println("connected.");
  //int Light = getLght();
  myBot.sendMessage(my_telegram_id, "Tady AC, boot, kontroluji update...");
  Serial.print("Checking for update...");
  checkForUpdates();
  Serial.println("checked.");
  //}
  readAcServer();
  OldCommand = newCommand;

  }

//################################### START ###############################################xx

// the loop function runs over and over again forever
void loop() {

  // Read Temp Humi every 50 cycles (5 minutes)
  currentCycle = currentCycle + 1;
  if (currentCycle >= cycleCount) {
    delay(dht.getMinimumSamplingPeriod());
    float humidity = dht.getHumidity();
    Serial.println(humidity);
    float temperature = dht.getTemperature();
    Serial.println(temperature);
    st = String(temperature);
    String sh = String(humidity);
    Serial.println("Temp: "+st+", Humi: "+sh);
    currentCycle = 0;
    logTS(sh,st);
    }

  // Read CTbot message
  TBMessage msg;
  String tms = "";
  int cmdsent = 0;

  recipient = my_telegram_id;

  readAcServer();

  Serial.print("Command retrieved: ");
  Serial.println(newCommand);

  if (newCommand != OldCommand) {
    tms = newCommand;
    Serial.print("Preparing to send command from server: ");
    Serial.println(tms);
    myBot.sendMessage(recipient, "Server command: "+newCommand);
    }
  OldCommand = newCommand;

  // if there is an incoming message...
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(20); 
  digitalWrite(LED_BUILTIN, HIGH);
  if (myBot.getNewMessage(msg)) {
    // ...forward it to the sender
    cmdsent = 1;
    tms = msg.text;
    tms.toUpperCase();
    recipient = msg.sender.id;
    myBot.sendMessage(recipient, "Příjem: "+tms);
    }

  valid = 0;

  char tm[50];

  tms.toCharArray(tm,50);
  char* word1 = strtok(tm," ");
  char* word2 = strtok(NULL," ");
  String w1 = String(word1);
  String w2 = String(word2);
  Serial.println("Word1: "+w1+"; Word2: "+w2);

  // Now send the IR signal.
  #if SEND_TOSHIBA_AC

    if (w1 == "POWER") {
      if (w2 == "ON") {
        ac.on();
        valid = 1;
        // OldCommand = "POWER ON";
        }
      if (w2 == "OFF") {
        ac.off();
        valid = 1;
        // OldCommand = "POWER OFF";
        }
      }
    if (w1 == "MODE") {
      if (w2 == "AUTO") {
        ac.setMode(kToshibaAcAuto);
        valid = 1;
        }
      if (w2 == "COOL") {
        ac.setMode(kToshibaAcCool);
        valid = 1;
        }
      if (w2 == "HEAT") {
        ac.setMode(kToshibaAcHeat);
        valid = 1;
        }
      }

    if (w1 == "FAN") {
      if (w2 == "AUTO") {
        ac.setFan(kToshibaAcFanAuto);
        valid = 1;
        }
      if (w2 == "MIN") {
        ac.setFan(1);
        valid = 1;
        }
      if (w2 == "MAX") {
        ac.setFan(kToshibaAcFanMax);
        valid = 1;
        }
      }

    if (w1 == "TEMP") {
      int tem = w2.toInt();
      ac.setTemp(tem);
      valid = 1;
      }

    if ((w1 == "RESET") || (w1 == "REBOOT")) {
      myBot.sendMessage(my_telegram_id, "REBOOT!");
      w1="";
      //ESP.restart();
      }     

  if (valid == 1) {
    Serial.println("Sending IR command to A/C ...");
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    delay(1000); 
    ac.send();
    digitalWrite(LED_BUILTIN, HIGH);
    #endif  // SEND_TOSHIBA_AC 
    printState(my_telegram_id);
    //myBot.sendMessage(recipient, "Příkaz poslán.");
    }
  else 
    if (cmdsent == 1) myBot.sendMessage(recipient, "Neznámý příkaz!");  

  delay(5000);

}

//############################## STOP ##################################xx

void logTS(String param1, String param2) {

  int cc = client.connect(TSserver,80);
  if (cc) {
    // vytvoření zprávy, která bude odeslána na Thingspeak
    // každé pole je označeno jako "field" + pořadí pole,
    // je nutné každý údaj převést na String
    String zprava = APIkey;
    zprava +="&field1=";
    zprava += param1;
    zprava +="&field2=";
    zprava += param2;
    zprava += "\r\n\r\n";
    // po vytvoření celé zprávy ji odešleme na server Thingspeak
    // včetně našeho API klíče
    client.print("POST /update HTTP/1.1\n");
    client.print("Host: api.thingspeak.com\n");
    client.print("Connection: close\n");
    client.print("X-THINGSPEAKAPIKEY: "+APIkey+"\n");
    client.print("Content-Type: application/x-www-form-urlencoded\n");
    client.print("Content-Length: ");
    client.print(zprava.length());
    client.print("\n\n");
    client.print(zprava);
    // vytištění informací po sériové lince o odeslání na Thingspeak
    Serial.print("param1: ");
    Serial.print(param1);
    Serial.print(" param2: "); 
    Serial.println(param2);
    Serial.println("Udaje odeslany na Thingspeak.");
    }
  else {
    ESP.restart();
    }  
  // ukončení spojení se serverem Thingspeak
  client.stop();
  }

void readAcServer() {
  String mac = String(D_Name);
  String fwURL = String( fwUrlBase );
  fwURL.concat( mac );
  String fwVersionURL = fwURL;
  fwVersionURL.concat( ".command" );

  Serial.println( "Checking for server command." );
  Serial.print( "MAC address: " );
  Serial.println( mac );
  Serial.print( "Command URL: " );
  Serial.println( fwVersionURL );

  HTTPClient httpClient;
  httpClient.begin( fwVersionURL );
  int httpCode = httpClient.GET();
  if( httpCode == 200 ) {
    newCommand = httpClient.getString();

    Serial.print( "Available Command: " );
    newCommand.toUpperCase();
    Serial.println( newCommand );
    }
  else {
    Serial.print( "Command failed, got HTTP response code " );
    Serial.println( httpCode );
    }
  httpClient.end();
  return;
  }

void connect_to_wifi() {
  Serial.println("Setting WiFi connection...");
  delay(100);
  pinMode(LED_ESP, OUTPUT);
  ticker.attach(0.6, tick);
  WiFiManager wifiManager;
  wifiManager.setConfigPortalTimeout(300);
  wifiManager.setConnectTimeout(60);
  wifiManager.setAPCallback(configModeCallback);
  if (!wifiManager.autoConnect(D_Name)) {
    Serial.println("failed to connect and hit timeout");
    ESP.reset();
    delay(1000);
  }
  Serial.println("connected...yeey :)");
  ticker.detach();
  digitalWrite(LED_ESP, LOW);
  delay(3000);
  digitalWrite(LED_ESP, HIGH);
  Serial.print("WiFi.SSID(): ");
  Serial.println(WiFi.SSID());
  //WiFi.SSID().toCharArray(ssid, 32);
  Serial.print("WiFi.psk(): ");
  Serial.println(WiFi.psk());
  //WiFi.psk().toCharArray(password, 32);
}

void OTAtick() {
  //ArduinoOTA.handle();
  }

void delayOTA(int duration) {                       // Use this loop to keep OTA alive
  for (int i=0; i <= duration/1000; i++) {
    //ArduinoOTA.handle();
    delay(1000);
  }
}

void blink_led(int which, int cas1, int cas2, int inverse) {
  pinMode(which, OUTPUT);
  digitalWrite(which, 1-inverse);
  delay(cas1);
  digitalWrite(which, inverse);
  delay(cas2);
}

void tick() {
  //toggle state
  int state = digitalRead(LED_ESP);  // get the current state of GPIO1 pin
  digitalWrite(LED_ESP, !state);     // set pin to the opposite state
}

//gets called when WiFiManager enters configuration mode
void configModeCallback (WiFiManager *myWiFiManager) {
  Serial.println("Entered config mode");
  Serial.println(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  Serial.println(myWiFiManager->getConfigPortalSSID());
  //entered config mode, make led toggle faster
  ticker.attach(0.2, tick);
}

void checkForUpdates() {
  String mac = String(D_Name);
  String fwURL = String( fwUrlBase );
  fwURL.concat( mac );
  String fwVersionURL = fwURL;
  fwVersionURL.concat( ".version" );

  Serial.println( "Checking for firmware updates." );
  Serial.print( "MAC address: " );
  Serial.println( mac );
  Serial.print( "Firmware version URL: " );
  Serial.println( fwVersionURL );

  HTTPClient httpClient;
  httpClient.begin( fwVersionURL );
  //httpClient.begin("http://7........../files/ESP_c_1.version");
  int httpCode = httpClient.GET();
  if( httpCode == 200 ) {
    String newFWVersion = httpClient.getString();

    Serial.print( "Current firmware version: " );
    Serial.println( FW_VERSION );
    Serial.print( "Available firmware version: " );
    Serial.println( newFWVersion );

    long newVersion = newFWVersion.toInt();

    if( newVersion > FW_VERSION ) {
      Serial.println( "Preparing to update" );
      myBot.sendMessage(my_telegram_id, "Updating to version "+newFWVersion);
      String fwImageURL = fwURL;
      fwImageURL.concat( ".bin" );
      ticker.attach(0.1, tick);
      t_httpUpdate_return ret = ESPhttpUpdate.update( fwImageURL );
      ticker.detach();

      switch(ret) {
        case HTTP_UPDATE_FAILED:
          Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
          break;

        case HTTP_UPDATE_NO_UPDATES:
          Serial.println("HTTP_UPDATE_NO_UPDATES");
          break;
      }
    }
    else {
      Serial.println( "Already on latest version" );
    }
  }
  else {
    Serial.print( "Firmware version check failed, got HTTP response code " );
    Serial.println( httpCode );
  }
  httpClient.end();
}

/*
String getMAC()
{
  uint8_t mac[6];
  char result[14];

 snprintf( result, sizeof( result ), "%02x%02x%02x%02x%02x%02x", mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ] );

  return String( result );
}

int getLght() {

  pinMode(PH_VCC, OUTPUT);
  pinMode(PH_GND, OUTPUT);
  digitalWrite(PH_VCC, HIGH);
  digitalWrite(PH_GND, LOW);

  //int Light = 1000; //analogRead(A0);
  int Light = analogRead(A0);
  Serial.print("Light: ");
  Serial.println(Light);
  delay(50);
  pinMode(PH_VCC, INPUT);
  pinMode(PH_GND, INPUT);

  return Light;  
}
*/

Debug Messages

Debug messages go here
TD-er commented 5 years ago

I've been skipping through your code a few times, but it is hardly the minimal code to show the issue. Where do you try to store the OTA binary? It looks like you're using the standard OTA library, which does not store the temp binary on the SPIFFS filesystem, but on the unused area between active sketch and SPIFFS. If you have split your 4M flash into 1M sketch and 3M SPIFFS, then you have 1M for the active sketch + OTA image (and a few kB for other things that get stored)

devyte commented 4 years ago

@jbrepogmailcom please reduce your sketch to something "M"inimal and "C"omplete with which we can "V"erify the issue.

jbrepogmailcom commented 4 years ago

Problem solved. It turned out that after flash on first http update the ESP module downloaded whole update over 400kB correctly and updated. However, on all subsequential updates, it only downloaded first 10-15kB and then reported error "New bin does not fit". It is quite strange, because http server's error log did not show any problem. ESP just cut off downloading of bin and gave an error. I fixed it by changing this:

HTTPClient httpClient; httpClient.begin( fwVersionURL ); .... some code .... t_httpUpdate_return ret = ESPhttpUpdate.update( fwImageURL );

to this

WiFiClient wclient; t_httpUpdate_return ret = ESPhttpUpdate.update( wclient, fwImageURL );

I would be glad if someone can explain me why it helped

jbrepogmailcom commented 4 years ago

Hello, unfortunately the issue is not resolved. I am not using any SPIFFS. I would say that main problem is that download from server gets cut off after about 13kB, server responds with code 200 (OK) and ESP reports the bin does not fit. In the reality the bin should have around 450kB.

jbrepogmailcom commented 4 years ago

I will reopen after I confirm it is not server or router problem

lightning003 commented 4 years ago

Facing the same issue, have you solved it?

jbrepogmailcom commented 4 years ago

Not yet. Seems that OTA example is working, but when used in real sketch it does not. If you can, please check your apache log to see if the bin file is sent completely or if server logs smaller file with OK 200 response code. Thanks

jbrepogmailcom commented 4 years ago

In one case it clearly helped adding wclient to ESPhttpUpdate.update( wclient, fwImageURL ); as in post above, in other case the update is still not working

KimAnhDuc commented 4 weeks ago

I use ESP8266 and I get an error when updating the firmware HTTP_UPDATE_FAILED Error (-107): New Binary Does Not Fit Flash Size while Sketch uses 326240 bytes (31%) of program storage space. Maximum is 1044464 bytes. Is there any way to fix it? Thank