• Hi,

    I have a relatively big codebase right now and I'm not sure what causes this error:

    Unable to create string as not enough memory
    

    I guess I'm having trouble while reading from FlashEEPROM with the following class:

    export pack = (x) ->
        JSON.stringify x
    
    export unpack = (wire-data) ->
        try
            x = JSON.parse wire-data
            throw if x is void
            return x
        catch
            console.log "Error on unpacking: ", e
            console.log "wire data: ", wire-data
            throw "Error on unpacking"
    
    export !function Config file-no
        self = this
        @file-no = file-no
        @f = new (require "FlashEEPROM")(0x076000)
        @f.endAddr = @f.addr + 1024
        @write-count = 0
    
        @write = (data) !->
            if @write-count++ > 10
                @f.cleanup!
                @write-count = 0
            @f.write self.file-no, pack data
    
        @read = ->
            try
                data = E.to-string @f.read self.file-no
                return unpack data
            catch
                console.log "ERROR CONFIG READ: ", e
                console.log "raw data read: ", data
                console.log "dump: ", @file-no
    
    

    Here is the Javascript output of above code:

    var pack, unpack, out$ = typeof exports != 'undefined' && exports || this;
    out$.pack = pack = function(x){
      return JSON.stringify(x);
    };
    out$.unpack = unpack = function(wireData){
      var x, e;
      try {
        x = JSON.parse(wireData);
        if (x === void 8) {
          throw null;
        }
        return x;
      } catch (e$) {
        e = e$;
        console.log("Error on unpacking: ", e);
        console.log("wire data: ", wireData);
        throw "Error on unpacking";
      }
    };
    out$.Config = Config;
    function Config(fileNo){
      var self;
      self = this;
      this.fileNo = fileNo;
      this.f = new (require("FlashEEPROM"))(0x076000);
      this.f.endAddr = this.f.addr + 1024;
      this.writeCount = 0;
      this.write = function(data){
        if (this.writeCount++ > 10) {
          this.f.cleanup();
          this.writeCount = 0;
        }
        this.f.write(self.fileNo, pack(data));
      };
      this.read = function(){
        var data, e;
        try {
          data = E.toString(this.f.read(self.fileNo));
          return unpack(data);
        } catch (e$) {
          e = e$;
          console.log("ERROR CONFIG READ: ", e);
          console.log("raw data read: ", data);
          return console.log("dump: ", this.fileNo);
        }
      };
    }
    

    I'm trying to use this code as follows:

    a = new Config 1
    
    inc-input = ->
        try
            x = a.read!
            throw if isNaN x
        catch
            console.log "error reading input counter: ", e
            x = 0
        a.write ++x
        x
    
    sim2 = !->
        <- :lo(op) ->
            console.log inc-input!
            <- sleep 5ms
            lo(op)
    
    

    ...the Javascript:

    var a, incInput, sim2;
    a = new Config(1);
    incInput = function(){
      var x, e;
      try {
        x = a.read();
        if (isNaN(x)) {
          throw null;
        }
      } catch (e$) {
        e = e$;
        console.log("error reading input counter: ", e);
        x = 0;
      }
      a.write(++x);
      return x;
    };
    sim2 = function(){
      (function lo(op){
        console.log(incInput());
        return sleep(5, function(){
          return lo(op);
        });
      })(function(){});
    };
    

    The error is:

    Error on unpacking:  null
    wire data:  undefined
    ERROR CONFIG READ:  Error on unpacking
    raw data read:  undefined
    dump:  1
    144
    145
    146
    147
    ERROR: Out of Memory!
    WARNING: Unable to create string as not enough memory
    ERROR: Error processing Serial data handler - removing it.
    Execution Interrupted during event processing.
    at line 1 col 148
    ....flash.read(a,r.addr+4),h==e))){if(r.­end+e.length+4>=this.en...
                                  ^
    in function "write" called from line 1 col 89
    ...is.f.write(n.fileNo,pack(e))
                       in function "write" called from line 1 col 81
    ...g.read(),t.set),cfg.write(o),inpCount­er.write(o.coinInpRx),o...
                                  ^
    in function called from system
    148
    149
    150
    151
    152
    

    I expect the sim2() function to increment flash memory area by 1 in every 50 ms and this is what happens most of the time. But sometimes, (in a random order of power cycles) it Config.read() throws an error and memory can not be read.

    Is there anything I might misunderstand about usage of FlashEEPROM?

  • Isn't this a recursive call that will call itself until all memory is consumed?

    (function lo(op){
        console.log(incInput());
        return sleep(5, function(){
          return lo(op);
    
  • I guess no, since sleep (is setTimeout under the hood) schedules lo(op) call and previous execution of lo(op) ends there. Which means there is nothing related with lo(op) function's execution within this 5ms duration.

  • As setTimeout() is not necessarily ending a context... and that's what I think is going here, just as @Wilberforce says... it depends what arguments are passed with the sleep. If they keep holding on to a context in order to return at a later time, and the whole is nested - like parsing in a tree - it is easy to run out of memory.

    Next thing is cross-compiling/generating: even though a powerful technique, it has its price... how is your sleep function look like?

  • Are you sure about that? Is it all because of this "recursive function"s memory consumption?

    sleep function is just a renamed setTimeout function:

    var sleep;
    sleep = function(ms, f){
      return setTimeout(f, ms);
    };
    
  • Output a process.memory() in the loop and see if the jsvars drop.

  • I reconnected the ESP8266 in order to add process.memory() in the loop and saw that it had been working for hours (11.25597 hours for now). I added process.memory() anyway:

    810428 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810429 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810430 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810431 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810432 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810433 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810434 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810435 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810436 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    810437 { "free": 122, "usage": 1278, "total": 1400, "history": 1 }
    
  • Well there's the first problem - you're walking on a knife edge there... only 122 free...

  • Well, I removed my variables (class instances) one by one, and available memory is increased to 836 bytes. Absolutely these classes are needed, but clearly they consume a lot of memory. Would this be my solution?

  • @DrAzzy After refactoring my classes (used prototypes instead), memory usage is decreased dramatically:

    41 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    42 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    43 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    44 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    45 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    46 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    47 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    48 { "free": 579, "usage": 821, "total": 1400, "history": 3 }
    49 { "free": 579, "usage": 821, "total": 1400, "history": 
    

    Edit

    ...but this didn't entirely help solving the problem. I cycled the power supply many times. Most of the time it read from FlashEEPROM as expected (correctly), but at 10th time or so, here is the result:

    Loading 7991 bytes from flash...
    Running onInit()...
    WARNING: Expecting a number or something iterable, got undefined
    WARNING: Expecting a number or something iterable, got undefined
    Error on unpacking:  null
    wire data:  
    ERROR CONFIG READ:  Error on unpacking
    raw data read:  
    dump:  1
    WARNING: Expecting a number or something iterable, got undefined
    WARNING: Expecting a number or something iterable, got undefined
    Error on unpacking:  null
    wire data:  
    ERROR CONFIG READ:  Error on unpacking
    raw data read:  
    dump:  2
    WARNING: Expecting a number or something iterable, got undefined
    WARNING: Expecting a number or something iterable, got undefined
    Error on unpacking:  null
    wire data:  
    ERROR CONFIG READ:  Error on unpacking
    raw data read:  
    dump:  2
    error reading input counter:  null
    ....
    
  • Here is the output after 30 power cycles:

    Loading 7981 bytes from flash...
    Running onInit()...
    299 { "free": 468, "usage": 932, "total": 1400, "history": 1 }
    ERROR: Out of Memory!
    WARNING: Truncating string as not enough memory
    Execution Interrupted during event processing.
    at line 1 col 127
    ...[0]]=this.flash.read(t,r+4)),r+=t+7&-­4,a=this.flash.read(4,r...
                                  ^
    in function "readAll" called from line 1 col 24
    var t,e,r=this.readAll();this.flash.erasePag­e(this.addr),t=t...
                           ^
    in function "cleanup" called from line 1 col 208
    ...dAddr&&(r.end=this.cleanup(),r.end+e.­length+4>=this.endAddr)...
                                  ^
    in function "write" called from line 1 col 96
    ...f.write(this.fileNo,pack(e)),sleep(3,­function(){return Confi...
                                  ^
    in function "write" called from line 1 col 83
    ...return inpCounter.write(++n),n}catch(o){return e=o,console.l...
                                  ^
    in function "incInput" called from line 1 col 22
    console.log(incInput(),process.memory())­,sleep(5,function(){...
                         ^
    in function "n" called from line 1 col 4
    n(e)
       ^
    in function called from system
    ESSID:  aea
    ESSID:  aktos-elektronik
    ESSID:  Enopan
    trying to connect to wifi... { 
    

    The Out of memory error is thrown right after onInit() runs. How could it be possible?

    Edit

    Program is not starting at all afterwards. Giving the following error:

    Loading 7981 bytes from flash...
    Running onInit()...
    ERROR: Out of Memory!
    WARNING: Truncating string as not enough memory
    Execution Interrupted during event processing.
    at line 1 col 127
    ...[0]]=this.flash.read(t,r+4)),r+=t+7&-­4,a=this.flash.read(4,r...
                                  ^
    in function "readAll" called from line 1 col 24
    var t,e,r=this.readAll();this.flash.erasePag­e(this.addr),t=t...
                           ^
    in function "cleanup" called from line 1 col 208
    ...dAddr&&(r.end=this.cleanup(),r.end+e.­length+4>=this.endAddr)...
                                  ^
    in function "write" called from line 1 col 96
    ...f.write(this.fileNo,pack(e)),sleep(3,­function(){return Confi...
                                  ^
    in function "write" called from line 1 col 83
    ...return inpCounter.write(++n),n}catch(o){return e=o,console.l...
                                  ^
    in function "incInput" called from line 1 col 22
    console.log(incInput(),process.memory())­,sleep(5,function(){...
                         ^
    in function called from line 1 col 110
    ...return n(e)})}(function(){})
                                  ^
    in function "sim2" called from line 1 col 44
    ...mitFilter(),setupIo(),sim2(),sleep(1e­3,function(){return con...
                                  ^
    in function called from system
    WARNING: Truncating string as not enough memory
    Execution Interrupted during event processing.
    at line 1 col 127
    ...[0]]=this.flash.read(t,r+4)),r+=t+7&-­4,a=this.flash.read(4,r...
                                  ^
    in function "readAll" called from line 1 col 24
    var t,e,r=this.readAll();this.flash.erasePag­e(this.addr),t=t...
                           ^
    in function "cleanup" called from line 1 col 208
    ...dAddr&&(r.end=this.cleanup(),r.end+e.­length+4>=this.endAddr)...
                                  ^
    in function "write" called from line 1 col 37
    Config.f.write(this.raidFile,pack(e))
                                        ^
    in function called from system
    
  • It turns out that I had corrupted FlashEEPROM's 0x076000+1024 area with so many writes. When I get the object with new require("FlashEEPROM")(0x077000), I can use this FlashEEPROM area.

    Now I changed the app code to use RAM, and flush very very rarely to the FlashEEPROM.

  • had corrupted FlashEEPROM...

    Switch over to something that does not wear out THAT quickly... tha's why some IoT MC chip maker use non-volatile RAM so they can power off / down anytime without saving to (FLASH)EEPROM. See FRAM/MRAM - 256-Kbit (32 K × 8) Serial (SPI) F-RAM - Ferroelecric, non-volative RAM - SPI: something that behaves like EEPROM AND RAM at the same time. For your needs, a 32KB works just fine and runs with @DrAzzy's EEPROM lib... and with the same driver but without wait-after-write (and no page erase), it's even better... you can write and write and write... For 'production' runtime EEPROM may work... it got corrupted at development time because of the unusual high rate of retries... ;)

  • That sounds great! I could never think about that I would face with such a problem... Maybe we can replace original flash chip or add another one to the board on our next release.

  • There is a trade off... easy integrate-able FRAM/MRAMs are only both of smaller capacities and more expensive than (Flash)EEPROMs. With a huge (Flash)EEPROM compared to the space needed and the (Flash)EEPROM run on a wear leveling driver you do not run THAT quickly into issues. The MCs with FRAM/MRAM have only 16..128..256 KB of that type of memory and for the vast amount of read-only needs they still run (Flash)EEPROM. Having all your application state in transparent, non-volatile RAM makes a sleep and resume very simple and and even more so quick. Last but not least not having to save and restore state saves battery...

  • Worth noting that cheap EEPROMs (though not big flash chips) are readily available in I2C interface too, not just SPI.

    http://www.espruino.com/AT24
    http://www.espruino.com/AT25

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

[WORKAROUND] Unable to create string as not enough memory (FlashEEPROM problem)

Posted by Avatar for ceremcem @ceremcem

Actions