{"id":1811,"date":"2024-06-25T21:35:53","date_gmt":"2024-06-25T20:35:53","guid":{"rendered":"https:\/\/coding.moris.org\/?p=1811"},"modified":"2025-11-04T14:34:40","modified_gmt":"2025-11-04T14:34:40","slug":"esp8266-how-to-register-partitions","status":"publish","type":"post","link":"https:\/\/priscimon.net\/coding\/2024\/06\/25\/esp8266-how-to-register-partitions\/","title":{"rendered":"ESP8266: How to register partitions"},"content":{"rendered":"\n<p><strong>UPDATE 2025-05-17:<\/strong> Ignore the addresses from the SDK documentation and calculate them based on the explanation here.  <\/p>\n\n\n\n<p>Starting with v3.0, the ESP8266 NONOS SDK requires that partitions be registered in function <code>void user_pre_init(void)<\/code>, like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:16px\"><code>#define SPI_FLASH_SIZE_MAP 2\nvoid ICACHE_FLASH_ATTR user_pre_init(void)\n{\n        bool rc;\n        static const partition_item_t part_table&#91;] =\n        {\n                {SYSTEM_PARTITION_RF_CAL, 0xfb000, 0x1000},\n                {SYSTEM_PARTITION_PHY_DATA, 0xfc000, 0x1000},\n                {SYSTEM_PARTITION_SYSTEM_PARAMETER, 0xfd000, 0x3000},\n        };\n        rc = system_partition_table_regist(part_table,\n                        sizeof(part_table)\/sizeof(part_table&#91;0]),\n                        SPI_FLASH_SIZE_MAP);\n        if (rc)\n                goto done;\n        os_printf(\"Error while registering partitions\\r\\n\");\n        while (1) ;\ndone:\n}<\/code><\/pre>\n\n\n\n<p>The above code initialises and registers a table of partitions, each with a type, a start address, and a size. Despite the documentation in <a href=\"https:\/\/www.espressif.com\/sites\/default\/files\/documentation\/2c-esp8266_non_os_sdk_api_reference_en.pdf\">ESP8266 Non-OS SDK API Reference<\/a> and <a href=\"https:\/\/github.com\/espressif\/ESP8266_NONOS_SDK\/blob\/release\/v3.0.5\/documents\/EN\/%20Partition%20Table.md\">README<\/a>, partitions can still be difficult to understand. This post aims to provide some clarity.<\/p>\n\n\n\n<p>As an aside:<\/p>\n\n\n\n<p>Some images are mandatory for ESP8266 to work, as<br \/>described in <a href=\"https:\/\/www.espressif.com\/sites\/default\/files\/documentation\/2a-esp8266-sdk_getting_started_guide_en.pdf\">ESP8266 SDK Getting Started Guide<\/a> (see screenshot below).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"567\" src=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image.png\" alt=\"\" class=\"wp-image-1812\" srcset=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image.png 768w, https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-300x221.png 300w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/a><\/figure>\n\n\n\n<p>They are obtained in <a href=\"https:\/\/github.com\/espressif\/ESP8266_NONOS_SDK\/tree\/release\/v3.0.5\/bin\"><code>bin<\/code> of the SDK code repository<\/a>  and can be written to the flash as per <a href=\"https:\/\/github.com\/espressif\/ESP8266_NONOS_SDK\/blob\/release\/v3.0.5\/bin\/at\/README.md\">the instructions<\/a>. The addresses here are for the ESP-01 but can be found in the documentation for other models.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>blank.bin<\/code> at address <strong>0xFB000<\/strong><\/li>\n\n\n\n<li><code>esp_init_data_default.bin<\/code> at <strong>0xFC000<\/strong><\/li>\n\n\n\n<li><code>blank.bin<\/code> at <strong>0xFD000<\/strong><\/li>\n<\/ul>\n\n\n\n<p>The bootloader and user program are written normally at <strong>0x0000<\/strong> and <strong>0x1000<\/strong> respectively. It is not necessary to rewrite the mandatory images every time, as long as they have not been overwritten. If in doubt, erase all the flash and write them again.<\/p>\n\n\n\n<p>Back to the partitions:<\/p>\n\n\n\n<p>The choice of partitions&#8217; addresses is driven by the flash size on the target ESP8266 controller. Referring to the flash map given below and the size of each partition, it is easy to calculate the start addresses by starting with the last partition and working backwards.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"511\" src=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-1.png\" alt=\"\" class=\"wp-image-1813\" srcset=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-1.png 768w, https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-1-300x200.png 300w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/a><\/figure>\n\n\n\n<p>Consider the 1 MiB flash of the ESP-01, with a size of 0x100000 bytes (1024 KiB).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"891\" height=\"394\" src=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-2.png\" alt=\"\" class=\"wp-image-1814\" srcset=\"https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-2.png 891w, https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-2-300x133.png 300w, https:\/\/priscimon.net\/coding\/wp-content\/uploads\/2024\/06\/image-2-768x340.png 768w\" sizes=\"auto, (max-width: 891px) 100vw, 891px\" \/><\/a><\/figure>\n\n\n\n<p>The SYSTEM_PARTITION_SYSTEM_PARAMETER partition at the last position must be 12 KiB large. Therefore, its start address must be at 0x100000 &#8211; 0x3000 (0x3000 being hex for 12288), giving a value of <strong>0xFD000<\/strong>.<\/p>\n\n\n\n<p>The second to last partition, SYSTEM_PARTITION_PHY_DATA, must be 4 KiB large. Using the same math, 0xFD000 &#8211; 0x1000 (0x1000 being hex for 4096) the start address is determined to be <strong>0xFC000<\/strong><\/p>\n\n\n\n<p>The first partition, SYSTEM_PARTITION_RF_CAL, with a size of 4 KiB, is then found to start at 0xFC000 &#8211; 0x1000, which is <strong>0xFB000<\/strong>.<\/p>\n\n\n\n<p>These calculated start addresses correspond to the values in the code above\u00e2\u20ac\u201dand the addresses where the mandatory images are written.<\/p>\n\n\n\n<p>The last argument passed to&nbsp;<strong><code class=\"prettyprinted\"><\/code><code class=\"prettyprinted\">system_partition_table_regist<\/code><\/strong> is the &#8220;map&#8221;. In the example above, the macro <code>SPI_FLASH_SIZE_MAP<\/code> is defined with a value of <code>2<\/code>. Where does it come from?<\/p>\n\n\n\n<p>The enum <code>flash_size_map<\/code> in <code>include\/user_interface.h<\/code> lists all possible values; in this case, the one corresponding to the 1 MiB flash of an ESP-01 (or 8M bits map) is <code>2<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>UPDATE 2025-05-17: Ignore the addresses from the SDK documentation and calculate them based on the explanation here. Starting with v3.0, the ESP8266 NONOS SDK requires that partitions be registered in function void user_pre_init(void), like this: The above code initialises and registers a table of partitions, each with a type, a start address, and a size. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1811","post","type-post","status-publish","format-standard","hentry","category-general"],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3I4g9-td","jetpack-related-posts":[],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/posts\/1811","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/comments?post=1811"}],"version-history":[{"count":18,"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/posts\/1811\/revisions"}],"predecessor-version":[{"id":2010,"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/posts\/1811\/revisions\/2010"}],"wp:attachment":[{"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/media?parent=1811"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/categories?post=1811"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/priscimon.net\/coding\/wp-json\/wp\/v2\/tags?post=1811"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}