GainSpan Wi-Fi library see: http://mbed.org/users/gsfan/notebook/gainspan_wifi/

Fork of GSwifi_old by gs fan

GainSpan Wi-Fi library

The GS1011 is an ultra low power 802.11b wireless module from GainSpan.

see: http://mbed.org/users/gsfan/notebook/gainspan_wifi/

Information

Please change the baud rate in advance.

  • ATB=115200
  • AT&W0

It may be better and sometimes faster.
GSwifi gs(p13, p14, baud);

Heavily modified new library: http://mbed.org/users/gsfan/code/GSwifi

ゲインスパン Wi-Fi モジュール ライブラリ

ゲインスパン社の低電力 Wi-Fiモジュール(無線LAN) GS1011 シリーズ用のライブラリです。

解説: http://mbed.org/users/gsfan/notebook/gainspan_wifi/

Information

モジュールはあらかじめ次のコマンドでボーレートを変更しておく。

  • ATB=115200
  • AT&W0

場合によってはもっと高速の方がいいかもしれない。クラス宣言時にレート設定をする。
GSwifi gs(p13, p14, baud);

大幅に更新された新しいライブラリ: http://mbed.org/users/gsfan/code/GSwifi

Revision:
23:a783c62c36d0
Parent:
22:9b077e2823ce
Child:
24:5c350ae2e703
--- a/GSwifi_httpd.cpp	Wed Dec 26 08:41:43 2012 +0000
+++ b/GSwifi_httpd.cpp	Mon Jan 21 05:58:28 2013 +0000
@@ -1,15 +1,11 @@
 #include "dbg.h"
 #include "mbed.h"
 #include "GSwifi.h"
+#include "sha1.h"
+#include <string.h>
 
 #ifdef GS_USE_HTTPD
 
-#define HTTPD_REQUEST 0
-#define HTTPD_HEAD 1
-#define HTTPD_SPACE 2
-#define HTTPD_BODY 3
-#define HTTPD_ERROR 4
-
 #define MIMETABLE_NUM 8
 struct {
     char ext[6];
@@ -31,10 +27,9 @@
 
     if (! _connect || _status != GSSTAT_READY) return -1;
 
+    memset(&_httpd, 0, sizeof(_httpd));
     for (i = 0; i < 16; i ++) {
-        _httpd[i].mode = HTTPD_REQUEST;
-        _httpd[i].buf = NULL;
-        _httpd[i].uri = NULL;
+        _httpd[i].mode = GSHTTPDMODE_REQUEST;
     }
     _handler_count = 0;
 
@@ -57,48 +52,62 @@
     char c;
 
     if (len == 0) {
-        _httpd[cid].mode = HTTPD_REQUEST;
+        // start request
+        _httpd[cid].mode = GSHTTPDMODE_REQUEST;
         _httpd[cid].len = 0;
         _httpd[cid].keepalive = 0;
+#ifdef GS_USE_WEBSOCKET
+        _httpd[cid].websocket = 0;
+#endif
         return;
     }
 
+#ifdef GS_USE_WEBSOCKET
+    if (_httpd[cid].mode >= GSHTTPDMODE_WEBSOCKET) {
+        poll_websocket(cid, len);
+        return;
+    }
+#endif
+
   while (_gs_sock[cid].connect && _gs_sock[cid].data->use()) {
     flg = 0;
     if (_httpd[cid].buf == NULL) {
         _httpd[cid].buf = new char[HTTPD_BUF_SIZE];
     }
+    // get 1 line
     for (j = 0; j < len; j ++) {
         _gs_sock[cid].data->get(&c);
         if (c == '\r') continue;
-        if (c == '\n' && _httpd[cid].mode != HTTPD_BODY) break;
+        if (c == '\n' && _httpd[cid].mode != GSHTTPDMODE_BODY) break;
         
         if (_httpd[cid].len < HTTPD_BUF_SIZE - 1) {
             _httpd[cid].buf[_httpd[cid].len] = c; 
         }
         _httpd[cid].len ++;
-        if (_httpd[cid].len >= _httpd[cid].length && _httpd[cid].mode == HTTPD_BODY) break;
+        if (_httpd[cid].mode == GSHTTPDMODE_BODY && _httpd[cid].len >= _httpd[cid].length) break; // end of body
     }
-    if (j >= len) return;
+    if (j >= len) return; // continue
     if (_httpd[cid].len < HTTPD_BUF_SIZE) {
         _httpd[cid].buf[_httpd[cid].len] = 0;
         DBG("httpd %d: %d %s (%d)\r\n", cid, _httpd[cid].mode, _httpd[cid].buf, _httpd[cid].len);
     }
 
+    // parse
     switch (_httpd[cid].mode) {
-    case HTTPD_REQUEST:
-        if (strncmp(_httpd[cid].buf, "GET ", 4) == 0) {
+    case GSHTTPDMODE_REQUEST:
+        if (strnicmp(_httpd[cid].buf, "GET ", 4) == 0) {
             _httpd[cid].type = GSPROT_HTTPGET;
             j = 4;
         } else
-        if (strncmp(_httpd[cid].buf, "POST ", 5) == 0) {
+        if (strnicmp(_httpd[cid].buf, "POST ", 5) == 0) {
             _httpd[cid].type = GSPROT_HTTPPOST;
             j = 5;
         } else {
-            _httpd[cid].mode = HTTPD_ERROR;
+            _httpd[cid].mode = GSHTTPDMODE_ERROR;
             break;
         }
 
+        // get uri
         for (i = j; i < _httpd[cid].len; i ++) {
             if (_httpd[cid].buf[i] == ' ') break;
         }
@@ -110,79 +119,105 @@
             strncpy(_httpd[cid].uri, &_httpd[cid].buf[j], i);
             _httpd[cid].uri[i] = 0;
         }
-        _httpd[cid].mode = HTTPD_HEAD;
+        _httpd[cid].mode = GSHTTPDMODE_HEAD;
         _httpd[cid].length = 0;
         DBG("uri: %s\r\n", _httpd[cid].uri);
         break;
         
-    case HTTPD_HEAD:
+    case GSHTTPDMODE_HEAD:
         if (_httpd[cid].len == 0) {
-            _httpd[cid].mode = HTTPD_BODY;
+            // blank line (end of header)
+            _httpd[cid].mode = GSHTTPDMODE_BODY;
             if (_httpd[cid].length == 0) flg = 1; // no body
+#ifdef GS_USE_WEBSOCKET
+            if (_httpd[cid].websocket && _httpd[cid].websocket_key) {
+                // enter websocket
+                _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET;
+                _httpd[cid].len = 0;
+                flg = 1;
+            }
+#endif
         } else
-        if (strncmp(_httpd[cid].buf, "Content-Length: ", 16) == 0) {
+        if (strnicmp(_httpd[cid].buf, "Content-Length: ", 16) == 0) {
             _httpd[cid].length = atoi(&_httpd[cid].buf[16]);
         } else
-        if (strncmp(_httpd[cid].buf, "Connection: Keep-Alive", 22) == 0 ||
-          strncmp(_httpd[cid].buf, "Connection: keep-alive", 22) == 0) {
+        if (strnicmp(_httpd[cid].buf, "Connection: Keep-Alive", 22) == 0) {
             if (! _httpd[cid].keepalive) {
                 _httpd[cid].keepalive = HTTPD_KEEPALIVE;
             }
+#ifdef GS_USE_WEBSOCKET
+        } else
+        if (strnicmp(_httpd[cid].buf, "Upgrade: websocket", 18) == 0) {
+            if (! _httpd[cid].websocket) _httpd[cid].websocket = 1;
+        } else
+        if (strnicmp(_httpd[cid].buf, "Sec-WebSocket-Version: ", 23) == 0) {
+            _httpd[cid].websocket = atoi(&_httpd[cid].buf[23]);
+        } else
+        if (strnicmp(_httpd[cid].buf, "Sec-WebSocket-Key: ", 19) == 0) {
+            if (_httpd[cid].websocket_key == NULL) {
+                _httpd[cid].websocket_key = new char[30];
+            }
+            strncpy(_httpd[cid].websocket_key, &_httpd[cid].buf[19], 30);
+#endif
         }
         break;
 
-    case HTTPD_BODY:
+    case GSHTTPDMODE_BODY:
         if (_httpd[cid].len >= _httpd[cid].length) {
             DBG("body: %s\r\n", _httpd[cid].buf);
             flg = 1;
         }
         break;
+
     }
 
+#ifdef GS_USE_WEBSOCKET
+    if (flg && _httpd[cid].mode == GSHTTPDMODE_WEBSOCKET) {
+        // websocket
+        send_websocket_accept(cid);
+        break; // break while
+
+    } else
+#endif
     if (flg) {
         // http request
         _httpd[cid].buf[_httpd[cid].len] = 0;
-        // scan handler
-        flg = 0;
-        for (i = 0; i < _handler_count; i ++) {
-            j = strlen(_handler[i].uri);
-            if (strncmp(_httpd[cid].uri, _handler[i].uri, j) == NULL) {
-                // found
-                _httpd[cid].host = _gs_sock[cid].host;
-                _httpd[cid].file = &_httpd[cid].uri[j];
-                _httpd[cid].query = NULL;
-                for (; j < strlen(_httpd[cid].uri); j ++) {
-                    if (_httpd[cid].uri[j] == '?') {
-                        // query string
-                        _httpd[cid].uri[j] = 0;
-                        _httpd[cid].query = &_httpd[cid].uri[j + 1];
-                        break;
-                    }
-                }
 
-                if (_handler[i].dir) {
-                    // file
-                    httpd_request(cid, &_httpd[cid], _handler[i].dir);
-                    flg = 1;
-                } else
-                if (_handler[i].onHttpCgi) {
-                    // cgi
-                    _handler[i].onHttpCgi(cid, &_httpd[cid]);
-                    _httpd[cid].keepalive = 0;
-                    LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
-                    LOG("%s %s %d 200 -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length);
-                    flg = 1;
+        i = get_handler(_httpd[cid].uri);
+        if (i >= 0) {
+            _httpd[cid].host = _gs_sock[cid].host;
+            j = strlen(_handler[i].uri);
+            _httpd[cid].file = &_httpd[cid].uri[j];
+            _httpd[cid].query = NULL;
+            for (; j < strlen(_httpd[cid].uri); j ++) {
+                if (_httpd[cid].uri[j] == '?') {
+                    // query string
+                    _httpd[cid].uri[j] = 0;
+                    _httpd[cid].query = &_httpd[cid].uri[j + 1];
+                    break;
                 }
-                break;
             }
-        }
-        if (! flg) {
+
+            if (_handler[i].dir) {
+                // file
+                httpd_request(cid, &_httpd[cid], _handler[i].dir);
+                flg = 1;
+            } else
+            if (_handler[i].onHttpCgi) {
+                // cgi
+                _handler[i].onHttpCgi(cid, &_httpd[cid]);
+                _httpd[cid].keepalive = 0;
+                LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
+                LOG("%s %s %d 200 -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length);
+                flg = 1;
+            }
+        } else {
             // not found
             send_httpd_error(cid, 403);
         }
         
         if (_httpd[cid].keepalive) {
-            _httpd[cid].mode = HTTPD_REQUEST;
+            _httpd[cid].mode = GSHTTPDMODE_REQUEST;
             _httpd[cid].len = 0;
             _httpd[cid].length = 0;
             _httpd[cid].keepalive --;
@@ -191,12 +226,25 @@
         }
     }
 
-    if (_httpd[cid].mode == HTTPD_ERROR) {
+    if (_httpd[cid].mode == GSHTTPDMODE_ERROR) {
         send_httpd_error(cid, 400);
     }
 
     _httpd[cid].len = 0;
-  }
+  } // while
+}
+
+int GSwifi::get_handler (char *uri) {
+    int i, j;
+
+    for (i = 0; i < _handler_count; i ++) {
+        j = strlen(_handler[i].uri);
+        if (strncmp(uri, _handler[i].uri, j) == NULL) {
+            // found
+            return i;
+        }
+    }
+    return -1;
 }
 
 int GSwifi::httpd_request (int cid, GS_httpd *gshttpd, char *dir) {
@@ -261,13 +309,26 @@
     DBG("<%s>\r\n", file);
     for (i = 0; i < MIMETABLE_NUM; i ++) {
         j = strlen(mimetable[i].ext);
-        if (strncmp(&file[strlen(file) - j], mimetable[i].ext, j) == NULL) {
+        if (strnicmp(&file[strlen(file) - j], mimetable[i].ext, j) == NULL) {
             return mimetable[i].type;
         }
     }
     return mimetable[0].type;
 }
 
+int GSwifi::strnicmp (char *p1, char *p2, int n) {
+    int i, r;
+    char c1, c2;
+    
+    for (i = 0; i < n; i ++) {
+        c1 = (p1[i] >= 'a' && p1[i] <= 'z') ? p1[i] - ('a' - 'A'): p1[i];
+        c2 = (p2[i] >= 'a' && p2[i] <= 'z') ? p2[i] - ('a' - 'A'): p2[i];
+        r = c1 - c2;
+        if (r) break;
+    }
+    return r;
+}
+
 void GSwifi::send_httpd_error (int cid, int err) {
     char buf[100], msg[30];
     
@@ -330,4 +391,182 @@
     }
 }
 
+#ifdef GS_USE_WEBSOCKET
+void GSwifi::poll_websocket (int cid, int len) {
+    int i, j, flg;
+    unsigned char c;
+
+    while (_gs_sock[cid].connect && _gs_sock[cid].data->use()) {
+        flg = 0;
+        // get 1 line
+        for (j = 0; j < len; j ++) {
+            _gs_sock[cid].data->get((char*)&c);
+//            DBG("_%c", c);
+
+            switch (_httpd[cid].mode) {
+            case GSHTTPDMODE_WEBSOCKET:
+                if (_httpd[cid].len == 0) {
+                    _httpd[cid].type = c & 0x0f;
+                    _httpd[cid].websocket_flg = c << 8;
+                    _httpd[cid].len ++;
+                } else
+                if (_httpd[cid].len == 1) {
+                    _httpd[cid].websocket_flg |= c;
+                    _httpd[cid].length = c & 0x7f;
+                    _httpd[cid].len ++;
+                    if (_httpd[cid].length < 126) {
+                        _httpd[cid].len = 0;
+                        if (_httpd[cid].websocket_flg & 0x0080) {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_MASK;
+                        } else {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
+                        }
+                        DBG("ws length %d\r\n", _httpd[cid].length);
+                    }
+                } else {
+                    // length 16bit,64bit
+                    if (_httpd[cid].len == 2) {
+                        _httpd[cid].length = c;
+                        _httpd[cid].len ++;
+                    } else
+                    if (_httpd[cid].len < 9 && (_httpd[cid].websocket_flg & 0x7f) == 127) {
+                        // 64bit
+                        _httpd[cid].length = (_httpd[cid].length << 8) | c;
+                        _httpd[cid].len ++;
+                    } else {
+                        _httpd[cid].length = (_httpd[cid].length << 8) | c;
+                        _httpd[cid].len = 0;
+                        if (_httpd[cid].websocket_flg & 0x0080) {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_MASK;
+                        } else {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
+                        }
+                        DBG("ws length2 %d\r\n", _httpd[cid].length);
+                    }
+                }
+                break;
+
+            case GSHTTPDMODE_WEBSOCKET_MASK:
+                _httpd[cid].websocket_mask[_httpd[cid].len] = c;
+                _httpd[cid].len ++;
+                if (_httpd[cid].len >= 4) {
+                    _httpd[cid].len = 0;
+                    _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
+                    DBG("ws mask\r\n");
+                }
+                break;
+
+            case GSHTTPDMODE_WEBSOCKET_BODY:
+                if (_httpd[cid].len < HTTPD_BUF_SIZE - 1) {
+                    if (_httpd[cid].websocket_flg & 0x0080) {
+                        _httpd[cid].buf[_httpd[cid].len] = c ^ _httpd[cid].websocket_mask[_httpd[cid].len & 0x03]; 
+                    } else {
+                        _httpd[cid].buf[_httpd[cid].len] = c; 
+                    }
+                    _httpd[cid].len ++;
+                }
+                break;
+            }
+
+            if (_httpd[cid].mode == GSHTTPDMODE_WEBSOCKET_BODY && _httpd[cid].len >= _httpd[cid].length) {
+                flg = 1;
+                break;
+            }
+        }
+        if (j >= len) return; // continue
+        if (_httpd[cid].len < HTTPD_BUF_SIZE) {
+            _httpd[cid].buf[_httpd[cid].len] = 0;
+            DBG("websocket %d: (%d)\r\n", cid, _httpd[cid].len);
+        }
+
+    if (flg) {
+        // websocket request
+      DBG("ws type %d\r\n", _httpd[cid].type);
+      switch (_httpd[cid].type) {
+      case 0x00: // continuation
+      case 0x01: // text
+      case 0x02: // binary
+        i = get_handler(_httpd[cid].uri);
+        if (i >= 0) {
+            _httpd[cid].host = _gs_sock[cid].host;
+            j = strlen(_handler[i].uri);
+            _httpd[cid].file = &_httpd[cid].uri[j];
+            _httpd[cid].query = NULL;
+
+            if (_handler[i].onHttpCgi) {
+                // cgi
+                _handler[i].onHttpCgi(cid, &_httpd[cid]);
+                LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
+                LOG("%s %s %d 200 -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length);
+                flg = 1;
+            }
+        }
+        break;
+
+      case 0x08: // close
+        close(cid);
+        break;
+
+      case 0x09: // ping
+        _gs_putc(0x8a); // pong
+        _gs_putc(0x04);
+        for (i = 0; i < _httpd[cid].len; i ++) {
+            _gs_putc(_httpd[cid].buf[i]);
+        }
+        break;
+
+      case 0x0a: // pong
+        break;
+      }
+      _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET;
+      _httpd[cid].len = 0;
+      _httpd[cid].length = 0;
+    }
+    } // while
+}
+
+int GSwifi::send_websocket (int cid, const char *buf, int len) {
+    int r;
+    char tmp[10];
+
+    tmp[0] = 0x81; // single, text frame
+    if (len < 126) {
+        tmp[1] = len;
+        r = send(cid, tmp, 2);
+    } else {
+        tmp[1] = 126;
+        tmp[2] = (len >> 8) & 0xff;
+        tmp[3] = len & 0xff;
+        r = send(cid, tmp, 4);
+    }
+    if (r == 0) {
+        r = send(cid, buf, len);
+    }
+    return r;
+}
+
+void GSwifi::send_websocket_accept (int cid) {
+    char buf[100], buf2[20];
+    
+    DBG("websocket accept: %d\r\n", cid);
+
+    send(cid, "HTTP/1.1 101 Switching Protocols\r\n", 34);
+    send(cid, "Upgrade: websocket\r\n", 20);
+    send(cid, "Connection: Upgrade\r\n", 21);
+
+    send(cid, "Sec-WebSocket-Accept: ", 22);
+    strcpy(buf, _httpd[cid].websocket_key);
+    strcat(buf, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+    sha1(buf, strlen(buf), buf2);
+    base64encode(buf2, 20, buf, sizeof(buf));
+    send(cid, buf, strlen(buf));
+    send(cid, "\r\n", 2);
+
+//    send(cid, "Sec-WebSocket-Protocol: chat\r\n", 30);
+    send(cid, "\r\n", 2);
+    LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
+    LOG("%s %s %d 101 - %s\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, buf);
+}
 #endif
+
+#endif