Monday, April 25, 2016

YellowJacket Arduino with WiShield Wifi (Part 19)

Using WiShield DNS in YellowJacket Arduino Apps — 
What is DNS?
Ref: http://www.networksolutions.com/ explains: “Domain Name Servers (DNS) are the Internet’s equivalent of a phone book. They maintain a directory of domain names and translate them to Internet Protocol (IP) addresses.
This is necessary because, although domain names are easy for people to remember, computers or machines, access websites based on IP addresses.
Information from all the domain name servers across the Internet are gathered together and housed at the Central Registry. Host companies and Internet Service Providers interact with the Central Registry on a regular schedule to get updated DNS information.
When you type in a web address, e.g., www.somePlace.com, your Internet Service Provider views the DNS associated with the domain name, translates it into a machine friendly IP address (for example 216.168.224.234 is the IP for somePlace.com) and directs your Internet connection to the correct website.
After you register a new domain name or when you update the DNS servers on your domain name, it usually takes about 12-36 hours for the domain name servers world-wide to be updated and able to access the information. This 36-hour period is referred to as propagation.”
As discussed in Part 18 of this series, WiShield Version 1.4 adds DNS and pre-calculated security key functions. The first two examples, will show the differences of sketch code using WPA security and pre-calculated security keys. The sketch was downloaded using the USB/FTDI programming cable to the YellowJacket (YJ57) which communicated with the local network over wifi to request LAX weather data from the remote server. The ip for LAX weather was set to 0.0.0.0 and DNS used the “w1.weather.gov” to identify the ip address which was returned as 65.202.58.9 or 65.202.58.54.
The following example, WiServerDNS_V14_01.ino, shows the complete sketch which uses the original WPA/WEP security. Be sure to edit apps-conf.h to use the necessary APP_WISERVER, APP_UDPAPP, and UIP_DNS code.
As discussed in Part 18 of this series, WiShield Version 1.4 adds DNS and pre-calculated security key functions. The first two examples, will show the differences of sketch code using WPA security and pre-calculated security keys. The sketch was downloaded using the USB/FTDI programming cable to the YellowJacket (YJ57) which communicated with the local network over wifi to request LAX weather data from the remote server.
The following example, WiServerDNS_V14_01.ino, shows the complete sketch which uses the original WPA/WEP security. Be sure to edit apps-conf.h to use the necessary APP_WISERVER, APP_UDPAPP, and UIP_DNS code.
/*
 * WiServerDNS_V14_01
 * 2016-04-21
 * 84Park
 * 
 * A simple sketch that uses WiServer to get the hourly weather data from LAX and prints
 * it via the Serial API
 * 
 *  Compiled with WiShield_V14 library (ref: github.com/hamityanik/WiShield_user_contrib) and Arduino 1.6.5
 *  This code works 2016-04-21 with YJ57 board with/without SD Card Shield
 *  Standard calculated security_data[] inits in 30 seconds
 *  
 */
 
// Requires APP_WISERVER, APP_UDPAPP and UIP_DNS to be defined in apps-conf.h
//  APP_WISERVER - for the WiServer components of the sketch
//  APP_UDPAPP   - for the UDP/DNS components of the sketch
//  UIP_DNS      - for the DNS components of the sketch

#include <WiServer.h>
extern "C" {
  #include "uip.h"
}
// uip.h adds u16_t typedef

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[]    = {10, 0, 1, 57};   // IP address of WiShield
unsigned char gateway_ip[]  = {10, 0, 1, 1};   // router or gateway IP address
unsigned char subnet_mask[] = {255,255,255,0}; // subnet mask for the local network
u16_t dns_ip[]              = {10, 0, 1, 1};   // DNS server addr
char ssid[]                 = {"Network"};   // max 32 bytes
unsigned char security_type = 3;               // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// 2, 3 - WPA/WPA2 Passphrase
// 8 to 63 characters which will be used to generate the 32 byte calculated key
// Expect the g2100 to take 30 seconds to calculate the key from a passphrase
const char security_data[] PROGMEM = {"Network_Passcode"};

// WPA/WPA2 passphrase
//const char security_passphrase[] PROGMEM = {"Network_Passcode"}; // max 64 characters

// WEP 128-bit keys
const uint8_t wep_keys[] PROGMEM = { 
 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // Key 0
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 1
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 2
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Key 3
};

// setup the wireless mode; infrastructure - connect to AP; adhoc - connect to another WiFi device
#define WIRELESS_MODE_INFRA 1
#define WIRELESS_MODE_ADHOC 2
unsigned char wireless_mode = WIRELESS_MODE_INFRA;
unsigned char ssid_len;
unsigned char security_passphrase_len;
// End of wireless configuration parameters ----------------------------------------


// Flag to know when the DNS query has completed
boolean dnsCalledBack = false;

// Function that prints data from the server
void printData(char* data, int len) {
  
  // Print the data returned by the server
  // Note that the data is not null-terminated, may be broken up into smaller packets, and 
  // includes the HTTP header. 
  while (len-- > 0) {
    Serial.print(*(data++));
  }
}


// Hardcoded IP Address for www.weather.gov
// If DNS lookup is succesful this address will be set in uip_dns_callback
// Otherwise use hardcoded working value...
// uint8 ip[] = {140,90,113,200};
// uint8 ip[] = {71,231,196,153};
uint8 ip[] = {23, 67, 242, 42}; // IP Address for w1.weather.gov (2016-02-25)
// Returned:
//    DNS ADDR RECEIVED: 65.202.58.9
//    DNS ADDR RECEIVED: 65.202.58.54

char destURL[]                 = {"w1.weather.gov"};
char destGet[]                 = {"/data/METAR/KLAX.1.txt"};

// A request that gets the latest METAR weather data for LAX
//GETrequest getWeather(ip, 80, "w1.weather.gov", "/data/METAR/KLAX.1.txt");
//GETrequest getWeather(ip, 80, destURL, "/data/METAR/KLAX.1.txt");
GETrequest getWeather(ip, 80, destURL, destGet);

void setup() {

  // Enable Serial output
  Serial.begin(115200);

    Serial.println (" ");
    Serial.println ("=== Initializing WiServer as Client to display WX web page on Serial Monitor ===");

  // Initialize WiServer (we'll pass NULL for the page serving function since we don't need to serve web pages)
  WiServer.init(NULL);

  // Ask WiServer to generate log messages (optional)
  //WiServer.enableVerboseMode(true);

  // Have the processData function called when data is returned by the server
  getWeather.setReturnFunc(printData);

    Serial.println (" ");
    Serial.println ("=== WiServer is running... ===");
    Serial.println (">>> Request response on 10 sec intervals... ");
    Serial.println (" ");


  // Start the DNS query
  uip_dns_conf(dns_ip);
  //uip_dns_query("www.weather.gov");
  uip_dns_query("w1.weather.gov");
  //uip_dns_query("slacklab.org");
}

// Time (in millis) when the data should be retrieved 
long updateTime = 0;

void loop()
{
  // Check if it's time to get an update
  if (true == dnsCalledBack && millis() >= updateTime) {
    // Shutdown DNS
    uip_dns_shutdown();
    // Call WiServer to fetch a page
    getWeather.submit();    
    // Get another update one hour from now
    updateTime += 1000 * 60 * 60;
  }
  
  // Run WiServer
  WiServer.server_task();
 
  delay(10);
}

extern "C" {

   // Process UDP UIP_APPCALL events
   void udpapp_appcall(void)
   {
      if(uip_poll()) {
         uip_dns_run();
      }
      if(uip_newdata()) {
         uip_dns_newdata();
      }
   }

   // DNS resolver will call this function for either succesful or failed DNS lookup
   // uip_dns_query() call (above) starts the chain of events leading to this callback
   void uip_dns_callback(char *name, u16_t *ipaddr)
   {
      dnsCalledBack = true;
      
      if(NULL != ipaddr) {
         // TODO: probably a better way to do this...
         ip[0] = uip_ipaddr1(ipaddr);
         ip[1] = uip_ipaddr2(ipaddr);
         ip[2] = uip_ipaddr3(ipaddr);
         ip[3] = uip_ipaddr4(ipaddr);
         Serial.print("DNS ADDR RECEIVED: "); 
         Serial.print(ip[0], DEC);
         Serial.print(".");
         Serial.print(ip[1], DEC);
         Serial.print(".");
         Serial.print(ip[2], DEC);
         Serial.print(".");
         Serial.println(ip[3], DEC);
      }
      else {
         Serial.println("DNS NULL - FALLBACK TO DEFAULT IP ADDRESS");
      }
   }

   // Not needed for this example but must exist
   void udpapp_init(void)
   {
   }

   // Not needed for this example but must exist
   void dummy_app_appcall(void)
   {
   }
}
The next example , WiServerDNS_V14_03.ino, shows the same sketch modified to use pre-calculated security keys. As can be seen, only the security at the top of the sketch changes and the latter code remains the same. So depending on whether your choice is one type of security or another can be made with some simple cut and paste editing. This example sketch, also shows the output to the Serial Monitor, near the top of the sketch code. The code is large and only 134 bytes of dynamic memory remains on an Arduino 328P device.
/*
 * WiServerDNS_V14_03
 * 2016-04-21
 * 84Park
 *
 * A simple sketch that uses WiServer to get the hourly weather data from LAX and prints
 * it via the Serial API
 *
 * WiServerDNS sketch from V14 Examples sketches
 * Differences from previous V13 sketch code.
 *   function getWeather.setuIP(ipaddr); in void uip_dns_callback(char *name, u16_t *ipaddr)
 *   prints out the ipaddr that is returned.
 *
 *  Compiled with WiShield_V14 library (ref: github.com/hamityanik/WiShield_user_contrib) and Arduino 1.6.5
 *  This code works 2016-04-21 with YJ57 board with/without SD Card Shield
 *  Pre-calculated security_data[] inits in 6 seconds
 *
 * Example Output to Serial Monitor
 * 
=== Initializing WiServer as Client to display WX web page on Serial Monitor ===
WiServer init called

=== WiServer is running... ===
>>> Request response on 10 sec intervals...

Servicing DNS query: udpapp_appcall() -> uip_dns_run()
Servicing DNS query: udpapp_appcall() -> uip_dns_run()
Servicing DNS query: udpapp_appcall() -> uip_dns_newdata()
DNS ADDR RECEIVED: 65.202.58.54
Got connection for w1.weather.gov
Connected to w1.weather.gov
TX 87 bytes
0 - 86 of 87
RX 0 bytes from w1.weather.gov
RX 387 bytes from w1.weather.gov
HTTP/1.0 200 OK
Server: Apache/2.2.15 (Red Hat)
X-NIDS-ServerID: www11.md
Last-Modified: Fri, 22 Apr 2016 09:57:01 GMT
Accept-Ranges: bytes
Content-Length: 117
Content-Type: text/plain; charset=iso-8859-1
Date: Fri, 22 Apr 2016 10:21:14 GMT
Connection: close

000
SAUS80 KWBC 221000 RRA
MTRLAX
METAR KLAX 220953Z 00000KT 10SM FEW015 SCT250 16/13 A2990 RMK AO2 SLP122
T01610128
Ended connection with w1.weather.gov
 *
 */

// Requires APP_WISERVER, APP_UDPAPP and UIP_DNS to be defined in apps-conf.h
//  APP_WISERVER - for the WiServer components of the sketch
//  APP_UDPAPP   - for the UDP/DNS components of the sketch
//  UIP_DNS      - for the DNS components of the sketch

#include <WiServer.h>
extern "C" {
#include "uip.h"
}
// uip.h adds u16_t typedef

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[]    = {10, 0, 1, 57};   // IP address of WiShield
unsigned char gateway_ip[]  = {10, 0, 1, 1};   // router or gateway IP address
unsigned char subnet_mask[] = {255, 255, 255, 0}; // subnet mask for the local network
u16_t dns_ip[]              = {10, 0, 1, 1};   // DNS server addr
char ssid[]                 = {"Network"};   // max 32 bytes
unsigned char security_type = 5;  // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2; 4 - WPA Precalc; 5 - WPA2 Precalc

// Depending on your security_type, uncomment the appropriate type of security_data
// 0 - None (open)
// const char security_data[] PROGMEM = {};

// 1 - WEP
// UIP_WEP_KEY_LEN. 5 bytes for 64-bit key, 13 bytes for 128-bit key
// Only supply the appropriate key, do not specify 4 keys and then try to specify which to use
//const char security_data[] PROGMEM = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, };

// 2, 3 - WPA/WPA2 Passphrase
// 8 to 63 characters which will be used to generate the 32 byte calculated key
// Expect the g2100 to take 30 seconds to calculate the key from a passphrase
// const char security_data[] PROGMEM = {"Network_Passcode"};

// 4, 5 - WPA/WPA2 Precalc
// The 32 byte precalculate WPA/WPA2 key. This can be calculated in advance to save boot time
//const prog_char security_data[] PROGMEM = {
//    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
//    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x1f,
//};
// Calc the Precalc:
// http://jorisvr.nl/wpapsk.html
// The derived key will appear in the form as a sequence of 64 hexadecimal digits.
/*0599967ce21000026c2cf439ea561c3d37abbf48085738cbe9b9a9ca093a6adb*/
const char security_data[] PROGMEM = {
  0x05, 0x99, 0x96, 0x7c, 0xe2, 0x10, 0x00, 0x02, 0x6c, 0x2c, 0xf4, 0x39, 0xea, 0x56, 0x1c, 0x3d,
  0x37, 0xab, 0xbf, 0x48, 0x08, 0x57, 0x38, 0xcb, 0xe9, 0xb9, 0xa9, 0xca, 0x09, 0x3a, 0x6a, 0xdb,
};

// setup the wireless mode; infrastructure - connect to AP; adhoc - connect to another WiFi device
//#define WIRELESS_MODE_INFRA  1
//#define WIRELESS_MODE_ADHOC 2
unsigned char wireless_mode = WIRELESS_MODE_INFRA;
// End of wireless configuration parameters ----------------------------------------

// Flag to know when the DNS query has completed
boolean dnsCalledBack = false;

// Function that prints data from the server
void printData(char* data, int len) {

  // Print the data returned by the server
  // Note that the data is not null-terminated, may be broken up into smaller packets, and
  // includes the HTTP header.
  while (len-- > 0) {
    Serial.print(*(data++));
  }
}

// Hardcoded IP Address for www.weather.gov
// If DNS lookup is succesful this address will be set in uip_dns_callback
// Otherwise use hardcoded working value...
// uint8 ip[] = {23, 67, 242, 42}; // IP Address for w1.weather.gov (2016-02-25)
uint8 ip[] = {0, 0, 0, 0}; // IP Address 0.0.0.0
// Returned:
//    DNS ADDR RECEIVED: 65.202.58.9
//    DNS ADDR RECEIVED: 65.202.58.54

char destURL[]                 = {"w1.weather.gov"};
char destGet[]                 = {"/data/METAR/KLAX.1.txt"};

// A request that gets the latest METAR weather data for LAX
//GETrequest getWeather(ip, 80, "w1.weather.gov", "/data/METAR/KLAX.1.txt");
//GETrequest getWeather(ip, 80, destURL, "/data/METAR/KLAX.1.txt");
GETrequest getWeather(ip, 80, destURL, destGet);

void setup() {

  // Enable Serial output
  Serial.begin(115200);

  Serial.println ("\n=== Initializing WiServer as Client to display WX web page on Serial Monitor ===");

  // Initialize WiServer (we'll pass NULL for the page serving function since we don't need to serve web pages)
  WiServer.init(NULL);

  // Ask WiServer to generate log messages (optional)
  //WiServer.enableVerboseMode(true);
  // not available in V1.4

  // Have the processData function called when data is returned by the server
  getWeather.setReturnFunc(printData);

  Serial.println ("\n=== WiServer is running... ===");
  Serial.println (">>> Request response on 10 sec intervals... \n");

  // Start the DNS query
  uip_dns_conf(dns_ip);
  //uip_dns_query("www.weather.gov");
  //uip_dns_query("slacklab.org");
  //uip_dns_query("w1.weather.gov");
  uip_dns_query(destURL);
}

// Time (in millis) when the data should be retrieved
long updateTime = 0;

void loop()
{
  // Check if it's time to get an update
  if (true == dnsCalledBack && millis() >= updateTime) {
    // Shutdown DNS
    uip_dns_shutdown();
    // Call WiServer to fetch a page
    getWeather.submit();
    // Get another update one hour from now
    updateTime += 1000 * 60 * 60; // 10 sec ; // 60 * 60; // hour
  }

  // Run WiServer
  WiServer.server_task();

  delay(10);
}

extern "C" {

  // Process UDP UIP_APPCALL events
  void udpapp_appcall(void)
  {
    if (uip_poll()) {
      Serial.println("Servicing DNS query: udpapp_appcall() -> uip_dns_run()");
      uip_dns_run();
    }
    if (uip_newdata()) {
      Serial.println("Servicing DNS query: udpapp_appcall() -> uip_dns_newdata()");
      uip_dns_newdata();
    }
  }

  // DNS resolver will call this function for either succesful or failed DNS lookup
  // uip_dns_query() call (above) starts the chain of events leading to this callback
  void uip_dns_callback(char *name, u16_t *ipaddr)
  {
    dnsCalledBack = true;

    if (NULL != ipaddr) {
      // better way: set the fetched ip
      getWeather.setuIP(ipaddr);

      /*
       // TODO: probably a better way to do this...
       ip[0] = uip_ipaddr1(ipaddr);
       ip[1] = uip_ipaddr2(ipaddr);
       ip[2] = uip_ipaddr3(ipaddr);
       ip[3] = uip_ipaddr4(ipaddr);
       Serial.print("DNS ADDR RECEIVED: ");
       Serial.print(ip[0], DEC);
       Serial.print(".");
       Serial.print(ip[1], DEC);
       Serial.print(".");
       Serial.print(ip[2], DEC);
       Serial.print(".");
       Serial.println(ip[3], DEC);
      */
    }
    else {
      Serial.println("DNS NULL - FALLBACK TO DEFAULT IP ADDRESS");
    }
  }

  // Not needed for this example but must exist
  void udpapp_init(void)
  {
  }

  // Not needed for this example but must exist
  void dummy_app_appcall(void)
  {
  }
}

(Apr 25, 2016)

No comments:

Post a Comment