TFLite to C++ Array ESP32

Ive been building a custom model for detection, and eventually tracking using YOLO11n. Building is fine, but when I try and convert the model down to a C++ array (for use on an ESP32S2 chip) Im seeing the file size jump significantly. So Im wondering if there is something I need to tweak in the model building.

Ill try and put in anything that could be relevant:

  • Initial image sizes: ~768x576 PNG files
  • Number used for the model: 100 images (for both training and validation)
  • Labeled with Label-Studio and exported out to YOLO format (manually created the yaml file)
  • Ultralytics version: 8.4.21

Training command:
yolo task=detect mode=train model="yolo11n.pt" data="dataset.yaml" epochs=50 imgsz=96 device=mps nms=false

Converting to TFLite:
yolo export model="/weights/best.pt" format=tflite imgsz=96 half=True int8=True device=mps data="dataset.yaml"

Image Size is set to 96 since thats all the model will see due to processing limits on the ESP32.

So after building the model:
best.pt → 5.2M
best_int8.tflite → 2.7M

Since I need to convert this to a c++ array:
xxd -i “/weights/best_saved_model/best_int8.tflite” > iris.cpp
iris.cpp → 16M

So Im trying to figure out why the model jumps in size when changing to the c++ array.

Ill move down to a 5n instead of the 11n model for training and check the sizes after that.

v5n: Looks like Id need to build a new conda env and build the 5n model. Ill try 8n for now:
This model is NOT forwards compatible with YOLOv8 at ``https://github.com/ultralytics/ultralytics``.

v8 cpp ended up at 19M

You can try converting the best_full_integer_quant.tflite file instead

That exported to 19M, so even larger.
3.1M Mar 10 12:50 best_full_integer_quant.tflite
xxd -i ‘/weights/best_saved_model/best_full_integer_quant.tflite’ > iris_v8_fiq.cpp
19M Mar 11 10:38 iris_v8_fiq.cpp

Is there something else that Im missing? I did determine that for my goal, there might be a better direction to head, but Id still like to understand why the cpp file is ending up huge like that.

Yep — that size jump is expected.

xxd -i turns a compact binary .tflite file into textual C source, so every single byte becomes something like 0x3f, plus commas, spaces, line breaks, and variable declarations. That means a 3.1 MB model often becomes a ~19 MB .cpp file on disk.

So the key point is: the .cpp file size is source text size, not the real in-memory model size. After compiling, the embedded byte array in .rodata should be much closer to the original .tflite size, not the xxd text size.

If you want to verify, check the final firmware/map output rather than the generated iris.cpp size. On ESP-IDF, idf.py size is the useful check.

If your final firmware is still too large, then the real problem is just that a 2.7–3.1 MB TFLite model is pretty heavy for an ESP32-S2. In that case, storing the model as a binary asset in flash instead of converting with xxd -i, or moving to a much smaller model / different architecture, is usually the better path. Also worth noting that for microcontroller targets, the TFLite deployment docs are the closest match, but full YOLO detection is generally very tight on ESP32-class hardware.

So in short: you’re not missing anything — xxd -i inflation is normal. The important size is the original .tflite and the final compiled firmware, not the generated .cpp text file.

Thanks for that! Running size on it, and letting it build made a huge difference.
Total image size: 189481 bytes (.bin may be padded larger)