
Originally Posted by
Davidelvig
...could I put a default in here, that after flashing, run a method to accept an input (from Serial, let's say) and set the values in usb_string_serial_number?
Yes, that seems to work. I played a bit with setting serial numbers and found two solutions for setting it from the EEPROM. I.e., you only need to write a new value into the EEPROM to set a new s/n. As always, both solutions have advantages and disadvantages.
First a simple sketch to write a s/n (here "MY_COOL_SN") to the EEPROM. It is important that the s/n contains exactly 10 chars and starts at address 0:
Code:
#include "EEPROM.h"
void setup(){
pinMode(LED_BUILTIN, OUTPUT);
EEPROM.put(0,"MY_COOL_SN"); // only needed once
}
void loop(){
digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
delay(500);
}
Solution 1 (no need to change core files)
Generate a file "eepromSN.c" (*.c is important, won't work with *.cpp) and place it in your sketch folder
Code:
#include "Arduino.h"
struct usb_string_descriptor_struct{
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wString[];
};
struct usb_string_descriptor_struct usb_string_serial_number ={
22, // 2 + 2*length of the sn string
3,
{'1', '0', '0', '0', '0', '0', '0', '0', '0', 0}, // dummy data
};
void startup_late_hook()
{
uint8_t *p = (uint8_t *)0;
for (int i = 0; i < 10; i++)
{
uint8_t c = eeprom_read_byte(p++);
usb_string_serial_number.wString[i] = c;
}
}
This will exchange the serial number in the usb string descriptor by the value it finds in the EEPROM. The exchange takes place in startup_late_hook() which will be called before your sketch but after the usb stack is initialized. Therefore, this only works if the computer enumerates the device after the exchange took place. To make this work we need to shorten the time between the initialization of the USB stack and the call to startup_late_hook(). This can easily be done by adding -DTEENSY_INIT_USB_DELAY_AFTER=1 to the compiler flags. Without this setting the timing might be borderline and the solution might not work for all PCs (worked here, but slow PC).
ADVANTAGE: No change of core files required
DISADVANTAGE: Ugly hack which relies on timing and needs a change in the build system (additional flag -DTEENSY_INIT_USB_DELAY_AFTER=1)
Solution 2 (requires a change in usb_desc.c)
Replace the code in usb_init_serialnumber(void) (see around line 1630 in usb_desc.c) by this:
Code:
void usb_init_serialnumber(void)
{
const unsigned snLen = 10;
uint8_t *p = (uint8_t *)0;
for (int i = 0; i < snLen; i++)
{
uint8_t chr = eeprom_read_byte(p++);
usb_string_serial_number_default.wString[i] = chr;
}
usb_string_serial_number_default.bLength = snLen * 2 + 2;
}
This will read the s/n chars from the eeprom instead of the PJRC pre programmed flash section.
ADVANTAGE: Sets the s/n before the usb stack is up => no timing issues
DISADVANTAGE: Needs a change in the core files, need to redo this on every Teensyduino upgrade
Both solutions generate this:

REMARK
Since the board reports the s/n stored in the bootloader during programming, uploaders might get confused if the s/n changes during a programming cycle. Might be best to first read out the bootloader s/n and use this to program your s/n. I assume you need this in a production environment? If so, the complete process can be fully automated:
- Switch board to bootloader mode
- Read out s/n
- upload some setup FW which reads s/n from serial and stores it in eeprom
- upload final FW which will then use the same s/n as stored in the bootloader chip