I'm trying to send data to and from my computer and an STM32H745 over Ethernet using LwIP and UDP. I have successfully configured the card and right now I can send data from the card to a Python script running on the computer. However, I don't understand how udp_recv works <udp,lwip> or how to receive data with UDP on LwIP in general, and I can't find examples that do just that. Where is the data being received? Should I even use udp_recv?
In the main loop I run MX_LWIP_Process, which runs ethernetif_input which somehow handles the received data, but I don't understand where it puts it.
Below is the main code, just for reference.
const char* message = "a";
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_1); // orange
ip_addr_t PC_IPADDR;
IP_ADDR4(&PC_IPADDR, 192, 168, 1, 200);
u16_t port = 8000;
struct udp_pcb* my_udp = udp_new();
struct pbuf* udp_buffer = NULL;
/* Infinite loop */
for (;; )
{
MX_LWIP_Process();
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_1); // orange
HAL_Delay(1000);
udp_buffer = pbuf_alloc(PBUF_TRANSPORT, strlen(message), PBUF_RAM);
if (udp_buffer != NULL)
{
memcpy(udp_buffer->payload, message, strlen(message));
udp_sendto(my_udp, udp_buffer,&PC_IPADDR, port);
pbuf_free(udp_buffer);
}
//udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
}
udp_recv() does not actually receive UDP datagrams (despite its name). It registers a callback function that will then be called by MX_LWIP_Process() when a datagram has been buffered. It would better be called udp_set_recv_callback(), but it is what it is.
To that end you should call it once before your executive loop:
udp_bind( my_udp, IP_ADDR_ANY, port ) ;
udp_recv( my_udp, udp_receive_callback, NULL ) ;
/* Infinite loop */
for (;; )
{
// Run the CubeMX LwIP stack
MX_LWIP_Process() ;
...
}
Where udp_receive_callback is a function that will be invoked on receipt of a datagram:
void udp_receive_callback( void* arg, // User argument - udp_recv `arg` parameter
struct udp_pcb* upcb, // Receiving Protocol Control Block
struct pbuf* p, // Pointer to Datagram
const ip_addr_t* addr, // Address of sender
u16_t port ) // Sender port
{
// Process datagram here (non-blocking code)
...
// Must free receive pbuf before return
pbuf_free(p);
}
Examples include:
https://gist.github.com/iwanbk/1399729
https://github.com/STMicroelectronics/STM32CubeF2/blob/master/Projects/STM322xG_EVAL/Applications/LwIP/LwIP_UDP_Echo_Client/Src/udp_echoclient.c
Documentation can be found at https://www.nongnu.org/lwip/2_0_x/group__udp__raw.html
Related
I have a client/server LWIP program, I want to use multicast features so I used IGMP library did the following setting like this:
Setting .IOC
Enable LWIP_IGMP
Enable LWIP_MULTICAST_TX_OPTION
Enable LWIP_IP4
in ethernet.c
netif->flags |= NETIF_FLAG_IGMP
in stm32f7xx_hal_eth.c (ETH_MACDMAConfig)
macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_ENABLE;
macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;
and i implemented code like this :
void UDP_Multicast_init(void *arg)
{
struct ip4_addr ipgroup, localIP;
struct udp_pcb *upcb;
char msg[] = "hello";
struct pbuf* p;
p = pbuf_alloc(PBUF_TRANSPORT,sizeof(msg),PBUF_RAM);
memcpy (p->payload, msg, sizeof(msg));
IP4_ADDR(&ipgroup, 224, 224, 0, 1); //Multicast IP address.
IP4_ADDR(&localIP, 192, 168, 1, 99); //Interface IP address
#if LWIP_IGMP
igmp_joingroup((ip4_addr_t *)(&localIP),(ip4_addr_t *)(&ipgroup));
#endif
upcb = ( struct udp_pcb*) udp_new();
MulticastStart(); //binding connection to the port 10
udp_recv(upcb, UDP_callback, NULL);
udp_sendto(upcb,p,&ipgroup,10);
}
void UDP_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p,
const ip_addr_t *addr, u16_t port)
{
printf("test");
}
and I try to see in wireshark, iplocal successfully joins into the multicast address, and sends a data to the multicast address.
enter image description here
but the callback function cannot be executed.
is there something missing??
thanks for the response.
I'm trying to UART transceiver on my ZYNQ-7000 board using interrupts. Basically, it just take data from the serial terminal and send back to it.
After initialization, a message Uart Initialization Successful! is sent to and shown on the terminal which confirms that the ZYNQ can send data to the PC. But whenever I input some random letters, it seems that interrupt handler function, called void Handler(), was never called. It seems there is no interrupt generated at all. I've looked up a lot but cannot slove the problem. Could any one please help me with this, please.
Here is my code,
#include "xparameters.h"
#include "xuartps.h"
#include "xil_printf.h"
#include "xscugic.h"
#include "stdio.h"
// serial device ID
#define UART_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID
// interrupt ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
// serial port interrupt id
#define UART_INT_IRQ_ID XPAR_XUARTPS_1_INTR
// interrupt controller driver instance
XScuGic Intc;
// serial port driver instance
XUartPs Uart_Ps;
// data buffer size
#define MAX_LEN 512
u8 ReceivedBuffer[MAX_LEN];
volatile u32 ReceivedByteNum;
XUartPsFormat UartFormat = {
115200,
XUARTPS_FORMAT_8_BITS,
XUARTPS_FORMAT_NO_PARITY,
XUARTPS_FORMAT_1_STOP_BIT
};
// function declaration
int UartInit(XUartPs *uart_ps);
// interrupt handler
void Handler(void *call_back_ref);
int UartIntrInit(XScuGic *intc, XUartPs *uart_ps);
// main function
int main(void){
int status;
// initialize the serial port
status = UartInit(&Uart_Ps);
if(status == XST_FAILURE){
xil_printf("Uart Initialization Failed\r\n");
return XST_FAILURE;
}
// interrupt initialization
status = UartIntrInit(&Intc, &Uart_Ps);
if(status == XST_FAILURE){
xil_printf("Uart Initialization Failed\r\n");
return XST_FAILURE;
}
xil_printf("Uart Initialization Successful!\r\n");
// main loop
while (1) {};
return status;
}
int UartInit(XUartPs *uart_ps){
int status;
XUartPs_Config *uart_cfg;
uart_cfg = XUartPs_LookupConfig(UART_DEVICE_ID);
if(NULL == uart_cfg) return XST_FAILURE;
status = XUartPs_CfgInitialize(uart_ps, uart_cfg, uart_cfg->BaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// UART self test
status = XUartPs_SelfTest(uart_ps);
if(status != XST_SUCCESS) return XST_FAILURE;
XUartPs_SetOperMode(uart_ps, XUARTPS_OPER_MODE_NORMAL);
XUartPs_SetDataFormat(uart_ps, &UartFormat);
XUartPs_SetFifoThreshold(uart_ps, 32);
XUartPs_SetRecvTimeout(uart_ps, 8);
return XST_SUCCESS;
};
// UART Interrupt handler service
void Handler(void *call_back_ref){
xil_printf("Enter INTR\r\n");
XUartPs *uart_instance_ptr = (XUartPs *) call_back_ref;
u32 ReceivedCount = 0;
u32 IsrValue;
IsrValue = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_IMR_OFFSET);
IsrValue &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET);
// if interrupt is asserted
if( IsrValue & ((u32) XUARTPS_IXR_RXOVR) ){
XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR);
ReceivedCount = XUartPs_Recv(&Uart_Ps, ReceivedBuffer, MAX_LEN);
ReceivedByteNum += ReceivedCount;
}
else if( IsrValue & ((u32) XUARTPS_IXR_TOUT) ){
// Rx FIFO timeout / idle
XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT);
ReceivedCount = XUartPs_Recv(&Uart_Ps, ReceivedBuffer, MAX_LEN);
ReceivedByteNum += ReceivedCount;
// send out
for(u32 sendByte=0;sendByte<ReceivedByteNum;sendByte++){
XUartPs_SendByte(XPAR_PS7_UART_1_BASEADDR, ReceivedBuffer[sendByte]);
}
ReceivedByteNum = 0;
}
}
// UART Interrupt init
int UartIntrInit(XScuGic *intc, XUartPs *uart_ps){
int status;
// initialize the interrupt controller
XScuGic_Config *intc_cfg;
intc_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID);
if(NULL == intc_cfg) return XST_FAILURE;
status = XScuGic_CfgInitialize(intc, intc_cfg, intc_cfg->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// set and enable interrupt exception handle function
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler, (void *) intc);
// set interrupt handler for interrupt
XScuGic_Connect(intc, UART_INT_IRQ_ID, (Xil_ExceptionHandler) Handler, (void *) uart_ps);
// set interrupt trigger mode
XUartPs_SetInterruptMask(uart_ps, XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT);
Xil_ExceptionEnable();
XScuGic_Enable(intc, UART_INT_IRQ_ID);
return XST_SUCCESS;
}
I'm using UART1 controller at MIO 48, 49 which is confirmed at both the Vivado and the schematic.
There are few problems of your program.
Receive function
It seems you have not called the UART receive function (XUartPs_Recv).
In interrupt mode, the UART controller will start receiving after you called XUartPs_Recv, this function is non-blocking. When all data received, the UART controller will generate an interrupt, and all data has been written to the receive buffer by the interrupt handler function.
XUartPs_Recv(uart_ps, RecvBuffer, SIZE_IN_BYTE);
Interrupt Handler
UARTPS library provided the interrupt handler function (XUartPs_InterruptHandler). So you need to bind it to the XScuGic
XScuGic_Connect(intc, UART_INT_IRQ_ID,
(XInterruptHandler) XUartPs_InterruptHandler, uart_ps);
This interrupt handler can help you receive the data from UART FIFO and write to the receive buffer.
Custom Callback
If you want to do something when the interrupt occoured, you don't need to write a new handler function by yourself, but a Callback function is needed.
XUartPs_SetHandler(uart_ps, (XUartPs_Handler)Handler, uart_ps);
Use this function, your custom handler will be called from the
interrupt context (XUartPs_InterruptHandler) when data has been sent or received.
Addition
You may set the receiver timeout. If it is not set, and the last few bytes of data do not trigger the over-water or full interrupt, the bytes will not be received. By default it is disabled.
XUartPs_SetRecvTimeout(uart_ps, 8);
Reference
Please refer the official example by Xilinx on GitHub.
I'm trying to send and receive UDP packets through the same endpoint. As far as I know this should be possible. But I can not get it to work with the asio library (version 1.20.0).
This is what I do:
asio::io_context io_context;
asio::ip::udp::socket* udpSendRecvSocket = new asio::ip::udp::socket(io_context, asio::ip::udp::endpoint(asio::ip::udp::v4(), 7782));
asio::error_code ec;
char data[1000];
//
// send packet
//
std::string ipAddress = "127.0.0.1";
asio::ip::address ip_address = asio::ip::address::from_string(ipAddress);
asio::ip::udp::endpoint remoteTarget_endpoint(ip_address, 5500);
udpSendRecvSocket->send_to(asio::buffer(data, 50), remoteTarget_endpoint, 0, ec);
if (ec) {
return 0;
}
//
// receive packets
//
size_t avLen = udpSendRecvSocket->available(ec);
while (avLen) {
asio::ip::udp::endpoint remote_endpoint;
size_t length = udpSendRecvSocket->receive_from(asio::buffer(data, 1000), remote_endpoint, 0, ec);
int p = remote_endpoint.port();
if (ec) {
return 0;
}
avLen -= length;
}
The receive does not work correctly. I do receive a packet that I send (from some other app). I know because avLen gets the right value. But when executing the receive_from(), if fails. And the port number in p gets the value 5500. This is the value of the target port of the send_to() call that was executed before.
The strange thing is that when I remove the send_to() call, the receive does work correctly and the p will reflect the correct port number of the sending application.
Is this a bug?
I have a Raspberry Pi working as a WiFi hotspot and an Arduino Uno trying to get data from it using an ESP8266 module.
This is my receiver code for Arduino:
#include <SoftwareSerial.h>
#include <SerialESP8266wifi.h>
#define sw_serial_rx_pin 4 // Connect this pin to TX on the esp8266
#define sw_serial_tx_pin 6 // Connect this pin to RX on the esp8266
#define esp8266_reset_pin 5 // Connect this pin to CH_PD on the esp8266, not reset. (let reset be unconnected)
SoftwareSerial swSerial(sw_serial_rx_pin, sw_serial_tx_pin);
// the last parameter sets the local echo option for the ESP8266 module..
SerialESP8266wifi wifi(swSerial, swSerial, esp8266_reset_pin, Serial);//adding Serial enabled local echo and wifi debug
String inputString;
boolean stringComplete = false;
unsigned long nextPing = 0;
void setup() {
inputString.reserve(20);
swSerial.begin(9600);
Serial.begin(9600);
while (!Serial)
;
Serial.println("Starting wifi");
wifi.setTransportToTCP();// this is also default
// wifi.setTransportToUDP();//Will use UDP when connecting to server, default is TCP
wifi.endSendWithNewline(true); // Will end all transmissions with a newline and carriage return ie println.. default is true
wifi.begin();
wifi.connectToAP("RPi", "raspberry");
wifi.connectToServer("192.168.50.1", "1234");
wifi.send(SERVER, "ESP8266 test app started");
}
void loop() {
//Make sure the esp8266 is started..
if (!wifi.isStarted())
wifi.begin();
//Send what you typed in the arduino console to the server
static char buf[20];
if (stringComplete) {
inputString.toCharArray(buf, sizeof buf);
wifi.send(SERVER, buf);
inputString = "";
stringComplete = false;
}
//Send a ping once in a while..
if (millis() > nextPing) {
wifi.send(SERVER, "Ping ping..");
nextPing = millis() + 10000;
}
//Listen for incoming messages and echo back, will wait until a message is received, or max 6000ms..
WifiMessage in = wifi.listenForIncomingMessage(6000);
if (in.hasData) {
if (in.channel == SERVER)
Serial.println("Message from the server:");
else
Serial.println("Message a local client:");
Serial.println(in.message);
//Echo back;
wifi.send(in.channel, "Echo:", false);
wifi.send(in.channel, in.message);
nextPing = millis() + 10000;
}
//If you want do disconnect from the server use:
// wifi.disconnectFromServer();
}
//Listen for serial input from the console
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
}
}
When I execute, the serial monitor shows:
OK ARFa C⸮C⸮j⸮H⸮AT+AWJAP="RPi",#raspberry" WIFI DISCONNECT WIFI
CONNECVED WIFI GOT IP
OK AT+CIFSR
+CIFSR:STAIP,"192.168.50.13"
+CIFQR:STAMAC,"2c:3a:eAT+CIPSTART=4,"TCP","192.0n8.50.1",121l
Link type ERROR
Raspberry Pi's ISC DHCP server:
wlan0: STA 2c:3a:e8:4e:bf:70 RADIUS: starting accounting session
5A3B2C85-000000E9 wlan0: STA 2c:3a:e8:4e:bf:70 WPA: pairwise key
handshake completed (RSN)
I also referred to this SO thread with no luck.
Some assumptions because you have not given the info:
Arduino IDE >=1.85
ESP8266 Community package >=2.41
ESP Module ESP8266-12E with latest AT firmware
if that is the case and these fragments (enclosed by X X) are no typos
+CIFQR:STAMAC,"2c:3a:eXAT+CIPSTART=4,"TCP","192.0XnX8.50.1",121l
this leaves the following points to check
hardware connectors - serial connectors between arduino and esp module
stable power source 3.3V for the ESP module
Sure this is ok - but just in case and as reference for other readers
serial speed - try to increase from 9600 to 57600 or even to 115200 baud
These pieces of code should be in the setup() and not in the loop()
//Make sure the esp8266 is started..
if (!wifi.isStarted())
wifi.begin();
//Send what you typed in the arduino console to the server
static char buf[20];
Code processing
nextPing = millis() + 10000;
at the end of
if (in.hasData) {
might lead to interuptions in the communication
Reason due to the processing of code this might trigger at an unwanted point
(also asked on SE: Electrical Engineering)
In my application, I've set up a STM32F4, SD-Card and USB-CDC (all with CubeMX).
Using a PC, I send commands to the STM32, which then does things on the SD-Card.
The commands are handled using a "communicationBuffer" (implemented by me) which waits for commands over USB, UART, ... and sets a flag, when a \n character was received. The main loop polls for this flag and if it is set, a parser handles the command. So far, so good.
When I send commands via UART, it works fine, and I can get a list of the files on the SD-Card or perform other access via FatFs without a problem.
The problem occurs, when I receive a command via USB-CDC. The parser works as expected, but FatFs claims FR_NO_FILESYSTEM (13) in f_opendir.
Also other FatFs commands fail with this error-code.
After one failed USB-command, commands via UART will also fail. It seems, as if the USB somehow crashes the initialized SD-Card-driver.
Any idea how I can resolve this behaviour? Or a starting point for debugging?
My USB-Implementation:
I'm using CubeMX, and therefore use the prescribed way to initialize the USB-CDC interface:
main() calls MX_USB_DEVICE_Init(void).
In usbd_conf.c I've got:
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
GPIO_InitStruct.Pin = OTG_FS_DM_Pin|OTG_FS_DP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(OTG_FS_IRQn, 7, 1);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */
}
}
and the receive-process is implemented in usbd_cdc_if.c as follows:
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
mRootObject->mUsbBuffer->fillBuffer(Buf, *Len);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
fillBuffer is implemented as follows (I use the same implementation for UART and USB transfer - with separate instances for the respective interfaces. mBuf is an instance-variable of type std::vector<char>):
void commBuf::fillBuffer(uint8_t *buf, size_t len)
{
// Check if last fill has timed out
if(SystemTime::getMS() - lastActionTime > timeout) {
mBuf.clear();
}
lastActionTime = SystemTime::getMS();
// Fill new content
mBuf.insert(mBuf.end(), buf, buf + len);
uint32_t done = 0;
while(!done) {
for(auto i = mBuf.end() - len, ee = mBuf.end(); i != ee; ++i) {
if(*i == '\n') {
newCommand = true;
myCommand = std::string((char*) &mBuf[0],i - mBuf.begin() + 1);
mBuf.erase(mBuf.begin(), mBuf.begin() + (i - mBuf.begin() + 1));
break;
}
}
done = 1;
}
}
I resolved the problem:
In usb_cdc_if.c the #define APP_RX_DATA_SIZE was set to 4 (for some unknown reason). As this is lower than the packet size, incoming packets of a larger size than 4 bytes were overwriting my memory.
It happened, that the following portion of my memory was the FATFS* FatFs[] pointer-list to the initialized FATFS-Filesystem structs.
So subsequently the address to this struct was overwritten, when a command of 5 or more bytes arrived.
Phew, that was a tough one.