DHCP with ioLibrary not connecting

I’m using Wiznet’s ioLibrary to set up DHCP on my W5500. I’ve got an issue with the DHCP code in the ioLibrary - it doesn’t manage to obtain an IP address, and fails after around 30 seconds.

Since I’m using an ATmega32U4, I can upload Arduino Leonardo code too - I’ve verified that the Arduino Ethernet2 DHCP address printer example works and successfully negotiates an IP address, so it’s apparently not a hardware issue.

My code includes two timers: one that triggers every 1s for the DHCP_time_handler() routine in the ioLibrary DHCP module, and another that triggers every 1s for sensor readings. I’ve verified that both of these timers are actually triggering at these rates. Here it is:

#include <stdbool.h>
#include <avr/io.h>
#include <avr/interrupt.h>

// ioLibrary headers
#include <socket.h>
#include <dhcp.h>

// code for SPI communication with W5500
#include "spi.h"

// DHCP data buffer
uint8_t dhcp_buf[2048];

// socket buffer sizes, in kB
uint8_t wiznet_buf_size[] = {2, 2, 2, 2, 2, 2, 2, 2};

// timer overflow counters
uint16_t dhcp_timer_ovf_count = 0;
uint16_t env_timer_ovf_count = 0;

// whether a new measurements have been made
bool env_measurement_ready = false;

// network info (only MAC used by DHCP)
wiz_NetInfo net_info = { .mac 	= {0x00, 0x08, 0xdc, 0xab, 0xcd, 0xef},
                         .ip 	= {192, 168, 0, 101},
                         .sn 	= {255, 255, 255, 0},
                         .gw 	= {192, 168, 0, 1},
                         .dns = {0, 0, 0, 0},
                         .dhcp = NETINFO_DHCP };

void dhcp_timer_enable(void) {
    // enable first timer
    TIMSK1 |= (1 << TOIE1);

    // set initial value to 0
    TCNT1 = 0x00;

    // start timer with 1/1 scaling
    // this gives overflows every (1/16e6)*65536 = 4.096 ms
    TCCR1B = (1 << CS01);
}

void sensor_timer_enable(void) {
    // enable first timer
    TIMSK3 |= (1 << TOIE3);

    // set initial value to 0
    TCNT3 = 0x00;

    // start timer with 1/1 scaling
    // this gives overflows every (1/16e6)*65536 = 4.096 ms
    TCCR3B = (1 << CS30);
}

// 16-bit TIMER1 for DHCP timing
ISR(TIMER1_OVF_vect) {
    dhcp_timer_ovf_count++;

    if (dhcp_timer_ovf_count >= 244) {
        // trigger every ~1 second
        DHCP_time_handler();

        dhcp_timer_ovf_count = 0;
    }
}

// 16-bit TIMER3 for sensor measurements
ISR(TIMER3_OVF_vect) {
    // triggers every 4.096 ms

    env_timer_ovf_count++;

    // approx 1 second
    if (env_timer_ovf_count >= 244) {
        // SENSOR MEASUREMENTS MADE HERE

        env_measurement_ready = true;

        // reset timer
        env_timer_ovf_count = 0;
    }
}

int main(void) {
    bool run_user_applications = false;

    hardware_init();

    sensor_timer_enable();

    // register chip select and SPI callback functions
    reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);
    reg_wizchip_spi_cbfunc(spi_receive, spi_send);

    // initialise W5500
    wizchip_init(wiznet_buf_size, wiznet_buf_size);
    wizchip_setnetinfo(&net_info);

    // initialise DHCP on socket 0
    dhcp_timer_enable();
    DHCP_init(0, dhcp_buf);

    for (;;) {
        switch (DHCP_run()) {
            case DHCP_IP_ASSIGN:
                usb_send_message("IP assigned\n");
                break;
            case DHCP_IP_CHANGED:
                usb_send_message("IP changed\n");
                break;
            case DHCP_IP_LEASED:
                run_user_applications = true;
                break;
            case DHCP_FAILED:
                usb_send_message("DHCP failed\n");
                break;
            default:
                break;
        }

        if (run_user_applications) {
            if (env_measurement_ready) {
                // SENSOR READINGS PRINTED HERE

                // reset
                env_measurement_ready = false;
            }
        }
    }
}

void hardware_init(void) {
    // disable watchdog if enabled by bootloader/fuses
    MCUSR &= ~(1 << WDRF);
    wdt_disable();

    // disable clock division
    clock_prescale_set(clock_div_1);

    // set W5500 chip select as output
    DDRB |= (1 << PORTB6);

    // disable W5500 chip select
    PORTB |= (1 << PORTB6);

    // initialise SPI
    spi_init();
}

(I’ve stripped out code for USB virtual serial, SPI chip select and send/receive, etc. to keep the code to a minimum. I’ve left in the timer code in case someone spots an issue with those.)

With that code, nothing happens for around 30 seconds until “DHCP failed” is printed to the USB terminal. This repeats every 30 seconds infinitely. What I want to happen is for DHCP_run() to return DHCP_IP_LEASED, which would indicate that the DHCP server has given the unit an address with which it can send and receive packets.

Anyone have any idea what’s wrong, or what to try in order to debug this?

What is going on the network? Do you see broadcast DHCP packets?

I just installed Wireshark and took a look - nothing is going out on the network at all in terms of DHCP packets. In contrast, the Arduino DHCP example sends out the DHCP discover, gets an offer in response, then sends a request and receives an ACK.

Is there some function I need to call before starting DHCP routines in ioLibrary? Or do I have to use a particular socket number?

First, you must capture packets within local network segment, where broadcasts are being routed. Most probably you will not see such packets behind the router.

Second, if you see nothing in this local segment - and Wireshark must report any packet, even malformed, then there’s a problem with programming the network chip - you really forgot to perform something to send data:

  1. you put required data to send into the TX buffer;
  2. you adjust TX buffer pointers appropriately
  3. you perform send command, and only then packet gets out of the chip to the network.

Well, when I run the Arduino code on the same board, I can see DHCP packets from my computer running Wireshark. I think if the Arduino packets are visible, then the ioLibrary ones should be too, no?

Do you know if there are any other required commands to get DHCP to work when using ioLibrary that are not in the above code? Putting data into TX buffers etc. should be abstracted by this library.

Yes, you are correct.

Seems it is. The only I can tell you from looking into source you provided is that function DHCP_init(0, dhcp_buf); should be expected to do the job, but for some reason it does not. Look into the library you use, or look here into hjjeon0608’s post.

Don’t you forget to setSHAR(gWIZNETINFO.mac);?

Argh, I found the problem - it was unrelated to the DHCP code - my usb_send_message function used a global variable as a buffer, defined in the code immediately above the DHCP buffer, and once I moved the USB buffer definition into the function itself as a local variable, the issues went away.

This is before, when I had the problem:

char usb_buf[1024];
uint8_t dhcp_buf[2048];

// DHCP code here...

// USB message with fprintf formatting support
void usb_send_message(const char *format, ...) {
    // list to store variable arguments
    va_list args;

    // start of variable arguments, with format the last required argument
    va_start(args, format);

    // variable argument print, (overflow safe)
    vsnprintf(usb_buf, sizeof(usb_buf), format, args);

    // end of variable arguments
    va_end(args);

    // send USB string
    usb_serial_putstr(usb_buf);
}

and afterwards, with the problem fixed:

uint8_t dhcp_buf[2048];

// DHCP code here...

// USB message with fprintf formatting support
void usb_send_message(const char *format, ...) {
    // USB message buffer
    static char usb_buf[1024];

    // list to store variable arguments
    va_list args;

    // start of variable arguments, with format the last required argument
    va_start(args, format);

    // variable argument print, (overflow safe)
    vsnprintf(usb_buf, sizeof(usb_buf), format, args);

    // end of variable arguments
    va_end(args);

    // send USB string
    usb_serial_putstr(usb_buf);
}

My hypothesis is that whenever I sent a USB message (which was before and during DHCP negotiation code), it affected the DHCP buffer where the UDP packets are assembled. I have to admit though, I don’t understand why the old code caused an overflow. My C knowledge is not great.

@Eugeny, thanks as always for your enthusiastic help!