W5500EVB as SMTP Client

This is a sample code for using W5500 EVB as an SMTP (Simple Mail Transfer Protocol) Client.

As the name means, it is a protocol for sending email. In general, to send an email to desire recipient, the sender has to send necessary packets to SMTP Server with information such as  your email address, recipient’s email address, your password for the email account and so on, which would be demonstrated in Code Explanation part.

Code Explanation

Before burning the bin file into your EVB:

      1. Connect W5500 EVB to the router/NAT that you are using, or any other device that allows you to connect to internet
      2. Manually change the Network configuration: 
        • Go to the definition of function set_default() (Should be inside device.c), make sure the subnet mask, local IP address, gateway and DNS are set according to the internet setting you are using with your router/NAT
      3. Rebuild the sample code and burn it into W5500EVB
      4. Turn off the firewall if necessary
      5. Change your PC’s IP address to a static IP if you are not using router


Code Explanation

There are two functions in the main loop: do_dns() and do_smtp(). For do_dns(), it is a function to find the IP address of SMTP server, which is not necessarily need in terms of SMTP or sending email; for do_smtp(), it aims at doing the whole process of email sending, which world be talked about in detail.

int main(void)
{


After boot or reboot W5500EVB, all necessary configurations are initialized.

Systick_Init(72);
  GPIO_Configuration();
  USART1_Init();
  at24c16_init();
  printf("W5500 EVB initialization over.\r\n");
  
  Reset_W5500();
  WIZ_SPI_Init();
  printf("W5500 initialized!\r\n");  
  
  set_default();
  set_network();
  mailmessage();
  printf("Network is ready!\r\n");
  
  while(1)
  {
    do_dns();
    do_smtp();
    if(Mail_Send_OK)
      while(1);  
  }
}

do_dns()

There are three conditions of if statement, which were divided below:

void do_dns(void)
{
  uint8 dns_retry_cnt=0;
  uint8 dns_ok=0;
  

  else if(memcmp(ConfigMsg.dns,"\x00\x00\x00\x00",4))
  {
    

  • If the dns has already done, or dns has not successfully done and has been tried for more than three times
if( (dns_ok==1) ||  (dns_retry_cnt > DNS_RETRY))
  {
    return;
  }

  • If the dns has done but invalid
else
  printf("Invalid DNS server [%d.%d.%d.%d]\r\n",ConfigMsg.dns[0],ConfigMsg.dns[1],ConfigMsg.dns[2],ConfigMsg.dns[3]);
			dns_num++;
}

  • If dns has not done and dns query is ready to be done
    • If the query succeeds, the IP of the SMTP server would be stored
    • Else it would retry sending dns query
  else if(memcmp(ConfigMsg.dns,"\x00\x00\x00\x00",4))
  {
    switch(dns_query(SOCK_DNS,domain_name))
    {
      case DNS_RET_SUCCESS:
        dns_ok=1;
        memcpy(ConfigMsg.rip,DNS_GET_IP,4);
        dns_retry_cnt=0;
        printf("Get [%s]'s IP address [%d.%d.%d.%d] from %d.%d.%d.%d\r\n",domain_name,ConfigMsg.rip[0],ConfigMsg.rip[1],ConfigMsg.rip[2],ConfigMsg.rip[3],ConfigMsg.dns[0],ConfigMsg.dns[1],ConfigMsg.dns[2],ConfigMsg.dns[3]);
        break;
      case DNS_RET_FAIL:
        dns_ok=0;
        dns_retry_cnt++;
        printf("Fail! Please check your network configuration or DNS server.\r\n");
        break;
      default:
        break;
    }
  }


dns_query()

 After declaring necessary variables, W5500EVB starts switch statement:

uint8 dns_query(uint8 s, uint8 * name)
{
  static uint32 dns_wait_time = 0;
  struct dhdr dhp;
  uint8 ip[4];
  uint16 len, port;

  switch(getSn_SR(s))
  {


If the socket is closed, it opens a socket in UDP mode and immediately send a dns query to DNS Server

    case SOCK_CLOSED:
      dns_wait_time = 0;
      socket(s, Sn_MR_UDP, 3000, 0);
      //if(ConfigMsg.debug) printf("dns socket init over\r\n");
      len = dns_makequery(0, name, BUFPUB, MAX_DNS_BUF_SIZE);
      //if(ConfigMsg.debug) printf("dns make query\r\n");
      sendto(s, BUFPUB, len, EXTERN_DNS_SERVERIP, IPPORT_DOMAIN);
      break;   


If the socket is opened as a UDP socket, since a query has already sent, W5500EVB waits for a response from server.
While the datagram packet is successfully parsed, UDP socket closes and it goes on to do_smtp(), else socket closes and opens again to re-send the query.

    case SOCK_UDP:
      if ((len = getSn_RX_RSR(s)) > 0)
      {
        if (len > MAX_DNS_BUF_SIZE) len = MAX_DNS_BUF_SIZE;
        len = recvfrom(s, BUFPUB, len, ip, &port);
        if(parseMSG(&dhp, BUFPUB))
        {
          close(s);
          return DNS_RET_SUCCESS;
        }
        else 
          dns_wait_time = DNS_RESPONSE_TIMEOUT;
      }
      else
      {
        Delay_ms(1000);
        dns_wait_time++;
        //if(ConfigMsg.debug) printf("dns wait time=%d\r\n", dns_wait_time);
      }
      if(dns_wait_time >= DNS_RESPONSE_TIMEOUT)   // 3ÃÊ
      {
        close(s);
        return DNS_RET_FAIL;
      }
      break;
  }
 return DNS_RET_PROGRESS;
}

do_smtp()

void do_smtp(void)
{
  uint8 ch=SOCK_SMTP;
  uint16 len;
  uint16 anyport=5000;
  uint8 Smtp_PORT=25;
  memset(RX_BUF,0,sizeof(RX_BUF));
  switch(getSn_SR(ch))
  {


Socket Initialized

Connect to SMTP server when a TCP socket has just opened.

case SOCK_INIT:
  connect(ch, ConfigMsg.rip ,Smtp_PORT );
  break;


Socket Established

Confirm if the state of socket is “connected”. When W5500 EVB is connected to SMTP server successfully, a datagram packet will be received from server. When it is received, the function send_mail() would be called.

case SOCK_ESTABLISHED:
  if(getSn_IR(ch) & Sn_IR_CON)
  {
    setSn_IR(ch, Sn_IR_CON);
  }
  if ((len = getSn_RX_RSR(ch)) > 0)
  {
    while(!Mail_Send_OK)
    {
      memset(RX_BUF,0,sizeof(RX_BUF));
      len = recv(ch, (uint8*)RX_BUF,len);
      send_mail();
    }
    disconnect(ch);
  }
  break;


Socket to be closed

W5500 turns into this state when it receives a FIN packet from the connected socket. This scenario normally would not happen in this sample code. According to the code, it works similarly as what it does during Socket Established state.

case SOCK_CLOSE_WAIT:
  if ((len = getSn_RX_RSR(ch)) > 0)
  {       
    while(!Mail_Send_OK)
    {
      len = recv(ch, (uint8*)RX_BUF, len);
      send_mail();
    } 
  }
  disconnect(ch);
  break;


Socket Closed

Socket is closed and would be opened as a TCP socket. This is the first state when do_smtp() is run.

    case SOCK_CLOSED:
      socket(ch, Sn_MR_TCP,anyport++, 0x00);
      break;
    default:
    break;
  } 
}
/*
Socket was closed and open again for
 next connection
*/


send_email()

This is a function for sending email to recipient based on SMTP. Messages are sent one by one upon the responses from the SMTP server. These kinds of messages are called “SMTP Commands”, and all these commands have already been set in mailmessage(), waiting to be sent through send_email().

void send_mail(void)
{
  switch(SMTP_STATE)
  {
    case waitfor220:
      if(strstr((const char *)RX_BUF,"220")!=NULL)
      {
        send(SOCK_SMTP,(const uint8 *)hello,strlen(hello));
        SMTP_STATE=waitforHELO250;   
      }
			printf("entered case: waitfor220 \r\n");
      break;
    case waitforHELO250:
      if(strstr((const char *)RX_BUF,hello_reply)!=NULL&&strstr((const char *)RX_BUF,"Mail")==NULL)
      {
        send(SOCK_SMTP,(const uint8 *)AUTH,strlen(AUTH));
        SMTP_STATE=waitforAUTH334;
				printf("entered case: waitforHELO250\r\n");
      }
      break;
    case waitforAUTH334:
      if(strstr((const char *)RX_BUF,AUTH_reply)!=NULL)
      {
        send(SOCK_SMTP,(const uint8 *)base64name_126,strlen(base64name_126));
        SMTP_STATE=waitforuser334;
				printf("entered case: waitforAUTH334\r\n");
				printf("base64name_: %s\r\n",base64name_126);
      }
      break;
    case waitforuser334:
      if(strstr((const char *)RX_BUF,name_reply)!=NULL)
      { 
        send(SOCK_SMTP,(const uint8 *)base64password_126,strlen(base64password_126));
        SMTP_STATE=waitforpassword235;
				printf("entered case: waitforuser334\r\n");
      }
      break;
    case waitforpassword235:
      if(strstr((const char *)RX_BUF,password_reply)!=NULL)
      {
        send(SOCK_SMTP,(const uint8 *)mailfrom,strlen(mailfrom));
        SMTP_STATE=waitforsend250;
				printf("entered case: waitforpassword235\r\n");
      }
      break;
    case waitforsend250:
      if(strstr((const char *)RX_BUF,from_reply)!=NULL&&strstr((const char *)RX_BUF,"queued as")==NULL)
      {
        send(SOCK_SMTP,(const uint8 *)rcptto,strlen(rcptto));
        SMTP_STATE=waitforrcpt250;
				printf("entered case: waitforsend250\r\n");
      }
      break;
    case waitforrcpt250:
      if(strstr((const char *)RX_BUF,to_reply)!=NULL)
      {
        send(SOCK_SMTP,(const uint8 *)data_init,strlen(data_init));
        SMTP_STATE=waitfordate354;
				printf("entered case: waitforrcpt250\r\n");
      }
      break;
    case waitfordate354:
      if(strstr((const char *)RX_BUF,data_reply)!=NULL)
      {
        send(SOCK_SMTP,(const uint8 *)mime,strlen(mime));  
        SMTP_STATE=waitformime250;
				printf("entered case: waitfordate354\r\n");
      }
      break;
    case waitformime250:
      if(strstr((const char *)RX_BUF,mime_reply)!=NULL)
      {
        Mail_Send_OK=1;
        printf("mail send OK\r\n"); 
      }
      break;
    default:
      break;
  }
}



Remarks

  • This example demonstrated only an ordinary way of sending email. It cannot be applied to most of the frequently used SMTP server such as Gmail and Yahoo since most of these SMTP servers require other sets of protocol for security.
  • For more information about SMTP, it is suggested to look into details about mailmessage() in the sample code, or SMTP commands and Base64 on the Internet.