Webserver only w/o any other functions, single thread. Running on STM32F013+W5500

Dependencies:   NTPClient W5500Interface Watchdog device_configuration eeprom_flash mbed-rpc-nucleo mbed-rtos mbed

Fork of F103-Serial-to-Ethernet by Chau Vo

Files at this revision

API Documentation at this revision

Comitter:
olympux
Date:
Fri Sep 26 20:07:34 2014 +0000
Parent:
8:64848959adb9
Child:
10:4cd965d79de0
Commit message:
Added & tested NTP

Changed in this revision

NTPClient.lib Show annotated file Show diff for this revision Revisions of this file
eeprom.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
readme.txt Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NTPClient.lib	Fri Sep 26 20:07:34 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/ban4jp/code/NTPClient/#c70ed0bfab2e
--- a/eeprom.lib	Sun Sep 21 20:25:35 2014 +0000
+++ b/eeprom.lib	Fri Sep 26 20:07:34 2014 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/olympux/code/eeprom/#5da14988d0e5
+http://mbed.org/users/olympux/code/eeprom/#4bf7c01c5a48
--- a/main.cpp	Sun Sep 21 20:25:35 2014 +0000
+++ b/main.cpp	Fri Sep 26 20:07:34 2014 +0000
@@ -6,6 +6,7 @@
 #include "mbed.h"
 //#include "eeprom.h"
 #include "EthernetInterface.h"
+#include "NTPClient.h"
 #include "rtos.h"
 
 
@@ -15,22 +16,38 @@
 #define ST_NUCLEO // hardware pin mapping
 
 #ifdef ST_NUCLEO
-// ST Nucleo
+// Ethernet
 SPI spi(PA_7, PA_6, PA_5); // mosi, miso, sclk
 EthernetInterface eth(&spi, PC_8, PC_9); // spi, cs, reset
 #endif
 
+// Serial
 Serial uart(USBTX,USBRX);
 
-// Variables' number
-#define NumbOfVar         ((uint8_t)0x0F) // REMEMBER: update this variable in eeprom.h too
+// Digital inputs
+// Digital outputs
+// Analog inputs
+// Analog outputs
+//AnalogOut ano0(p18);
+
+
+// eeprom
+#define NumbOfVar         ((uint8_t)0x80) // REMEMBER: update this variable in eeprom.h too
+#define IP_ADDRESS_POS         0
+#define IP_SUBNET_POS          4
+#define IP_GATEWAY_POS         8
+#define TCP_SERVER_PORT_POS    12
+#define UDP_SERVER_PORT_POS    13
+#define FIRST_RUN_FLAG_POS     14
+#define MAC_ADDRESS_POS        15
 // Virtual address defined by the user: 0xFFFF value is prohibited
 uint16_t VirtAddVarTab[NumbOfVar] = {0x1212, 0x1313, 0x1414, 0x1515, // IP_Addr
                                      0x2212, 0x2313, 0x2414, 0x2515, // IP_Subnet
                                      0x3212, 0x3313, 0x3414, 0x3515, // IP_Gateway
                                      0x4212, // TCP server port, not used
                                      0x5212, // UDP server port, not used
-                                     0x8888  // 1st run? 
+                                     0x8888,  // 1st run? 
+                                     0x6212, 0x6313, 0x6414 // MAC
                                      };
 extern "C" uint16_t EE_Init(void);
 extern "C" uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data);
@@ -49,22 +66,56 @@
 #define DEFAULT_IP_ADDRESS      "192.168.0.249"
 #define DEFAULT_IP_SUBNET       "255.255.255.0"
 #define DEFAULT_IP_GATEWAY      "192.168.0.1"
+#define DEFAULT_MAC3            0x00
+#define DEFAULT_MAC4            0x00
+#define DEFAULT_MAC5            0x01
 
 #define TCP_SERVER_WAIT_CLIENT_TIMEOUT     200
 #define TCP_SERVER_RECEIVE_TIMEOUT         3000
 #define UDP_SERVER_RECEIVE_TIMEOUT         200
 
 // for static IP setting
-char * IP_Addr;
+uint8_t mac[6];
+char * IP_Addr; // pointers to 
 char * IP_Subnet;
 char * IP_Gateway;
-char   ip_addr[16], ip_subnet[16], ip_gateway[16]; // loaded from eeprom
 
-uint16_t tcp_server_port = 10000; // fixed
-uint16_t udp_server_port = 11000; // fixed
+uint16_t u16ip_addr[4], u16ip_subnet[4], u16ip_gateway[4]; // directly loaded from eeprom
+uint16_t first_run = 0;  // first run flag
+uint16_t u16mac_addr[3]; // loaded from eeprom
+char   ip_addr[16], ip_subnet[16], ip_gateway[16]; // chars, converted from u16ip_xxx
+
+const uint16_t tcp_server_port = 10000; // fixed
+const uint16_t udp_server_port = 11000; // fixed
 
 char buffer[256]; // socket buffer
 
+NTPClient ntp;
+
+// Commands
+#define DEVICE_ID                   "NNIO"
+#define DISCOVERY_COMMAND           "NNIODS"
+#define TCP_SERVER_PORT_COMAMND     "NNIOTP"
+#define UDP_SERVER_PORT_COMAMND     "NNIOUP"
+#define QUERY_STATUS_COMMAND        (uint8_t)'Q'
+
+
+// Positions
+#define RECEIVING_PROTOCOL_OP_POS           4
+#define RECEIVING_PROTOCOL_EN_DO_POS        RECEIVING_PROTOCOL_OP_POS + 0
+#define RECEIVING_PROTOCOL_EN_A0O_POS       RECEIVING_PROTOCOL_OP_POS + 1
+#define RECEIVING_PROTOCOL_EN_A1O_POS       RECEIVING_PROTOCOL_OP_POS + 2
+#define RECEIVING_PROTOCOL_EN_UART_POS      RECEIVING_PROTOCOL_OP_POS + 3
+#define RECEIVING_PROTOCOL_COMMAND_POS      RECEIVING_PROTOCOL_OP_POS + 4
+#define RECEIVING_PROTOCOL_ENABLE_OUTPUT    (uint8_t)'O'
+
+
+#define RECEIVING_PROTOCOL_POS_IP           9
+#define RECEIVING_PROTOCOL_POS_DO           13
+#define RECEIVING_PROTOCOL_POS_A0O          21
+#define RECEIVING_PROTOCOL_POS_A01          23
+#define RECEIVING_PROTOCOL_POS_UART         25
+
 
 
 /*
@@ -102,17 +153,11 @@
 * Ethernet init
 */
 int ethernet_init(void) {
-    uint8_t mac[6];
-        
-    mbed_mac_address((char *)mac);     // using the MAC address in LPC11U24 or LPC1178
-    mac[0] = 0x00; mac[1] = 0x08; mac[2] = 0xDC; mac[3] = 0x00; mac[4] = 0x00; mac[5] = 0x00; 
-    
-    
     //printf("Start\n");
     int ret = eth.init(mac, IP_Addr, IP_Subnet, IP_Gateway); // static
 
     if (!ret) {
-        //uart.printf("Initialized, MAC: %s\n", eth.getMACAddress());
+        uart.printf("Initialized, MAC: %s\n", eth.getMACAddress());
     } else {
         uart.printf("Error eth.init() - ret = %d\n", ret);
         return -1;
@@ -134,72 +179,98 @@
 * EEPROM functions
 */
 void write_eeprom(char *buffer) {
+    // Write network configuration
+    // 4-byte IP address + 4-byte subnet + 4-byte gateway + 3-byte MAC
+    
     // Unlock the Flash Program Erase controller */
     FLASH_Unlock();
     // EEPROM Init
     EE_Init();
     // IP address
-    EE_WriteVariable(VirtAddVarTab[0], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[1], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[2], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[3], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_ADDRESS_POS+0], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_ADDRESS_POS+1], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_ADDRESS_POS+2], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_ADDRESS_POS+3], *buffer++);
+    
     // IP subnet
-    EE_WriteVariable(VirtAddVarTab[4], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[5], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[6], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[7], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_SUBNET_POS+0], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_SUBNET_POS+1], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_SUBNET_POS+2], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_SUBNET_POS+3], *buffer++);
+    
     // IP gateway
-    EE_WriteVariable(VirtAddVarTab[8], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[9], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[10], *buffer++);
-    EE_WriteVariable(VirtAddVarTab[11], *buffer++);
-    //// TCP server port
-    //EE_WriteVariable(VirtAddVarTab[12], *buffer++);
-    //// UDP server port
-    //EE_WriteVariable(VirtAddVarTab[13], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_GATEWAY_POS+0], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_GATEWAY_POS+1], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_GATEWAY_POS+2], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[IP_GATEWAY_POS+3], *buffer++);
+    
+    //// TCP server port, not used
+    //EE_WriteVariable(VirtAddVarTab[TCP_SERVER_PORT_POS], *buffer++);
+    //// UDP server port, not used
+    //EE_WriteVariable(VirtAddVarTab[UDP_SERVER_PORT_POS], *buffer++);
     
     // erase first_run flag
-    EE_WriteVariable(VirtAddVarTab[14], 0xA5A5);
+    EE_WriteVariable(VirtAddVarTab[FIRST_RUN_FLAG_POS], 0xA5A5);
+    
+    // MAC address
+    EE_WriteVariable(VirtAddVarTab[MAC_ADDRESS_POS+0], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[MAC_ADDRESS_POS+1], *buffer++);
+    EE_WriteVariable(VirtAddVarTab[MAC_ADDRESS_POS+2], *buffer++);
     FLASH_Lock();
 }
 
 
 void load_eeprom(void) {
-    uint16_t u16ip_addr[4], u16ip_subnet[4], u16ip_gateway[4];
-    uint16_t first_run = 0;
+    mbed_mac_address((char *)mac);
     
     EE_Init();
     
     // check if 1st run
-    EE_ReadVariable(VirtAddVarTab[14], &first_run);
+    EE_ReadVariable(VirtAddVarTab[FIRST_RUN_FLAG_POS], &first_run);
     // if not first run, load network config
     if (first_run == 0xA5A5) {
         // IP address
-        EE_ReadVariable(VirtAddVarTab[0], &u16ip_addr[0]);
-        EE_ReadVariable(VirtAddVarTab[1], &u16ip_addr[1]);
-        EE_ReadVariable(VirtAddVarTab[2], &u16ip_addr[2]);
-        EE_ReadVariable(VirtAddVarTab[3], &u16ip_addr[3]);
+        EE_ReadVariable(VirtAddVarTab[IP_ADDRESS_POS+0], &u16ip_addr[0]);
+        EE_ReadVariable(VirtAddVarTab[IP_ADDRESS_POS+1], &u16ip_addr[1]);
+        EE_ReadVariable(VirtAddVarTab[IP_ADDRESS_POS+2], &u16ip_addr[2]);
+        EE_ReadVariable(VirtAddVarTab[IP_ADDRESS_POS+3], &u16ip_addr[3]);
+        
         // IP subnet
-        EE_ReadVariable(VirtAddVarTab[4], &u16ip_subnet[0]);
-        EE_ReadVariable(VirtAddVarTab[5], &u16ip_subnet[1]);
-        EE_ReadVariable(VirtAddVarTab[6], &u16ip_subnet[2]);
-        EE_ReadVariable(VirtAddVarTab[7], &u16ip_subnet[3]);
+        EE_ReadVariable(VirtAddVarTab[IP_SUBNET_POS+0], &u16ip_subnet[0]);
+        EE_ReadVariable(VirtAddVarTab[IP_SUBNET_POS+1], &u16ip_subnet[1]);
+        EE_ReadVariable(VirtAddVarTab[IP_SUBNET_POS+2], &u16ip_subnet[2]);
+        EE_ReadVariable(VirtAddVarTab[IP_SUBNET_POS+3], &u16ip_subnet[3]);
+        
         // IP gateway
-        EE_ReadVariable(VirtAddVarTab[8], &u16ip_gateway[0]);
-        EE_ReadVariable(VirtAddVarTab[9], &u16ip_gateway[1]);
-        EE_ReadVariable(VirtAddVarTab[10], &u16ip_gateway[2]);
-        EE_ReadVariable(VirtAddVarTab[11], &u16ip_gateway[3]);
+        EE_ReadVariable(VirtAddVarTab[IP_GATEWAY_POS+0], &u16ip_gateway[0]);
+        EE_ReadVariable(VirtAddVarTab[IP_GATEWAY_POS+1], &u16ip_gateway[1]);
+        EE_ReadVariable(VirtAddVarTab[IP_GATEWAY_POS+2], &u16ip_gateway[2]);
+        EE_ReadVariable(VirtAddVarTab[IP_GATEWAY_POS+3], &u16ip_gateway[3]);
+        
         //// TCP server port
-        //EE_ReadVariable(VirtAddVarTab[12], &tcp_server_port);
+        //EE_ReadVariable(VirtAddVarTab[TCP_SERVER_PORT_POS], &tcp_server_port);
         //// UDP server port
-        //EE_ReadVariable(VirtAddVarTab[13], &udp_server_port);
+        //EE_ReadVariable(VirtAddVarTab[UDP_SERVER_PORT_POS], &udp_server_port);
+        
+        // First run flag, already read above
+
+        // MAC address
+        EE_ReadVariable(VirtAddVarTab[MAC_ADDRESS_POS+0], &u16mac_addr[0]);
+        EE_ReadVariable(VirtAddVarTab[MAC_ADDRESS_POS+1], &u16mac_addr[1]);
+        EE_ReadVariable(VirtAddVarTab[MAC_ADDRESS_POS+2], &u16mac_addr[2]);
+        mac[3] = (uint8_t)(u16mac_addr[0] & 0x00FF);
+        mac[4] = (uint8_t)(u16mac_addr[1] & 0x00FF);
+        mac[5] = (uint8_t)(u16mac_addr[2] & 0x00FF);
         //FLASH_Lock();
+        
         sprintf(ip_addr, "%d.%d.%d.%d", (uint8_t)u16ip_addr[0], (uint8_t)u16ip_addr[1], (uint8_t)u16ip_addr[2], (uint8_t)u16ip_addr[3]);
         sprintf(ip_subnet, "%d.%d.%d.%d", (uint8_t)u16ip_subnet[0], (uint8_t)u16ip_subnet[1], (uint8_t)u16ip_subnet[2], (uint8_t)u16ip_subnet[3]);
         sprintf(ip_gateway, "%d.%d.%d.%d", (uint8_t)u16ip_gateway[0], (uint8_t)u16ip_gateway[1], (uint8_t)u16ip_gateway[2], (uint8_t)u16ip_gateway[3]);
     }
     // if 1st run, use default addresses
     else {
+        mac[0] = 0x00; mac[1] = 0x08; mac[2] = 0xDC;
+        mac[3] = DEFAULT_MAC3; mac[4] = DEFAULT_MAC4; mac[5] = DEFAULT_MAC5; 
         sprintf(ip_addr, DEFAULT_IP_ADDRESS);
         sprintf(ip_subnet, DEFAULT_IP_SUBNET);
         sprintf(ip_gateway, DEFAULT_IP_GATEWAY);
@@ -216,12 +287,12 @@
 int main()
 {
     message_t message;
-    int ret;
+    int n, ret;
     
     /*
-    * configure
+    * Configure
     */
-    uart.baud(115200);
+    //uart.baud(115200);
     
   
     /*
@@ -271,6 +342,23 @@
 
     // Network processor
     while (true) {
+#ifdef NTP
+        printf("Trying to update time...\r\n");
+        if (ntp.setTime("0.pool.ntp.org") == 0)
+        {
+          printf("Set time successfully\r\n");
+          time_t ctTime;
+          ctTime = time(NULL);
+          printf("Time is set to (UTC): %s\r\n", ctime(&ctTime));
+        }
+        else
+        {
+          printf("Error\r\n");
+        }
+#endif
+
+        
+// FOR INTERFACING
 #ifdef TCP_SERVER
         // no tcp client connected
         if (!tcp_client.is_connected())
@@ -285,73 +373,117 @@
                 // loop waiting and receiving data within timeout
                 tcp_client.set_blocking(false, TCP_SERVER_RECEIVE_TIMEOUT); // Timeout after x seconds
                 while (true) {
-                    int n = tcp_client.receive(buffer, sizeof(buffer));
+                    n = tcp_client.receive(buffer, sizeof(buffer));
                     if (n <= 0) break;
                     
-                    // got data, process it
-                    // send to uart
-                    buffer[n] = '\0';
-                    message.len = n;
-                    message.msg = buffer;
-                    uart_queue.put(&message);
+                    // got some data, test it
+                    //// send to uart
+                    //buffer[n] = '\0';
+                    //message.len = n;
+                    //message.msg = buffer;
+                    //uart_queue.put(&message);
+                    //// echo to tcp client
+                    //tcp_client.send_all(buffer, n);
+                    //if (n <= 0) break;
                     
-                    // echo to tcp client
-                    tcp_client.send_all(buffer, n);
-                    if (n <= 0) break;
+                    // process received data
+                    switch (n) {
+                        // length 58-bytes, Receiving protocol
+                        case 58: {
+                            // check device id
+                            char* id = strstr(buffer, DEVICE_ID);
+                            if (id == NULL)
+                                break;
+                            else if ((id - buffer) > 0)
+                                break;
+                            
+                            
+                            // firstly, update outputs if required
+                            // digital outputs
+                            if (((uint8_t)buffer[RECEIVING_PROTOCOL_EN_DO_POS] - RECEIVING_PROTOCOL_ENABLE_OUTPUT) == 0) {
+                            }
+                            // analog output 0
+                            if (((uint8_t)buffer[RECEIVING_PROTOCOL_EN_A0O_POS] - RECEIVING_PROTOCOL_ENABLE_OUTPUT) == 0) {
+                            }
+                            // analog output 0
+                            if (((uint8_t)buffer[RECEIVING_PROTOCOL_EN_A1O_POS] - RECEIVING_PROTOCOL_ENABLE_OUTPUT) == 0) {
+                            }
+                            // UART
+                            if (((uint8_t)buffer[RECEIVING_PROTOCOL_EN_UART_POS] - RECEIVING_PROTOCOL_ENABLE_OUTPUT) == 0) {
+                            }
+                            
+                            // then, check query status command and sending protocol if required
+                            if (((uint8_t)buffer[RECEIVING_PROTOCOL_COMMAND_POS] - QUERY_STATUS_COMMAND) == 0) {
+                                // sending protocol
+                            }
+                            
+                            break;
+                        }
+                        default:
+                            break;
+                    }
+                    
                 } // end loop if no data received within timeout
                 tcp_client.close();
             } // if client connected
         } // if no client connected
 #endif
     
-
-#ifdef UDP_SERVER // used for setting configuration
+    
+    
+// ONLY FOR CONFIGRATION
+#ifdef UDP_SERVER
         // wait for udp packet within timeout
-        int n = udp_server.receiveFrom(ep_udp_client, buffer, sizeof(buffer));
-        if (n < 0) continue;
+        n = udp_server.receiveFrom(ep_udp_client, buffer, sizeof(buffer));
+        if (n <= 0) continue;
 
-        // got some data, process it
-        // send to uart
+        // got some data, test it
+        //// send to uart
         //buffer[n] = '\0';
         //message.len = n;
         //message.msg = buffer;
         //uart_queue.put(&message);
-            
-        // echo
+        //// echo
         //printf("Received packet from: %s\n", client.get_address());
         //udp_server.sendTo(ep_udp_client, buffer, n);
         
         // process received data
         switch (n) {
-            // length = 4, may be this is a discovery command, TCP port, or UDP port
-            // Format: NNDS, NNTP or NNUP
-            case 4:
+            // length = 6, a CONFIGURATION command (discovery command, TCP port, or UDP port)
+            // Format: NNIODS, NNIOTP or NNIOUP
+            case 6:
                 // discovery command
-                if (strstr(buffer, "NNDS") != NULL) {
+                if (strstr(buffer, "NNIODS") != NULL) {
                     udp_server.sendTo(ep_udp_client, ip_addr, strlen(ip_addr));
                 }
                 // ask for TCP server port
-                else if (strstr(buffer, "NNTP") != NULL) {
+                else if (strstr(buffer, "NNIOTP") != NULL) {
                     char port[5];
                     sprintf(port, "%5d", tcp_server_port);
                     udp_server.sendTo(ep_udp_client, port, strlen(port));
                 }
                 // ask for UDP server port
-                else if (strstr(buffer, "NNUP") != NULL) {
+                else if (strstr(buffer, "NNIOUP") != NULL) {
                     char port[5];
                     sprintf(port, "%5d", udp_server_port);
                     udp_server.sendTo(ep_udp_client, port, strlen(port));
                 }
                 break;
-            // length = 14, maybe this is a command to set network configuration
-            // Format: 4E 4E C0 A8 00 78 FF FF FF 00 C0 A8 0 1 (NN 192.168.0.120; 255.255.255.0; 192.168.0.1)
-            case 14:
-                // check if two first chars = NN
-                if (strstr(buffer, "NN") != NULL) {
-                    //printf("Received new network configuration\n");
-                    write_eeprom(&buffer[2]); // parameters from 3rd char
-                }
+            // length = 19, SET NETWORK CONFIGURATION
+            // Format: 4E 4E 49 4F      C0 A8 00 78        FF FF FF 00            C0 A8 00 01      00 00 01
+            //        (NNIO;            IP: 192.168.0.120; Subnet: 255.255.255.0; GW: 192.168.0.1; MAC: 0 0 1)
+            case 19:{
+                // check device id
+                char* id = strstr(buffer, DEVICE_ID);
+                if (id == NULL)
+                    break;
+                else if ((id - buffer) > 0)
+                    break;
+
+                //printf("Received new network configuration\n");
+                write_eeprom(&buffer[4]); // parameters from 3rd char, 15-bytes
                 break;
+            }
             default:
                 break;
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/readme.txt	Fri Sep 26 20:07:34 2014 +0000
@@ -0,0 +1,63 @@
+CONFIGURATION SECTION (UDP)
+1. DISCOVERY Command
+    + UDP broadcast: 192.168.0.255 to port 11000
+    + Send: NNIODS
+    + Receive: IP address
+
+2. TCP SERVER PORT Command
+    + Send: NNIOTP
+    + Receive: 10000
+
+3. UDP SERVER PORT Command
+    + Send: NNIOUP
+    + Receive: 11000
+    
+3. Set new network configuration
+    + Send: 19 bytes in total, NNIO + 15-byte
+      15-byte = 4-byte IP address + 4-byte subnet + 4-byte gateway + 3-byte MAC
+
+INTERFACING SECTION (TCP)
+4. Receiving Protocol: 58-bytes in total
+    + Field ID (4-bytes) = NNIO
+    + Field OP (5-bytes): output control enable flag
+      Byte 1: Digital output
+      Byte 2: Analog output 0
+      Byte 3: Analog output 1
+      Byte 4: UART output
+      Byte 5: Command (Q: query status)
+      'O': enable controlling output
+      Others: disable controlling output
+      If Command is 'Q', device will update its outputs if needed and send its status including new outputs
+    + Field IP (4-bytes): device IP address
+    + Field DO[n] (8-bytes): digital output values
+      'H': output set to high
+      'L': output set to low
+    + Field AO_0 (2-bytes): 16-bit analog output value, channel 0
+      Range is from 0x0000 to 0x0FFF
+    + Field AO_1 (2-bytes): 16-bit analog output value, channel 1
+      Range is from 0x0000 to 0x0FFF
+    + UART output (32-bytes): max 32 bytes, stop string by NULL
+    + End char: CR
+    
+5. Sending Protocol: 39-bytes in total
+    + Field ID (4-bytes) = NNIO
+    + Field MAC (6-bytes)
+    + Field IP (4-bytes)
+    + Field DI[n]: 8-bit digital input values
+      'H' if input is HIGH
+      'L' if input is LOW
+    + Field DO[n]: 8-bit digital output values
+      'H' if output is HIGH
+      'L' if output is LOW
+    + Field AI_0 (2-bytes): analog 16-bit input value, channel 0
+      Range is from 0x0000 to 0x0FFF
+    + Field AI_1 (2-bytes): analog 16-bit input value, channel 1
+      Range is from 0x0000 to 0x0FFF
+    + Field AO_0 (2-bytes): 16-bit analog output value, channel 0
+      Range is from 0x0000 to 0x0FFF
+    + Field AO_1 (2-bytes): 16-bit analog output value, channel 1
+      Range is from 0x0000 to 0x0FFF
+    + End char: CR
+    
+6 Commands:
+    + QUERY STATUS ('Q')
\ No newline at end of file