{"id":4256,"date":"2026-03-12T09:15:54","date_gmt":"2026-03-12T09:15:54","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=4256"},"modified":"2026-03-12T09:17:06","modified_gmt":"2026-03-12T09:17:06","slug":"modbus-sensor-emulation-part-5-slave-accept-from-its-address","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=4256","title":{"rendered":"Modbus Sensor Emulation Part 5: Slave Accept from Its Address"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-1024x683.png\" alt=\"\" class=\"wp-image-4257\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-1024x683.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-300x200.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-768x512.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-1150x767.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-750x500.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-400x267.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM-250x167.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/ChatGPT-Image-Jan-6-2026-at-09_38_15-AM.png 1536w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Part 5 focuses on implementing address filtering so that the Modbus RTU slave responds only to requests directed to its assigned slave address. This ensures that in a multi-device RS-485 network, each slave processes and responds only to frames that match its address while ignoring all other traffic on the bus.<\/p>\n\n\n\n<p>In this guide, we shall cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Slave firmware modification.<\/li>\n\n\n\n<li>Master firmware modification.<\/li>\n\n\n\n<li>Results.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Slave Firmware Modification:<\/h2>\n\n\n\n<p>Open the slave project. <\/p>\n\n\n\n<p>In <strong>HAL_UARTEx_RxEventCallback<\/strong> function, we shall modify it as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)\n\n{\tif(huart-&gt;Instance==USART1)\n\t{\n\t\tif(rs485_buffer[0]==address)\n\t\t{\n\t\t\tif(rs485_buffer[1]==Read)\n\t\t\t{\n\t\t\t\tmodbus_receive(rs485_buffer,&amp;modbusFrame);\n\n\t\t\t\tnewData=1;\n\t\t\t}\n\t\t\tif(rs485_buffer[1]==Write)\n\t\t\t{\n\t\t\t\t\/\/TODO\n\t\t\t}\n\t\t}\n\n\n\n\t}\n\n\tHAL_UARTEx_ReceiveToIdle_IT(&amp;huart1, rs485_buffer, bufferSize);\n}<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>This function is the&nbsp;<strong>UART receive event callback<\/strong>&nbsp;used by STM32 HAL when data is received using&nbsp;<strong><code>HAL_UARTEx_ReceiveToIdle_IT()<\/code><\/strong>. It is triggered when the UART detects an&nbsp;<strong>IDLE condition<\/strong>&nbsp;or when the receive buffer is filled, indicating that a Modbus frame has likely been completely received.<\/p>\n\n\n\n<p>Inside the callback, the code first checks whether the interrupt was generated by&nbsp;<strong>USART1<\/strong>, which is the UART used for RS-485 communication. It then verifies that the&nbsp;<strong>slave address in the received frame (<code>rs485_buffer[0]<\/code>) matches the device\u2019s configured address<\/strong>, ensuring the slave only processes frames intended for it. If the function code corresponds to a&nbsp;<strong>read request<\/strong>, the received buffer is passed to&nbsp;<code>modbus_receive()<\/code>&nbsp;to extract fields such as address, function code, register address, and data count, and a flag&nbsp;<code>newData<\/code>&nbsp;is set to indicate that a valid request is ready for processing. A placeholder is left for handling&nbsp;<strong>write requests<\/strong>.<\/p>\n\n\n\n<p>Finally, the function calls&nbsp;<code>HAL_UARTEx_ReceiveToIdle_IT()<\/code>&nbsp;again to&nbsp;<strong>restart the UART reception in interrupt mode<\/strong>, ensuring the system continues listening for the next Modbus frame on the RS-485 bus.<\/p>\n\n\n\n<p>Also, in user code begin PV, declare the following defines:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define address 0x11\n\n#define Read 0x03\n\n#define Write 0x04<\/pre><\/div>\n\n\n\n<p>Next, in user code begin 3 in while 1 loop:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">if(newData==1)\n\t  {\n\t\t\t\t  uint8_t data[2];\n\t\t\t\t  data[0]=random()%255;\n\t\t\t\t  data[1]=random()%255;\n\t\t\t\t  modbus_slave_transmit(modbusFrame.Slave_Address,modbusFrame.Function_Code,2,data);\n\t\t\t\t  newData=0;\n\t  }<\/pre><\/div>\n\n\n\n<p>This code block checks whether a&nbsp;<strong>new valid Modbus request<\/strong>&nbsp;has been received and parsed. The flag&nbsp;<code>newData<\/code>&nbsp;is set to&nbsp;<code>1<\/code>earlier in the UART receive callback after a frame passes the CRC check and its fields are extracted.<\/p>\n\n\n\n<p>When&nbsp;<code>newData<\/code>&nbsp;equals&nbsp;<code>1<\/code>, the code generates two bytes of simulated data using&nbsp;<code>random() % 255<\/code>, representing sensor values to be returned to the Modbus master. These bytes are placed in the&nbsp;<code>data<\/code>&nbsp;array and sent back using&nbsp;<code>modbus_slave_transmit()<\/code>, which builds and transmits the Modbus response frame using the previously parsed slave address and function code. After the response is sent,&nbsp;<code>newData<\/code>&nbsp;is cleared to&nbsp;<code>0<\/code>&nbsp;so the system waits for the next incoming request.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>That all for the slave firmware.<\/p>\n\n\n\n<p>Save, build and run the project as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1024x34.jpg\" alt=\"\" class=\"wp-image-4132\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1024x34.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-300x10.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-768x26.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1536x51.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1150x38.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-750x25.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-400x13.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-250x8.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13.jpg 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Master Firmware Modification:<\/h2>\n\n\n\n<p><\/p>\n\n\n\n<p>Open master project.<\/p>\n\n\n\n<p>In user code begin 3 in main.c file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">DataFrame.Slave_Address=0x11;\nDataFrame.Function_Code=0x03;\nDataFrame.Register_Address=0x01;\nDataFrame.Number_of_Data=0x02;\n\nModbus_ReadRequest(&amp;DataFrame);\n\nHAL_Delay(100);\n\nDataFrame.Slave_Address=0x17;\nDataFrame.Function_Code=0x03;\nDataFrame.Register_Address=0x01;\nDataFrame.Number_of_Data=0x02;\n\nModbus_ReadRequest(&amp;DataFrame);\n\nHAL_Delay(100);<\/pre><\/div>\n\n\n\n<p>By changing the address from 0x11 to 0x17 each time, the slave shall not response when master request data from 0x17 address.<\/p>\n\n\n\n<p>Thats all for the master firmware.<\/p>\n\n\n\n<p>Save, build and run the project as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1024x34.jpg\" alt=\"\" class=\"wp-image-4132\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1024x34.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-300x10.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-768x26.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1536x51.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-1150x38.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-750x25.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-400x13.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13-250x8.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/12\/2025-12-31_11-12-13.jpg 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Results:<\/h2>\n\n\n\n<p>By launching a debug session for the master project, you can see that the variable are updating each 200ms.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"459\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-1024x459.png\" alt=\"\" class=\"wp-image-4258\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-1024x459.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-300x134.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-768x344.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-1150x515.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-750x336.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-400x179.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image-250x112.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/image.png 1156w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Here when master request data from address 0x11:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"630\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS7.png\" alt=\"\" class=\"wp-image-4259\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS7.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS7-300x185.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS7-768x473.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS7-750x461.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS7-400x246.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS7-250x154.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>When  master request data from 0x17:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"630\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS8.png\" alt=\"\" class=\"wp-image-4260\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS8.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS8-300x185.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS8-768x473.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS8-750x461.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS8-400x246.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/03\/RigolDS8-250x154.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>As you can see, the slave send the data only when master sends the correct address.<\/p>\n\n\n\n<p>Next, we shall add the write capabilities to the slave to store the configuration data send by the master.<\/p>\n\n\n\n<p>Stay tuned.<\/p>\n\n\n\n<p>Happy coding \ud83d\ude09<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Part 5 focuses on implementing address filtering so that the Modbus RTU slave responds only to requests directed to its assigned slave address. This ensures that in a multi-device RS-485 network, each slave processes and responds only to frames that match its address while ignoring all other traffic on the bus. In this guide, we [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,11,12],"tags":[],"class_list":["post-4256","post","type-post","status-publish","format-standard","hentry","category-embedded-systems","category-peripheral-drivers","category-stm32"],"_links":{"self":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4256"}],"collection":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4256"}],"version-history":[{"count":2,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4256\/revisions"}],"predecessor-version":[{"id":4262,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4256\/revisions\/4262"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4256"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4256"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4256"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}