Firmware Over-the-Air (FOTA)

Posted on
Page
of 2
/ 2
Next
  • I am trying to implement FOTA .... Initially i am trying to send my code from Serialport3. Application receiving the code after that i am using eval.. (function )

    function Update(serial) {
      var code = {line:''};
      serial.on('data', function(data) {
        code.line += data;
        var idx = code.line.indexOf('\n');
        while (idx>=0) {    var line = code.line.substr(0, idx);
          code.line = code.line.substr(idx+1);
          print ('\r\n code.line:'+line);
          eval(line);
          idx = code.line.indexOf('\n'); }  }); 
    }
    function onInit(){
      Serial3.setup(115200);
      Serial3.println("Data on uart3");
      Update(Serial3);  
    }
    onInit();
    save();
    

    ```
    I am trying this code ..
    but here i am getting old and new application from the flash on power on... how we can overwrite old application with new one..?

    Thanks and Regards
    Vishnu

  • Hi thank for the reply... If I reset before eval I will loose the variable(code).. Any other suggestions...

  • It's a little unclear at what point in the project you are from this. First off, I assume you mean updating the application code (the javascript), not the firmware (the Espruino interpreter itself)

    So you've got your code to read off the serial, and you've got it save()'ed, so it loads on power-on?

    And now you want it to save the state in response to code sent down the serial? Depending on the rest of your code, you might be able to just send a save();, which will save the code (replacing what was previously saved) and rebooting the Espruino.

    Now - if you're just sending code over serial, is there a reason you're not using the console instead? You can move the console onto a UART by doing something like Serial3.setConsole()...

    What's your overall goal/plan with this project?

    What is it going to be doing, and over what medium will it be getting the updates?

  • Hi DrAzzy , Main Goal of the project is , i would like to update application (Java Script) by sending it over the TCP using GSM modem. To do this initially i am sending my code from a Serail3 (The same code which i will receive from the GSM (TCP)) . As you said i am sending save(); along with the code.. After that i am doing Reset(); and Load(); then i am checking the Application with dump() .. at that time i can see my OLD and New code both

    Thanks
    Vishnu

  • Hi Vishnu,

    You've got three options for this I think:

    Load the code from the SD card at startup, then update it on the sd card

    This is quite nice and easy. Just add something like:

    function onInit() {
      eval(require("fs").readFile("boot.js"));­
    }
    save();
    

    Then don't call save any more. Then, just by overwriting boot.js and rebooting (even via load()) you can do an update.

    Make sure you delete everything else first

    So you could do:

    clearInterval();
    clearWatch();
    delete onInit;
    ...
    

    or perhaps even programatically do it by looping over Object.keys(this).

    Update the code saved in flash itself, then reboot

    This might be 'nicest'. I've put some code up here to let you update it from an SD card, so it shouldn't be too hard for you to get it over the air.

    To update the firmware you'd have to get an Espruino working exactly as you want it, and then create an 'image' (using the above code) which you'd send to the board

  • Hi Gordon,

    Thanks for the reply. Using SD card i am able to do update. There is no problem with the SD CARD ....
    I would like to update even SD card is not available in the design....
    if i got any solution i will update here..

    Thanks & Regards
    Vishnu

  • Hi,

    The other solution is to get code from an external EEPROM, however the third suggestion from my last post would do what you want.

  • Hi,

    I am trying the same thing ....able to read() from M25p16 spi flash..... but i am unable to write to my M25p16 spi flash
    i am checking that....

  • @Vishnu, can you read the status/write protection information of the M25P16? Did you check the soft bits that control write protection of the segments? M25P16 has - like practically all other such devices have - a multi-level protection from accidentally write to areas that should not be overwritten/written to. Other that that - as consulted from the data sheet - I do not see any issue you should face, because @DrAzzy's AT25 module does what such devices need to be read from and written to.

    An interesting comment was made in one of @DrAzzy's post that by writing to some areas the device may become read only. In the devices I used so far, this is always controlled by the status registers an not just by address areas. M25P16 has this 'nice' feature of hard protection bits which can only be written once... and then the related protected areas become read-only for ever (similar to fuse/anti-fuse, OTP - one time programmable - ROMs).

  • An interesting comment was made in one of @DrAzzy's post that by writing to some areas the device may become read only.

    That was the DS2xxx OneWire eeproms, which has the status registers as part of the normal address space.

    I looked a bit more at the datasheet....

    • Erase can only be done by sectors.
    • After erase, everything will be 255, and you can only write 1's to 0's, not the other way around (ie, you need another erase to rewrite).
    • Can write 256 bytes at a time.

    So to make this work:

    • pgsz should be specified as 256
    • erase() method would need to be written
    • write() would need to check that it can write the content without erasing it first. Read/Modify/Write will be tricky because the pages are larger than the ram on most boards, so you need someplace to stuff all that data while you erase it and wait to rewrite it.

    .....

    In any event, he can't read the status register with my module, cause I didn't put that feature in (I2C eeproms don't have a status register, and on OW, they're very different), though it can certainly be done manually, and he should do that and report the results back. A function to read and write the status register on SPI eeproms might not be a bad thing to add to the AT25 module, but it's normally superfluous.

  • In order to use M25P16 transparently - reading and writing byte arrays / string - something like a M25P16 memory manager would be needed... The restrictions look like the UV light erasable EPROMS, where after erasing every bit is 'set' to 1... and 0 can be written electrically. The memory manager would pick an unwritten area and write the new value an mare the old as gone (by clearing bit that is not cleared at initial write).

    A function to read and write the status register on SPI eeproms might not be a bad thing to add to the AT25 module, but it's normally superfluous.

    For my FRAM/MRAM module I had that, but as @DrAzzy mentions, it is not worth to spend the memory for that by the module, because use is os rare, that a direct accessing the chip is good enough... and the code is very simple, as you can see in this post: id() - id read, sta() - status read, wen() - write enable, wdi() - functions.

  • Hi DrAzzy and allObjects,

    Here i am attaching the C code which is working fine for me(M25p16)...
    and the Java Script which i am trying to access M25p16..
    As you mentioned i am trying to read write protect register...
    but no luck with write operation.... Page Erase is working... reading is working.. but unable to do write() operation ...


    2 Attachments

  • @Vishnu, I could not find FlashPageWrite implementation in any of the C sources in the provide .rar file. Do I miss something?

    PS: What kind of code page / character set encoding are you using? I get some scrambled stuff...

  • Looked at your .js code... wondered why you use WREN for just reading the busy flag...

    AT25.prototype.isBusy= function() {
    	if (add===undefined) {
    		add=this.ca;
    	}
    	var t=new Uint8Array([0]);
        this.spi.send(6,this.cspin); //WREN
    //  this.spi.send(6,this.cspin); //WREN
    	var i=0;
    	t[i++]=0x05;
      var ov=new Uint8Array([255]);
      do
      {  
         
         ov[0]=this.spi.send(t,this.cspin); 
      }while((ov[0] & 0x01) == 0x01);
        print("\r\n ov is :" +ov[0]);
    	
    };
    

    I could imagine that this could mess up the whole thing... I would just go for this simple approach:

    // returns true if busy
    AT25.prototype.isBusy= function() {
      return ((this.spi.send([0x05],this.cspin)[0] & 0x01) === 0x01);
    }
    

    You can then use this method in the read, write, erase, etc. at the begin like this:

    ...
    while (this.isBuys()) { "";  }
    ...
    

    Invoke .isBusy() Is no big deal, because you want to - and have to - waste time anyway...

    If you do not like the extra invocation, you could just imbed the code in the while condition:

    ...
    while ((this.spi.send([0x05],this.cspin)[0] & 0x01) === 0x01) { ""; }
    ...
    

    @Gordon, is the below code about the same as the above - assuming both work?

    ...
    while ((this.spi.send(5,this.cspin)[0] & 1) === 1) { ""; }
    ...
    

    And if both work, which one would you prefer?

    I'm asking, because of the conversion of literal ints to uInt8?

  • You've got the WREN line commented out in your .js file in write(). I don't think it should be - the datasheet seems to imply that you do need to send the WREN command (0x06) before each write command.

  • The (commented) write code has the issue of early terminating the write cycle:

    AT25.prototype.write= function(add,data,num) {
    	var l=data.length;
    	if(typeof data!="string"){data=E.toString(data);}
    	
         var t=new Uint8Array([0,1,2,3]);
    	var i=0;
    	t[i++]=2;
    	t[i++]=add>>16;
    	t[i++]=add>>8; 
    	t[i++]=add;
        
      print("\r\nin write  cmd and address :"+ t+'\n');
    	this.spi.send(t,this.cspin);
        i=0;
       // while (i < l) 
        
      
            this.spi.send(data,this.cspin);
        print("\r\n wrinting data:"+ data);
    		i++; 
        
           
    	return l;
    };
    

    By using the cspin in the spi.send() of the write command and addresses, the write cycle is terminated... if you want to split the write into two *spi.send()*s, you do separately lower the cspin, send the command and address, send the data to be written, and the rise the cspin again separately... you can also rise the cspin a bit hacky by mentioning it in the spi.send() of the data. You then do not need to do it separately. As per data sheet, you have to send the command, address and at least one byte for a successful write.

    Something like this should actually work (single, combined send for command, address and data, until all data is written - first to write to (and fill up) the page holding add, then next page/s (if there are), and then remainder of data for the last page (if there is):

    AT25.prototype.write= function(add,data) {
    	if(typeof data!="string"){data=E.toString(data);}
    	var l=data.length, idx=0, i, t;
    	while (l > 0) {
    		i=((i = this.pgsz-(add%this.pgsz)) > l) ? l : i; 
    		t=(this.cap>65536)?E.toString(2,add>>16,­add>>8,add):E.toString(2,add>>8,add);
    		t=t+data.substr(idx,i);
    		while ((this.spi.send(5,this.cspin)[0] & 1) === 1) { ""; }
    		this.spi.send(6,this.cspin);
    		this.spi.send(t,this.cspin);
    		l-=i;idx+=i; add+=i;
    	}
    	return idx;
    };
    

    The above code does also a bit reorder the sequence to do everything before actually being ready to write in the time while a possible write from before still has to complete. Furthermore, the length is gathered after the possible conversion to string.

  • @DrAzzy, would below code fix the issue - if there ever was any (because the .substr() would not have not concatenated more to t than would have been left from the last piece of data - would it)?

        while (l > 0) {
            i=((i = this.pgsz-(add%this.pgsz)) > l) ? l : i; 
    
    
  • If you want to avoid the concatenation, you can implement as follows (with a split write of writing command and address first and then the data):

    AT25.prototype.write= function(add,data) {
        if(typeof data!="string"){data=E.toString(data);}
        var l=data.length, idx=0, c, i, t;
        while (l>0) {
            c=E.toString(2,add>>16,add>>8,add);
            i=((i=this.pgsz-(add%this.pgsz)) > l) ? l : i;
            t=data.substr(idx,i);
            while ((this.spi.send(5,this.cspin)[0] & 1) === 1) { ""; }
            this.cspin.reset(); this.spi.send(c);
            this.spi.send(t,this.cspin);
            l-=i;idx+=i;add+=i;
        }
        return idx;
    };
    

    Since you know you have 3 address bytes, you can also drop the cap(acity) check.

  • @DrAzzy, afaiu, the WREN has to be sent before every PAGE PROGRAM command, because of the last sentence in this data sheet paragraph of the PAGE PROGRAM command description: ...before (PAGE PROGRAM) cycle completed and WEL bit is reset.

    As soon as S# is driven HIGH, the self-timed PAGE PROGRAM cycle is initiated; the cycles's duration is tPP. While the PAGE PROGRAM cycle is in progress, the status register may be read to check the value of the write in progress (WIP) bit. The WIP bit is 1 during the self-timed PAGE PROGRAM cycle, and 0 when the cycle is completed. At some un- specified time before the cycle is completed, the write enable latch (WEL) bit is reset.

    So this device is for sure a very interesting one...

    ...with the biggest draw-back that an in-place rewrite needs an erase of the whole page. For full page writes, this is not that an issue, but for partial ones where multiple logical data items share the page, the maximum write cycles of the device are reached in multiples earlier. Therefore, for data that is expected to be a memory extension and involve in continuous processing, EEPROM technology requires ahead determination of how many write cycles are to be expected in life time. Since this may be a daunting task - and a run away write loop may get there in 'no time', application of EEPROM technology in recurring processing is out.

  • Hi Drazzy and allObjects,
    eeprom.isBusy(); in this function i am using WREN that's why i commented it in write operation . As i am using

    AT25.prototype.writedata= function(add,data,num) 
    {
      
       eeprom.isBusy();
       eeprom.page_erase();
       console.log("\r\n page erased");
       eeprom.isBusy();
       eeprom.write(add,data); 
    };
    

    this one .In this first i am erasing the page and trying to write...

  • As you mentioned i am trying to read write protect register...
    but no luck with write operation.... Page Erase is working... reading is working.. but unable to do write() operation ...

    How do you know that page erasing is working without begin able to write?

    Fairly assuming you do no know what the memory has stored, you would need the following sequence to proof that your page erase worked, read, and write work:

    1. erase page
    2. read from page and get all 0xFF back
    3. write onto page 0x41 0x42 0x43 (=ABC=[65,66,67])
    4. read from page and get 0x41 0x42 0x43 ([65,66,67]=ABC)
    5. erase page
    6. read from page and get all 0xFF back

    In code this may look like below.

    Copy it, paste it into the IDE code pane, upload it to the board, and post the result with copy paste of the output you get in IDE console / terminal pane.

    SPI1.setup({sck:A5, miso:A6, mosi:A7,baud:4500000});
    while ((SPI1.send(0x05,A4)[0] & 1) === 1) { ""; }
    SPI1.send(0x06,A4);
    SPI1.send([0xD8,0x00,0x00,0x00],A4);
    while ((SPI1.send(0x05,A4)[0] & 1) === 1) { ""; }
    console.log(SPI1.send([0x03,0x00,0x00,0x­10,0x00,0x00,0x00],A4); // ---> 255,255,255
    SPI1.send(0x06,A4);
    SPI1.send([0x02,0x00,0x00,0x10,0x41,0x42­,0x43],A4);
    while ((SPI1.send(0x05,A4)[0] & 1) === 1) { ""; }
    console.log(SPI1.send([0x03,0x00,0x00,0x­10,0x00,0x00,0x00],A4); // ---> 65,66,67
    SPI1.send(0x06,A4);
    SPI1.send([0xD8,0x00,0x00,0x00],A4);
    while ((SPI1.send(0x05,A4)[0] & 1) === 1) { ""; }
    console.log(SPI1.send([0x03,0x00,0x00,0x­10,0x00,0x00,0x00],A4); // ---> 255,255,255
    
  • Hi allObjects,

    hi i have uploaded C Code na using that i am writing to flash.... after that i am reading from the flash and erasing ....
    i hope you got it...
    and one thing i forgot to mention ....
    i have modified this one to

     SPI_InitStructure.SPI_CPOL = (inf->spiMode&SPIF_CPOL)?SPI_CPOL_High:S­PI_CPOL_Low;
      SPI_InitStructure.SPI_CPHA = (inf->spiMode&SPIF_CPHA)?SPI_CPHA_2Edge:­SPI_CPHA_1Edge; 
    

    like this

     SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    

    i have modified this in Espruino SPI_init();

    Thanks & Regards
    Vishnu

  • Hi Vishnu,

    Is there a reason you didn't just set mode to the right value when initialising SPI?

    Did you mean to attach the C code to that post? Just wondering - why did you write it in C rather than JavaScript? I would have thought that would have been much harder to debug.

  • I assumed he got working C code from somewhere that he's trying to use as a reference to make the JS driver.

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

Firmware Over-the-Air (FOTA)

Posted by Avatar for Vishnu @Vishnu

Actions