Simple UDP Test Program for STM32F407 - Page 1
Forum section: electronics / general

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 12, 2020, 11:06:36 pm
content: Can someone point me to a simple UDP client/server example that I can use to test a STM32F4 Discovery board? I would like to test by sending a text string to either a PC or another STM32F4 Discovery board. Either method would be acceptable. I just want to confirm how the LwIP UDP functions work. I could also watch the packets in Wireshark if necessary. I have one of the boards running a Webserver with a static webpage at this point.I am running STMCubeIDE for the programming environment but can't find any examples in the install directories.

author: newbrain
author_rank: Super Contributor
timestamp: March 12, 2020, 11:30:26 pm
content: As the F4 Disco does not come with an Ethernet connector and PHY, there's no LwIP example in its own Project folder in the STM32F4 Cube.LwIP UDP echo client and server examples can instead be found in theSTM32Cube_FW_F4_Vx.x.x/Projects/STM324xG_EVAL/Applications/LwIP

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 13, 2020, 12:04:48 am
content: I have an external PHY and Ethernet connector installed and the board running as a webserver. STM32CubeIDE generated all of the code.I don't find a STM32Cube_FW_F4_Vx.x.x folder on my computer. Do I have to trick the IDE into thinking I have another board by creating a project for one of the F4 boards that has a PHY and connector?

author: newbrain
author_rank: Super Contributor
timestamp: March 13, 2020, 07:38:08 am
content: That might be a strategy - I don't personally use the STM32CubeIDE, as my doctor told me to stay away fromEclipse based tools...In any case:Vx.x.x is not literal, I should have explained it depends on the revision of the STM32Cube library that is installed.The latest one is 1.25.0, see below.The standalone MX Cube code generator (included as a plug-in in STM32CubeIDE), on Windows, uses something as:C:\Users\<username>\STM32Cube as base directory for the Cube libraries. I expect something similar for Linux.As a last resort, you can download the F4 Cube librarieshere

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 13, 2020, 02:53:56 pm
content: Agreed. I always suggest starting from the example projects of Cube SDK directly instead of even trying the horrid CubeIDE. And yes you can download the SDKs independently and just extract the files anywhere you like.Browse example projects for all boards (as newbrain suggested), as not all boards have equivalent projects.

author: ajb
author_rank: Super Contributor
timestamp: March 13, 2020, 06:20:49 pm
content: Assuming you have the required device drivers configured correctly, the following should work. I have not tested the receive.Code:static struct udp_pcb * test_pcb;static uint16_t test_port = 5555; // arbitrarystatic const ip_addr_t * test_destIp = IP_ADDR_BROADCAST; //or wherever your destination device isconst char test_msg[] = "This is a UDP test packet";#define TEST_MSG_SIZE (sizeof(test_msg)/sizeof(test_msg))void test_recv(void *arg, struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *srcAddr, uint16_t port);void udp_test_send(void);void udp_test_init(void){test_pcb = udp_new();if(!test_pcb){//error!__asm volatile ("BKPT\r\n");}udp_bind(test_pcb, IP4_ADDR_ANY, test_port);udp_recv(test_pcb, test_recv, 0);}void udp_test_send(void){struct pbuf * p = pbuf_alloc(PBUF_TRANSPORT, TEST_MSG_SIZE, PBUF_POOL); // arguments are configuration dependent!if(!p){//error!return;}strcpy(p->payload, test_msg);udp_sendto(test_pcb, p, test_destIp, test_port);pbuf_free(p);}void test_recv(void *arg, struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *srcAddr, uint16_t port){printf("Test msg received: %s\r\n", (char*)pb->payload);pbuf_free(pb);}Code:udp_test_init();while(1){tick_delay(1000);udp_test_send();net_process();}

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 13, 2020, 06:29:00 pm
content: QuoteThanks, found examples.QuoteInteresting... I haven't had any real issues with it. The code running on my board for my test webserver was all generated by STM32CubeIDE and it uses less than 5% of flash memory. However, the latest upgrade from earlier in the week broke the httpd server. Turns out that for some reason they moved all of the httpd code from the httpd folder to the http folder. I don't understand the reasoning but I guess they do.ajb, thanks for the code I will give it a test. I looked at the UDP example code underneath 1.25.0 and it looks fairly simple.

author: newbrain
author_rank: Super Contributor
timestamp: March 13, 2020, 10:18:46 pm
content: Quote from: rcbuck on March 13, 2020, 06:29:00 pmI have already stated my opinion on the whole IDE (and Eclipse based stuff in general).The STM32CubeMX code generator itself is maybe not stellar, but does an honest job, and setting up pins, peripherals and clocks is easy and (mostly) reliable.This is especially true when compared with the efforts(?) of other vendors (I'm looking at you, NXP, with the clunky, half unreadable, incoherent and incomprehensible mess that Config Tools is...).

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 14, 2020, 01:55:53 am
content: newbrain,Out of curiosity, what programming IDE/or tools do you use for STM32 devices?

author: newbrain
author_rank: Super Contributor
timestamp: March 14, 2020, 07:15:21 am
content: My personal preference goes to Visual Studio plus VisualGDB.VisualGDB is commercial SW but they provide a 30 days full feature free trial. It has good support for many different MCUs and works quite well for my purposes (note that I am an hobbyist).Visual Studio Community is free (as in beer) and lacks very little wrt its paid siblings.Of course, Windows only...Alternatively, I use VS Code (handy multiple platforms and remote editing, great git integration, the only editor that managed to make me switch from emacs), but some effort on your part is needed to support compilation and debugging.That said, tastes in IDE/editors are as personal as can be; you'll find many threads in this very forum about it...EtA: case in point, someone lovesMCUxpresso.I'm currently building a small SDR with a friend (and two iMX RT1020 boards I got for free from NXP).I compile and debug inside MCUxpresso but all editing and git handling is done in VS Code.

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 15, 2020, 03:42:34 pm
content: Quote from: rcbuck on March 13, 2020, 06:29:00 pmIt's probably not the worst out there. It's just that I don't like GUI-based code generators in general. I also don't like the code style they generate (so it would just be wasting my time as I wouldn't leave the generated code as is, it would be too inconsistent with my own code).And you learn a lot more not using those tools. As I said, there are many provided examples to get you started.Mainly a personal preference though.

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 23, 2020, 09:13:16 pm
content: Quoteajb, can you tell me how to format the IP address for the STM32? I've tried everything I know and get either warnings or errors about the format. The send code works fine with IP_ADDR_BROADCAST. However, I want to assign it the address of my STM32 which is 172.16.1.122.I haven't tried the receive code yet. What is the first argument to the test_recv function (arg)?

author: ajb
author_rank: Super Contributor
timestamp: March 24, 2020, 03:56:32 am
content: `ip_addr_t ` is an LWIP type, I didn't find any great explainers in a quick google, only the documentation, which is here:https://www.nongnu.org/lwip/2_0_x/group__ipaddr.htmlThe easier way to understand it is probably to just look at the headers, so from ip_addr.h:Code:typedef struct ip_addr {union {ip6_addr_t ip6;ip4_addr_t ip4;} u_addr;/** @ref lwip_ip_addr_type */u8_t type;} ip_addr_t;We see that ip_addr is tagged union that can hold either an IPv4 address or an IPv6 address. Most (all?) functions in LWIP that take an IP address can handle either address type, so the tagged union struct saves us from having to use two different object types and deal with twice as many functions in the API.To find the structure of the ip4_addr_t and ip6_addr_t types referenced in the above struct we have to look at ip4_addr.h and ip6_addr.h, respectively:Code:struct ip4_addr {u32_t addr;};typedef struct ip4_addr ip4_addr_t;Code:struct ip6_addr {u32_t addr;#if LWIP_IPV6_SCOPESu8_t zone;#endif /* LWIP_IPV6_SCOPES */};/** IPv6 address */typedef struct ip6_addr ip6_addr_t;The typedefs here just allow us to write "ip4_addr_t" instead of "struct i4_addr". So, ip4_addr_t holds the IPv4 address as a 32 bit integer. So we could construct the 32 bit integer by shifting each octet of the IP address in the appropriate way, as long as we make sure we know the endianness of the host and the endianness that LWIP expects, ORRRRrrrr we could use one of the bajillion conveniencemacros that LWIP provides for working with IP addresses, which are conveniently also found on the header files we just looked at. Specifically, we probably the IP_ADDR4 macro, which takes a pointer to an ip_addr and values representing the four octets of the IP address, and stuffs the struct full of the octets and sets the tag to indicate that it's IPv4 address. So you should be able to do:Code:ip_addr_t myIpAddress;IP_ADDR4(&myIpAddress, 172, 16, 1, 122);And then you should be set.LWIP is a bit of a monster, and unfortunately AFAIK there aren't very many good guides to it, so the best thing I can recommend is to dig through the lib and trace out the various nested macros (there are a LOT of nested macros) to see how things work. A good IDE where you can right click->go to implementation is a big help, as is just stopping the program and examining memory.

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 24, 2020, 06:55:59 am
content: ajb,Thanks for the detailed explanation. It helps a lot with understanding how some of the LwIP stuff works. I spent most of today picking through the header and source files and picked up some of what you are describing.I actually had tried these two lines you describe:Code:ip_addr_t myIpAddress;IP_ADDR4(&myIpAddress, 172, 16, 1, 122);and was receiving a compile error message:main.c:81:10: error: expected identifier or '(' before '&' tokenSince you said it should work, I knew something was wrong. After staring at the code for a few minutes it FINALLY dawned on me that the IP_ADDR4(&myIP, 172, 16, 1, 122) is actually a function call and had to be called from inside of a function to work. I thought the IP_ADDR4 was doing something special in LwIP because of this line in ip_addr.h:#define IP_ADDR4(ipaddr,a,b,c,d). But a closer look reveals the do{ } while loop is actually assigning the IP address.I googled trying to find info but as you said there isn't a lot of good info available for LwIP. I also tried to find a book on the subject but came up empty handed.Again, thanks for your help. Now I need to figure out how to make the receive function work. I'll do that tomorrow.

author: ajb
author_rank: Super Contributor
timestamp: March 24, 2020, 05:28:31 pm
content: Happy to help!To clarify, IP_ADDR4 is not a function, but a macro dressed up like one. There are a lot of these in LWIP; because it tends to target resource-limited platforms a lot of the library is heavily preprocessor dependent so that as much as possible gets resolved at compile time to improve run-time efficiency and help reduce the size of the application. Unfortunately this makes the whole thing quite a mess to navigate.Anyway, the IP_ADDR4 macro is actually macros all the way down, and if you fully expanded those macros you'd get two expressions that initialize the two main members of the ip_addr_t struct. So you COULD actually create atagged initializerthat appears outside of your function definitions for your ip address, but it would be pretty gross due to the nested types and the fact that you have to do some platform-dependent bitshifting, so it's a heck of a lot nicer to just put the initialization into your init function.Some IDEs have nice macro expansion explorers, I know Atollic has one, but I'm not sure if that's inherited from Eclipse CDT or if it's custom. It might be in the Cube IDE?

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 24, 2020, 08:33:17 pm
content: Yes, CubeMX has macro expansion. It really comes in handy a lot of times. You hover over the macro and it pops up in a small window. A lot of times you can't see the entire macro expansion. However, if you press F2 it locks the window so you can then adjust size or use scroll bars to see the entire macro.

author: rcbuck
author_rank: Frequent Contributor
timestamp: March 28, 2020, 02:46:44 am
content: Some searching turned up Adam Dunkels' book on LwiP. If you search using this phrase: "design and implementation of the lwip tcp/ip stack pdf Adam Dunkels" you will find it. The link is kind of strange so I can't post it. The link you are looking for sayswww.artila.com> download ? RIO ? lwip PDF. The book was written in 2001 but most of the information still applies to LwIP.I managed to get the test_recv function working. I am able to send 8294 bytes (11 x 754 bytes) of information using Packet Sender to the STM32F4 Discovery board with no issues. I capture the data and send it out the UART to my terminal program (CoolTerm) with no errors. I am going to be capturing 8000 samples per second of an audio stream and passing it off to the Ethernet UDP send process. Since the UDP transmission is much faster than the UART I should have no issues.This is my receive code:Code:void test_recv(void *arg, struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *srcAddr, uint16_t port){uint16_t len, x;len = pb->len;for(x=0; x<11; x++){HAL_UART_Transmit(&huart6, pb->payload, len, 1000);}pbuf_free(pb);}Maybe someone will find a use for it. For some reason the HAL_UART_Transmit indentation is wrong when I post the code. It is actually indented between the { and } braces.Thanks again ajb for your help.