Hi, … I purchased this device and am trying to get it to work on an ESP32-S3-DevKitC-1. This version of ESP32 does not support the standard Aurduino libraries. So I am having to adapt what I can with using the resources located on the TMF8821 website as well as code samples created by Spark Fun. I’m having difficulty with the firmware upload process. The code samples by the manufacturer of the TMF8821 are also written for Arduino. I have looked through the sparkfun code samples to try to learn what I can there, but it is difficult to do when I am unable to complile and run the code. In the code, I see in the comments the following “Written by Kirk Benell @ SparkFun Electronics, April 2022” … I’m wondering if there is any information I can get that can help me best know how to handle the firmware upload. I have spent several days working through this, and I would appreciate any tips that may be helpful. The documentation provided by the manufacture talks bout two possible methods (uploading the firmware as one transaction or in pieces). I noticed in the Sparkfun code a comment suggesting it had to be done as one single transaction. I’m wondering if there is some information that might be helpful for me. the firmware is 2600 bytes long, … and the command to i2c push it in one chunk requires a length parameter … but the parameter only allows 0xFF in size definability. where the entire firmware is 2600 bytes, then the ability to define the length (as one single chunk) doesn’t seem possible. I’m hoping my description can help provide some feedback that can help me.
Hello -
All that we do to upload the firmware into the device is contained in the file qwiic_tmf882x.cpp of our library and was taken/reformatted from examples in the vendors software SDK. Basically we init the underlying driver (call tmf882x_o0pen()), and the load the firmware image (which was taken from the vendors SDK). Take a look at the QwDevTM882X::loadFirmware() method on how the firmware is loaded. Its a couple of calls into the vendors SDK methods: one to throw the driver into bootloader mode, the other to upload the firmware. I do remember this sensor being finicky, but the reliable once the command sequence was correct.
Note: The method to load/transfer data over the I2C bus in chunks was different for this device. See the file qwiic_i2c.cpp - you can’t upload in chunks, and downloads are different than other devices. This was frustrating. The write method in our driver is sending the length over as an int.
-K
Thank you for this information. It has definitely got me closer. Though, I am still having difficulty. Take a look at this reference document from AMS
https://ams.com/documents/20143/6015057 … 5_6-00.pdf
On page 13, it talks about the firmware upload process. In the middle of page 14, it shows a demonstration “chunk” that is supposed to be repeated chunks at a time (in this example 20 bytes at a time, though, I’m a little confused, I think it means 20 HEX amount of bytes … so 32 bytes? (if you count them in the demo string here copy pasted.)) … this is also some confusion for me in that if I am supposed to include the number of bytes (20Hex/32Decimal) in this command string … then It seems the highest amount of bytes I could send would be FFHex/255Dec? (not in one single large I2C transaction 2600 bytes?)
S 41 W 08 41 20 7F 7E 7D 7C 7B 7A 79 78 77 76 75 74 73 72 71 70 5F 5E 5D 5C 5B 5A 59 58 57 56
55 54 53 52 51 50 AE P
The S 41 (Is the I2C address) The first byte (08) is the register followed by what I interpret to be the “amount” of subsequent bytes 20 (decimal 32? … if I count them) … the last byte (AE) is the checksum of (the previous 32 bytes + the command byte) (see page 12 for Checksum Calculation and example).
As I iterate through the firmware data in this fashion … I am getting positive responses for each chunk sent with the recommended status checks (as described in the document), … but when I execute the final command RAMREMAP_RESET … it fails and will not go into APP mode. This seems very consistent with the comment you had me read in the qwiic_i2c.cpp file. (though, likely what is happening is what is described on the bottom of page 16 under “3.2.1 If a Download Fails”)
So what I am getting confused about is the assertion from AMS that I am supposed to be chunking this out in 32 byte pieces (per the AMS documentation) … but SparkFun concluded it needs to be sent out in 1 single transaction (2600 bytes? … btw, there is a newer firmware file which is different size than the one included with the Sparkfun download) … I tend to believe that SparkFun had this same frustration and was able to get it to work (albeit different than what it seems AMS is suggesting)
Here is the link of the arduino code reference that I am trying to translate for ESP32-S3 …
https://ams.com/o/download-server/docum … ad/8502278
This link is found on this page
https://ams.com/en/tmf8821#tab/tools
with the following instructions:
TMF882x_Driver_Arduino_Source_v17.zip Arduino driver written from scratch. TMF8820/21 driver is intended to be easy to use and understand. Simplifies porting to other non-Linux platforms.
So I am juggling between the one from AMS, and the one from SparkFun to help me piece together clues.
I am also a little confused by what is meant in the comment in “qwiic_i2c.h” where is says:
To work around this, we reduce the chunk size for firmware upload in the file “inc/tmf882x_mode_bl.h” - the #define BL_NUM_DATA is adjusted based on the platform being used (what is it’s I2C buffer size).
in the inc/tmf882x_mode_bl.h … it sets 3 #defines
#ifdef ESP32
#define BL_NUM_DATA 120
// The nrf52840 - this #define is from the Arduino biuld setup
#elif NRF52840_XXAA
#define BL_NUM_DATA 30
// Default
#else
#define BL_NUM_DATA 128
So am I supposed to be sending over chunks of say 120 (the one defined for ESP32) …
I realize this is a lot of information I’m sending over … I’m hoping that some of this description will help resonate something that you may be able to identify where I am miss-understanding something for further suggestion or clarification of my lack of understanding.
I really appreciate your help a ton (I really want to get this sensor working for a product that I am developing with it)
I realized I may have been a little un-organized my last post (though still contains info I think may be helpful). I have spent some further time to perhaps distill this down a bit more intelligently and a bit more isolated.
In my efforts to problem solve this, I found myself attempting to re-factor large portions of the manufacturer SDK (which surfaced a bunch of the observations that I previously articulated). I decided to (sort of) start over, in light of the intended recommendation (from the downloaded sample code from the manufacture (previously mentioned)). Their approach (as documented in their SDK) is to Ideally use their sample code “as is” (which is coded as platform neutral as possible) and then any adaptations to your target platform should be handled within their “shim” files (tmf882x_shim.h, tmf882x_shim.c) … which is mostly just the I2C TX/RX functions (that have to be modified) and print results/histogram functions. (perhaps all I am dealing with is what was dealt with by SparkFun => (getting the I2C interactions correct))
[ FILE DESCRIPTIONS ]
Here are all the essential files from the simplified SDK download (with my best attempt of describing their purpose).
Here is the link to download these from the manufactures website: https://ams.com/o/download-server/docum … ad/8502278
tmf882x.h
tmf882x.c (this handles the logic behind the simplified SDK)
tmf882x_calib.h
tmf882x_calib.c (this handles upload-able sample calibration profiles)
tmf882x_image.h
tmf882x_image.c (this is the firmware that will be uploaded to the device during the initialization)
registers_i2c.h (this is a reference of all the device registers)
tmf882x_shim.h
tmf882x_shim.c (this is the exposed portion that is to be altered for your intended target platform)
tmf882x_a.ino (this is a sample arduino project file that is used to interact with the device)
In addition to these, here is a copy paste from the portion of my code (from my project) that is used to initiate the tmf882x (mostly the small portion in the initializeTMF882x() function):
[ CODE COPY PASTE ]
***** BEGIN COPY/PASTE ******
// tmf states
#define TMF882X_STATE_DISABLED 0
#define TMF882X_STATE_STANDBY 1
#define TMF882X_STATE_STOPPED 2
#define TMF882X_STATE_MEASURE 3
#define TMF882X_STATE_ERROR 4
// convendience macro to have a pointer to the driver structure
#define TMF882X_A ( &tmf882x )
// ---------------------------------------------- constants -----------------------------------------
// for each configuration specifiy a period in milli-seconds
const uint16_t configPeriod[3] = {33, 500, 100};
// for each configuration specify the number of Kilo Iterations (Kilo = 1024)
const uint16_t configKiloIter[3] = {537, 550, 900};
// for each configuration select a SPAD map through the id
const uint8_t configSpadId[3] = {TMF882X_COM_SPAD_MAP_ID__spad_map_id__map_no_1, TMF882X_COM_SPAD_MAP_ID__spad_map_id__map_no_2, TMF882X_COM_SPAD_MAP_ID__spad_map_id__map_no_7};
// ---------------------------------------------- variables -----------------------------------------
tmf882xDriver tmf882x; // instance of tmf882x
extern uint8_t logLevel; // for i2c logging in shim
int8_t stateTmf882x; // current state of the device
int8_t configNr; // this sample application has only a few configurations it will loop through, the variable keeps track of that
int8_t clkCorrectionOn; // if non-zero clock correction is on
int8_t dumpHistogramOn; // if non-zero, dump all histograms
void initializeTMF882x(){
stateTmf882x = TMF882X_STATE_DISABLED;
configNr = 0; // rotate through the given configurations
clkCorrectionOn = 1;
dumpHistogramOn = 0; // default is off
tmf882xInitialise( TMF882X_A,0,0 ); // this just sets the driver information for the device, passing 0,0 for pins (that we are not going to use)
if ( stateTmf882x == TMF882X_STATE_DISABLED ) {
tmf882xWakeup( TMF882X_A ); // did the TMF882x power up correctly?
if ( tmf882xIsCpuReady( TMF882X_A, CPU_READY_TIME_MS ) ) { // is the CPU ready?
if ( tmf882xDownloadFirmware( TMF882X_A ) == BL_SUCCESS_OK ) {
stateTmf882x = TMF882X_STATE_STOPPED;
} else {
stateTmf882x = TMF882X_STATE_ERROR;
}
}
}
}
***** END COPY/PASTE ******
[ DESCRIPTION OF MODIFICATIONS AND REASONS ]
I’ve also attached a zipped up version of my copies of these same files (which include all the modifications I have made in effort to adapt to the ESP32-S3 platform) … I tried to be as minimal as possible. You can do a search for the following strings in these files to quickly CTRL-F through each instance (commented with either LCN_CHANGE or LCN_CONVERT) …
Here is a description of the essential elements that I changed:
-
They use the ( #include <avr/pgmspace.h> ) library for how they package their uploads data (firmware, calibration profile data, etc) … which the ESP32-S3 processor doesn’t support … so, I’ve modified portions pertaining to using this.
-
They use the enable pin and interrupt pin of the device to initiate portions of the device (I do not want to do this, I want to execute similar to SparkFuns QWIIC standard and only exclusively use I2C for all communications), so I made some modifications to these references.
-
I added a minor include references (which I use within my project)
I made 21 modifications total (which is very easy to quickly CTRL-F through each to examine), … I was careful to leave the original code commented next to each modification that I made so it is easy to compare the original code next to the modifications.
tmf882x.c (10 modifications)
tmf882x_calib.c (4 modifications)
tmf882x_calib.h (1 modification)
tmf882x_image.c (1 modification)
tmf882x_image.h (1 modification)
tmf882x_shim.c (2 modifications)
tmf882x_shim.h (2 modifications)
[ SUMMARY ]
Even though I am following the manufactures suggested path for adapting to a particular targeted platform (ESP32-S3) … The firmware upload is still not working … their code breaks up the firmware into 128 byte chunks (and performs a check after each chunk to verify there wasn’t any error, … (which I actually tend to believe what is also happening in the SDK files in the SparkFun sample code (I did a little deeper digging into this to sharpen my impression)) … this chunking (size) is governed by this variable in tmf882x.c:
#define BL_MAX_DATA_PAYLOAD 128 // bootloader data payload can be up to 128 (which I also tend to believe is the similar variable SparkFun modified in their sample)
the firmware appears to be uploading correctly, but in the end when it performs the last step to initiate the device into APP mode, it fails. I hope this description is a bit more helpful in getting your input?
I’m hoping it is just my I2C interactions (that may be the best place to look at first … within the tmf882x_shim.c file) … here is a link to the API I am using:
https://docs.espressif.com/projects/esp … s/i2c.html
[ LOGGING OUTPUT ]
Here is a copy paste from my logging output:
I (554) TMF882x: I2C-RX (0x41) Reg=0xE0 Len=1
I (554) TMF882x: len=1 0x41
I (554) TMF882x: awake ENABLE=0x
I (554) TMF882x: 41
I (554) TMF882x:
I (554) TMF882x: I2C-RX (0x41) Reg=0xE0 Len=1
I (564) TMF882x: len=1 0x41
I (564) TMF882x: I2C-RX (0x41) Reg=0xE0 Len=1
I (574) TMF882x: len=1 0x41
I (574) TMF882x: CPU ready
I (574) TMF882x:
I (584) TMF882x: Image addr=0x
I (584) TMF882x: 2097152
I (584) TMF882x: len=
I (594) TMF882x: 2600
I (594) TMF882x: 0x
I (594) TMF882x: 0x
I (594) TMF882x: 0x
I (604) TMF882x: 0x
I (604) TMF882x: 0x
I (604) TMF882x: 0x
I (614) TMF882x: 0x
I (614) TMF882x: 0x
I (614) TMF882x: 0x
I (614) TMF882x: 0x
I (624) TMF882x: 0x
I (624) TMF882x: 0x
I (624) TMF882x: 0x
I (634) TMF882x: 0x
I (634) TMF882x: 0x
I (634) TMF882x: 0x
I (634) TMF882x:
I (644) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (644) TMF882x: len=3 0x0
I (654) TMF882x: Download addr=0x
I (654) TMF882x: 0
I (654) TMF882x:
I (754) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (754) TMF882x: len=3 0x0
I (754) TMF882x: Download addr=0x
I (754) TMF882x: 80
I (754) TMF882x:
I (864) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (864) TMF882x: len=3 0x0
I (864) TMF882x: Download addr=0x
I (864) TMF882x: 100
I (864) TMF882x:
I (974) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (974) TMF882x: len=3 0x0
I (974) TMF882x: Download addr=0x
I (974) TMF882x: 180
I (974) TMF882x:
I (1084) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1084) TMF882x: len=3 0x0
I (1084) TMF882x: Download addr=0x
I (1084) TMF882x: 200
I (1084) TMF882x:
I (1194) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1194) TMF882x: len=3 0x0
I (1194) TMF882x: Download addr=0x
I (1194) TMF882x: 280
I (1194) TMF882x:
I (1304) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1304) TMF882x: len=3 0x0
I (1304) TMF882x: Download addr=0x
I (1304) TMF882x: 300
I (1304) TMF882x:
I (1414) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1414) TMF882x: len=3 0x0
I (1414) TMF882x: Download addr=0x
I (1414) TMF882x: 380
I (1414) TMF882x:
I (1524) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1524) TMF882x: len=3 0x0
I (1524) TMF882x: Download addr=0x
I (1524) TMF882x: 400
I (1524) TMF882x:
I (1634) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1634) TMF882x: len=3 0x0
I (1634) TMF882x: Download addr=0x
I (1634) TMF882x: 480
I (1634) TMF882x:
I (1744) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1744) TMF882x: len=3 0x0
I (1744) TMF882x: Download addr=0x
I (1744) TMF882x: 500
I (1744) TMF882x:
I (1854) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1854) TMF882x: len=3 0x0
I (1854) TMF882x: Download addr=0x
I (1854) TMF882x: 580
I (1854) TMF882x:
I (1964) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (1964) TMF882x: len=3 0x0
I (1964) TMF882x: Download addr=0x
I (1964) TMF882x: 600
I (1964) TMF882x:
I (2074) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2074) TMF882x: len=3 0x0
I (2074) TMF882x: Download addr=0x
I (2074) TMF882x: 680
I (2074) TMF882x:
I (2184) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2184) TMF882x: len=3 0x0
I (2184) TMF882x: Download addr=0x
I (2184) TMF882x: 700
I (2184) TMF882x:
I (2294) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2294) TMF882x: len=3 0x0
I (2294) TMF882x: Download addr=0x
I (2294) TMF882x: 780
I (2294) TMF882x:
I (2404) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2404) TMF882x: len=3 0x0
I (2404) TMF882x: Download addr=0x
I (2404) TMF882x: 800
I (2404) TMF882x:
I (2514) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2514) TMF882x: len=3 0x0
I (2514) TMF882x: Download addr=0x
I (2514) TMF882x: 880
I (2514) TMF882x:
I (2624) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2624) TMF882x: len=3 0x0
I (2624) TMF882x: Download addr=0x
I (2624) TMF882x: 900
I (2624) TMF882x:
I (2734) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2734) TMF882x: len=3 0x0
I (2734) TMF882x: Download addr=0x
I (2734) TMF882x: 980
I (2734) TMF882x:
I (2844) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2844) TMF882x: len=3 0x0
I (2844) TMF882x: Download addr=0x
I (2844) TMF882x: A00
I (2844) TMF882x:
I (2954) TMF882x: I2C-RX (0x41) Reg=0x8 Len=3
I (2954) TMF882x: len=3 0x0
I (3554) TMF882x: I2C-RX (0x41) Reg=0x0 Len=4
I (3554) TMF882x: len=4 0x80
I (3654) TMF882x: I2C-RX (0x41) Reg=0x0 Len=4
I (3654) TMF882x: len=4 0x80
I (3654) TMF882x: #Err
I (3654) TMF882x: ,
I (3654) TMF882x: timeout
I (3654) TMF882x: 100699
I (3664) TMF882x: reg=0x
I (3664) TMF882x: 0
I (3664) TMF882x: 0x
I (3664) TMF882x: 80
I (3674) TMF882x: 0x
I (3674) TMF882x: 29
I (3674) TMF882x: 0x
I (3684) TMF882x: 0
I (3684) TMF882x: 0x
I (3684) TMF882x: 0
I (3684) TMF882x:
I (3694) TMF882x: #Vers
I (3694) TMF882x: ,
I (3694) TMF882x: 128
I (3704) TMF882x: .
I (3704) TMF882x: 41
I (3704) TMF882x: .
I (3704) TMF882x: 0
I (3714) TMF882x: .
I (3714) TMF882x: 0
I (3714) TMF882x:
I (3724) TMF882x: #Err
I (3724) TMF882x: ,
I (3724) TMF882x: FW downl or REMAP
I (3734) TMF882x:
tmf882x.zip (33.7 KB)
I made some further modifications to add more intuitive logging … compare this sequence of events with the information contained in he host communication document from the manufacture.
Host communication document
https://ams.com/documents/20143/6015057 … 5_6-00.pdf
So when the SDK goes to perform the firmware upload, it chunks up the firmware image (2600 bytes) into 128byte chunks (function call tmf882xDownloadFirmware in file tmf882x.c) … and then the I2C TX function further chunks this up into 32byte pieces (function call i2c_tx in tmf882x_shim.c file)
Attached is the logging output of the whole sequence (tmf882xInitialise → tmf882xWakeup → tmf882xDownloadFirmware)
2022-12-19_logging.txt (83.2 KB)
slightly better log file … this really should illuminate the sequence better … I really feel I am doing what the documentation is saying …
2022-12-19_logging.txt (84.6 KB)
re-delivery of the code (see attached)
tmf882x.zip (33.8 KB)
This has been tough, but I finally got a breakthrough. I was able to get the manufacturers sample code working on a standard arduino uno. I was then able to have a better cross comparison against the ESP32-S3 translation that I’m working on.
In light of reporting my findings, there were two items I had to correct on my ESP32-S3 translation:
-
I had to make use of the enable pin per the manufacturers sample code. (which is confusing to me because their documentation states that it is optional, which I am still working on)
-
I was able to make changes to the shim files to accommodate the appropriate I2C adaptations. Within “i2c_tx” function of the tmf882x_shim.c file I replaced the Arduino I2C code:
– ARDUINO PORTION —
Wire.beginTransmission( slave_addr );
Wire.write( reg );
Wire.write( buf, tx );
len -= tx;
buf += tx;
reg += tx;
Wire.endTransmission( );
– ARDUINO PORTION —
With the applicable ESP-32 code (I commented the offending line of code with the concluded replacement correction):
– ESP32-S3 ADAPTATION —
esp_err_t i2c_check; int ic2_buf_iterator=0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_check=i2c_master_start(cmd);
//i2c_check=i2c_master_write_byte(cmd, slave_addr, false);
i2c_check=i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, false);
i2c_check=i2c_master_write_byte(cmd, reg, false);
i2c_check=i2c_master_write(cmd, buf, tx, false);
i2c_check=i2c_master_stop(cmd);
i2c_check=i2c_master_cmd_begin(I2C_HOST, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
len -= tx;
buf += tx;
reg += tx;
– ESP32-S3 ADAPTATION —
With the previous post being stated, … I would like to eliminate the need to use the enable pin and only use the sensor via I2C exclusively. The datasheet states that if you intend not to use the enable pin, it can simply be tied to the high supply voltage. this made sense to me in that the enable pin goes from low to high in order to “boot” the device. Though, the sparkfun version somehow only requires the Qwiic connection (so standard i2c pwr, gnd, scl, scd lines) to operate. Is there any information that was discovered by SparkFun (when getting this to work) that may help me achieve this?