How to Integrate ENC28J60 with lwIP Using an STM32 Microcontroller

Software and Hardware Requirements

To follow this tutorial, you should have completed this section.

What is DNS?

DNS stands for Domain Name System. It is an application layer protocol that allows a domain name such as www.google.com to be resolved to an Internet Protocol (IP) address. This is very important as TCP/IP protocols use IP addresses for communication and not domain names.

Domain names were invented so that humans would not have to memorise IP addresses of sites they have visited or are interested in. For instance, is much easier for a human to remember a domain name like www.google.com than an IP address like 192.168.8.50.

When to use it?

Suppose that you are developing an application in any of the commonly used programming languages: C, C++, C#, Python etc, and you wish to open a TCP socket so that you can start data transmission. Most often you will notice that the function prototypes take IP address as a parameter, in which case you will need to obtain an IP address and pass it to the relevant function.

In the case of lwIP they also decided to require an IP address as an input to some "application layer protocols" functions. For instance MQTT, HTTP etc all require the IP address of the remote server.

NB: Please note that IP addresses of some domain names change continously, therefore it is better to use DNS than resolve the domain name manually and hardcode it in your code. The system will fail down the line should the IP address change.

DNS: Domain Name resolution on Linux

It is always better to use multiple platforms to confirm results that we get from different systems. In this case I used Linux to get an IP address so that we can compare it with the IP address obtained from lwIP. The command to resolve a domain name to an IP address in Linux is:


nestor@nestor:~$ host test.mosquitto.org
test.mosquitto.org has address 54.36.178.49
test.mosquitto.org has IPv6 address 2001:41d0:303:4831::1
Domain name resolution on Linux

DNS: Domain Name resolution on lwIP

The below are needed for the server to resolve a domain name to an IP address.

One function worth noting is :


void dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, void *callback_arg);

The below code snippets show the struct that I defined to be used with DNS functions.


struct mqttBrokerDetails {
    const char * name;
    union {
        ip_addr_t ip;
        uint8_t bytes[4];
    };
    const char * user;
    const char * password;
};
Struct containing the name and IP for MQTT usage

DNS: How to initialize and use DNS on lwIP


dns_init();

// Set the server
ip_addr_t dnsServer;
IP4_ADDR(&dnsServer, 8, 8, 8, 8);
dns_setserver(0, &dnsServer);

ip_addr_t mosquitoIp;
dns_gethostbyname(mqttBroker.name, &mosquitoIp, ipObtained, NULL);
Initialization of DNS module

DNS: Callback method upon successful conversion from Domain Name to IP

Once all the code has been executed and the stack resolves the Domain Name to an IP address, the below function will be called:


static void ipObtained(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
    if(strcmp(mqttBroker.name, name) == 0) {
        mqttBroker.ip = *ipaddr;
    }
}
Callback for DNS Client success

Confirmation from a screenshot from STM32CubeIDE

DNS Client IP Screenshot
DNS Client IP Screenshot

Conclusion

In this article we have explained the purpose of DNS Client, when to use it, how to use global functions provided by lwIP to obtain an IP address from a domain name. The source code for the project can be found here - checkout the dns tag.

NB: The union in Figure 2 was added to expose individual bytes. lwIP stores IP addresses as a uint32_t value.