Closed diegopivoto closed 3 years ago
Thanks for raising this feature request.
Hi @diegopivoto,
I just completed implementation and preliminary testing of Modbus TCP Master/Slave support in ESP_IDF and it was merged recently. The stack allows to connect to network over Ethernet or WiFi (with using SSID and password) and establish Modbus TCP communication as Modbus master or slave. I can help you to create Eclipse ESP-IDF project for TCP slave application. Could you provide additional information: I need to know what kind of data the PLC is going to access and Modbus register map for your slave device. Once you provided the specification of your future slave device I will help you to create your TCP slave project.
If it is enough for you to simply access ten holding registers of your device (as in the code above) I can provide this. But the modbus controller interface in the stack could provide access to complex parametrs in your device memory. You could create the structure in your code for example and allow PLC to read and write its fields over Modbus TCP. Then you can easily access its data.
Thanks.
Dear @alisitsyn Thank you so much for this. It will help a lot! So, my project is about a Master Degree Thesis, which I will implement a Future Internet Architecture (FIA) that allows the user to access industrial data remotely and then change industrial parameters. For example, an user that wants to read the status ON/OFF of a Lamp (Action 1) or to change the status of a motor from ON to OFF (Action 2) remotely: This user, with a remote PC with the FIA, sends the Action that he wants to do (i.e., Action 1 or 2) to the ESP32. The chosen Action will be linked with a Holding Register (HR). For example, if the user wants the Action 1, the HR_0 will receive value 1, and if the user wants the Action 2, the HR_1 will receive the value 1 (just an example to simplify). Finally, these HRs values will communicate with a PLC Siemens S71200 via Modbus TCP and then the actions will occur with the PLC code later. What I really need for now is just a program able to process the HRs and communicate with the PLC. So, I think that access 10 HRs as described in the code above is enough for now. But, my PLC is connected with the internet via Ethernet cable, while my ESP32 is connected with the Internet via Wifi by addressing the SSID and Password. If you think that there is another way to do this more easily or in a more interesting way, as you cited the possibility of create a structure in conde to allow PLC to read/write the fields over Modbus TCP, I am accepting suggestions, because I'm new in this area.
Thank you very much and sorry about the big text, but I try to explain as best as possible. (Just remembering that the FIA part is just another thing that is not included in the code that I want in this moment. I just cited all the context of the project in order to you understand better what I am planning to do.)
Dear @diegopivoto,
Your project is pretty clear. I am going to complite some current tasks and will prepare and then test the example for you. I plan to complete this up to end of this week.
I will return back to you ASAP.
Thank you very much, @alisitsyn I will wait for your return!
@diegopivoto,
The modbus slave example project to access IIOT data and perform user defined actions is attached to this post.
CONFIG_EXAMPLE_CONNECT_IPV6
should be set per your network requirements to enable IPV6 communication or just IPV4.mb_tcp_s/main/tcp_slave.c
- main file that contains your logic to perform your actions on access to holding register parameters defined in mb_example_common/modbus_params.c/h
files. The parameters defined as fields of structure holding_reg_params.device_action_0 - 9
they start from address 0 of modbus holding registers area. Once you get access to it, the application set/reset the discrete discrete_reg_params.device_action_stateN
and set the corresponded LED to on/off. The discrete inputs can be read over Modbus to get state of leds/LAMPs.
Once you compiled and started the project (idf.py build, idf.py flash monitor) it connects to AP using SSID and Password you configured and shows its IP address in the line:
I (3652) example_connect: - IPv4 address: 192.168.1.39
I (3662) example_connect: - IPv6 address: fe80:0000:0000:0000:bedd:c2ff:fed1:b210, type: ESP_IP6_ADDR_IS_LINK_LOCAL
Use this address to connect your slave from PLC or just from external PC with Modbus poll software installed.
You can filter R/W access (MB_EVENT_HOLDINGREG{RD/WR}) and perform your actions using this code per your requirements.
Your PLC should be connected to the same network over Ethernet and be able to establish client connection to slave IP with port 502.
mb_tcp_slave_action_handler.zip
Please also check the official ESP-IDF TCP examples and documentation in GitHub.
Hi @diegopivoto,
Could you share some results related to this issue?
Hi @alisitsyn ! Sorry for take a long time to answer this. Due to fact that I am using the PLC S71200 and other equipment from the lab of my university, and the pandemy is limitating the use of students there, I did not have the chance to test the example in the practice. I think that I will be able to use the lab tomorrow (07/08), because there is a schedule here for organizing the number and hours of use of people in the same area in order to avoid crowds and for security reasons. So, for now I will test today with simulation, and tomorrow in the real scenario, and I will post here the results in saturday. Thank you very much, and again, sorry for the long time to answer, but unfortunately it is a situation we are living that is above us. But, I am sure that everything is gonna be ok!
@diegopivoto,
Please take your time and good luck with your project! Just give me some feedback once you get results or have any questions.
Hi @alisitsyn I am trying to learn all the project as soon as possible, because I didn't imagine that it was so big! But when I finish my studies I will publish here the results. If I have some doubts about the code that you sent me, I will publish here too. Thank you very much and I will keep you informed about everthing!
I just need to learn more about how to use this example. For example, I am a little bit lost, because I want to monitore the Holding Registers via the Serial Terminal from the Eclipse, as I did in the IDE Arduino. In the Arduino Interface, the 0-9 Holding Registers (HRs) were seen in the terminal during all the time with the respective values in the moment, and when I changed one of the HRs, they updated in the serial terminal. Another thing that I was a little bit confused is about how to send values of the HRs to the master, and how to write in the HRs when the master is sending. I know that you said that the option "MB_EVENT_HOLDINGREG{RD/WR}" is responsible for this, but I don't know how to make this work easily. My questions in the moment are How can I change this example for transmit in the serial terminal of the Eclipse the HR values and see their updates in real time as I saw in the Arduino, and where I set the values of the HRs to send them to the Master? Thank you so much!
Hi @diegopivoto,
Ah, sorry, I did not know how good you are familiar with ESP-IDF. It is a bit different compare to Arduino IDE.
cd esp32_tcp/mb_tcp_s
idf.py menuconfig
in your terminal, this will show the kconfig menu.device_action_0 - N
esp32_tcp/mb_tcp_s/tcp_slave.c::app_main()
function.idf.py build
to compile project.idf.py -p ESP32PORT -b 921600 flash monitor
to flash your app into board. The ESP32PORT is the virtual serial port of the connected esp32 board under your system.I (4645) example_connect: - IPv4 address: 192.168.1.19
to establish client connection from your external Modbus master.I (1957104) SLAVE_TEST: Handle ACTION_0 READ , value = 1.
and can be changed later from master application.
portENTER_CRITICAL(¶m_lock); // The critical section is required because these values accessible also from stack
holding_reg_params.device_action_2 = 0x1234; // the value set here will be showed in MB_ACTION_2 of the project.
portEXIT_CRITICAL(¶m_lock);
File -> Open Projects From File System...-> Input source -> Directory
and point to the esp32_tcp/mb_tcp_s
then push Finish
button.When I am going to the step 8 that you type here to build the project, I think it is not working, but I think that it can be caused by some configs that can be wrong in my computer. I will put the print of the terminal when I try to build with the comman idf.py build.
Att.
Diego Gabriel Soares Pivoto Engenharia de Controle e Automação Instituto Nacional de Telecomunicações - Inatel
De: Alex Lisitsyn notifications@github.com Enviado: terça-feira, 11 de agosto de 2020 07:10 Para: espressif/esp-idf esp-idf@noreply.github.com Cc: diegopivoto24 diegopivoto@gea.inatel.br; Mention mention@noreply.github.com Assunto: Re: [espressif/esp-idf] Support for use Wifi Communication in Modbus Protocol for ESP32 (IDFGH-3730) (#5652)
Hi @diegopivotohttps://github.com/diegopivoto,
Ah, sorry, I did not know how good you are familiar with ESP-IDF. It is a bit different compare to Arduino IDE. Hope the instruction below is not to complex for you.
89882438-1ffef980-dbc7-11ea-8f8b-165256d48b5b.png)
portENTER_CRITICAL(¶m_lock); // The critical section is required because these values accessible also from stack holding_reg_params.device_action_2 = 0x1234; // the value set here will be showed in MB_ACTION_2 of the project. portEXIT_CRITICAL(¶m_lock);
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/espressif/esp-idf/issues/5652#issuecomment-671857391, or unsubscribehttps://github.com/notifications/unsubscribe-auth/APRRLWU7RRBLGXQUHABSXA3SAEKIZANCNFSM4PKFFMQA.
Hi @alisitsyn ! I got until the Step 5 of the steps that you put here. The Steps 6 and 7 are just comments, so when I go to the Step 8 and put "idf.py build" to build the project, it starts to running, but in the middle of the process it failed
Here is the problem for now. Do you know what is going on here? Thank you for the help and patience!
Hi @diegopivoto,
Your current branch is not latest and this causes this issue. Please go to your github idf folder then type the command: git describe
, record the showed version in format v4.3-dev-nnn-g_________
and send it to me. Then type git checkout origin/master
then again git describe
. The version after this should be showed like v4.3-dev-771-gc77c4ccf6. Once completed type idf.py fullclean
, type idf.py build
to build application and follow the compilation process and then flash software when compiled.
Let me know if you have further issues.
Hi @alisitsyn , When I use the command "git describe", it shows the version "v4.0" and nothing else. I don't know why the version wasn't showed in format "v4.3-dev-nnn-g_____" as you said. So, typing after that "git checkout origin/master" an error appears and then it is aborted. So, typing "git describe", it is showed just "v4.0". Here is a print of the git describe command and the error:
Hi @diegopivoto,
You are on the v4.0 branch that was an issue. Please type git log
- push Q. Send me the output, then git stash
, git checkout origin/master
, git log
and send me the log (copy the text output from cmd window). This will allow you to go further and checkout the origin/master branch then you can follow with idf.py build
item in the instruction above.
Hi @alisitsyn , I followed yours instructions step-by-step, but I think that the error holds on. I will put the prints here: First, I typed "git log":
Then, I typed "Q" to enter in the IDF Folder, and type "git stash", followed by the command "git checkout origin/master", but the message was the same before, aborting, as follows:
Finally, I typed "git log" again:
So, using "idf.py build" won't work yet.
Hi @diegopivoto,
Use these comands to force checkout origin/master and build example:
git fetch
, git checkout origin/master -f
, git submodule update
then follow idf.py build
to compile application.
Once you are able to build: git checkout -b feature/modbus_slave_action_tcp_example
Hi @alisitsyn ! I think it doesn't work again and some things got wrong, because before that I could at least use the command "idf.py menuconfig" to configure the application, but now neither this command is working. The scream shows me that some packages were uninstalled and I need to run a .bat archive to install it, but I don't know how to do it. In addition, the other errors that were happening still persists and weren't solved and I think that a lot of thing got wrong now, but I don't know how to solve it.
First of all, as you said, I typed "git fetch" and "git checkout origin/master -f", but the same error before has persisted as printed in the second printof the last message that I sent here, with the message "Aborting" and asking me to commit my changes or stash them before switch branches. So I followed by the command "git submodule update", but it doesn't work perfectly. I can't print what is the message that these commands returned to me because something got wrong and now when I try to use these commands again other error messages are given for me like a lot of things had been uninstalled:
When I execute the ESP-IDF CMD, it starts with a lot of errors that wasn't happening before, with a lot of things uninstalled. Then, when I try to use these commands again, a lot of errors persists. I will comment in the prints the errors and commands requested for you in a detailed way. Please check the prints:
Thank you very much for the patience and for trying to teach me. I hope it can works in the end haha
Hi @diegopivoto,
Don't worry this should work just fine. Something might be broken. The good think is that you are on the master branch now.
python.exe %IDF_PATH%\tools\idf_tools.py check
. This command will check the tools and will show if some tools not found or installed incorrectly. The output should be similar to this if there are differences:
Checking for installed tools...
Checking tool xtensa-esp32-elf
version found in PATH: esp-2020r2-8.2.0
version installed in tools directory: esp-2020r2-8.2.0
Checking tool esp32ulp-elf
version found in PATH: 2.28.51-esp-20191205
version installed in tools directory: 2.28.51.20170517
Checking tool cmake
version found in PATH: 3.16.4
version installed in tools directory: 3.13.4
Checking tool openocd-esp32
version found in PATH: v0.10.0-esp32-20200406
version installed in tools directory: v0.10.0-esp32-20200709
Checking tool mconf
version found in PATH: v4.6.0.0-idf-20190628
version installed in tools directory: v4.6.0.0-idf-20190628
Checking tool ninja
version found in PATH: 1.10.0
version installed in tools directory: 1.9.0
Checking tool idf-exe
version found in PATH: 1.0.1
version installed in tools directory: 1.0.1
Checking tool ccache
version found in PATH: 3.7
version installed in tools directory: 3.7
python.exe %IDF_PATH%\tools\idf_tools.py install
. Alternatively you can just start the install.bat
script from esp-idf folder to install tools.export.bat
this shall update the %PATH% environment and then show:
Adding ESP-IDF tools to PATH...
Checking if Python packages are up to date...
Python requirements from c:\esp32\esp-idf\requirements.txt are satisfied.
Done! You can now compile ESP-IDF projects. Go to the project directory and run:
idf.py build
4. Now you can go to project directory and try to configure and compile the project.
5. As about git submodule update try
git submodule foreach git fetch git submodule update
5. If the above does not work I think it is better to remove existing IDF folder then clone the esp-idf repo again as described here: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started
and install tools again and export PATH. Hope the above works.
To clone esp-idf repo to your current esp folder:
git clone --recursive https://github.com/espressif/esp-idf.git
Please feel free to ask! This is little bit hard to perform just for first time. The above must work finally!
Hi @diegopivoto,
Was it possible to solve the above issues on your side? It is possible to install eclipse and the plugin for ESP32. The plugin wizard will download all required artifacts and allow you to go through the installation process. The plugin: https://github.com/espressif/idf-eclipse-plugin/blob/master/README.md
Please feel free to ask. I can help you to follow this process.
Hi @alisitsyn, Unfortunately, I used my time on September in order to write my Master Thesis, because the project wasn't working and I tried a lot of things to solve the problmes for running the ESP32 projects on Eclipse and it didn't work. I followed your instructions of the comment in the day 17 Aug, but in the Step 3 that you said me "Once it is installed you can export path using export.bat this shall update the %PATH% environment", the following message was send: In the last comment from 28 Aug, I tried to follow the steps by plugging ESP32 on Eclipse, but it also didn't work. I performed the Step "Installing IDF Plugin using update site URL", but the next one "Installing ESP-IDF Tools" asked to Navigate to Help > ESP-IDF Tools Manager > Install Tools, but clicking on Help, the option "ESP-IDF Tools Manager wasn't found and I don't have any idea what I can do for just test the project. I can confess it is a little bit frustrating for me, because there are more than one month I am just trying to install the basic programs to just test ESP projects and I just can't do it. That is the cause I startet to write my thesis and focus on other things, but now everything is done, and the only thing that rests is the project test. I really don't know what to do anymore.
Hi, @alisitsyn I have some news. First of all, I could fix some issues. Now, I can enter on the menu by the command "idf.by menu config" on ESP-IDF Prompt Command. So, I updated the SSID and Password from the Wi-fi parameter. Then, I put in the ESP-IDF Prompt Command the command "idf.py build" and it started to build the project. But, an error has occurred. I will put the print of the error while trying to build your code:
These were the failings now. What can I do in order to try to fix these failures and build the project?
Thank you fot your attention.
Just a comment here. When I fixed the problems before, it appears in the prompt that there wasn't directories added to PATH. But I continued to use the idf.py menuconfig and build. So, I don't knowif these failures has something in common with this.
C:\Users\diego\Desktop\esp-idf>export.bat Setting IDF_PATH: C:\Users\diego\Desktop\esp-idf
Adding ESP-IDF tools to PATH... No directories added to PATH:
C:\Users\diego.espressif\python_env\idf4.3_py3.7_env\Scripts;C:\Users\diego.espressif\tools\xtensa-esp32-elf\esp-2020r2-8.2.0\xtensa-esp32-elf\bin;C:\Users\diego.espressif\tools\xtensa-esp32s2-elf\esp-2020r2-8.2.0\xtensa-esp32s2-elf\bin;C:\Users\diego.espressif\tools\esp32ulp-elf\2.28.51-esp-20191205\esp32ulp-elf-binutils\bin;C:\Users\diego.espressif\tools\esp32s2ulp-elf\2.28.51-esp-20191205\esp32s2ulp-elf-binutils\bin;C:\Users\diego.espressif\tools\cmake\3.16.4\bin;C:\Users\diego.espressif\tools\openocd-esp32\v0.10.0-esp32-20200709\openocd-esp32\bin;C:\Users\diego.espressif\tools\ninja\1.10.0\;C:\Users\diego.espressif\tools\idf-exe\1.0.1\;C:\Users\diego.espressif\tools\ccache\3.7\;C:\Users\diego.espressif\tools\dfu-util\0.9\dfu-util-0.9-win64;C:\Users\diego\Desktop\esp-idf\tools;C:\Users\diego\AppData\Local\Programs\Python\Python37\;C:\Program Files\Git\cmd;C:\Program Files\Common Files\Siemens\Automation\Simatic OAM\bin;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\PuTTY\;C:\Program Files\Git\cmd;C:\Program Files\Java\jdk-14.0.1\bin;C:\Users\diego\AppData\Local\Programs\Python\Python37\Scripts\;C:\Users\diego\AppData\Local\Programs\Python\Python37\;C:\Users\diego\AppData\Local\Microsoft\WindowsApps;C:\Users\diego\AppData\Local\Programs\MiKTeX 2.9\miktex\bin\x64\
Checking if Python packages are up to date... Python requirements from C:\Users\diego\Desktop\esp-idf\requirements.txt are satisfied.
Done! You can now compile ESP-IDF projects. Go to the project directory and run:
idf.py build
Hi @diegopivoto,
I understand your complaints, but the ESP-IDF has a dependency on the python interpreter and git and requires some familiarity with required instruments. Usually, it is enough to follow the Get started guide and install tools using IDF Tools installer. I don't know where your failures happen during installation and can not check your steps.
I followed your instructions of the comment on the day 17 Aug, but in the Step 3 that you said me "Once it is installed you can export path using export.bat this shall update the %PATH% environment", the following message was send:
This requires to update the python modules using the command (this should also be updated on installation steps): python -m pip install -r %IDF_PATH%\requirements.txt
Step "Installing IDF Plugin using update site URL", but the next one "Installing ESP-IDF Tools" asked to Navigate to Help > ESP-IDF Tools Manager > Install Tools, but clicking on Help, the option "ESP-IDF Tools Manager wasn't found and I don't have any idea what I can do for just test the project.
I can not check what you missed during your install but the ESP-IDF Tools Manager
shall exist there after correct installation. Did you try to restart Eclipse? I followed the guide ESP-IDF Eclipse Plugin and was able to install the plugin on my Windows machine successfully. Make sure you correctly installed prerequisites in Installing Prerequisites
.
Then, I put in the ESP-IDF Prompt Command the command "idf.py build" and it started to build the project. But, an error has occurred. I will put the print of the error while trying to build your code:
I think you missed the submodule update stage here.
git submodule sync
git submodule update
According to your feedback, you are missing something during your installation steps. In order to help you, I need more than just get messages once a week or per month. Can you install Skype tool for example to be able to guide you during your setup?
Hi @alisitsyn I used the commands "git submodule sync" and "git submodule update", but when I try to use "idf.py build" the error persists. I will put the print below.
Do you know how can I fix this? I f you have another idea, I wanna try again and I'm here for listen and try to fix this. If it goes wrong again, we can talk in Skype as you said, and we can make an appointment for solve this.
Thank you so much for your attention!
Related to the Skype, we can talk
Hi, @alisitsyn I have some good news here! I didn't get to use the Eclipse for testing ESP32 projects for now (that is an issue to be solve yet), but in the ESP-IDF prompt command, finally and with your help, I got it, and the commands 'idf.py menuconfig' and 'idf.py build' run without errors. I will put below the prints:
Now, I have another problem, that is try to finally test it on the ESP32 using the command 'idf.py -p ESP32PORT -b 921600 flash monitor', but I download the Modbus Slave one month ago and now that I can test the free license has expired. I can't use the PLCs on the lab university at this moment because things are really bad here due to the pandemy and the access is restrict for a while. So, I need to use some simulator for try to test it. Do you have any suggestion? Is there another simulator for free for the Modbus Slave? Thank you so much for your attention!
@diegopivoto There are various bits of software for testing MODBUS TCP/IP. Any of them should work fine with the esp-idf implementation. I used this, which does have a free trial, although after the free trial runs out you still get 10 minutes of testing time before you simply have to restart the software.
Hi, @gecko242 The problem is that I used the software Modbus Slave that is of the same company of the Modbus Poll that you mentioned. In the free trial, I could use only for 10min, and after that I can't use any time without a license key. The Modbus Poll is a software for its use on the Master mode, and for my implementation I need a software for the Slave mode, which is the Modbus Slave that I downloaded and it has expired before I start testing.
Hi @diegopivoto,
There are bunch of software for Modbus. The code which I sent you is the Modbus TCP Slave. You can use the Modbus Master simulator to test your slave as example. However I did not try this yet. This simulator supports Modbus TCP/IP and has interface very similar to ModbusPoll tool. Try to use it to check your slave device while you don't have access to your PLC. Also you can use ModbusPoll started under Windows virtual machine. There are some other options (pymodbus as example).
Hi @alisitsyn Unfortunetaly, the Modbus Master Simulator simulates a device as a master, and in my case I need to test using the ESP32 as a Master and the PLC as a Slave. So, the software needs to use the Modbus Protocol as a slave. The Modbus TCP Slave is the option for testing it, but the license has expired and there is no way to use it unless I had a license key. I will try to use a Windows virtual machine for running the Modbus TCP Slave software and when I test it I return here with the results. Thank you for your attention!
Hi @diegopivoto, Ok, I was thinking right opposite, that your PLC will be Modbus TCP master and you need Modbus slave. Just in case the Modbus TCP Master ESP32 example is also available and can be used. Take your time and let me know if you need help.
Hi @alisitsyn So, in the code example you sent me, what do I need to change in order to perform the ESP32 as a masterand not as a slave? Because I thought that the project was adapted to work with a Master ESP32 and a Slave PLC. I will use the ESP32 as a master because it will be responsible for specifying the service function to be performed in the PLC, such as an I/O reading or writing. I'm not sure, but maybe specifically in my project the master/slave device won't be relevant for the project operation (I'm not sure about this).
Thank you for your attention!
@diegopivoto,
You can find the Modbus TCP Master example here: %IDF_PATH%\examples\protocols\modbus\tcp\mb_tcp_master
.
It is not hard to adopt it for your needs:
Enable CONFIG_MB_MDNS_IP_RESOLVER
option in your kconfig, this allows Master to find active Modbus TCP slave in the same network. Also provide the WiFi SSID and pass options as I described before.
Configure your slave Modbus data dictionary, configure characteristics (parameters or even Modbus registers) you are going to read/write in your slave(s):
...
examples\protocols\modbus\tcp\mb_tcp_master\main\tcp_master.c:
// Enumeration of all supported CIDs for device (used in parameter definition table)
enum {
CID_ACTION0 = 0,
CID_ACTION1,
CID_ACTION2,
// .... put your additional characteristic IDs here
CID_COUNT
};
mb_parameter_descriptor_t device_parameters[] = {
// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
{ CID_ACTION0, STR("Action 0"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 1,
HOLD_OFFSET(device_action_0), PARAM_TYPE_U16, 2, OPTS( 0, 10000, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// >>>>> add all characteristics you are going to access in the slave device here with unique CID (similar to first one, the description of the fields are in the example)
....
};
3. Configure the data structures (add your action parameter instances into corresponded holding register structure) `%IDF_PATH%\examples\protocols\modbus\mb_example_common\include\modbus_params.h`
typedef struct { // Actions to be performed by device when write uint16_t device_action_0; uint16_t device_action_1; uint16_t device_action_2; uint16_t device_action_3; uint16_t device_action_4; uint16_t device_action_5; uint16_t device_action_6; uint16_t device_action_7; uint16_t device_action_8; uint16_t device_action_9; uint16_t device_action_last; // >>>>>> add instances for each characteristic you are going to access here as above (can be accessed from your code later) uint16_t test_regs[150]; } holding_reg_params_t;
4. The actual read/write of parameters (logic of your master) is performed here: `tcp_master.c::master_operation_func()`.
By default it reads all parameters defined in object dictionary and keeps values in the fields of structures defined in object dictionary accordingly (HOLD_OFFSET(device_action_0) ->holding_reg_params.device_action_0 ) and prints its values.
You can use the `mbc_master_get_parameter(), mbc_master_set_parameter()` API to read and write corresponding parameter from/to Modbus TCP slave.
I think you should try the above to configure TCP master/slave by yourself to be familiar with the API and functionality. Let me know if you have trouble with the configuration and read/write operations.
Refer to the [Modbus official documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/modbus.html) and example readme.md files for more information.
> I'm not sure, but maybe specifically in my project the master/slave device won't be relevant for the project operation (I'm not sure about this).
Both approaches are possible. It is up to you to select your approach. With official two examples the ESP32 board can communicate as a Modbus slave or master. The TCP slave supports multimaster connection, so you can read the data from ESP32 master and access it from ModbusPoll for example. Also it is possible to use smart phone with Modbus TCP application to access the ESP32 Modbus slave.
Hi @diegopivoto,
Ah, sorry, I did not know how good you are familiar with ESP-IDF. It is a bit different compare to Arduino IDE.
- Please follow this instruction first to setup ESP-IDF: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started
- Create the project folder (for example esp32_tcp) and unpack the files from archive I sent you.
- Open your terminal or MSYS32 window and type
cd esp32_tcp/mb_tcp_s
- Type
idf.py menuconfig
in your terminal, this will show the kconfig menu.- Set the kconfig options as below: Save the settings (Push S button) and confirm saving.
- Your HR registers structure is defined in esp32_tcp/mb_example_common/include/modbus_params.h as
device_action_0 - N
- Your application code is located in
esp32_tcp/mb_tcp_s/tcp_slave.c::app_main()
function.- Type
idf.py build
to compile project.- Type
idf.py -p ESP32PORT -b 921600 flash monitor
to flash your app into board. The ESP32PORT is the virtual serial port of the connected esp32 board under your system.- Configure your Wifi AP to allow board to connect to your Wifi network. Once Application starts you will see the picture below: Use the ip address and port 502 showed in the line:
I (4645) example_connect: - IPv4 address: 192.168.1.19
to establish client connection from your external Modbus master.- Once you connected the app shows the log like below (this is analog of serial terminal in Arduino): The value in the output line reflects the value of holding_reg_params.device_action_0:
I (1957104) SLAVE_TEST: Handle ACTION_0 READ , value = 1.
and can be changed later from master application.- You can use the ModbusPoll master software https://www.modbustools.com/modbus_poll.html for example to connect to your slave. The ModbusPoll configuration file is below: tcp_slave_mb_action_tracker_config.zip
- The example is designed to work on ESP32_WROVER board: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-wrover-kit-v3.html but can be easily be changed for other ESP32 board.
- See the description below on how to configure Modbus Poll:
- The RGB LED on the board will reflect the changes in 'MB_ACTION_0 - 2' registers. For example the value in holding_reg_params.device_action_0 corresponds to MB_ACTION_0 in master project.
- You can change the value of your action registers in your modbus slave application as below:
portENTER_CRITICAL(¶m_lock); // The critical section is required because these values accessible also from stack holding_reg_params.device_action_2 = 0x1234; // the value set here will be showed in MB_ACTION_2 of the project. portEXIT_CRITICAL(¶m_lock);
- The link can be used to setup eclipse for the project: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/eclipse-setup.html
- Open the project follow
File -> Open Projects From File System...-> Input source -> Directory
and point to theesp32_tcp/mb_tcp_s
then pushFinish
button.
Hi, @alisitsyn In the weekend, I will finally test the program, because I will be able to use another computer and then install/use the trial version of the Modbus Poll, due to the fact that both Modbus Poll and Modbus Slave Simulator expired on my computer. In order to advance and test it with no doubts, I checked the commands idf.py menuconfig and idf.py build, flash and monitor it on my ESP32m and it returned me the expected values, such as the connection to the Wi-fi network and also the IP Address of my slave to be set in the Modbus Poll later (I will use the ESP32 as the slave, as the code was firstly done). So, finally, my question is: The code has 10 holding registers (0-9), discrete inputs and etc. In order to simplify my doubt here, I will just talk about the Holding Registers. In the Arduine code that I had, there was 10 holding registers for writing (0-9) and 10 holding registers for reading (11-20). In this example for Eclipse that you implemented, there are 10 holding registers (holding_reg_params.device_action_0 - 9). I'm not sure about this, but these 10 holding registers can be used as reading as writing, differently in the Arduine code whose holding registers for reading and writing had specific positions? If i'm right, I don't know exactly how to configure these 10 HRs ((holding_reg_params.device_action_0 - 9) for reading or for writing. For example, what do I need to do if I want to use the "holding_reg_params.device_action_0" to write a value (e.g. write value "1" in holding_reg_params.device_action_0) and send it to the Master (e.g. see the value written in MB_Action_0 of the Modbus Poll Simulator). In addition, what do I need to do if I want to use the "holding_reg_params.device_action_1" to read a value from the Master (e.g. set value "1" in MB_Action_1 of the Modbus Poll and see this value in the monitor of the prompt)? This is a little bit confused to me and I think that you can help me to test it in the weekend with less doubt.
Thank you!
Hi @diegopivoto,
For example, what do I need to do if I want to use the "holding_reg_params.device_action_0" to write a value (e.g. write value "1" in holding_reg_params.device_action_0) and send it to the Master (e.g. see the value written in MB_Action_0 of the Modbus Poll Simulator).
Your slave device sends the data by request of Master. The master can set holding_reg_params.device_action_X
parameter in the slave to the appropriate value to control LEDs for example. The written value can be also read back by master.
All read write registers values are logged into console. So, the device_action_x
registers corresponds to MBHoldingRegister[10-19]
in your example. You can easily change the value of the register. The master can read it back and do appropriate action accordingly. The code below allows to change the values of additional holding_reg_params.device_data_x
registers in your slave example to random value each time master reads these values (change is performed in critical section to avoid access conflicts from Modbus stack). This is something similar to your example MBHoldingRegister[0-9]
. The same way you can change the device_action_0
modbus_params.h:
#pragma pack(push, 1)
typedef struct
{
// Actions to be performed by device when write
uint16_t device_action_0; // register address 0
uint16_t device_action_1;
uint16_t device_action_2;
uint16_t device_action_3;
uint16_t device_action_4;
uint16_t device_action_5;
uint16_t device_action_6;
uint16_t device_action_7;
uint16_t device_action_8;
uint16_t device_action_9; // register address 10
uint16_t device_data_0; // register address 11
uint16_t device_data_1;
uint16_t device_data_2;
uint16_t device_data_3;
uint16_t device_data_4;
uint16_t device_data_5;
uint16_t device_data_6;
uint16_t device_data_7;
uint16_t device_data_8;
uint16_t device_data_9;
uint16_t test_regs[150];
} holding_reg_params_t;
#pragma pack(pop)
tcp_slave.c::app_main():
// ReInitialize the holding_reg_params.device_data_X to random value each time we read device data
if ((event & MB_EVENT_HOLDING_REG_RD) && ((uint32_t)reg_info.address > (uint32_t)&holding_reg_params.device_action_9)) {
// This is how to safely change fields in the Modbus structure that being accessed by Modbus driver
// (Implementation of critical section)
portENTER_CRITICAL(¶m_lock);
holding_reg_params.device_data_0 = (uint16_t)esp_random(); // set register to random value
holding_reg_params.device_data_1 = (uint16_t)esp_random();
holding_reg_params.device_data_2 = (uint16_t)esp_random();
holding_reg_params.device_data_3 = (uint16_t)esp_random();
holding_reg_params.device_data_4 = (uint16_t)esp_random();
holding_reg_params.device_data_5 = (uint16_t)esp_random();
holding_reg_params.device_data_6 = (uint16_t)esp_random();
holding_reg_params.device_data_7 = (uint16_t)esp_random();
holding_reg_params.device_data_8 = (uint16_t)esp_random();
holding_reg_params.device_data_9 = (uint16_t)esp_random();
portEXIT_CRITICAL(¶m_lock);
}
In addition, what do I need to do if I want to use the "holding_reg_params.device_action_1" to read a value from the Master (e.g. set value "1" in MB_Action_1 of the Modbus Poll and see this value in the monitor of the prompt)?
You can check that the value is changed by master and print its value for example using ESP_LOGI() macro for example.
if (IS_FIELD_IN_RANGE(reg_info.address, reg_info.size, device_action_1, action_value) ) {
if (event & (MB_EVENT_HOLDING_REG_WR)) { // master writes the value!
// perform whatever you need with the value of holding_reg_params.device_action_1 == action_value = value updated by master
ESP_LOGI(SLAVE_TAG, "Action1 is changed by master: %u", holding_reg_params.device_action_1);
}
}
Similar approach was already explained in the example I sent you. Please refer to the Modbus official documentation and try experimenting with it in order to understand.
@diegopivoto,
Do you have some progress related to your project? Thanks.
@alisitsyn, I would like to apologize for the long time to answer you, because I was focused on an article to be re-submitted in a Journal and it had a short deadline to do it. So, I have good news! I've tested the code and it finally works! I used the ESP-IDF Prompt Command and the software Modbus Poll, changing the values of the holding registers in the code and checking the new values in the simulator. I tested the other way too, by changing the value of the holding registers in the ModBus Poll simulator and checking the new values in the ESP-IDFPrompt Command monitor. I did not test the code in the Eclipse, because I did not have time to try to make ESP32 codes work on this platform, but I think it will work, because it works in the ESP32-IDF Prompt and the language/code is the same, right? So, I will try to fix the Eclipse issues for running ESP codes there and run this code, but as I said I think it will works. If it doesn't work, I will return here and ask for some help. But again, thank you very much for the help, and mainly for your patience on teach me due to the fact I've never worked on it before! Finally, if I have some problems related to Eclipse I return here, but if it works on ESP-IDF CMD it will probably work on Eclipse too (Correct me if I'm wrong haha).
Thank you for everything!
@diegopivoto,
Good news.
I did not test the code in the Eclipse, because I did not have time to try to make ESP32 codes work on this platform, but I think it will work, because it works in the ESP32-IDF Prompt and the language/code is the same, right?
Eclipse is just IDE, the final binary after compilation will be the same. When install Eclipse please make sure that you installed prerequisites in "Installing Prerequisites" section correctly. What is more important you need to install Java11 on your machine for plugin to work.
Once you completed your project, please let me know and let us close the issue.
Hi @alisitsyn I tested on Eclipse of my friend and it works. Now, I just have one question: I will add in the ESP an architecture for data exchange and this architecture was modeled for exchanging data directly from the wifi raw socket, physically. On the other words, this architecture doesn't use the TCP on transport layer. So, I'm not sure, but I think I have two ways to follow: 1) Use two different ESP32, one for implementing this architecture and another one for using this code already done for communicate via MODBUS TCP with the PLC, and an UART or another way for communicating the 2 ESP32; 2) Use just one ESP32, implementing the architecture and the communication MODBUS in the same ESP32 code. In this case, for the correct operation of the architecture, I should use Modbus RTU instead Modbus TCP because as I said the architecture doesn't work with TCP and it would be a problem.
So, my question is, if I want to use the option 2, my code already supports Modbus RTU and I need to do just few changes? If yes, what would be that changes in order to change the code from Modbus TCP to Modbus RTU? Or the answer is No, and I will need to do another code totally different if I want to use Modbus RTU?
As I said, I just want to know all my possibilities, if I can easily change the Modbus or if I would need to do another code and it will be easier to use 2 ESP32.
Thank you very much!
@diegopivoto,
I just can guess about what logic you mean under "architecture". Probably you mean something like Modbus RTU over TCP. Because of this I can not answer your questions related to architecture of your system. It is up to you.
If yes, what would be that changes in order to change the code from Modbus TCP to Modbus RTU? Or the answer is No, and I will need to do another code totally different if I want to use Modbus RTU?
The ESP-IDF Modbus supports TCP and RTU mode. Examples in the ESP-IDF for serial RTU. The master and slave code for these modes are very similar and use just different options in the configuration. You can compare the examples for TCP and RTU and easily change the communication settings.
Hi @diegopivoto,
Any news on the project? Was it possible to solve the issues?
@diegopivoto,
Could you give me update on the status?
Hello, @alisitsyn Sorry abou the long time without updates, but now the project is in the same situation as before. The reason for that is because the future internet architecture I will implement embedded in ESP32 need to be checked, seen that it possible won't work woth the transport layer and consequently TCP protocol. So, for a while the project is working very well with the initial propose, but I really don't know if I will have to change the features of the code for working using ModBus RTU, using ModBus TCP with two ESP32 working separetely (one withe the future internet architecture and another one with this code), or using ModBus TCP maintaing this code. In other words, it will depend on how the future internet architecture deals with the Modbus TCP, if it works with the transport layer or not, and if it will be capable of work with TCP ou change the code to RTU. I am now waiting for answers to these questions, but just my teacher and developer of the architecture is able to give me a solution. Finally, I will keep using this code, which is working very well, and if I need to change my code I return here for a possible solution. Furthermore, if I don't need to change anything in the code, I will return here too to keep you informed about everything.
Thank you very much!
Hello @diegopivoto,
Thank you for update. As I understand you don't have any issues related to ESP-IDF library and it works on your side. Since it is opened for very long time, I would suggest you to close the issue for now. Later if you have any problems you can reopen the issue. Thanks.
Thanks for reporting, feel free to reopen.
Hello, my friends. I used to program my ESP32 in the IDE Arduino, but now I have a project which is necessary to use the Eclipse IDE with the expressif IDF. So, I need to configure my ESP32 to act as a MODBUS SLAVE and communicate with an industrial PLC S71200, which is the Modbus Master. The communication needs to be via WI-FI. So, my ESP32 needs to connect to the AP Wi-fi by entering with the respective SSID and Password, and then establish a connection with the PLC. The problem is that I am a new programmer and I'm not getting this program done in the Eclipse. I've tried to change the ESP_IDF Example available abou Modbus Slave, but this example doesn't use Wi-Fi communication and I don't know how to change it for make my project works. Can someone help me? The program in the IDE Arduino is working perfectly. I will show above the IDE Arduino code, that is correct. The ESP_IDF project template in the Eclipse that I am trying to adapt in order to do exactly what the Arduino one does is available in the folder Protocols/Modbus_Slave (which is the template's name).
(The point here is just try to adapt the Modbus_Slave template example in the ESP_IDF in order to use Wifi communication for connecting the ESP32 and then communicate with the Modbus Master via port 502 (The example uses UART comm and not Wifi).
//IDE Arduino Code (working perfectly)
/*
include
const char ssid = "Diego"; const char password = "dgsp1618"; int ModbusTCP_port = 502;
//////// Required for Modbus TCP / IP /// Requerido para Modbus TCP/IP /////////
define maxInputRegister 20
define maxHoldingRegister 20
define MB_FC_NONE 0
define MB_FC_READ_REGISTERS 3 //implemented
define MB_FC_WRITE_REGISTER 6 //implemented
define MB_FC_WRITE_MULTIPLE_REGISTERS 16 //implemented
// // MODBUS Error Codes //
define MB_EC_NONE 0
define MB_EC_ILLEGAL_FUNCTION 1
define MB_EC_ILLEGAL_DATA_ADDRESS 2
define MB_EC_ILLEGAL_DATA_VALUE 3
define MB_EC_SLAVE_DEVICE_FAILURE 4
// // MODBUS MBAP offsets //
define MB_TCP_TID 0
define MB_TCP_PID 2
define MB_TCP_LEN 4
define MB_TCP_UID 6
define MB_TCP_FUNC 7
define MB_TCP_REGISTER_START 8
define MB_TCP_REGISTER_NUMBER 10
byte ByteArray[260]; unsigned int MBHoldingRegister[maxHoldingRegister];
//////////////////////////////////////////////////////////////////////////
WiFiServer MBServer(ModbusTCP_port);
void setup() {
pinMode(14, OUTPUT);
Serial.begin(115200); delay(100) ; WiFi.begin(ssid, password); delay(100) ; Serial.println("."); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } MBServer.begin(); Serial.println("Connected "); Serial.print("ESP8266 Slave Modbus TCP/IP "); Serial.print(WiFi.localIP()); Serial.print(":"); Serial.println(String(ModbusTCP_port)); Serial.println("Modbus TCP/IP Online");
}
void loop() {
// Check if a client has connected // Modbus TCP/IP WiFiClient client = MBServer.available(); if (!client) { return; }
boolean flagClientConnected = 0; byte byteFN = MB_FC_NONE; int Start; int WordDataLength; int ByteDataLength; int MessageLength;
// Modbus TCP/IP while (client.connected()) {
if(client.available()) { flagClientConnected = 1; int i = 0; while(client.available()) { ByteArray[i] = client.read(); i++; }
client.flush();
///// code here --- codigo aqui
///////// Holding Register [0] A [9] = 10 Holding Registers Escritura ///////// Holding Register [0] A [9] = 10 Holding Registers Writing
MBHoldingRegister[0] = random(0,12); MBHoldingRegister[1] = random(0,12); MBHoldingRegister[2] = random(0,12); MBHoldingRegister[3] = random(0,12); MBHoldingRegister[4] = random(0,12); MBHoldingRegister[5] = random(0,12); MBHoldingRegister[6] = random(0,12); MBHoldingRegister[7] = random(0,12); MBHoldingRegister[8] = random(0,12); MBHoldingRegister[9] = random(0,12);
///////// Holding Register [10] A [19] = 10 Holding Registers Lectura ///// Holding Register [10] A [19] = 10 Holding Registers Reading
int Temporal[10];
Temporal[0] = MBHoldingRegister[10]; Temporal[1] = MBHoldingRegister[11]; Temporal[2] = MBHoldingRegister[12]; Temporal[3] = MBHoldingRegister[13]; Temporal[4] = MBHoldingRegister[14]; Temporal[5] = MBHoldingRegister[15]; Temporal[6] = MBHoldingRegister[16]; Temporal[7] = MBHoldingRegister[17]; Temporal[8] = MBHoldingRegister[18]; Temporal[9] = MBHoldingRegister[19];
/// Enable Output 14 digitalWrite(14, MBHoldingRegister[14] );
//// debug
for (int i = 0; i < 10; i++) {
Serial.print("["); Serial.print(i); Serial.print("] "); Serial.print(Temporal[i]);
} Serial.println("");
//// end code - fin
//// rutine Modbus TCP byteFN = ByteArray[MB_TCP_FUNC]; Start = word(ByteArray[MB_TCP_REGISTER_START],ByteArray[MB_TCP_REGISTER_START+1]); WordDataLength = word(ByteArray[MB_TCP_REGISTER_NUMBER],ByteArray[MB_TCP_REGISTER_NUMBER+1]); }
// Handle request
switch(byteFN) { case MB_FC_NONE: break;
case MB_FC_READ_REGISTERS: // 03 Read Holding Registers ByteDataLength = WordDataLength 2; ByteArray[5] = ByteDataLength + 3; //Number of bytes after this one. ByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data). for(int i = 0; i < WordDataLength; i++) { ByteArray[ 9 + i 2] = highByte(MBHoldingRegister[Start + i]); ByteArray[10 + i 2] = lowByte(MBHoldingRegister[Start + i]); } MessageLength = ByteDataLength + 9; client.write((const uint8_t )ByteArray,MessageLength);
byteFN = MB_FC_NONE;
break;
case MB_FC_WRITE_REGISTER: // 06 Write Holding Register MBHoldingRegister[Start] = word(ByteArray[MB_TCP_REGISTER_NUMBER],ByteArray[MB_TCP_REGISTER_NUMBER+1]); ByteArray[5] = 6; //Number of bytes after this one. MessageLength = 12; client.write((const uint8_t *)ByteArray,MessageLength); byteFN = MB_FC_NONE; break;
case MB_FC_WRITE_MULTIPLE_REGISTERS: //16 Write Holding Registers ByteDataLength = WordDataLength 2; ByteArray[5] = ByteDataLength + 3; //Number of bytes after this one. for(int i = 0; i < WordDataLength; i++) { MBHoldingRegister[Start + i] = word(ByteArray[ 13 + i 2],ByteArray[14 + i 2]); } MessageLength = 12; client.write((const uint8_t )ByteArray,MessageLength); byteFN = MB_FC_NONE;
break; } } }