'Over the Air' firmware updates, changes for flashing Teensy 3.5 & 3.6

Here you go.

https://github.com/joepasquariello/FlasherX

Some of the things I plan to do are:

- support read from SD file per @CollinK
- provide example of update via packetized communication
- packetized update via USB/UART Serial or UDP/TCP Ethernet

I tried this on my T4.1 and it looks like FLASH_RESERVE is too small? I have data in my EEPROM section and the init stops at 0x607FC000 with a length of zero. From what I can see reading eeprom.c there are 63 sectors for EEPROM emulation, and the last sector is for the blink program.

So something like this for the T4.1

Code:
#define FLASH_RESERVE		(64*FLASH_SECTOR_SIZE)	// reserve top of flash

And 16 sectors for the T4.0

Code:
#define FLASH_RESERVE		(16*FLASH_SECTOR_SIZE)	// reserve top of flash

Somebody should confirm that's right though, I only think I know how all that stuff is organized.

P.S. - Looks like LittleFS_Program uses data below the EEPROM as well. That could create the same issue determining free space.
 
That's a good question. Pending further input here, those could be default values, with notes regarding possible increase/decrease depending on the use of EEPROM, LittleFS, etc.

However, from this thread (https://forum.pjrc.com/threads/5737...-RAM-Questions?p=214582&viewfull=1#post214582) we see Paul's warning:

the main downside to using flash below the top 64K is Teensy Loader will erase any data each time you upload a new program.

With all of the work on LittleFS, is this still true?
 
> Some of the things I plan to do are:

IMO the right way is to implement a Stream class and use the existing code (eg, update_firmware()) without changes. Should be easy once you realize that .available() and .read() are virtual functions that you write to match the input device.
 
IMO the right way is to implement a Stream class and use the existing code (eg, update_firmware()) without changes. Should be easy once you realize that .available() and .read() are virtual functions that you write to match the input device.

Hi Jon. I agree and definitely will work toward a single routine for reading and processing Intel Hex records from an input stream, whether it be Serial, SD, etc. I'm pretty sure the person who implemented the SD input did that, but I need to look at it again.
 
...
With all of the work on LittleFS, is this still true?

With the release of TD 1.56 and upgrade of T_4.0 and 4.1 and MicroMod 1062's bootloader from 1.05 to 1.07 on the next upload with Teensy Loader, there is no longer a full flash erase on each upload.

See TD 1.56 release notes. Enough room for the sketch, or 512KB (or 1MB on Locked Teensy), whichever is larger, will be erased on each upload. All flash above that is left 'as is' for LittleFS ... or any other data that is stored there.
 
With the release of TD 1.56 and upgrade of T_4.0 and 4.1 and MicroMod 1062's bootloader from 1.05 to 1.07 on the next upload with Teensy Loader, there is no longer a full flash erase on each upload.

See TD 1.56 release notes. Enough room for the sketch, or 512KB (or 1MB on Locked Teensy), whichever is larger, will be erased on each upload. All flash above that is left 'as is' for LittleFS ... or any other data that is stored there.

Can you point me to release notes? I don't see anything about that in the list of changes in the TD 1.56 announcement.
 
See TD 1.56 release notes. Enough room for the sketch, or 512KB (or 1MB on Locked Teensy), whichever is larger, will be erased on each upload. All flash above that is left 'as is' for LittleFS ... or any other data that is stored there.

Okay, I found the info here (https://www.pjrc.com/teensy/td_code_security.html). For FlasherX, the user will have to configure the amount of upper flash they want to reserve. The default can be the space used for EEPROM, and they can increase if they use LittleFS or decrease if they do not use EEPROM. As before, after an update via FlasherX, all flash from the top of the new program to the bottom of the reserved space will be erased.
 
Okay, I found the info here (https://www.pjrc.com/teensy/td_code_security.html). For FlasherX, the user will have to configure the amount of upper flash they want to reserve. The default can be the space used for EEPROM, and they can increase if they use LittleFS or decrease if they do not use EEPROM. As before, after an update via FlasherX, all flash from the top of the new program to the bottom of the reserved space will be erased.

Opps - was a rushed post ... short mention of the reserved space was in the beta 6 posts where @mjs513 did a final test. Indeed the linked page is the best ref. Good luck.
 
Looks like the flash write/erase functions in flasherX were from before the T41 came out so there's a 2MB 'AND' in there on the address.

Thanks. The T4.x flash functions (wait/write/erase_sector) were taken from cores\Teensy4\eeprom.c. They were not from before T4.1, but I'm not sure which version of TeensyDuino that was. In any case, I checked TD 1.56, and there has been an update that fixes the 2MB limit. It replaces the mask 0x001fffff with 0x00ffffff. I'll incorporate that into FlasherX v3. I will copy the functions as I did before, with no changes, so that means I won't incorporate the change you made to the function signature by adding a return value.

EDIT: Tested update with a T4.1 sketch > 4MB, and it worked fine.
 
Last edited:
It replaces the mask 0x001fffff with 0x00ffffff

Interesting, I forgot I'm behind a few versions, on Teensyduino 1.53. That's where the 0x007FFFFF came from. I was just reading the newest eeprom.c and realized the MicroMod Teensy has 16MB of flash, that's where the 0x00FFFFFF write limit comes from. EEPROM moves to 0x60FC0000 also. So FLASH_SIZE for the MicroMod should be 0x1000000 instead of 0x800000 I think.

I won't incorporate the change you made to the function signature

That makes sense, I mostly just did that to help figure out what was going wrong.
 
Here you go.

https://github.com/joepasquariello/FlasherX

Some of the things I plan to do are:

- support read from SD file per @CollinK
- provide example of update via packetized communication
- packetized update via USB/UART Serial or UDP/TCP Ethernet

Woohoo! This is awesome, we now have reliable firmware update of our remote IoT device working over an unreliable radio link. Nice!

-Mark

Code:
Fri Jan 07 10:22:48 EST 2022 - INFO,Sensor,Firmware update starting (current version 21)
Fri Jan 07 10:22:48 EST 2022 - INFO,Controller,Controller preparing firmware download
Fri Jan 07 10:22:48 EST 2022 - INFO,Sensor,Firmware update buffer = 196K FLASH (0000F000 - 00040000)
Fri Jan 07 10:22:49 EST 2022 - INFO,Controller,Firmware transfer 0% complete
Fri Jan 07 10:23:20 EST 2022 - INFO,Controller,Firmware transfer 5% complete
Fri Jan 07 10:23:52 EST 2022 - INFO,Controller,Firmware transfer 10% complete
Fri Jan 07 10:24:24 EST 2022 - INFO,Controller,Firmware transfer 15% complete
Fri Jan 07 10:24:56 EST 2022 - INFO,Controller,Firmware transfer 21% complete
Fri Jan 07 10:25:27 EST 2022 - INFO,Controller,Firmware transfer 26% complete
Fri Jan 07 10:25:59 EST 2022 - INFO,Controller,Firmware transfer 31% complete
Fri Jan 07 10:26:24 EST 2022 - TRANSMIT ERROR,Controller,No START marker found
Fri Jan 07 10:26:37 EST 2022 - INFO,Controller,Firmware transfer 37% complete
Fri Jan 07 10:27:09 EST 2022 - INFO,Controller,Firmware transfer 42% complete
Fri Jan 07 10:27:41 EST 2022 - INFO,Controller,Firmware transfer 47% complete
Fri Jan 07 10:28:13 EST 2022 - INFO,Controller,Firmware transfer 53% complete
Fri Jan 07 10:28:44 EST 2022 - INFO,Controller,Firmware transfer 58% complete
Fri Jan 07 10:29:17 EST 2022 - INFO,Controller,Firmware transfer 63% complete
Fri Jan 07 10:29:49 EST 2022 - INFO,Controller,Firmware transfer 69% complete
Fri Jan 07 10:30:01 EST 2022 - TRANSMIT ERROR,Controller,No START marker found
Fri Jan 07 10:30:26 EST 2022 - INFO,Controller,Firmware transfer 74% complete
Fri Jan 07 10:30:58 EST 2022 - INFO,Controller,Firmware transfer 79% complete
Fri Jan 07 10:31:30 EST 2022 - INFO,Controller,Firmware transfer 85% complete
Fri Jan 07 10:32:01 EST 2022 - INFO,Controller,Firmware transfer 90% complete
Fri Jan 07 10:32:49 EST 2022 - INFO,Controller,Firmware transfer 98% complete
Fri Jan 07 10:32:59 EST 2022 - INFO,Sensor,New code contains correct FSEC value FFFFF9DE
Fri Jan 07 10:33:01 EST 2022 - INFO,Sensor,New code contains correct target ID fw_teensy32
Fri Jan 07 10:33:02 EST 2022 - INFO,Controller,Firmware transfer complete
Fri Jan 07 10:33:02 EST 2022 - INFO,Sensor,Firmware file data: 3763 lines, 60164 bytes (00000000 - 0000EB04)
Fri Jan 07 10:33:02 EST 2022 - INFO,Sensor,Firmware update complete, rebooting
Fri Jan 07 10:33:18 EST 2022 - INFO,Sensor,Sensor started, firmware version 23, serial number 1
 
Woohoo! This is awesome, we now have reliable firmware update of our remote IoT device working over an unreliable radio link. Nice!

That's great! Can you tell us more about your implementation? I'm curious if you are using a packetized protocol, and if so, did you roll your own or use an existing library? I'm working toward an example that uses the SerialTransfer library.

BTW, I plan to post an update to FlasherX in the next few days. The main change is to incorporate a TeensyDuino 1.56 update to the T4.x flash functions to support the entire flash of T4.1 (8MB) and TMM (16MB).
 
We already had our own protocol in place for the unreliable radio link (very simplistic checksum with ACK/NACK) so we added file transfer capability which is done 1 line at a time so that errors don't cause re-transmit of the entire file. The radio link is quite slow (the 60K transfer above took about 11 minutes). Each line is transmitted with a checksum (which actually duplicates the line-by-line checks in the HEX file itself) and is ACKed individually. The Teensy device will request a re-transmit if the protocol headers are wrong, the checksum fails, or it times out waiting for the next line.

The radio link uses these simple HC-12 transmitter/receiver pairs: https://www.amazon.com/gp/product/B07YKJ4LVF/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1 (they are cheaper elsewhere). The host side of this is a Raspberry Pi running Java with the jSerialComm library. The HC-12 is dead simple to use with the Teensy and just looks like a serial device. Literally just connect the HC-12 to the right Teensy pins, and it works out of the box.

We reused the FlasherX code parse_hex_line() and process_hex_record(), but all the higher level parts we rewrote to accommodate our line-at-a-time ack/nack/retry protocol.
 
FlasherX v2.1

v2.1 requires TD 1.54 or higher.

Changes relative to v2:

update FlashTxx.cpp to delete local T4x wait/write/erase to use functions exposed in TD 1.54
(eepromemu_flash_write() in TD 1.54 supports flash size up to 16 MB, from 2 MB)
update FlashTxx.h to update FLASH_SIZE for Teensy Micromod from 8 to 16 MB
update FlasherX.ino to print "FlasherX v2.1" on bootup, add option to artificially increase code size via const array

https://github.com/joepasquariello/FlasherX/releases/tag/v2.1
 
Last edited:
A blink.ino.hex, (#include "FlashTXX.h" and Serial.println(FLASH_ID)), file is stored on a remote server. With the help of a nano 33 iot, I downloaded the file from the remote server. With the code below, I was able to "see" the hex file arriving to a 3.6 board via Serial port. RemoteData.jpg
Code:
void setup() {
  Serial.begin(57600);
  Serial2.begin(57600);
}
void loop() {
  while (Serial2.available() > 0) {
    String str = Serial2.readStringUntil('\n');
    Serial.println(str);
  }
}

Using this "same" code concept , I want to be able to flash the 3.6 with the FlashX sketch but I haven't be able to do so. I am at the point that I am stuck hopefully someone can please show me the way.? I have two jumpers. One from Tx nano 33 connected to Pin 9 (Rx) and GND to GND. Thanks
 
Using this "same" code concept , I want to be able to flash the 3.6 with the FlashX sketch but I haven't be able to do so. I am at the point that I am stuck hopefully someone can please show me the way.? I have two jumpers. One from Tx nano 33 connected to Pin 9 (Rx) and GND to GND. Thanks

I don't understand your test. Is the T3.6 running the FlasherX sketch, or is it running the code you show? For this to work, the T3.6 would have to be running FlasherX.ino. After the last hex record, it would display the total number of records received and prompt for input. The required input to flash the new firmware would be the number of hex records sent, followed by newline. If the values match, the T3.6 would write the new program to flash and reboot.
 
Afterrunning.JPG The Teensy board has a fresh install of FlasherX. I run the code on the nano tu pull the data off the remote server and instruct the nano to hander over to the Teensy board. The same black screen shows no sign of any activity. I know I have some communication with the Teensy board because the teensy blinks every time I press the enter key. I connected the TX jumper coming from nano to pin 0 on teensy RX1 to see if anything change but no luck.
The nano's code
Code:
//Sketch loaded on a nano 33 IOT
//It  has its pin 8 connected to pin 9 on Teensy 3.6
//Teensy ground with Nano 33 IOT gnd
//This sketch is called ConnectingtoVPNChatServer; 
//The other sketch is called Sniffiing IOT. This sktech reads hex file from HTTP server
//And send it to Teensy to its serial port Serial port 2 in this case

//Connect both boards to USB as usual




#include <SPI.h>
#include <WiFiNINA.h>
#include <HttpClient.h>

#include <Arduino.h>
#include "wiring_private.h"

Uart mySerial (&sercom1, 13,  8, SERCOM_RX_PAD_1, UART_TX_PAD_2);

// Attach the interrupt handler to the SERCOM
void SERCOM1_Handler()
{
  mySerial.IrqHandler();
}

char ssid[] = "mmmm";          //  your network SSID (name)
char pass[] = "mmmm+";   // your network password
// Initialize the client library
WiFiClient client;
/////////////////////

int    HTTP_PORT   = 8080;
String HTTP_METHOD = "GET"; // or POST
char   HOST_NAME[] = "\99.99.99.99";//Format 99.99.99.99
String PATH_NAME   = "/Blink5000.ino.hex";//Format "/file_name.abc.xyz"




int status = WL_IDLE_STATUS;

String readString, readString1;
int x = 0; //for counting line feeds
char lf = 10; //line feed character
//////////////////////

void setup() {
  Serial.begin(57600);

  Serial.println("Attempting to connect to WPA network...");
  Serial.print("SSID: ");
  Serial.println(ssid);

  status = WiFi.begin(ssid, pass);
  if ( status != WL_CONNECTED) {
    Serial.println("Couldn't get a wifi connection");
    // don't do anything else:
    while (true);
  }
  // Reassign pins 13 and 8 to SERCOM (not alt this time)
  pinPeripheral(13, PIO_SERCOM);
  pinPeripheral(8, PIO_SERCOM);

  // Start my new hardware serial
  mySerial.begin(57600);
}

void loop() {
  // check for serial input
  if (Serial.available() > 0) //if something in serial buffer
  {
    byte inChar; // sets inChar as a byte
    inChar = Serial.read(); //gets byte from buffer
    if (inChar == 'e') // checks to see byte is an e
    {
      sendGET(); // call sendGET function below when byte is an e
    }
  }
}








void sendGET() //client function to send/receive GET request data.
{
  if (client.connect(HOST_NAME, HTTP_PORT)) {  //starts client connection, checks for connection
    Serial.println("connected");
    client.println(HTTP_METHOD + " " + PATH_NAME + " HTTP/1.1"); //download text
    client.println("Host: " + String(HOST_NAME));
    client.println("Connection: close");  //close 1.1 persistent connection
    client.println(); //end of get request
  }
  else {
    Serial.println("connection failed"); //error message if no client connect
    Serial.println();
  }

  while (client.connected() && !client.available()) delay(1); //waits for data
  while (client.connected() || client.available()) { //connected or data available
    char c = client.read(); //gets byte from ethernet buffer
    char displayByte = c; // sets inChar as a byte
    Serial.print(c); //prints raw feed for testing
    mySerial.write(displayByte);

    if (c == lf) {
      x = (x + 1); //counting line feeds

    }
    if (x == 9) {
      readString += c; //building readString
    }

  }
  Serial.println();
  Serial.println("done");
  Serial.println("disconnecting.");
  Serial.println("==================");
  Serial.println();
  client.stop(); //stop client
}
 
Last edited:
FlasherX is designed to communicate with the client (PC or whatever is sending the code) via RX/TX on a serial port. By default, that is USB (Serial). If you want to use UART (Serial1), you must modify FlasherX to use Serial1 instead of Serial. If you have not already done that, my suggestion is to do this in two steps. Step 1 is to modify FlasherX to user Serial1, at whatever baud rate you plan to use for Nano-T36 communication, and program via your PC using a terminal emulator, such as TeraTerm. Once you have that working, step 2 is to program your Nano to do everything that you do manually in order to send the file, then confirm the number of lines, etc.
 
Hi,

Has anyone had any success in impleneting FlasherX via I2C? I've currently got it working via RS-485 using a T3.6 to pass through from USB Serial to Serial1 with a max485 attached. However I'm struggling with the software side of things to do this over I2C.

Currently my code will read each hex line in on serial and send this over the i2c bus, and call the parse_hex_line, process_hex_line set of functions with each line received. In the serial monitor the HEX data looks as expected, however I'm getting a mixture of error messages, example below.
Code:
abort - bad hex line :1008B00003F08EFA0028A5D0029B2344DCB2B4FAE0
abort - error 03 in flash_write_block()
abort - error 02 in flash_write_block()


This is my loop and function I'm calling when receiving data on I2C:

Code:
void loop ()
{
if(received)
    {
       // digitalWrite(LED_BUILTIN,HIGH);
      // Serial.printf("Slave received: '%s'\n", databuf);
        received = 0;
      //  digitalWrite(LED_BUILTIN,LOW);
         processFirmwareFile();
    }

}




  void processFirmwareFile() {
    
      static char data[32] __attribute__ ((aligned (8)));  // buffer for hex data
  hex_info_t hex = {          // intel hex info struct
    data, 0, 0, 0,          //   data,addr,num,code
    0, 0xFFFFFFFF, 0,           //   base,min,max,
    0, 0            //   eof,lines
  };
  
      while (!hex.eof)  {


    if (parse_hex_line( (const char*)databuf, hex.data, &hex.addr, &hex.num, &hex.code ) == 0) {
      serial->printf( "abort - bad hex line %s\n", databuf );
      return;
    }
    else if (process_hex_record( &hex ) != 0) { // error on bad hex code
      serial->printf( "abort - invalid hex code %d\n", hex.code );
      return;
    }
    else if (hex.code == 0) { // if data record
      uint32_t addr = buffer_addr + hex.base + hex.addr - FLASH_BASE_ADDR;
      if (hex.max > (FLASH_BASE_ADDR + buffer_size)) {
        serial->printf( "abort - max address %08lX too large\n", hex.max );
        return;
      }
      
      else if (!IN_FLASH(buffer_addr)) {
        memcpy( (void*)addr, (void*)hex.data, hex.num );
       
      }
      else if (IN_FLASH(buffer_addr)) {
 
        int error = flash_write_block( addr, hex.data, hex.num );
        if (error) {
          serial->printf( "abort - error %02X in flash_write_block()\n", error );
          Serial.println(addr, HEX);
    return;
        }
      }
    }
    hex.lines++;
  }
    }
 
I'm not sure of your setup. You say you're passing the file through a T3.6. To what? If the code shown is from the T3.6 that is receiving on Serial, it's incomplete, and you're calling processFirmwareFile() from loop() without checking its return value. If there was an error, you'll just call it again, and you'll get another error.

Please explain in more detail what you're trying to do, and how you adapted FlasherX.ino to your purpose.
 
Hi,

Heres an image to explain it a bit clearer. A T3.6 is used to convert USB Serial to I2C, on each /n an I2C packet is transmitted containing the HEX payload.
Screenshot 2022-01-19 at 18.43.07.png

Here is the sketch running on the converter T3.6, this seems to be transmitting fine, as running the basic slave example on the receiving side I can see the hex lines being received.
Code:
#include <i2c_t3.h>


String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete

uint8_t target = 0x66; // target Slave address

void setup() {

  Serial.begin(115200);
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
  Wire.setDefaultTimeout(200000); // 200ms
}


void loop() {

  if (Serial.available()) {     // If anything comes in Serial1 (pins 0 & 1)

    char inChar = (char)Serial.read();

    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
    sendLineOverI2c();

      //clear input
      inputString = "";
    }
  }
}



#define MEM_LEN 256
char databuf[MEM_LEN];
void sendLineOverI2c()
{
  Wire.beginTransmission(target);   // Slave address
  inputString.toCharArray(databuf, MEM_LEN);
  Wire.write(databuf, strlen(databuf) + 1); // Write string to I2C Tx buffer (incl. string null at end)
  Wire.endTransmission();           // Transmit to Slave
}

Process firmware file is called on an I2C receive event. This should then process the line of HEX data and store it into the buffer. However its during this operation that I'm getting the errors, even though the HEX printed out back to the serial monitor looks as expected. Fundamentally I must be doing something wrong, so was wondering if there are any examples using the code from a data source other than a UART stream, admittedly I'm in a little over my head with this stuff. I'm just using the process_hex_record and parse_hex_line functions of the original example. Attached is the code for the receiving T3.6, this has the code to do actual flashing removed currently, I'm just trying to get it to receive over I2C, parse and check the data to confirm its valid for flashing. Any help would be appreciated.

Code:
//******************************************************************************
// FlasherX -- firmware "OTA" update via Intel Hex file over serial byte stream
//******************************************************************************
//
// WARNING: You can brick your Teensy with incorrect flash erase/write, such as
// incorrect flash config (0x400-40F). This code may or may not prevent that.
//
// Based on Flasher3 (Teensy 3.x) and Flasher4 (Teensy 4.x) by Jon Zeeff
//
// Jon Zeeff 2016, 2019, 2020
// This code is in the public domain.  Please retain my name and
// in distributed copies, and let me know about any bugs
//
// I, Jon Zeeff, give no warranty, expressed or implied for this software
// and/or documentation provided, including, without limitation, warranty of
// merchantability and fitness for a particular purpose.

// 7th Modifications 01/07/22 updates by Joe Pasquariello (v2.1)
//   - FlashTxx.cpp delete local T4x wait/write/erase to use functions in TD 1.56
//   - FlashTxx.h update FLASH_SIZE for Teensy Micromod from 8 to 16 MB
//   - FlasherX.ino update to print "FlasherX OTA firmware update v2.1" on bootup
//   - add option to artificially increase code size via const array (in flash)
// 6th Modifications 11/18/21 bug fix in file FlashTXX.cpp by Joe Pasquariello
//   - fix logic in while loop in flash_block_write() in FlashTXX
// 5th Modifications 10/27/21 add support for Teensy Micromod by Joe Pasquariello
//   - define macros for TEENSY_MICROMOD w/ same values as for TEENSY40
//   - update FLASH_SIZE for T4.1 and TMM from 2MB to 8MB
// 4th Modifications merge of Flasher3/4 and new features by Joe Pasquariello
//   - FLASH buffer dynamically sized from top of existing code to FLASH_RESERVE
//   - optional RAM buffer option for T4.x via macro RAM_BUFFER_SIZE > 0
//   - Stream* (USB or UART) and buffer addr/size set at run-time
//   - incorporate Frank Boesing's FlashKinetis routines for T3.x
//   - add support for Teensy 4.1 and Teensy LC
//    This code is released into the public domain.
// 3rd Modifications for T3.5 and T3.6 in Dec 2020 by Joe Pasquariello
//    This code is released into the public domain.
// 2nd Modifications for teensy 3.5/3/6 by Deb Hollenback at GiftCoder
//    This code is released into the public domain.
//    see https://forum.pjrc.com/threads/43165-Over-the-Air-firmware-updates-changes-for-flashing-Teensy-3-5-amp-3-6
// 1st Modifications by Jon Zeeff
//    see https://forum.pjrc.com/threads/29607-Over-the-air-updates
// Original by Niels A. Moseley, 2015.
//    This code is released into the public domain.
//    https://namoseley.wordpress.com/2015/02/04/freescale-kinetis-mk20dx-series-flash-erasing/

#include <Arduino.h>
#include "FlashTxx.h"		// TLC/T3x/T4x/TMM flash primitives
#include <i2c_t3.h>

// Function prototypes
void receiveEvent(size_t count);


// Memory
#define MEM_LEN 256
char databuf[MEM_LEN];
volatile uint8_t received;

const int ledPin = 13;		// LED
Stream *serial = &Serial
                 ;	// Serial (USB) or Serial1, Serial2, etc. (UART)

#define LARGE_ARRAY_TEST (0)	// 1 = define large array for large code download

#if (LARGE_ARRAY_TEST)
// nested arrays of integers to add code size for testing
#define A0 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}  // 16  elements 64
#define A1 {A0,A0,A0,A0,A0,A0,A0,A0,A0,A0,A0,A0,A0,A0,A0,A0}  // 256 elements 1KB 
#define A2 {A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1,A1}  // 4K  elements 16KB
#define A3 {A2,A2,A2,A2,A2,A2,A2,A2,A2,A2,A2,A2,A2,A2,A2,A2}  // 64K elements 256KB 
#define A4 {A3,A3,A3,A3,A3,A3,A3,A3,A3,A3,A3,A3,A3,A3,A3,A3}  // 1M  elements 4MB

// const variables reside in flash and get optimized out if never accessed
// use uint8_t -> 1MB, uint16_t -> 2MB, uint32_t -> 4MB, uint64_t -> 8MB)
PROGMEM const uint8_t a[16][16][16][16][16] = A4;
#endif

//******************************************************************************
// hex_info_t	struct for hex record and hex file info
//******************************************************************************
typedef struct {	//
  char *data;		// pointer to array allocated elsewhere
  unsigned int addr;	// address in intel hex record
  unsigned int code;	// intel hex record type (0=data, etc.)
  unsigned int num;	// number of data bytes in intel hex record

  uint32_t base;	// base address to be added to intel hex 16-bit addr
  uint32_t min;		// min address in hex file
  uint32_t max;		// max address in hex file

  int eof;		// set true on intel hex EOF (code = 1)
  int lines;		// number of hex records received
} hex_info_t;

void read_ascii_line( Stream *serial, char *line, int maxbytes );
int  parse_hex_line( const char *theline, char *bytes,
                     unsigned int *addr, unsigned int *num, unsigned int *code );
int  process_hex_record( hex_info_t *hex );
void update_firmware( Stream *serial, uint32_t buffer_addr, uint32_t buffer_size );


// variables for serial input
String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
int feature = 0;

uint32_t buffer_addr, buffer_size;

void setup ()
{
  Wire.begin(I2C_SLAVE, 0x66, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);

  // Data init
  received = 0;
  memset(databuf, 0, sizeof(databuf));

  pinMode(7, OUTPUT);
  digitalWrite(7, LOW);


  if (serial == (Stream*)&Serial) {
    Serial1.begin(115200);
    Serial.begin(115200);
    while (!Serial1) {}
  }
  else {
    ((HardwareSerial*)serial)->begin( 115200 );
  }

  serial->printf( "\nFlasherX v2.1 -- OTA firmware update -- %s %s\n", __DATE__, __TIME__ );
  serial->printf( "WARNING: this can ruin your device\n" );
  serial->printf( "target = %s (%dK flash in %dK sectors)\n",
                  FLASH_ID, FLASH_SIZE / 1024, FLASH_SECTOR_SIZE / 1024);

#if (LARGE_ARRAY_TEST) // if true, access array so it doesn't get optimized out
  serial->printf( "Large Array Test -- %08lX\n", (uint32_t)&a[15][15][15][15][15] );
#endif

  pinMode(ledPin, OUTPUT);	// assign output
  digitalWrite(ledPin, HIGH);	// set the LED on
  delay(200);			// delay
  digitalWrite(ledPin, LOW);	// set the LED off



  serial->printf( "Begining Firmware Upgrade...\n");
  if (firmware_buffer_init( &buffer_addr, &buffer_size ) == 0) {
    serial->printf( "unable to create buffer\n" );
    serial->flush();
    for (;;) {}
  }

  serial->printf( "buffer = %1luK %s (%08lX - %08lX)\n",
                  buffer_size / 1024, IN_FLASH(buffer_addr) ? "FLASH" : "RAM",
                  buffer_addr, buffer_addr + buffer_size );


  // register events
  Wire.onReceive(receiveEvent);
}






void receiveEvent(size_t count)
{
  Wire.read(databuf, count);  // copy Rx data to databuf
  received = count;           // set received flag to count, this triggers print in main loop
}




void loop ()
{
  if (received)
  {
    // digitalWrite(LED_BUILTIN,HIGH);
    // Serial.printf("Slave received: '%s'\n", databuf);
    received = 0;
    //  digitalWrite(LED_BUILTIN,LOW);
    processFirmwareFile();
  }

}




void processFirmwareFile() {

  static char data[32] __attribute__ ((aligned (8)));  // buffer for hex data
  hex_info_t hex = {          // intel hex info struct
    data, 0, 0, 0,          //   data,addr,num,code
    0, 0xFFFFFFFF, 0,           //   base,min,max,
    0, 0            //   eof,lines
  };

  while (!hex.eof)  {


    if (parse_hex_line( (const char*)databuf, hex.data, &hex.addr, &hex.num, &hex.code ) == 0) {
      serial->printf( "abort - bad hex line %s\n", databuf );
      return;
    }
    else if (process_hex_record( &hex ) != 0) { // error on bad hex code
      serial->printf( "abort - invalid hex code %d\n", hex.code );
      return;
    }
    else if (hex.code == 0) { // if data record
      uint32_t addr = buffer_addr + hex.base + hex.addr - FLASH_BASE_ADDR;
      if (hex.max > (FLASH_BASE_ADDR + buffer_size)) {
        serial->printf( "abort - max address %08lX too large\n", hex.max );
        return;
      }

      else if (!IN_FLASH(buffer_addr)) {
        memcpy( (void*)addr, (void*)hex.data, hex.num );

      }
      else if (IN_FLASH(buffer_addr)) {

        int error = flash_write_block( addr, hex.data, hex.num );
        if (error) {
          serial->printf( "abort - error %02X in flash_write_block()\n", error );
          Serial.println(addr, HEX);
          return;
        }
      }
    }
    hex.lines++;
  }
}
//******************************************************************************
// process_hex_record()		process record and return okay (0) or error (1)
//******************************************************************************
int process_hex_record( hex_info_t *hex ) {
  if (hex->code == 0) { // data -- update min/max address so far
    if (hex->base + hex->addr + hex->num > hex->max)
      hex->max = hex->base + hex->addr + hex->num;
    if (hex->base + hex->addr < hex->min)
      hex->min = hex->base + hex->addr;
  }
  else if (hex->code == 1) { // EOF (:flash command not received yet)
    hex->eof = 1;
  }
  else if (hex->code == 2) { // extended segment address (top 16 of 24-bit addr)
    hex->base = ((hex->data[0] << 8) | hex->data[1]) << 4;
  }
  else if (hex->code == 3) { // start segment address (80x86 real mode only)
    return 1;
  }
  else if (hex->code == 4) { // extended linear address (top 16 of 32-bit addr)
    hex->base = ((hex->data[0] << 8) | hex->data[1]) << 16;
  }
  else if (hex->code == 5) { // start linear address (32-bit big endian addr)
    hex->base = (hex->data[0] << 24) | (hex->data[1] << 16)
                | (hex->data[2] <<  8) | (hex->data[3] <<  0);
  }
  else {
    return 1;
  }

  return 0;
}

//******************************************************************************
// Intel Hex record foramt:
//
// Start code:  one character, ASCII colon ':'.
// Byte count:  two hex digits, number of bytes (hex digit pairs) in data field.
// Address:     four hex digits
// Record type: two hex digits, 00 to 05, defining the meaning of the data field.
// Data:        n bytes of data represented by 2n hex digits.
// Checksum:    two hex digits, computed value used to verify record has no errors.
//
// Examples:
//  :10 9D30 00 711F0000AD38000005390000F5460000 35
//  :04 9D40 00 01480000 D6
//  :00 0000 01 FF
//******************************************************************************

/* Intel HEX read/write functions, Paul Stoffregen, paul@ece.orst.edu */
/* This code is in the public domain.  Please retain my name and */
/* email address in distributed copies, and let me know about any bugs */

/* I, Paul Stoffregen, give no warranty, expressed or implied for */
/* this software and/or documentation provided, including, without */
/* limitation, warranty of merchantability and fitness for a */
/* particular purpose. */

// type modifications by Jon Zeeff

/* parses a line of intel hex code, stores the data in bytes[] */
/* and the beginning address in addr, and returns a 1 if the */
/* line was valid, or a 0 if an error occured.  The variable */
/* num gets the number of bytes that were stored into bytes[] */

#include <stdio.h>		// sscanf(), etc.
#include <string.h>		// strlen(), etc.

int parse_hex_line( const char *theline, char *bytes,
                    unsigned int *addr, unsigned int *num, unsigned int *code )
{
  unsigned sum, len, cksum;
  const char *ptr;
  int temp;

  *num = 0;
  if (theline[0] != ':')
    return 0;
  if (strlen (theline) < 11)
    return 0;
  ptr = theline + 1;
  if (!sscanf (ptr, "%02x", &len))
    return 0;
  ptr += 2;
  if (strlen (theline) < (11 + (len * 2)))
    return 0;
  if (!sscanf (ptr, "%04x", (unsigned int *)addr))
    return 0;
  ptr += 4;
  /* Serial.printf("Line: length=%d Addr=%d\n", len, *addr); */
  if (!sscanf (ptr, "%02x", code))
    return 0;
  ptr += 2;
  sum = (len & 255) + ((*addr >> 8) & 255) + (*addr & 255) + (*code & 255);
  while (*num != len)
  {
    if (!sscanf (ptr, "%02x", &temp))
      return 0;
    bytes[*num] = temp;
    ptr += 2;
    sum += bytes[*num] & 255;
    (*num)++;
    if (*num >= 256)
      return 0;
  }
  if (!sscanf (ptr, "%02x", &cksum))
    return 0;

  if (((sum & 255) + (cksum & 255)) & 255)
    return 0;     /* checksum error */
  return 1;
}
 
Back
Top