i was searching the whole internet but couldn’t find a solution for my problem…
I built a UDP broadcast receiver with an ESP32-Wroom and the W5500 using the SPI interface. So far so good, the Receiver works whilst receiving packages up to the size range of 1257 bytes. The langer ones fail to get recognized or are dumped by udp.parsePacket() using the Ethernet2.h library. I can see these packages in Wireshark but the receiver doesnt. The maximum size of the packages is 1464 which is below the threshold I believe. this package could be received via the ESP WLAN feature but that was way to slow for my puropses.
Here’s the part of the code… let me know what else you need to find my mistake…
#include <SPI.h>
#include <Ethernet2.h>
#include <EthernetUdp2.h>
#include "local_config.h"
#define PIN_CS 5
#define UDP_PACKET_SIZE 1500
byte packetBuffer[UDP_PACKET_SIZE];
//The IP address that this arduino has requested to be assigned to.
EthernetUDP Udp;
uint16_t packetSize = 0;
bool state = 0;
const uint16_t localPort = 22815;
/*
* Wiz W5500 reset function. Change this for the specific reset
* sequence required for your particular board or module.
*/
void WizReset() {
Serial.print("Resetting Wiz W5500 Ethernet Board... ");
pinMode(RESET_P, OUTPUT);
digitalWrite(RESET_P, HIGH);
delay(250);
digitalWrite(RESET_P, LOW);
delay(50);
digitalWrite(RESET_P, HIGH);
delay(350);
Serial.println("Done.");
}
/*
* This is a crock. It's here in an effort
* to help people debug hardware problems with
* their W5100 ~ W5500 board setups. It's
* a copy of the Ethernet library enums and
* should, at the very least, be regenerated
* from Ethernet.h automatically before the
* compile starts (that's a TODO item).
*
*/
/*
* Print the result of the hardware status enum
* as a string.
* Ethernet.h currently contains these values:-
*
* enum EthernetHardwareStatus {
* EthernetNoHardware,
* EthernetW5100,
* EthernetW5200,
* EthernetW5500
* };
*
*/
void prt_hwval(uint8_t refval) {
switch (refval) {
case 0:
Serial.println("No hardware detected.");
break;
case 1:
Serial.println("WizNet W5100 detected.");
break;
case 2:
Serial.println("WizNet W5200 detected.");
break;
case 3:
Serial.println("WizNet W5500 detected.");
break;
default:
Serial.println
("UNKNOWN - Update espnow_gw.ino to match Ethernet.h");
}
}
/*
* Print the result of the ethernet connection
* status enum as a string.
* Ethernet.h currently contains these values:-
*
* enum EthernetLinkStatus {
* Unknown,
* LinkON,
* LinkOFF
* };
*
*/
void prt_ethval(uint8_t refval) {
switch (refval) {
case 0:
Serial.println("Uknown status.");
break;
case 1:
Serial.println("Link flagged as UP.");
break;
case 2:
Serial.println("Link flagged as DOWN. Check cable connection.");
break;
default:
Serial.println
("UNKNOWN - Update espnow_gw.ino to match Ethernet.h");
}
}
void sendNTPpacket(const char *address) {
// Set all bytes in the buffer to 0.
memset(packetBuffer, 0, 1500);
// Initialize values needed to form NTP request
// (see http://en.wikipedia.org/wiki/Network_Time_Protocol).
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// All NTP fields have been given values, now
// send a packet requesting a timestamp.
Udp.beginPacket(address, 123); // NTP requests are to port 123
Udp.write(packetBuffer, 1500);
Udp.endPacket();
}
void InitEthernet()
{
Serial.println("\n\tUDP NTP Client v3.0\r\n");
// Use Ethernet.init(pin) to configure the CS pin.
Ethernet.init(PIN_CS); // GPIO5 on the ESP32.
WizReset();
/*
* Network configuration - all except the MAC are optional.
*
* IMPORTANT NOTE - The mass-produced W5500 boards do -not-
* have a built-in MAC address (depite
* comments to the contrary elsewhere). You
* -must- supply a MAC address here.
*/
Serial.println("Starting ETHERNET connection...");
Ethernet.begin(eth_MAC, eth_IP, eth_DNS, eth_GW, eth_MASK);
delay(200);
Serial.print("Ethernet IP is: ");
Serial.println(Ethernet.localIP());
/*
* Sanity checks for W5500 and cable connection.
Serial.print("Checking connection.");
bool rdy_flag = false;
for (uint8_t i = 0; i <= 20; i++) {
if ((Ethernet.hardwareStatus() == EthernetNoHardware)
|| (Ethernet.linkStatus() == LinkOFF)) {
Serial.print(".");
rdy_flag = false;
delay(80);
} else {
rdy_flag = true;
break;
}
}
if (rdy_flag == false) {
Serial.println
("\n\r\tHardware fault, or cable problem... cannot continue.");
Serial.print("Hardware Status: ");
prt_hwval(Ethernet.hardwareStatus());
Serial.print(" Cable Status: ");
prt_ethval(Ethernet.linkStatus());
while (true) {
delay(10); // Halt.
}
} else {
Serial.println(" OK");
}*/
while (!Udp.begin(localPort));
Serial.print("UDP Started! Listening on Port ");
Serial.println(localPort);
}
void setup()
{
Serial.begin(115200);
delay(1000);
InitEthernet();
Serial.println("Startup process finished!\n\n");
}
void loop(){
uint16_t packetSize = Udp.parsePacket();
//If we received something.
if(packetSize){
Serial.println(packetSize);
}
}
Not familiar with the library, found this, I would start looking into the library and putting diagnostic output after each operation or register access to identify what is going on.
I think i’ll have to do that… yet I don’t know how to do it… you mean put a serial output in the library parts that are of interest?
I did some further research and found a very strange behaviour and another issue that I’ll have to attend to and seek help.
Last one is that the controller only recognises broadcast packets that are send to 255.255.255.255 in IP (Multicast!?), dedicated messages to the specific IP do not get through. Are there any options in the code that I have to attend to, to get the direct messaging to work? In the datasheet it seems that this issue shouldn’t be there because i use standard udp.begin() which semas to set the registers correctly.
The other strange thing I noticed is that the controller is “picky” from which tools/programms/games the large messages come from. A packet sent from the Microsoft UDP sender/receiver tool with size of 1472 can be seen in Wireshark AND is recognised by the controller! BUT a package of the same Size commming from the game F1 22 (EA) is not recognised? How’s that possible? I’ll attach the Wireshark readings below…
What is the RX buffer size? If W5500 receives packet, and next packet arrives which has data larger than remaining RX buffer space, then this next packet will be discarded - you will not get it into the buffer because there’s simply no space. How does your code free the RX buffer? I do not see parsePacket() getting data out of there.
I didn’t change the size right now but already expermiented with that - no effect. As far as I know the basic size is 2kb per socket, 8 sockets, 16kb altogether.
The Data itself is processed by udp.read() but only if there is something in it. But all of this is standard Ethernet2 library:
int EthernetUDP::read(unsigned char* buffer, size_t len)
{
if (_remaining > 0)
{
int got;
if (_remaining <= len)
{
// data should fit in the buffer
got = recv(_sock, buffer, _remaining);
}
else
{
// too much data for the buffer,
// grab as much as will fit
got = recv(_sock, buffer, len);
}
if (got > 0)
{
_remaining -= got;
return got;
}
}
// If we get here, there's no data available or recv failed
return -1;
}
int16_t recv(SOCKET s, uint8_t *buf, int16_t len)
{
// Check how much data is available
int16_t ret = w5500.getRXReceivedSize(s);
if ( ret == 0 )
{
// No data available.
uint8_t status = w5500.readSnSR(s);
if ( status == SnSR::LISTEN || status == SnSR::CLOSED || status == SnSR::CLOSE_WAIT )
{
// The remote end has closed its side of the connection, so this is the eof state
ret = 0;
}
else
{
// The connection is still up, but there's no data waiting to be read
ret = -1;
}
}
else if (ret > len)
{
ret = len;
}
if ( ret > 0 )
{
w5500.recv_data_processing(s, buf, ret);
w5500.execCmdSn(s, Sock_RECV);
}
return ret;
}
uint16_t W5500Class::getRXReceivedSize(SOCKET s)
{
uint16_t val=0,val1=0;
do {
val1 = readSnRX_RSR(s);
if (val1 != 0)
val = readSnRX_RSR(s);
}
while (val != val1);
return val;
}
void W5500Class::recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek)
{
uint16_t ptr;
ptr = readSnRX_RD(s);
read_data(s, ptr, data, len);
if (!peek)
{
ptr += len;
writeSnRX_RD(s, ptr);
}
}
void W5500Class::read_data(SOCKET s, volatile uint16_t src, volatile uint8_t *dst, uint16_t len)
{
uint8_t cntl_byte = (0x18+(s<<5));
read((uint16_t)src , cntl_byte, (uint8_t *)dst, len);
}
void W5500Class::execCmdSn(SOCKET s, SockCMD _cmd) {
// Send command to socket
writeSnCR(s, _cmd);
// Wait for command to complete
while (readSnCR(s))
;
}
uint16_t W5500Class::read(uint16_t _addr, uint8_t _cb, uint8_t *_buf, uint16_t _len)
{
SPI.beginTransaction(wiznet_SPI_settings);
setSS();
SPI.transfer(_addr >> 8);
SPI.transfer(_addr & 0xFF);
SPI.transfer(_cb);
for (uint16_t i=0; i<_len; i++){
_buf[i] = SPI.transfer(0);
}
resetSS();
SPI.endTransaction();
return _len;
}
You are right, the nr. of parameters from call and function don’t add up. Yet there’s no warning concerning that. Should I worry?
The peek output just before the if statement is and always stays “0”. Which means the pointer+length is calculated and written in the register, right? Btw. peek is initialized in the header file as “0”.
If it is normal under programming language specification then someone changing default value will make application inoperable.
This was my question - is it always calculated and target register updated? Can you please ensure everything is defined explicitly (e.g. value of this peek).
as far as I kept on reading I found out, that it may be possible that I can’t pickup the contents of the buffer fast enough to ensure to get every packet. Despite the ESP32 ist pretty fast compared to e.g. an AtMega32U4. But what i’m still wondering about is the fact that only these big packages seem to be the problem yet they are sent at the same rate!?
So still struggling with this issue. The rest works really well for my purposes I think, if could sort out that size matter even If I didn’t catch every single packet it would still be great. therefore I was experimenting with the sending frequency of the data… (can be set to 10/20/30/60Hz). Sending only in 10Hz didn’t help either - the big ones are still gone.
“peek” as i said is defined as “uint8_t peek = 0” in the header. I found no other operation that is altering peek whatsoever.
Concering the parameters given for recv_data_processing function I investigated if there’s any other function to call with the right ammount of arguments… couldn’t find anything else but the already shown funtionality.
If what you say is the case (must prove!) then the speed of buffer pointer advancement matters. And this speed may depend on many factors - your algorithm, time for ISR etc. How to prove? Speed up the communication by NOT reading data from the buffer. Just advance pointer, logging that you have got a packet. This way you will not spend time on reading packet data.
This `peek is an argument to the function. Can you explicitly set to pass 0 every time you call function, or remove the condition to always advance the pointer?
Try increasing socket 0 RX buffer size to max 16KB.
I just solved my problem… But not by fixing the Ethernet2 lib. I did a fast test with the ethernet_generic lib an it worked instantly. At 30Hz send rate i receive 28.4Hz which ist far more than i expected!
Thanks for all the effort you put Info this for me!
@everyone else having Problems with receiving bigger packets… Switch to ethernet_generic lib!