Serial & MAC Address - Teensy 4.0

Status
Not open for further replies.

thagh05t

Active member
Hello All,

I was wondering if anyone has found a way to pull the serial or MAC address for the Teensy 4.0

I started looking over my code for 3.x and scouring Paul's git for some clues, but I am not seasoned enough to pull this off quickly.

If anyone is willing to collaborate, I would be willing to create a library for this supporting all teens'y >= 3.x. Maybe a pull request from here.
 
Didn't see it come up in beta or since - but the Serial Number of Teensy is derived from the MAC ID in this code in ...\hardware\teensy\avr\cores\teensy4\usb_desc.c:
Code:
void usb_init_serialnumber(void)
{
	char buf[11];
	uint32_t i, num;

	num = [B][U]HW_OCOTP_MAC0[/U][/B] & 0xFFFFFF;
	// add extra zero to work around OS-X CDC-ACM driver bug
	if (num < 10000000) num = num * 10;
	ultoa(num, buf, 10);
	for (i=0; i<10; i++) {
		char c = buf[i];
		if (!c) break;
		usb_string_serial_number_default.wString[i] = c;
	}
	usb_string_serial_number_default.bLength = i * 2 + 2;
That is similar to prior Teensys but MCU specific.

The RefMan likely explains that and why there are three MAC's indicated and which is desired:
Code:
#define HW_OCOTP_MAC0			(IMXRT_OCOTP_VALUE.offset220)
#define HW_OCOTP_MAC1			(IMXRT_OCOTP_VALUE.offset230)
#define HW_OCOTP_MAC2			(IMXRT_OCOTP_VALUE.offset240)
 
It appears that all 3 are parts of the MAC address, they are as follows:

HW_OCOTP_MAC2 - Padding
HW_OCOTP_MAC1 - OUI
HW_OCOTP_MAC0 - UID

Together they make up the mac address.

The serial is just a derivative of the UID. Is the algorithm used some sort of standard, or just something Paul came up with, anyone?
 
It appears that all 3 are parts of the MAC address, they are as follows:

HW_OCOTP_MAC2 - Padding
HW_OCOTP_MAC1 - OUI
HW_OCOTP_MAC0 - UID

Together they make up the mac address.

The serial is just a derivative of the UID. Is the algorithm used some sort of standard, or just something Paul came up with, anyone?

Nice work on the research.

As noted AFAIK that is the PJRC standard serial number generation from the unique device MAC : "similar to prior Teensys but MCU specific" in where MAC comes from.
 
I have hammered out methods for determining the MAC and in turn USB Serial Number for each MCU >= 3.1.

The library linked in my original post has a lot of the work already done, so I could quite simply just extend the code, but I do not see any information on a UUID for the Teensy 4. Here is a snippet of how it was done for Teensy 3.x

Code:
void teensyUUID(uint8_t *uuid) {
	uint8_t mac[6];
	teensyMAC(mac);
	uuid[0] = SIM_UIDML >> 24;
	uuid[1] = SIM_UIDML >> 16;
	uuid[2] = SIM_UIDML >> 8;
	uuid[3] = SIM_UIDML;
	uuid[4] = SIM_UIDL >> 24;
	uuid[5] = SIM_UIDL >> 16;
	uuid[6] = 0x40; // marked as version v4, but this uuid is not random based !!!
	uuid[7] = SIM_UIDL >> 8;
	uuid[8] = 0x80; //variant
	uuid[9] = SIM_UIDL;
	uuid[10] = mac[0];
	uuid[11] = mac[1];
	uuid[12] = mac[2];
	uuid[13] = mac[3];
	uuid[14] = mac[4];
	uuid[15] = mac[5];
	}


It isn't really a big deal to me, I wouldn't be using the UID anyways, but just in case anyone was interested.

If nobody really cares, or it doesn't exist I could either generate a UUID 4 based on known values as a seed or just omit it from the library I intend to write/extend. That is, if anyone is interested or thinks it would be useful. I like what sstaub did with the original, but I feel like on init, having a struct with the data available as a couple different types may be useful.

Thoughts?
 
Are you aware of TeensyID.h ? It reads out the MAC, Serial # and UUID as well as a few other identifiers. Don't recall who wrote it, but, it works well. Not sure if it supports Teensy 4.0 yet.
 
Are you aware of TeensyID.h ? It reads out the MAC, Serial # and UUID as well as a few other identifiers. Don't recall who wrote it, but, it works well. Not sure if it supports Teensy 4.0 yet.

Hey @grease_lightning,

Yea, I am aware of TeensyID... That is the library I have been talking about since my initial post albeit I didn't specifically refer to it by name.

TeensyID does not support Teensy 4.0 and has not been touched in close to 3 years. I was going to fork it and extend the lib, or rewrite it entirely to suit my needs better if others were in line. If not, I got what I needed out of this thread already.
 
I basically just used the code from usb_desc.c to port my project over. Big help btw, I'd seen the code you posted a snippet of before, but just thought it was too easy.

I've not created a library for it yet, but I'll make some time to do this. In the mean time, if anyone is asking similar questions, just point them at this post and ill be glad to answer any questions.

I've been busy on python projects more lately, so this project is slow moving, please forgive me. Which segues into my next thought.. why not try and run micropython on teensy4? I know theres been some work on this, but I'd really like to push this along, I love python so would naturally love to work my magic with teensy4 using python. Am I the only person who feels this way, is this something others would like to see, or do you guys think the work outweighs the reward?
 
There was another thread looking for T_3.6 serial # info so I came here to see if there was an updated/extended unified TeensyID to point at.

I've not gotten into python - after BASIC I moved to "C" and stuck there at work - and left that 21 years back and just happy to use what I know … going back to an interpreter just seems super lame :)

There have been Teensy Python ports done before IIRC, not sure if there are usable thread links - I even backed a KS to put it on ESP8266 and it has evolved there to ESP32, but on the new ESP32's delivered with python - I just went onto Arduino with C/cpp.
 
All of the serial number generation for each teensy is found in the same file. I'll throw together something easily imported that returns a struct of the different identifying components broken down. By modifying those same files I found that you can generate your own serial and mac address. Amongst other things, but you're really not supposed to.

C is my second favorite language, but pythons simplicity has made it very easy to spin up otherwise complex programs with much less code. I'll have to check out the ESP32 to see if I like it enough to try and make teensy python. I love the teensy for any little project and will likely stick with it. I cannot wait for the moment teensy 4.x comes out with wifi. If not, I'll keep coupling it with the esp8266.

I've been working on an environment control system for reptiles that I think will be a hit if I ever finish it. As well as some other iot sort of projects.
 
Hello,

has anyone tried the Teensy 3.x methods for getting the chip UID & MAC address from a Teensy 4.0?
Maybe they all are just the same / can perhaps the 128bit Teensy 3.x UID section simply be extended with the chip definition below?
Pieced together from earlier work of contributors defragster, cmason et. al., I am copying some code snippets in below (both tested, working fine on a device using T36), perhaps useful to others, too.
I'd be really keen to do the same for T40, allowing for inter alia porting a product design from T36 to T40.

Chip UID
Does anyone know if these SIM_**** registers are still correct / what the (__MK???????__) is for T40?

Code:
  // 80bit UID Teensy LC
  #if defined (__MKL26Z64__)
  uint32_t uid[3];
  uid[0] = SIM_UIDMH;
  uid[1] = SIM_UIDML;
  uid[2] = SIM_UIDL;
  char uidString[27];
  sprintf(uidString, "%08X-%08X-%08X", uid[0], uid[1], uid[2]);
  
  // 128bit UID Teensy 3.0, 3.1, 3.2, 3.5, 3.6
  #elif defined (__MK20DX128__) || defined (__MK20DX256__) || defined (__MK64FX512__) || defined (__MK66FX1M0__)
  uint32_t uid[4];
  uid[0] = SIM_UIDH;
  uid[1] = SIM_UIDMH;
  uid[2] = SIM_UIDML;
  uid[3] = SIM_UIDL;
  char uidString[36];
  sprintf(uidString, "%08lX-%08lX-%08lX-%08lX", uid[0], uid[1], uid[2], uid[3]);

MAC address
Is this still applicable to T40?

Code:
  byte macB[6];                          // 
  FTFL_FCCOB0   = 0x41;                  // Selects the READONCE command
  FTFL_FCCOB1   = 0x0E;                  // read the 1st half of MAC read once area
  FTFL_FSTAT    = FTFL_FSTAT_CCIF;       // launch command (could just wait a few ms after / suspend until complete if req'd)
  macB[0]       = FTFL_FCCOB5;           // collect only the top three bytes,
  macB[1]       = FTFL_FCCOB6;           // in the right orientation (big endian).
  macB[2]       = FTFL_FCCOB7;           // Skip FTFL_FCCOB4, always 0
  macB[3]       = FTFL_FCCOB9;           // collect only the top three bytes,
  macB[4]       = FTFL_FCCOBA;           // in the right orientation (big endian).
  macB[5]       = FTFL_FCCOBB;           // Skip FTFL_FCCOB8, always 0
  char macC[17] = {0};                   // 
  sprintf(macC,"%02X:%02X:%02X:%02X:%02X:%02X",macB[0],macB[1],macB[2],macB[3],macB[4],macB[5]);


Thanks in advance for any of your additional insight.
 
Last edited:
Chip UID SIM_**** registers do not seem to work (not declared), had some hopes.
Having looked at usb_desc.c for instance, T40 only has (__IMXRT1052__) defined, yet when testing, it's definitely (__IMXRT1062__) using a test as per previous post.

MAC address FTFL_****** business also does not work on T40, unfortunately.
However, this does (thanks to defragster):

Code:
  uint32_t macNUM_0;                                    // num 0
  macNUM_0 = HW_OCOTP_MAC0 & 0xFFFFFF;                  // okay could try
  if (macNUM_0 < 10000000) macNUM_0 = macNUM_0 * 10;    // add extra zero to work around OS-X CDC-ACM driver bug
  String macS0 = String(macNUM_0,HEX);                  // get a hex string from 0

Getting unique (?) but at least different values for the two T40s I now have.
Tried with HW_OCOTP_MAC1 and HW_OCOTP_MAC2, too, but getting 311A and 0 respectively (latter indeed is just padding), this is identical across my two T40s.
To construct the actual 12 byte MAC address from this: HW_OCOTP_MAC0 + HW_OCOTP_MAC1 in order, or perhaps (probably) the other way around?
In any case:
HW_OCOTP_MAC0 & 0xFFFFFF could just be used as a 3 byte UID I assume, or even
HW_OCOTP_MAC0 on its own (4 bytes).
 
To construct the actual 12 byte MAC address

A mac address is 48 bits, or 6 bytes, not 12.

The low 24 bits are meant to be the product serial number and the top 24 bits are a OUI (organizationally unique identifier) assigned by IEEE.

PJRC's OUI is "04:E9:E5". Here's a list of all the registered organizations, in case you're wondering if PJRC really is properly registered.

https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=manuf

NXP reserved space in the fuses for 2 mac addresses. That's why it's 12 bytes. As usual, they used a confusing naming scheme, and in the earlier manuals for 1050 the info was copied incorrectly with parts missing in some places it appeared. Rather confusing...
 
PJRC's OUI is "04:E9:E5". Here's a list of all the registered organizations, in case you're wondering if PJRC really is properly registered.

https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=manuf

Many thanks. Was wondering indeed, but more about whether I am looking at the right data / bitmask when seeking the full unique set, rather than PJRC's registered status...

Just for correspondence:

I am no longer sure if the code snippet I posted is resolving the unique part by HW_OCOTP_MAC0 & 0xFFFFFF properly, at least as far as the intended unique MAC address in concerned.
Reason being, if I do the same mask business on HW_OCOTP_MAC1, I get 00311A rather than 04E9E5, as per Paul's information on the top 24bit OUI (different bitmask perhaps / should I get 04E9E5 from HW_OCOTP_MAC1 somehow?).
It still works & gets (a hex string for those) unique 3 bytes, which is good enough for identifying hw for code locking purposes (as opposed to a massive i2c pain I just discovered with T40 which definitely wasn't the case with T36, but, different topic).
 
... good enough for identifying hw for code locking purposes (as opposed to a massive i2c pain I just discovered with T40 which definitely wasn't the case with T36, but, different topic).

I was about to start a topic on this, noting T36's compliance in contrast of T40's lack of it, but I'd like to most happily withdraw that above.
It seems the brand new Teensyduino 1.48 released yesterday fully resolved I2C slave failures / SDA down while SCK up / hot swap / power loss / etc causing a T40 I2C Master to lock up, therefore, not launching a topic on that.
So glad it has been addressed, no master/slave comms should ever allow any slave to suspend master op in an infinite wait / loop (I understand that, shockingly, it has been the case with Arduino's Wire lib for years).
Hot-swapping / overdriving sensors now for testing, with T40 now unphased, great!
 
Pulling all the above together I've come up with this (obviously pulling a lot from the teensyID library). Seems to work fine on 4.0, 3.2, LC but far from exhaustively tested. May or may not be useful to someone else.

Code:
void teensyMAC(uint8_t *mac) {

  static char teensyMac[23];
  
  #ifdef HW_OCOTP_MAC1 && HW_OCOTP_MAC0
    Serial.println("using HW_OCOTP_MAC* - see https://forum.pjrc.com/threads/57595-Serial-amp-MAC-Address-Teensy-4-0");
    for(uint8_t by=0; by<2; by++) mac[by]=(HW_OCOTP_MAC1 >> ((1-by)*8)) & 0xFF;
    for(uint8_t by=0; by<4; by++) mac[by+2]=(HW_OCOTP_MAC0 >> ((3-by)*8)) & 0xFF;

    #define MAC_OK

  #else
    
    mac[0] = 0x04;
    mac[1] = 0xE9;
    mac[2] = 0xE5;

    uint32_t SN=0;
    __disable_irq();
    
    #if defined(HAS_KINETIS_FLASH_FTFA) || defined(HAS_KINETIS_FLASH_FTFL)
      Serial.println("using FTFL_FSTAT_FTFA - vis teensyID.h - see https://github.com/sstaub/TeensyID/blob/master/TeensyID.h");
      
      FTFL_FSTAT = FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL;
      FTFL_FCCOB0 = 0x41;
      FTFL_FCCOB1 = 15;
      FTFL_FSTAT = FTFL_FSTAT_CCIF;
      while (!(FTFL_FSTAT & FTFL_FSTAT_CCIF)) ; // wait
      SN = *(uint32_t *)&FTFL_FCCOB7;

      #define MAC_OK
      
    #elif defined(HAS_KINETIS_FLASH_FTFE)
      Serial.println("using FTFL_FSTAT_FTFE - vis teensyID.h - see https://github.com/sstaub/TeensyID/blob/master/TeensyID.h");
      
      kinetis_hsrun_disable();
      FTFL_FSTAT = FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL;
      *(uint32_t *)&FTFL_FCCOB3 = 0x41070000;
      FTFL_FSTAT = FTFL_FSTAT_CCIF;
      while (!(FTFL_FSTAT & FTFL_FSTAT_CCIF)) ; // wait
      SN = *(uint32_t *)&FTFL_FCCOBB;
      kinetis_hsrun_enable();

      #define MAC_OK
      
    #endif
    
    __enable_irq();

    for(uint8_t by=0; by<3; by++) mac[by+3]=(SN >> ((2-by)*8)) & 0xFF;

  #endif

  #ifdef MAC_OK
    sprintf(teensyMac, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    Serial.println(teensyMac);
  #else
    Serial.println("ERROR: could not get MAC");
  #endif
}

Use as:
Code:
uint8_t mac[6];
teensyMAC(mac);
Ethernet.begin(mac);
 
Compiling this on T_4.1 just now had to edit FIRST #ifdef to :: #if defined(HW_OCOTP_MAC1) && defined(HW_OCOTP_MAC0)
 
Status
Not open for further replies.
Back
Top