Toyomasa Watarai
/
Mbed-example-WS-W27
Mbed Cloud example program for workshop in W27 2018.
easy-connect/wifi-ism43362/ISM43362/ISM43362.cpp
- Committer:
- MACRUM
- Date:
- 2018-06-30
- Revision:
- 0:119624335925
File content as of revision 0:119624335925:
/* ISM43362 Example * * Copyright (c) STMicroelectronics 2018 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <string.h> #include "ISM43362.h" #include "mbed_debug.h" // activate / de-activate debug #define ism_debug false ISM43362::ISM43362(PinName mosi, PinName miso, PinName sclk, PinName nss, PinName resetpin, PinName datareadypin, PinName wakeup, bool debug) : _bufferspi(mosi, miso, sclk, nss, datareadypin), _parser(_bufferspi), _resetpin(resetpin), _packets(0), _packets_end(&_packets) { DigitalOut wakeup_pin(wakeup); ISM43362::setTimeout((uint32_t)5000); _bufferspi.format(16, 0); /* 16bits, ploarity low, phase 1Edge, master mode */ _bufferspi.frequency(20000000); /* up to 20 MHz */ _active_id = 0xFF; reset(); _ism_debug = debug || ism_debug; _parser.debugOn(debug); } /** * @brief Parses and returns number from string. * @param ptr: pointer to string * @param cnt: pointer to the number of parsed digit * @retval integer value. */ #define CHARISHEXNUM(x) (((x) >= '0' && (x) <= '9') || \ ((x) >= 'a' && (x) <= 'f') || \ ((x) >= 'A' && (x) <= 'F')) #define CHARISNUM(x) ((x) >= '0' && (x) <= '9') #define CHAR2NUM(x) ((x) - '0') extern "C" int32_t ParseNumber(char* ptr, uint8_t* cnt) { uint8_t minus = 0, i = 0; int32_t sum = 0; if (*ptr == '-') { /* Check for minus character */ minus = 1; ptr++; i++; } while (CHARISNUM(*ptr) || (*ptr=='.')) { /* Parse number */ if (*ptr == '.') { ptr++; // next char } else { sum = 10 * sum + CHAR2NUM(*ptr); ptr++; i++; } } if (cnt != NULL) { /* Save number of characters used for number */ *cnt = i; } if (minus) { /* Minus detected */ return 0 - sum; } return sum; /* Return number */ } const char *ISM43362::get_firmware_version(void) { char tmp_buffer[250]; char *ptr, *ptr2; /* Use %[^\n] instead of %s to allow having spaces in the string */ if(!(_parser.send("I?") && _parser.recv("%[^\n^\r]\r\n", tmp_buffer) && check_response())) { debug_if(_ism_debug, "ISM43362: get_firmware_version is FAIL\r\n"); return 0; } // Get the first version in the string ptr = strtok((char *)tmp_buffer, ","); ptr = strtok(NULL, ","); ptr2 = strtok(NULL, ","); if (ptr == NULL) { debug_if(_ism_debug, "ISM43362: get_firmware_version decoding is FAIL\r\n"); return 0; } strncpy(_fw_version, ptr , ptr2-ptr); debug_if(_ism_debug, "ISM43362: get_firmware_version = [%s]\r\n", _fw_version); return _fw_version; } bool ISM43362::reset(void) { char tmp_buffer[100]; debug_if(_ism_debug,"ISM43362: Reset Module\r\n"); _resetpin = 0; wait_ms(10); _resetpin = 1; wait_ms(500); /* Wait for prompt line : the string is "> ". */ /* As the space char is not detected by sscanf function in parser.recv, */ /* we need to use %[\n] */ if (!_parser.recv(">%[^\n]", tmp_buffer)) { debug_if(_ism_debug,"ISM43362: Reset Module failed\r\n"); return false; } return true; } void ISM43362::print_rx_buff(void) { char tmp[150] = {0}; uint16_t i = 0; debug_if(_ism_debug,"ISM43362: "); while(i < 150) { int c = _parser.getc(); if (c < 0) break; tmp[i] = c; debug_if(_ism_debug,"0x%2X ",c); i++; } debug_if(_ism_debug,"\n"); debug_if(_ism_debug,"ISM43362: Buffer content =====%s=====\r\n",tmp); } /* checks the standard OK response of the WIFI module, shouldbe: * \r\nDATA\r\nOK\r\n>sp * or * \r\nERROR\r\nUSAGE\r\n>sp * function returns true if OK, false otherwise. In case of error, * print error content then flush buffer */ bool ISM43362::check_response(void) { char tmp_buffer[100]; if(!_parser.recv("OK\r\n")) { print_rx_buff(); _parser.flush(); return false; } /* Then we should get the prompt: "> " */ /* As the space char is not detected by sscanf function in parser.recv, */ /* we need to use %[\n] */ if (!_parser.recv(">%[^\n]", tmp_buffer)) { debug_if(_ism_debug, "ISM43362: Missing prompt in WIFI resp\r\n"); print_rx_buff(); _parser.flush(); return false; } /* Inventek module do stuffing / padding of data with 0x15, * in case buffer contains such */ while(1) { int c = _parser.getc(); if ( c == 0x15) { debug_if(_ism_debug, "ISM43362: Flush char 0x%x\n", c); continue; } else { /* How to put it back if needed ? */ break; } } return true; } bool ISM43362::dhcp(bool enabled) { return (_parser.send("C4=%d", enabled ? 1:0) && check_response()); } int ISM43362::connect(const char *ap, const char *passPhrase, ism_security_t ap_sec) { char tmp[256]; if (!(_parser.send("C1=%s", ap) && check_response())) { return NSAPI_ERROR_PARAMETER; } if (!(_parser.send("C2=%s", passPhrase) && check_response())) { return NSAPI_ERROR_PARAMETER; } /* Check security level is acceptable */ if (ap_sec > ISM_SECURITY_WPA_WPA2 ) { debug_if(_ism_debug, "ISM43362: Unsupported security level %d\n", ap_sec); return NSAPI_ERROR_UNSUPPORTED; } if (!(_parser.send("C3=%d", ap_sec) && check_response())) { return NSAPI_ERROR_PARAMETER; } if(_parser.send("C0")) { while (_parser.recv("%[^\n]\n", tmp)) { if(strstr(tmp, "OK")) { _parser.flush(); return NSAPI_ERROR_OK; } if(strstr(tmp, "JOIN")) { if(strstr(tmp, "Failed")) { _parser.flush(); return NSAPI_ERROR_AUTH_FAILURE; } } } } return NSAPI_ERROR_NO_CONNECTION; } bool ISM43362::disconnect(void) { return (_parser.send("CD") && check_response()); } const char *ISM43362::getIPAddress(void) { char tmp_ip_buffer[250]; char *ptr, *ptr2; /* Use %[^\n] instead of %s to allow having spaces in the string */ if(!(_parser.send("C?") && _parser.recv("%[^\n^\r]\r\n", tmp_ip_buffer) && check_response())) { debug_if(_ism_debug,"ISM43362: getIPAddress LINE KO: %s\n", tmp_ip_buffer); return 0; } /* Get the IP address in the result */ /* TODO : check if the begining of the string is always = "eS-WiFi_AP_C47F51011231," */ ptr = strtok((char *)tmp_ip_buffer, ","); ptr = strtok(NULL, ","); ptr = strtok(NULL, ","); ptr = strtok(NULL, ","); ptr = strtok(NULL, ","); ptr = strtok(NULL, ","); ptr2 = strtok(NULL, ","); if (ptr == NULL) return 0; strncpy(_ip_buffer, ptr , ptr2-ptr); tmp_ip_buffer[59] = 0; debug_if(_ism_debug,"ISM43362: receivedIPAddress: %s\n", _ip_buffer); return _ip_buffer; } const char *ISM43362::getMACAddress(void) { if(!(_parser.send("Z5") && _parser.recv("%s\r\n", _mac_buffer) && check_response())) { debug_if(_ism_debug,"ISM43362: receivedMacAddress LINE KO: %s\n", _mac_buffer); return 0; } debug_if(_ism_debug,"ISM43362: receivedMacAddress:%s, size=%d\r\n", _mac_buffer, sizeof(_mac_buffer)); return _mac_buffer; } const char *ISM43362::getGateway() { char tmp[250]; /* Use %[^\n] instead of %s to allow having spaces in the string */ if(!(_parser.send("C?") && _parser.recv("%[^\n^\r]\r\n", tmp) && check_response())) { debug_if(_ism_debug,"ISM43362: getGateway LINE KO: %s\r\n", tmp); return 0; } /* Extract the Gateway in the received buffer */ char *ptr; ptr = strtok(tmp,","); for (int i = 0; i< 7;i++) { if (ptr == NULL) break; ptr = strtok(NULL,","); } strncpy(_gateway_buffer, ptr, sizeof(_gateway_buffer)); debug_if(_ism_debug,"ISM43362: getGateway: %s\r\n", _gateway_buffer); return _gateway_buffer; } const char *ISM43362::getNetmask() { char tmp[250]; /* Use %[^\n] instead of %s to allow having spaces in the string */ if(!(_parser.send("C?") && _parser.recv("%[^\n^\r]\r\n", tmp) && check_response())) { debug_if(_ism_debug,"ISM43362: getNetmask LINE KO: %s\n", tmp); return 0; } /* Extract Netmask in the received buffer */ char *ptr; ptr = strtok(tmp,","); for (int i = 0; i< 6;i++) { if (ptr == NULL) break; ptr = strtok(NULL,","); } strncpy(_netmask_buffer, ptr, sizeof(_netmask_buffer)); debug_if(_ism_debug,"ISM43362: getNetmask: %s\r\n", _netmask_buffer); return _netmask_buffer; } int8_t ISM43362::getRSSI() { int8_t rssi; char tmp[25]; if(!(_parser.send("CR") && _parser.recv("%s\r\n", tmp) && check_response())) { debug_if(_ism_debug,"ISM43362: getRSSI LINE KO: %s\r\n", tmp); return 0; } rssi = ParseNumber(tmp, NULL); debug_if(_ism_debug,"ISM43362: getRSSI: %d\r\n", rssi); return rssi; } /** * @brief Parses Security type. * @param ptr: pointer to string * @retval Encryption type. */ extern "C" nsapi_security_t ParseSecurity(char* ptr) { if(strstr(ptr,"Open")) return NSAPI_SECURITY_NONE; else if(strstr(ptr,"WEP")) return NSAPI_SECURITY_WEP; else if(strstr(ptr,"WPA2 AES")) return NSAPI_SECURITY_WPA2; else if(strstr(ptr,"WPA WPA2")) return NSAPI_SECURITY_WPA_WPA2; else if(strstr(ptr,"WPA2 TKIP")) return NSAPI_SECURITY_UNKNOWN; // no match in mbed else if(strstr(ptr,"WPA2")) return NSAPI_SECURITY_WPA2; // catch any other WPA2 formula else if(strstr(ptr,"WPA")) return NSAPI_SECURITY_WPA; else return NSAPI_SECURITY_UNKNOWN; } /** * @brief Convert char in Hex format to integer. * @param a: character to convert * @retval integer value. */ extern "C" uint8_t Hex2Num(char a) { if (a >= '0' && a <= '9') { /* Char is num */ return a - '0'; } else if (a >= 'a' && a <= 'f') { /* Char is lowercase character A - Z (hex) */ return (a - 'a') + 10; } else if (a >= 'A' && a <= 'F') { /* Char is uppercase character A - Z (hex) */ return (a - 'A') + 10; } return 0; } /** * @brief Extract a hex number from a string. * @param ptr: pointer to string * @param cnt: pointer to the number of parsed digit * @retval Hex value. */ extern "C" uint32_t ParseHexNumber(char* ptr, uint8_t* cnt) { uint32_t sum = 0; uint8_t i = 0; while (CHARISHEXNUM(*ptr)) { /* Parse number */ sum <<= 4; sum += Hex2Num(*ptr); ptr++; i++; } if (cnt != NULL) { /* Save number of characters used for number */ *cnt = i; } return sum; /* Return number */ } bool ISM43362::isConnected(void) { return getIPAddress() != 0; } int ISM43362::scan(WiFiAccessPoint *res, unsigned limit) { unsigned cnt = 0, num=0; char *ptr; char tmp[256]; if(!(_parser.send("F0"))) { debug_if(_ism_debug,"ISM43362: scan error\r\n"); return 0; } /* Parse the received buffer and fill AP buffer */ /* Use %[^\n] instead of %s to allow having spaces in the string */ while (_parser.recv("#%[^\n]\n", tmp)) { if (limit != 0 && cnt >= limit) { /* reached end */ break; } nsapi_wifi_ap_t ap = {0}; debug_if(_ism_debug,"ISM43362: received:%s\n", tmp); ptr = strtok(tmp, ","); num = 0; while (ptr != NULL) { switch (num++) { case 0: /* Ignore index */ case 4: /* Ignore Max Rate */ case 5: /* Ignore Network Type */ case 7: /* Ignore Radio Band */ break; case 1: ptr[strlen(ptr) - 1] = 0; strncpy((char *)ap.ssid, ptr+ 1, 32); break; case 2: for (int i=0; i<6; i++) { ap.bssid[i] = ParseHexNumber(ptr + (i*3), NULL); } break; case 3: ap.rssi = ParseNumber(ptr, NULL); break; case 6: ap.security = ParseSecurity(ptr); break; case 8: ap.channel = ParseNumber(ptr, NULL); num = 1; break; default: break; } ptr = strtok(NULL, ","); } res[cnt] = WiFiAccessPoint(ap); cnt++; } /* We may stop before having read all the APs list, so flush the rest of * it as well as OK commands */ _parser.flush(); debug_if(_ism_debug, "ISM43362: End of Scan: cnt=%d\n", cnt); return cnt; } bool ISM43362::open(const char *type, int id, const char* addr, int port) { /* TODO : This is the implementation for the client socket, need to check if need to create openserver too */ //IDs only 0-3 if((id < 0) ||(id > 3)) { debug_if(_ism_debug, "ISM43362: open: wrong id\n"); return false; } /* Set communication socket */ debug_if(_ism_debug, "ISM43362: OPEN socket\n"); _active_id = id; if (!(_parser.send("P0=%d", id) && check_response())) { return false; } /* Set protocol */ if (!(_parser.send("P1=%s", type) && check_response())) { return false; } /* Set address */ if (!(_parser.send("P3=%s", addr) && check_response())) { return false; } if (!(_parser.send("P4=%d", port) && check_response())) { return false; } /* Start client */ if (!(_parser.send("P6=1") && check_response())) { return false; } /* request as much data as possible - i.e. module max size */ if (!(_parser.send("R1=%d", ES_WIFI_MAX_RX_PACKET_SIZE)&& check_response())) { return -1; } return true; } bool ISM43362::dns_lookup(const char* name, char* ip) { char tmp[30]; if (!(_parser.send("D0=%s", name) && _parser.recv("%s\r\n", tmp) && check_response())) { debug_if(_ism_debug,"ISM43362: dns_lookup LINE KO: %s\n", tmp); return 0; } strncpy(ip, tmp, sizeof(tmp)); debug_if(_ism_debug, "ISM43362: ip of DNSlookup: %s\n", ip); return 1; } bool ISM43362::send(int id, const void *data, uint32_t amount) { // The Size limit has to be checked on caller side. if (amount > ES_WIFI_MAX_RX_PACKET_SIZE) { return false; } /* Activate the socket id in the wifi module */ if ((id < 0) ||(id > 3)) { return false; } debug_if(_ism_debug, "ISM43362: SEND socket amount %d\n", amount); if (_active_id != id) { _active_id = id; if (!(_parser.send("P0=%d",id) && check_response())) { return false; } } /* Change the write timeout */ if (!(_parser.send("S2=%d", _timeout) && check_response())) { return false; } /* set Write Transport Packet Size */ int i = _parser.printf("S3=%d\r", (int)amount); if (i < 0) { return false; } i = _parser.write((const char *)data, amount, i); if (i < 0) { return false; } if (!check_response()) { return false; } return true; } int ISM43362::check_recv_status(int id, void *data) { int read_amount; static int keep_to = 0; debug_if(_ism_debug, "ISM43362: ISM43362 req check_recv_status\r\n"); /* Activate the socket id in the wifi module */ if ((id < 0) ||(id > 3)) { return -1; } if (_active_id != id) { _active_id = id; if (!(_parser.send("P0=%d",id) && check_response())) { return -1; } } /* MBED wifi driver is meant to be non-blocking, but we need anyway to * wait for some data on the RECV side to avoid overflow on TX side, the * tiemout is defined in higher layer */ if (keep_to != _timeout) { if (!(_parser.send("R2=%d", _timeout) && check_response())) { return -1; } keep_to = _timeout; } if (!_parser.send("R0")) { return -1; } read_amount = _parser.read((char *)data); if(read_amount < 0) { debug_if(_ism_debug, "ISM43362: ERROR in data RECV, timeout?\r\n"); return -1; /* nothing to read */ } /* If there are spurious 0x15 at the end of the data, this is an error * we hall can get rid off of them :-( * This should not happen, but let's try to clean-up anyway */ char *cleanup = (char *) data; while ((read_amount > 0) && (cleanup[read_amount-1] == 0x15)) { debug_if(_ism_debug, "ISM43362: spurious 0X15 trashed\r\n"); /* Remove the trailling char then search again */ read_amount--; } if ((read_amount >= 6) && (strncmp("OK\r\n> ", (char *)data, 6) == 0)) { debug_if(_ism_debug, "ISM43362: recv 2 nothing to read=%d\r\n", read_amount); return 0; /* nothing to read */ } else if ((read_amount >= 8) && (strncmp((char *)((uint32_t) data + read_amount - 8), "\r\nOK\r\n> ", 8)) == 0) { /* bypass ""\r\nOK\r\n> " if present at the end of the chain */ read_amount -= 8; } else { debug_if(_ism_debug, "ISM43362: ERROR in data RECV?, flushing %d bytes\r\n", read_amount); int i = 0; debug_if(_ism_debug, "ISM43362: "); for (i = 0; i < read_amount; i++) { debug_if(_ism_debug, "%2X ", cleanup[i]); } cleanup[i] = 0; debug_if(_ism_debug, "\r\n%s\r\n", cleanup); return -1; /* nothing to read */ } debug_if(_ism_debug, "ISM43362: read_amount=%d\r\n", read_amount); return read_amount; } bool ISM43362::close(int id) { if ((id <0) || (id > 3)) { debug_if(_ism_debug,"ISM43362: Wrong socket number\n"); return false; } /* Set connection on this socket */ debug_if(_ism_debug,"ISM43362: CLOSE socket id=%d\n", id); _active_id = id; if (!(_parser.send("P0=%d", id) && check_response())) { return false; } /* close this socket */ if (!(_parser.send("P6=0") && check_response())) { return false; } return true; } void ISM43362::setTimeout(uint32_t timeout_ms) { _timeout = timeout_ms; _parser.setTimeout(timeout_ms); } bool ISM43362::readable() { /* not applicable with SPI api */ return true; } bool ISM43362::writeable() { /* not applicable with SPI api */ return true; } void ISM43362::attach(Callback<void()> func) { /* not applicable with SPI api */ }