This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Sending Ethernet Frames to a Client

Part Number: MSP432E401Y
Other Parts Discussed in Thread: MSP-EXP432E401Y, TIDA-00226,

Hi,

I'm trying to re-purpose the ethernet_with_lwip example (running on an MSP-EXP432E401Y development board using CCS) so I can receive short (10-byte) commands from a client and respond with a short burst back to the client. With help from a previous post I am able to receive the command successfully. Now I'm trying to send a response back to the client. To make it as simple as possible for now, I'm just trying to send the same 10 bytes that I received (05 64 05 C9 01 00 01 00 90 25).

Here is a typical ethernet frame I'm receiving from the client:

1C76FF70 265C4EBB 251E220A 00450008 63C23200 06800040 A8C0A7B4 A8C00501 C0626501 D04F0050 1980FD95 18500000 ABEBFAF0 64050000 0001C905 25900001 F143669C

And here is my "first guess" attempt at a response:

220A265C FF70251E 4EBB1C76 00450008 61C13200 06800040 A8C0A9B5 A8C06501 50000501 2854FDCD 1B05CC95 18500000 F2B2FAF0 64050000 0001C905 25900001 345D01BD

I'm pretty confident that I have the 6 byte destination mac address, 6 byte source mac address, and 2 byte type values for the ethernet frame header correct. I'm also confident I have the source and destination ip addresses correct in the IP Header, but less confident about the source and destination port #'s in the TCP header. I assume that the options and padding for both the IP header and TCP header have been left out since the data starts after 54 bytes and, after the 14 bytes necessary for the ethernet frame header are subtracted leaves only 20 bytes for each of the IP and TCP headers.

Beyond that, I pretty much don't know what I'm doing. I did a test to record some receive and transmit frames using a browser to give me some flag settings and other values to use for my first guess response but I don't know if they are correct or not. I also have no idea how the checksums should be calculated.

Does anyone know if there are any routines already present in the ethernet_with_lwip example that can be used to generate the response?

Any help anyone can provide that will get me closer to being able to get a response back to the client successfully would be greatly appreciated.

Thank you

  • Hey Brad,

    Sorry for the delay, I'm going back through open threads. Were you able to figure out the correct packet structure?  

    I don't have detailed Ethernet experience to be able to assist with the packet structure but I'll leave the thread open in case anyone in the community can give some insight.  

    Thanks,

    JD 

  • I still recommend tcp_write(). The lwIP people have already figured out how to build a TCP packet for you.

    Or, perhaps simpler, adopt an existing ModbusTCP (+lwIP) implementation such as TIDA-00226:

    https://www.ti.com/tool/TIDA-00226

  • Thank you for your response. Your suggestions look like they will be very useful but I have couple of questions before I can proceed.

    WRT tcp_write():- I'm using the ethernet_with_lwip example. I looked through all of the files and couldn't find tcp_write() function. Can you tell me where it is located?

    WRT TIDA-00226:- I downloaded the project at this link which was tid565 that had the firmware folder enet_modbus. I tried to open the project using Code Computer Studio. All of the other examples I've used with CCS had a css folder which had a project startup file, but the enet_modbus folder doesn't have a ccs folder so I'm not sure how to work with enet_modbus using CCS. Can you tell me how to get started with enet_modbus?

    Thank you.

  • 1) On my machine, the tcp_write() source is in

    C:\ti\simplelink_msp432e4_sdk_3_20_00_10\source\third_party\lwip\src\core\tcp_out.c

    A shortcut: Find the call to tcp_write in httpd.c, then right-click on the name and "go to definition".

    2) In theory: You import from the .zip file using "File->Import->CCS Projects->From Archive". In practice: that will get you a Tiva project, and I wasn't successful in getting CCS to re-target it to the MSP432E (despite the close similarity of the chips). Even if you succeed at that, you'll need to fix up the header files.

    What I suggest  instead is copying the .c and .h files into an existing MSP432E+lwIP project, e.g. your current enet_lwip project, then disable enet_lwip.c with "Exclude from build". You'll still need to fix the header files, but this avoids the re-targeting problem (a bunch of symbols and links).

    That is how I would do it. Since I then wondered whether it would actually work, I went ahead and did it.

    I stubbed much of the "serial" function, but it builds. I haven't tried to run it since I don't have a  Modbus client. This project should maybe give you a head-start, and with your Modbus background you can probably tell what it's trying to do.

    modbus_with_lwip_MSP_EXP432E401Y_nortos_ccs.zip

  • I've run into a problem importing the project into CCS.  Whenever I imported a project before I would go to Projects > Import CCS Project to open the Import CCS Projects box. Then I would browse to the ccs folder of my project and press the Finish button which would load in my project.

    The problem I'm having is that when I do this now the Finish button is not enabled so I can't press it.  Can you tell me what I need to do in order for the Finish button to be enabled?  I'm sure it's probably something simple I'm not doing, but I don't know what it is so I can't get past this point.

    Thank you

  • Try starting up one level from the "ccs' folder. Some projects have a ccs folder which doesn't actually contain a .project file.

    Import normally looks (downward) through the entire tree; your goal is to see the project name in the text-box in the middle.

    Which project (tree) are you looking at?

  • That did it.  I'm starting to work with the modbus_with_lwip example you sent.  It looks like I'll be able to make some real progress with it.

    Thank you so much!  

  • I'm just getting back to this after working on another project for a couple of weeks and I'm trying to get to the point using modbus_with_lwip where I can see the incoming ethernet frames in the same way we did for the ethernet_with_lwip example a while back.

    I set it up to use the static ip address 192.168.1.101, run it in Debug mode, and look at the receive byte count (EMAC0_EMAC_RXCNTGB located at 0x400EC180). I also set up the client side on another computer with the same ip address with port 80 as we did before.

    In most cases when I run this setup the receive byte count doesn't increment and the receive descriptors do not change, so it looks like the connection has not been made successfully

    (There is one case when the receive byte count does increment and that is in my first attempt to run this test right after it was working with the ethernet_with_lwip example. The receive descriptors do not change here either.)

    Do you have any ideas on what I need to do so I can receive the incoming ethernet frames successfully using modbus_with_lwip?

    Thank you.

  • When you pause the program, where is it executing? I suspect the link startup hasn't finished. There are some number of UARTprintf()-s which were (for whatever reason) commented out. You may want to un-comment them and watch the progress.

    I don't see an httpd_init() call, so I don't expect you'll get a response on port 80. It looks like this program serves port 502 ("ModbusListen(502);", line 397). This wouldn't prevent your receiving Ethernet packets.

  • Following your suggestions and after a little more testing I think the problem is in this loop:

    for(i = 0; i < 0x08; i++)
    {
    RS485_TxEnable();
    SysCtlDelay(10);
    UARTCharPut(UART3_BASE, "0xAA");
    SysCtlDelay(1);
    RS485_TxDisable();
    }

    I had commented out the entire loop because the line 'UARTCharPut(UART3_BASE, "0xAA")' was throwing a warning:

    warning #169-D: argument of type "char *" is incompatible with parameter of type "unsigned char"

    My understanding is that RS485 was referring to a serial port so I didn't think it would have any impact on the Ethernet connection, but apparently it does.

    I reactivated the loop and observed it with and without the UARTCharPut() command active. With the command active the receive byte count increments (with the client side set to port 502), but execution never gets past the loop. With the command inactive execution goes past the loop, but the receive byte count never advances.

    I'm sure there is a simple fix to get past the UARTCharPut() command successfully, but I don't know what it is. I work mostly in assembly code.

    Thank you in advance for your reply.

  • My guess is that it was intended to say

    UARTCharPut(UART3_BASE, 0xAA);

    though I don't know the Modbus significance of 8 bytes of 0xAA. 

    I dummied the Rs485 calls, and I would expect the CharPut call to just write a random byte.

    What happens if you remove (comment) that line? Where does it stop then?

  • Changing to UARTCharPut(UART3_BASE, 0xAA) stopped the warning but it still gets hung up on that command. If I remove the rest of the loop execution enters UARTCharPut(UART3_BASE, 0xAA), but it never returns, though the receive byte advances.

    I can't find any other reference to UART3_BASE anywhere else in the file and I found these in the includes section:

    void RS485_TxEnable(void) {return;} // Used also by modbusTCP
    void RS485_TxDisable(void){return;} // Used also by modbusTCP

    They might be relevant.

    If comment out UARTCharPut(UART3_BASE, 0xAA), execution proceeds, but the receive byte never advances.

  • I dummied the Rs485 functions but I left the calls in so they'll be there in case your (current) RTU uses RS485. 

    It appears the UART isn't running properly, but at this stage it may not be needed. Without the call, where does the program proceed to?

    Also, I think there are LEDs on the RJ45 which indicate link status. Are they lit?

  • It wasn't working before (the receive byte count wasn't advancing) whenever I commented out the UARTCharPut(), but now it is (the receive byte count is advancing when I send it a string from the client) so maybe we can make some progress now. There is still a problem, however, because there aren't any changes to the receive descriptors even though the receive byte count is advancing. BTW, the receive descriptors are in a different location now (at 0x20017508) than they were in the ethernet_with_lwip example.

    There are no leds on the RJ45 connector itself, but there are 4 of them on the board, D1-D4, that appear to be associated. All 4 are off when the program is first loaded. Then when I begin execution D4 stays on solid and D3 flickers a few times whenever I send a string of data to it.

    Can you think of any ideas on why the receive descriptors are not changing even though it looks like I'm receiving data (the byte count advances and the led flickers)?

    Thanks again.

  • I'm not familiar with the Rx counter, so I'm not sure what (exactly) it's counting.

    Are you fairly certain the Rx descriptors aren't changing? One possibility is that the data is being accepted and the descriptor is given back to the EMAC between breakpoints.

    Do you reach ModbusReceive when you send something from the client?

  • Here is the receive byte counter. It appears to work the same way the receive byte counter worked in the ethernet_with_lwip example we worked with a couple of months ago.

    0x400EC180 EMAC0_EMAC_RXCNTGB
    0x400EC180 00000159

    Here are the receive descriptors. They don't change at all when I send a string of characters from the client. They used to change whenever I sent a string of characters when we were testing with the ethernet_with_lwip example.

    0x20017508 g_pRxDescriptors
    0x20017508 00400320 00004200 2001611C 2001752C 00000000 00000000 00000000 00000000 2001610C
    0x2001752C 00400320 00004200 20015F0C 20017550 00000000 00000000 00000000 00000000 20015EFC
    0x20017550 00520321 00004200 20015CFC 20017574 00000083 00000000 00000000 00000000 20015CEC
    0x20017574 00420321 00004200 20015AEC 20017598 00000083 00000000 00000000 00000000 20015ADC
    0x20017598 005E0321 00004200 200158DC 200175BC 00000083 00000000 00000000 00000000 200158CC
    0x200175BC 00990321 00004200 200156CC 200175E0 00000081 00000000 00000000 00000000 200156BC
    0x200175E0 005E0321 00004200 200154BC 20017604 00000083 00000000 00000000 00000000 200154AC
    0x20017604 00400320 00004200 200152AC 20017508 00000000 00000000 00000000 00000000 2001529C

    I set a breakpoint in these two functions and sent a string from the client:

    In modbus.c -----------ModbusReceive(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
    In modbusTCP.c -----void ModbusTCPReceiveSerialSend(unsigned long ulPort, unsigned char ucChar)

    And execution never stopped at the breakpoint in either case.

    Is there another ModbusReceive function somewhere else where I should try setting a breakpoint?

    Thank you.

  • The next step would be a breakpoint at ModbusAccept, which is called when the client initiates the connection.

    Where is the program executing when you pause it in the debugger?

  • I put the breakpoint in ModbusAccept at line 537 of modbus.c and it looks like the board is not recognizing the connection because it never stops executing at that point. It's very puzzling because that same client works ok with the same setup when I use the ethernet_with_lwip example. The only difference is that in this case I'm setting the port # to 502 where it was set to 80 when I was using ethernet_with_lwip before.

    I'm confident that the program gets all the way to the infinite loop at the bottom of main() in enet_modbus.c because it successfully executes a print statement located just before the loop.

  • I should mention that I changed the method of assigning an ip address from DHCP to STATIC and I'm pretty sure from what I'm seeing that I need to look at that modification in more detail. I'll do that tomorrow and let you know how it goes.

    Thank you for your help.

  • When you pause the program (the button with the two vertical bars), where is it executing?

    I just tried this, and in my case it was running in Default_Handler(), because the SysTick ISR name wasn't correct. On line ~249, I changed "SysTickIntHandler" to "SysTick_Handler" and it stopped doing that.

    I then used telnet to connect to port 502, and typed something, and I got to the Accept, then to the Receive functions.

  • I don't have the slightest idea what changing "SysTickIntHandler" to "SysTick_Handler" does, but I did it and now I also got to the Accept and Receive functions whenever I send a string (a modbus command) from the client.

    I think this is a major breakthrough and I'm hoping I will be able to see my command come in and get it to respond back to the client (a project for tomorrow).

    Thank you so much. I'll let you know how it goes.

  • I can see the modbus command coming in, so that's a big step.

    I originally thought that modbus_with_lwip would respond to an incoming modbus command itself, but it looks like it sends the incoming command to an external modbus device over the serial port (at case MODBUS_STATE_SEND_SERIAL in modbusTCP.c) then waits for a response over the serial port and sends that response back over ethernet (at case MODBUS_RTU_STATE_FRAME_SENT in modbusRTU.c). This makes things a little more complicated.

    In order to get the program to respond directly to the incoming command I tried to take the loop in modbusRTU.c that had the "RingBufWriteOne(&g_sRxBuf, g_mbapRTU[i])" command in it and recreate it with some modification at case MODBUS_STATE_SEND_SERIAL in modbusTCP.c, but I got a compilation error, "g_sRxBuf undefined", even though I included ringbuf.h and the g_sRxBuf declaration:

    #include ringbuf.h, &
    extern tRingBufObject g_sRxBuf;

    Do you think this is a good approach to get the program to respond whenever it receives a modbus command? And if so, can you see what I'm doing wrong that is causing the compilation error?

    Also, do you think this will cause the response to be sent over Ethernet back to the client, or is there something else that needs to be done after it writes the response to the buffer?

    Thank you.

  • g_sRxBuf is defined (instantiated) in serial.c, which I removed from the build; I'm not sure getting serial.c to build is worth the trouble. I suggest

    1) remove the word "extern" from the declaration above, so it gets instantiated right there.

    2) Copy the source for SerialReceive() from serial.c to modbus.c, where it is currently dummied-out.

    I don't know if this will do everything you want, but I think it will get you closer.

  • I removed extern from the declaration and was able to compile the program successfully. Then I tried running it under debug, put a breakpoint at the "RingBufWriteOne(&g_sRxBuf, g_mbapRTU[i]);" command in modbusTCP.c, and sent it a command from the client. It received the command successfully because it stopped at the breakpoint.

    I stepped into RingBufWriteOne() and it went there successfully in ringbuf.c. But, when I tried to jump into "psRingBuf->pui8Buf[psRingBuf->ui32WriteIndex] = ui8Data;" which is in RingBufWriteOne(), it immediately sent me to the Default_Handler in "startup_msp432e401y.ccs.c" so there is a problem.

    I'm very confused why SerialReceive() should go into modbus.c since it deals with the serial port and everything I'm doing is over Ethernet. The serial port is not involved at all.

    Thank you.

  • 1) I guess if you want to use g_sRxBuf, you'll also need:

    > #define RX_RING_BUF_SIZE        1024

    > static unsigned char g_pucRXBuffer[RX_RING_BUF_SIZE];

    > RingBufInit(&g_sRxBuf, g_pucRXBuffer, sizeof(g_pucRXBuffer));

    from serial.c

    2) Strictly speaking, SerialReceive [serial.c] doesn't receive from the serial port, it receives from g_sRxBuf, which is where you wanted to put your response. It's what's called in ModbusHandler to get the data to give to tcp_write(), which is the ultimate goal. I expect you'll eventually delete most of this and put whatever your RTU does in its place. I figured a simple copy/paste would get you going.

    3) I also dummied SerialReceiveAvailable(), so you may want to copy that too.

  • I'm not real familiar with the c language so I had to guess at how to make a couple of the changes you suggested.

    The command "RingBufInit(&g_sRxBuf, g_pucRXBuffer, sizeof(g_pucRXBuffer))"

    appears in SerialInit() in serial.c and since there is no SerialInit() in modbus.c I guessed and placed it in ModbusInit().

    The first pass compilation after adding SerialReceive() couldn't find g_sRxBuf so I included ringbuf.h and added:

    tRingBufObject g_sRxBuf;

    The compiler is now giving me these warnings that look like they need to be fixed before I start my testing:

    "../modbus.c", line 1009: warning #225-D: function "SerialReceive" declared implicitly
    "../modbus.c", line 1067: warning #161-D: declaration is incompatible with previous "SerialReceive" (declared at line 1009)

    I commented out SerialReceiveAvailable(void) because it throws warnings similar to the above.

    Thank you.

  • If you look up at the top of modbus.c, you'll see where I dummied those two functions. You'll want to get rid of (replace) those duplicate definitions.

  • I thought I already did that by commenting out these functions

    static inline void SerialSetDefault(void){ return;} // Needed?
    //static inline long SerialReceive(void) {return((long)UARTgetc());} // Blocks!
    static inline bool SerialSendFull(void) { return(0);} // pretend never full
    //static inline unsigned long SerialReceiveAvailable(void) {return(0);} // Never available

    Is there something else I need to do to get rid of them?

  • OK, then you probably just need to move the function definitions you added up towards the beginning of the file (before the first references to them).

  • That solved the problem.  And, when I sent a command from the client I got a response from the board (hooray!).  Now I can start making progress developing my application.

    Thank you so much for all the help!  You spent a lot of time getting me to this point and I really appreciate it.  I'll go ahead and mark the issue as resolved.

**Attention** This is a public forum