Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: Unique Teensy Serial Number: using getTeensySerial() from TeensyID.h

  1. #1
    Senior Member Davidelvig's Avatar
    Join Date
    Aug 2015
    Location
    Wisconsin
    Posts
    308

    Unique Teensy Serial Number: using getTeensySerial() from TeensyID.h

    I'm using a Teensy 3.2-type board with a PJRC boot loader.

    Stefan Staub's Teensy ID library returns the following:

    From my Teensy 3.2-type board with PJRC boot loader
    USB Serialnumber: 4294967295
    Code:
    Array Serialnumber: FF-FF-FF-FF
    String Serialnumber: ff-ff-ff-ff
    Array MAC Address: 04:E9:E5:FF:FF:FF
    String MAC Address: 04:e9:e5:ff:ff:ff
    Array 128-bit UniqueID from chip: 95710000-E0CD001A-00385009-49634E45
    String 128-bit UniqueID from chip: 95710000-e0cd001a-00385009-49634e45
    Array 128-bit UUID RFC4122: 00385009-4963-404E-8045-04E9E5FFFFFF
    String 128-bit UUID RFC4122: 00385009-4963-404e-8045-04e9e5ffffff
    From off-the-shelf Teensy 3.2
    Code:
    USB Serialnumber: 1484710
    Array Serialnumber: 00-02-43-F7
    String Serialnumber: 00-02-43-f7
    Array MAC Address: 04:E9:E5:02:43:F7
    String MAC Address: 04:e9:e5:02:43:f7
    Array 128-bit UniqueID from chip: E2710000-879A0021-000F0010-32304E45
    String 128-bit UniqueID from chip: e2710000-879a0021-000f0010-32304e45
    Array 128-bit UUID RFC4122: 000F0010-3230-404E-8045-04E9E50243F7
    String 128-bit UUID RFC4122: 000f0010-3230-404e-8045-04e9e50243f7
    (These are outputs from the example code that comes with the TeensyID library).

    For no particular reason, I had thought the boot loader would install some serial number.
    What steps do I take after flashing my boards to give them a unique SN in that first slot?

    Dave

  2. #2
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,369
    You can set it with this code from the WIKI (https://github.com/TeensyUser/doc/wi...-Serial-Number)

    Code:
    extern "C"
    {
        struct usb_string_descriptor_struct
        {
            uint8_t bLength;
            uint8_t bDescriptorType;
            uint16_t wString[10];
        };
    
        usb_string_descriptor_struct usb_string_serial_number =
        {
             22,  // 2 + 2*length of the sn string
             3,
             {'M','Y', 'S','N', '0', '0', '0','0', '1', 0},
        };
    }
    
    //-----------------
    void setup()
    {
      pinMode(LED_BUILTIN, OUTPUT);
    }
    
    void loop()
    {
      digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
      delay(200);
    }

  3. #3
    Senior Member Davidelvig's Avatar
    Join Date
    Aug 2015
    Location
    Wisconsin
    Posts
    308
    Thanks!
    Are these stored in flash?
    Is this modifiable at run-time?
    i.e. 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?

  4. #4
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,369
    Looks like it is copied at runtime from a pre-programmed Flash region. Probably needs to happen before USB is up. Here the code from the core:
    https://github.com/PaulStoffregen/co...b_desc.c#L1806

    Don't know if it works, but it might be possible to change the linked code to read out the SN from the EEPROM instead of the FLASH. You could then set the value after flashing the firmware. But these are wild guesses only. Might be a fun project however :-). Maybe Paul reads this, he will know what's possible and what not...

  5. #5
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,955
    Should be doable to burn the fuses for the serial, too.. but don't ask me, how

  6. #6
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,369
    Quote Originally Posted by Davidelvig View Post
    ...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:

    Click image for larger version. 

Name:	Anmerkung 2020-12-07 193631.jpg 
Views:	8 
Size:	20.1 KB 
ID:	22786


    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:
    1. Switch board to bootloader mode
    2. Read out s/n
    3. upload some setup FW which reads s/n from serial and stores it in eeprom
    4. upload final FW which will then use the same s/n as stored in the bootloader chip

  7. #7
    Senior Member Davidelvig's Avatar
    Join Date
    Aug 2015
    Location
    Wisconsin
    Posts
    308
    Thanks so much for explaining these options!
    I'm using a solution like #1 for setting my device's USB-MIDI name.
    Now that I think of it, I wonder if it has the same timing limitations - that it might not reliably set the name if USB connection is made before program execution.
    I have seen seemingly random failures to name the device.

    I like the more deterministic result of Solution 2, and will consider the downside of every-Arduino-update code maintenance.

    Both of these options are a bit closer-to-the-metal than my typical programming comfort level.
    I could "programmer-up", I suppose.

    Thanks again!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •