Why is this code not reporting the error more quickly?

I am using the attached sketch, along with the attached .cpp and .h files on an Arduino Pro Mini 5-volt version.

The project consists of the Arduino, an LCD, and an Enc28j60 network module.

I am attempting to make some improvements on the .cpp and .h files so that I can be made aware of problems if the network module fails to make a connection.

The sketch works with no problems, and if I do not connect the network module to my router, the LCD screen correctly informs me that there is a problem by displaying the message saved in the ErrorString, i.e., “DHCPError”.

The only problem is that the error will only appear after exactly one minute. I don’t see anything in the .cpp file that would cause this delay. Does anyone have an idea as to what is causing the slowness?

EthernetSketch.ino (868 Bytes)
Listener.cpp (4.2 KB)
Listener.h (579 Bytes)

You will have far greater success if you post your code here, inside code tags, as the Intro post at the top of the forum suggests. That being said, your connect() function contains this

	while ( ether.begin( sizeof Ethernet::buffer, this->mac ) == 0 ) {
		//Serial.println( "Failed to access Ethernet controller" );
		delay( 1000 );
	}

which will loop indefinitely until a connection is made. You will have to look in the ether.begin() code

Read the forum guidelines to see how to properly post code.
Use the IDE autoformat tool (ctrl-t or Tools, Auto format) before posting code in code tags.

The code for the sketch is:

#include <Arduino.h>
#include "Listener.h"
#include "EtherCard.h"
#include "Wire.h"
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);

// Here you setup static IP or DHCP
static bool dhcp = true;
static byte myip[] = { 192,168,10,5 }; // Use for static IP
static byte gwip[] = { 192,168,10,1 }; // Use for static IP
static byte mymac[] = { 0xAB,0xAB,0xAB,0xAB,0x00,0x01 }; //

ListenerClass listener;

void setup()
{
  Serial.begin(115200);
  
  //Turn on LCD ==============
  lcd.init();
  lcd.backlight();
  
  //Ethernet ==============
	Wire.begin();
	if ( dhcp ) listener.connect( mymac );
	if (!dhcp)  listener.connect(myip,gwip,mymac);
  
  if (listener.ErrorString != "ok")
  {
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print(listener.ErrorString);
  }
}

void loop()
{
	
}

The code for Listener.cpp is:

#include "Listener.h"
#include "EtherCard.h"


byte Ethernet::buffer[500];


// Connectes to the enc28j60 network module with a static IP address
void ListenerClass::connect(byte* ip, byte* gw, byte* mac) {
	memcpy( this->ip, ip, 4 );
	memcpy( this->gw, gw, 4 );
	memcpy( this->mac, mac, 6 );

	//Serial.println( "Connecting to ethernet controller" );
	while ( ether.begin( sizeof Ethernet::buffer, this->mac ) == 0 ) {
    //Serial.println( "Failed to access Ethernet controller" );
		delay( 1000 );
	}

	Serial.println( "Connected" );
	ether.staticSetup( this->ip, this->gw );
}


// Connectes to the enc28j60 network module and gets DHCP address
void ListenerClass::connect( byte* mac ) {
	memcpy( this->mac, mac, 6 );

	while ( ether.begin( sizeof Ethernet::buffer, this->mac ) == 0 ) {
		//Serial.println( "Failed to access Ethernet controller" );
		delay( 1000 );
	}
	
	if ( !ether.dhcpSetup() ) {
    ErrorString = "DHCPError";
		Serial.println( F( "DHCP failed" ) );
  }
}


// Returns true if a packet was received
bool ListenerClass::gotPacket() {
	ether.packetLoop( packetLength );

	packetLength = ether.packetReceive();
	return packetLength > 0;
	
}


// Returns true if the last received packet was for the IP of the device
bool ListenerClass::isForMe() {
	for ( uint8_t pos = 0; pos < 4; pos++ ) {
		if ( ether.myip[pos] != *( (byte*) Ethernet::buffer + 0x1e + pos ) ) {
			return false;
		}
	}
	return true;
}


// Returns true if the last received packet was an ICMP ping request
bool ListenerClass::isIcmpPingReq() {
	if ( getProtocol() == 1 ) {
		byte *type = (byte*) Ethernet::buffer + 0x22;
		if ( *type == 8 ) return true;
	}
	return false;
}


// Returns true if the last received packet was an UDP packet
bool ListenerClass::isUdp() {
	if ( getProtocol() == 17 ) {
		return true;
	}
	return false;
}


// Returns true if the last received packet was a TCP SYN/connect packet
bool ListenerClass::isTcpSyn() {

	if ( getProtocol() == 6 ) {
		byte *flags = (byte*) Ethernet::buffer + 0x2f;
		bool syn = *flags & 0b00000010;
		if ( syn ) {
			return true;
		}
	}
	return false;
}


// Returns the protocol number of the last received packet (eg. 6=tcp, 17=udp etc)
uint8_t ListenerClass::getProtocol() {
	byte *protocol = (byte*) Ethernet::buffer + 0x17;
	return *protocol;
}


// Returns the destination port number that the last received packet was destined for
uint16_t ListenerClass::getDstPort() {
	uint8_t port_msb = *( Ethernet::buffer + 0x24 );
	uint8_t port_lsb = *( Ethernet::buffer + 0x25 );
	uint16_t dstPort = (uint16_t)port_msb * 256 + (uint16_t)port_lsb ;
	return dstPort;
}


// Returns a string with the source IP number of the last received packet
void ListenerClass::getSrcIp(char* str) {
	uint8_t b1 = *( (byte*) Ethernet::buffer + 0x1a );
	uint8_t b2 = *( (byte*) Ethernet::buffer + 0x1b );
	uint8_t b3 = *( (byte*) Ethernet::buffer + 0x1c );
	uint8_t b4 = *( (byte*) Ethernet::buffer + 0x1d );
	sprintf( str, "%d.%d.%d.%d", b1, b2, b3, b4 );
}


// Returns a string representation of an IP number in the ether library (byte array representation)
void ListenerClass::getStrIp( char* str, uint8_t* ip ) {
	sprintf( str, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3] ); 
}


// Returns a string with the mac address of the device
void ListenerClass::getMyMac( char* str ) {
	sprintf( str, "%02x:%02x:%02x:%02x:%02x:%02x", ether.mymac[0], ether.mymac[1], ether.mymac[2], ether.mymac[3], ether.mymac[4], ether.mymac[5] );
}


// Prints to serial a hex representation of the last received packet
void ListenerClass::debug() {
	char *var = (byte*) Ethernet::buffer;
	String hexString;
	for ( uint8_t i = 0; i < packetLength; i++ ) { // traverse the var byte by byte
		char* p = (char *) var;

		uint8_t currentByte = *( p + i ); // get byte number i
		char currentByteHex[3];
		sprintf( currentByteHex, "%02X", currentByte ); // convert it to hex
		hexString = hexString + currentByteHex; // and concatenate it into a printable string of all bytes

		if ( i != packetLength - 1 ) hexString = hexString + ",";
	}
	Serial.print( "Packet: " );
	Serial.println( hexString );
}

The code for Listener.h is:

#ifndef _NETWORK_h
#define _NETWORK_h

#include <Arduino.h>

class ListenerClass {

protected:
	byte ip[4];
	byte gw[4];
	byte mac[6];
	int packetLength = 0;
  

public:
	void connect( byte * mac );
	void connect( byte * ip, byte * gw, byte * mac );
	void debug();

	bool gotPacket();
	bool isForMe();
	bool isIcmpPingReq();
	bool isUdp();
	bool isTcpSyn();
	uint8_t getProtocol();
	uint16_t getDstPort();
	void getSrcIp(char* str);
	void getStrIp( char * str, uint8_t * ip );
	void getMyMac( char * str );
	String ErrorString = "ok";
};

#endif

A minute is a suspicious amount of time. I suspect that one of your routines has a timeout. I'll guess that it's connect.

60 seconds does look like a preset timeout time.

I think I found the solution in the EtherCard.h library which the sketch is also using and which can be found here.

In the zip folder, there is a file called EtherCard.h, which contains the following on lines 368-374:

    /**   @brief  Configure network interface with DHCP
    *     @param  hname The hostname to pass to the DHCP server
    *     @param  fromRam Set true to indicate whether hname is in RAM or in program space. Default = false
    *     @return <i>bool</i> True if DHCP successful
    *     @note   Blocks until DHCP complete or timeout after 60 seconds
    */
    static bool dhcpSetup (const char *hname = NULL, bool fromRam =false);

The timeout of 60 seconds can be changed in the file dhcp.cpp, at line 346:

while (dhcpState != DHCP_STATE_BOUND && uint16_t(millis()) - start < 60000) {
        if (isLinkUp()) DhcpStateMachine(packetReceive());
    }

At least it’s clear why one has to wait exactly one minute for the error to appear.

Thanks everyone for your input!

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.