Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:8f8e8f3cbd1c, committed 2018-06-21
- Comitter:
- mayur098
- Date:
- Thu Jun 21 17:50:21 2018 +0000
- Commit message:
- first commit;
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Jenkinsfile Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,96 @@ +properties ([[$class: 'ParametersDefinitionProperty', parameterDefinitions: [ + [$class: 'StringParameterDefinition', name: 'mbed_os_revision', defaultValue: '', description: 'Revision of mbed-os to build. Use format "pull/PR-NUMBER/head" to access mbed-os PR'] + ]]]) + +if (params.mbed_os_revision == '') { + echo 'Use mbed OS revision from mbed-os.lib' +} else { + echo "Use mbed OS revisiong ${params.mbed_os_revision}" + if (params.mbed_os_revision.matches('pull/\\d+/head')) { + echo "Revision is a Pull Request" + } +} + +// List of targets with supported RF shields to compile +def targets = [ + "UBLOX_EVK_ODIN_W2": ["builtin"], + "REALTEK_RTL8195AM": ["builtin"], + "K64F": ["WIFI_ESP8266"], + "NUCLEO_F401RE": ["WIFI_IDW0XX1"], + "NUCLEO_F429ZI": ["WIFI_ESP8266"] + ] + +// Map toolchains to compilers +def toolchains = [ + ARM: "armcc", + GCC_ARM: "arm-none-eabi-gcc", + IAR: "IAR-linux" + ] + +// Supported RF shields +def radioshields = [ + "builtin", + "WIFI_IDW0XX1", + "WIFI_ESP8266" + ] + +def stepsForParallel = [:] + +// Jenkins pipeline does not support map.each, we need to use oldschool for loop +for (int i = 0; i < targets.size(); i++) { + for(int j = 0; j < toolchains.size(); j++) { + for(int k = 0; k < radioshields.size(); k++) { + def target = targets.keySet().asList().get(i) + def allowed_shields = targets.get(target) + def toolchain = toolchains.keySet().asList().get(j) + def compilerLabel = toolchains.get(toolchain) + def radioshield = radioshields.get(k) + + def stepName = "${target} ${toolchain} ${radioshield}" + if(allowed_shields.contains(radioshield)) { + stepsForParallel[stepName] = buildStep(target, compilerLabel, toolchain, radioshield) + } + } + } +} + +timestamps { + parallel stepsForParallel +} + +def buildStep(target, compilerLabel, toolchain, radioShield) { + return { + stage ("${target}_${compilerLabel}_${radioShield}") { + node ("${compilerLabel}") { + deleteDir() + dir("mbed-os-example-wifi") { + checkout scm + def config_file = "mbed_app.json" + + if ("${radioShield}" != "internal") { + // Replace default rf shield + execute("sed -i 's/\"value\": \"internal\"/\"value\": \"${radioShield}\"/' ${config_file}") + } + + // Set mbed-os to revision received as parameter + execute ("mbed deploy --protocol ssh") + if (params.mbed_os_revision != '') { + dir ("mbed-os") { + if (params.mbed_os_revision.matches('pull/\\d+/head')) { + execute("git fetch origin ${params.mbed_os_revision}:PR") + execute("git checkout PR") + } else { + execute ("git checkout ${params.mbed_os_revision}") + } + } + } + execute("mbed new .") + execute ("mbed compile --build out/${target}_${toolchain}_${radioShield}/ -m ${target} -t ${toolchain} -c --app-config ${config_file}") + } + stash name: "${target}_${toolchain}_${radioShield}", includes: '**/mbed-os-example-wifi.bin' + archive '**/mbed-os-example-wifi.bin' + step([$class: 'WsCleanup']) + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,115 @@ +# mbed-os-example-wifi # + +Wi-Fi example for Mbed OS + +## Getting started with the Wi-Fi API ## + +This is an example of a Wi-Fi application using the Wi-Fi and network socket APIs that [Mbed OS](https://github.com/ARMmbed/mbed-os) provides. + +The program brings up the Wi-Fi and the underlying network interface and uses it to scan available networks, connects to a network, prints interface and connection details and performs an HTTP operation. + +For more information about Wi-Fi APIs, please visit the [Mbed OS Wi-Fi](https://os.mbed.com/docs/latest/reference/wi-fi.html) documentation. + +### Supported hardware ### + +* [u-blox Odin board](https://os.mbed.com/platforms/ublox-EVK-ODIN-W2/) built-in Wi-Fi module. +* [Realtek RTL8195AM](https://os.mbed.com/platforms/REALTEK-RTL8195AM/) built-in Wi-Fi module. +* [NUCLEO-F401RE](https://os.mbed.com/platforms/ST-Nucleo-F401RE/) with [X-NUCLEO-IDW04A1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw04a1.html) Wi-Fi expansion board using pins D8 and D2 _(of the Arduino connector)_. +* [NUCLEO-F401RE](https://os.mbed.com/platforms/ST-Nucleo-F401RE/) with [X-NUCLEO-IDW01M1](https://os.mbed.com/components/X-NUCLEO-IDW01M1/) Wi-Fi expansion board using pins PA_9 and PA_10 _(of the Morpho connector)_. +* [NUCLEO-F429ZI](https://os.mbed.com/platforms/ST-Nucleo-F429ZI/) with ESP8266-01 module using pins D1 and D0. +* [NUCLEO-L476RG](https://os.mbed.com/platforms/ST-Nucleo-L476RG/) with ESP8266-01 module using pins D8 and D2. +* [GR-LYCHEE](https://os.mbed.com/platforms/Renesas-GR-LYCHEE/) with ESP32 module using pins P5_3, P3_14, P7_1 and P0_1. +* Other Mbed targets with an ESP8266 module, [X-NUCLEO-IDW04A1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw04a1.html) or [X-NUCLEO-IDW01M1](https://os.mbed.com/components/X-NUCLEO-IDW01M1/) expansion board. + *(The Mbed target board the Wi-Fi shield connects to shouldn't have any other network interface, for example Ethernet.)* + +ESP8266 is a fallback option if the build is for unsupported platform. + +#### Connecting the ESP8266 #### + +To connect the ESP8266 module to your development board, look at the [ESP8266 Cookbook page](https://developer.mbed.org/users/4180_1/notebook/using-the-esp8266-with-the-mbed-lpc1768/). In general, this means hooking up the ESP8266 TX pin to `D0` and the ESP8266 RX pin to `D1` on your development board. + +**Note:** On NUCLEO development boards, pins `D0` and `D1` are used for serial communication with the computer. Use pins `D8` (to ESP8266 TX) and `D2` (to ESP8266 RX) instead. + +#### Connecting the X-NUCLEO-IDW0XX1 #### + +To connect the [X-NUCLEO-IDW04A1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw04a1.html) or [X-NUCLEO-IDW01M1](https://developer.mbed.org/components/X-NUCLEO-IDW01M1/) expansion board to your NUCLEO development board, plug the expansion board on top of the NUCLEO board using the Arduino or Morpho connector. + +## Getting started ## + +1. Import the example. + + ``` + mbed import mbed-os-example-wifi + cd mbed-os-example-wifi + ``` + +2. Configure the Wi-Fi shield to use. + + Edit ```mbed_app.json``` to include the correct Wi-Fi shield, SSID and password: + + ``` + "config": { + "wifi-shield": { + "help": "Options are internal, WIFI_ESP8266, WIFI_IDW0XX1, WIFI_ESP32", + "value": "internal" + }, + "wifi-ssid": { + "help": "WiFi SSID", + "value": "\"SSID\"" + }, + "wifi-password": { + "help": "WiFi Password", + "value": "\"Password\"" + } + }, + ``` + + Sample ```mbed_app.json``` files are provided for ESP8266 (```mbed_app_esp8266.json```), X-NUCLEO-IDW04A1 (```mbed_app_idw04a1.json```), X-NUCLEO-IDW01M1 (```mbed_app_idw01m1```) and ESP32 (```mbed_app_esp32.json```). + + For built-in Wi-Fi, ignore the value of `wifi-shield`. + +3. Compile and generate binary. + + For example, for `GCC`: + + ``` + mbed compile -t GCC_ARM -m UBLOX_EVK_ODIN_W2 + ``` + + 4. Open a serial console session with the target platform using the following parameters: + + * **Baud rate:** 9600 + * **Data bits:** 8 + * **Stop bits:** 1 + * **Parity:** None + + 5. Copy or drag the application `mbed-os-example-wifi.bin` in the folder `mbed-os-example-wifi/BUILD/<TARGET NAME>/<PLATFORM NAME>` onto the target board. + + 6. The serial console should display a similar output to below, indicating a successful Wi-Fi connection: + + ``` + WiFi example + +Scan: +Network: Dave Hot Spot secured: Unknown BSSID: 00:01:02:03:04:05 RSSI: -58 Ch: 1 +1 network available. + +Connecting... +Success + +MAC: 00:01:02:03:04:05 +IP: 192.168.0.5 +Netmask: 255.255.255.0 +Gateway: 192.168.0.1 +RSSI: -27 + +Sending HTTP request to www.arm.com... +sent 38 [GET / HTTP/1.1] +recv 64 [HTTP/1.1 301 Moved Permanently] + +Done +``` + +## Troubleshooting + +If you have problems, you can review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html) for suggestions on what could be wrong and how to fix it.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver.lib Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/d-kato/esp32-driver/#3c3bd8e6ca6fe77acf81bf9a8eedbafa0581c997
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ATParser_os/ATParser_os.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,336 @@ +/* Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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. + * + * @section DESCRIPTION + * + * Parser for the AT command syntax + * + */ + +#include "ATParser_os.h" +#include "mbed_debug.h" + + +// getc/putc handling with timeouts +int ATParser_os::putc(char c) +{ + int wait_cnt = 0; + + while (true) { + if (_serial->writeable()) { + return _serial->putc((uint8_t)c); + } + if (wait_cnt > _timeout) { + return -1; + } + wait_cnt++; + Thread::wait(1); + } +} + +int ATParser_os::getc() +{ + int wait_cnt = 0; + + while (true) { + if (_serial->readable()) { + return (uint8_t)_serial->getc(); + } + if (wait_cnt > _timeout) { + return -1; + } + wait_cnt++; + Thread::wait(1); + } +} + +void ATParser_os::flush() +{ + while (_serial->readable()) { + _serial->getc(); + } +} + + +// read/write handling with timeouts +int ATParser_os::write(const char *data, int size) +{ + int i = 0; + for ( ; i < size; i++) { + if (putc(data[i]) < 0) { + return -1; + } + } + return i; +} + +int ATParser_os::read(char *data, int size) +{ + int i = 0; + for ( ; i < size; i++) { + int c = getc(); + if (c < 0) { + return -1; + } + data[i] = c; + } + return i; +} + + +// printf/scanf handling +int ATParser_os::vprintf(const char *format, va_list args) +{ + if (vsprintf(_buffer, format, args) < 0) { + return false; + } + int i = 0; + for ( ; _buffer[i]; i++) { + if (putc(_buffer[i]) < 0) { + return -1; + } + } + return i; +} + +int ATParser_os::vscanf(const char *format, va_list args) +{ + // Since format is const, we need to copy it into our buffer to + // add the line's null terminator and clobber value-matches with asterisks. + // + // We just use the beginning of the buffer to avoid unnecessary allocations. + int i = 0; + int offset = 0; + + while (format[i]) { + if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') { + _buffer[offset++] = '%'; + _buffer[offset++] = '*'; + i++; + } else { + _buffer[offset++] = format[i++]; + } + } + + // Scanf has very poor support for catching errors + // fortunately, we can abuse the %n specifier to determine + // if the entire string was matched. + _buffer[offset++] = '%'; + _buffer[offset++] = 'n'; + _buffer[offset++] = 0; + + // To workaround scanf's lack of error reporting, we actually + // make two passes. One checks the validity with the modified + // format string that only stores the matched characters (%n). + // The other reads in the actual matched values. + // + // We keep trying the match until we succeed or some other error + // derails us. + int j = 0; + + while (true) { + // Ran out of space + if (j+1 >= _buffer_size - offset) { + return false; + } + // Recieve next character + int c = getc(); + if (c < 0) { + return -1; + } + _buffer[offset + j++] = c; + _buffer[offset + j] = 0; + + // Check for match + int count = -1; + sscanf(_buffer+offset, _buffer, &count); + + // We only succeed if all characters in the response are matched + if (count == j) { + // Store the found results + vsscanf(_buffer+offset, format, args); + return j; + } + } +} + + +// Command parsing with line handling +bool ATParser_os::vsend(const char *command, va_list args) +{ + // Create and send command + if (vsprintf(_buffer, command, args) < 0) { + return false; + } + for (int i = 0; _buffer[i]; i++) { + if (putc(_buffer[i]) < 0) { + return false; + } + } + + // Finish with newline + for (int i = 0; _delimiter[i]; i++) { + if (putc(_delimiter[i]) < 0) { + return false; + } + } + + debug_if(dbg_on, "AT> %s\r\n", _buffer); + return true; +} + +bool ATParser_os::vrecv(const char *response, va_list args) +{ + // Iterate through each line in the expected response + while (response[0]) { + // Since response is const, we need to copy it into our buffer to + // add the line's null terminator and clobber value-matches with asterisks. + // + // We just use the beginning of the buffer to avoid unnecessary allocations. + int i = 0; + int offset = 0; + + while (response[i]) { + if (memcmp(&response[i+1-_delim_size], _delimiter, _delim_size) == 0) { + i++; + break; + } else if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') { + _buffer[offset++] = '%'; + _buffer[offset++] = '*'; + i++; + } else { + _buffer[offset++] = response[i++]; + } + } + + // Scanf has very poor support for catching errors + // fortunately, we can abuse the %n specifier to determine + // if the entire string was matched. + _buffer[offset++] = '%'; + _buffer[offset++] = 'n'; + _buffer[offset++] = 0; + + // To workaround scanf's lack of error reporting, we actually + // make two passes. One checks the validity with the modified + // format string that only stores the matched characters (%n). + // The other reads in the actual matched values. + // + // We keep trying the match until we succeed or some other error + // derails us. + int j = 0; + + while (true) { + // Recieve next character + int c = getc(); + if (c < 0) { + return false; + } + _buffer[offset + j++] = c; + _buffer[offset + j] = 0; + + // Check for oob data + for (int k = 0; k < _oobs.size(); k++) { + if (j == _oobs[k].len && memcmp( + _oobs[k].prefix, _buffer+offset, _oobs[k].len) == 0) { + debug_if(dbg_on, "AT! %s\r\n", _oobs[k].prefix); + _oobs[k].cb(); + + // oob may have corrupted non-reentrant buffer, + // so we need to set it up again + return vrecv(response, args); + } + } + + // Check for match + int count = -1; + sscanf(_buffer+offset, _buffer, &count); + + // We only succeed if all characters in the response are matched + if (count == j) { + debug_if(dbg_on, "AT= %s\r\n", _buffer+offset); + // Reuse the front end of the buffer + memcpy(_buffer, response, i); + _buffer[i] = 0; + + // Store the found results + vsscanf(_buffer+offset, _buffer, args); + + // Jump to next line and continue parsing + response += i; + break; + } + + // Clear the buffer when we hit a newline or ran out of space + // running out of space usually means we ran into binary data + if (j+1 >= _buffer_size - offset || + strcmp(&_buffer[offset + j-_delim_size], _delimiter) == 0) { + + debug_if(dbg_on, "AT< %s", _buffer+offset); + j = 0; + } + } + } + + return true; +} + + +// Mapping to vararg functions +int ATParser_os::printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + int res = vprintf(format, args); + va_end(args); + return res; +} + +int ATParser_os::scanf(const char *format, ...) +{ + va_list args; + va_start(args, format); + int res = vscanf(format, args); + va_end(args); + return res; +} + +bool ATParser_os::send(const char *command, ...) +{ + va_list args; + va_start(args, command); + bool res = vsend(command, args); + va_end(args); + return res; +} + +bool ATParser_os::recv(const char *response, ...) +{ + va_list args; + va_start(args, response); + bool res = vrecv(response, args); + va_end(args); + return res; +} + + +// oob registration +void ATParser_os::oob(const char *prefix, Callback<void()> cb) +{ + struct oob oob; + oob.len = strlen(prefix); + oob.prefix = prefix; + oob.cb = cb; + _oobs.push_back(oob); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ATParser_os/ATParser_os.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,244 @@ +/* Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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. + * + * @section DESCRIPTION + * + * Parser for the AT command syntax + * + */ +#ifndef AT_PARSER_OS_H +#define AT_PARSER_OS_H + +#include "mbed.h" +#include <cstdarg> +#include <vector> +#include "BufferedSerial.h" +#include "Callback.h" + + +/** +* Parser class for parsing AT commands +* +* Here are some examples: +* @code +* ATParser_os at = ATParser_os(serial, "\r\n"); +* int value; +* char buffer[100]; +* +* at.send("AT") && at.recv("OK"); +* at.send("AT+CWMODE=%d", 3) && at.recv("OK"); +* at.send("AT+CWMODE?") && at.recv("+CWMODE:%d\r\nOK", &value); +* at.recv("+IPD,%d:", &value); +* at.read(buffer, value); +* at.recv("OK"); +* @endcode +*/ +class ATParser_os +{ +private: + // Serial information + BufferedSerial *_serial; + int _buffer_size; + char *_buffer; + int _timeout; + + // Parsing information + const char *_delimiter; + int _delim_size; + bool dbg_on; + + struct oob { + unsigned len; + const char *prefix; + mbed::Callback<void()> cb; + }; + std::vector<oob> _oobs; + +public: + /** + * Constructor + * + * @param serial serial interface to use for AT commands + * @param buffer_size size of internal buffer for transaction + * @param timeout timeout of the connection + * @param delimiter string of characters to use as line delimiters + */ + ATParser_os(BufferedSerial &serial, const char *delimiter = "\r\n", int buffer_size = 256, int timeout = 8000, bool debug = false) : + _serial(&serial), + _buffer_size(buffer_size) { + _buffer = new char[buffer_size]; + setTimeout(timeout); + setDelimiter(delimiter); + debugOn(debug); + } + + /** + * Destructor + */ + ~ATParser_os() { + delete [] _buffer; + } + + /** + * Allows timeout to be changed between commands + * + * @param timeout timeout of the connection + */ + void setTimeout(int timeout) { + _timeout = timeout; + } + + /** + * Get timeout to be changed between commands + * + * @return timeout timeout of the connection + */ + int getTimeout() { + return _timeout; + } + + /** + * Sets string of characters to use as line delimiters + * + * @param delimiter string of characters to use as line delimiters + */ + void setDelimiter(const char *delimiter) { + _delimiter = delimiter; + _delim_size = strlen(delimiter); + } + + /** + * Allows echo to be on or off + * + * @param echo 1 for echo and 0 turns it off + */ + void debugOn(uint8_t on) { + dbg_on = (on) ? 1 : 0; + } + + /** + * Sends an AT command + * + * Sends a formatted command using printf style formatting + * @see ::printf + * + * @param command printf-like format string of command to send which + * is appended with the specified delimiter + * @param ... all printf-like arguments to insert into command + * @return true only if command is successfully sent + */ + bool send(const char *command, ...); + bool vsend(const char *command, va_list args); + + /** + * Recieve an AT response + * + * Recieves a formatted response using scanf style formatting + * @see ::scanf + * + * Responses are parsed line at a time using the specified delimiter. + * Any recieved data that does not match the response is ignored until + * a timeout occurs. + * + * @param response scanf-like format string of response to expect + * @param ... all scanf-like arguments to extract from response + * @return true only if response is successfully matched + */ + bool recv(const char *response, ...); + bool vrecv(const char *response, va_list args); + + /** + * Write a single byte to the underlying stream + * + * @param c The byte to write + * @return The byte that was written or -1 during a timeout + */ + int putc(char c); + + /** + * Get a single byte from the underlying stream + * + * @return The byte that was read or -1 during a timeout + */ + int getc(); + + /** + * Write an array of bytes to the underlying stream + * + * @param data the array of bytes to write + * @param size number of bytes to write + * @return number of bytes written or -1 on failure + */ + int write(const char *data, int size); + + /** + * Read an array of bytes from the underlying stream + * + * @param data the destination for the read bytes + * @param size number of bytes to read + * @return number of bytes read or -1 on failure + */ + int read(char *data, int size); + + /** + * Direct printf to underlying stream + * @see ::printf + * + * @param format format string to pass to printf + * @param ... arguments to printf + * @return number of bytes written or -1 on failure + */ + int printf(const char *format, ...); + int vprintf(const char *format, va_list args); + + /** + * Direct scanf on underlying stream + * @see ::scanf + * + * @param format format string to pass to scanf + * @param ... arguments to scanf + * @return number of bytes read or -1 on failure + */ + int scanf(const char *format, ...); + int vscanf(const char *format, va_list args); + + /** + * Attach a callback for out-of-band data + * + * @param prefix string on when to initiate callback + * @param func callback to call when string is read + * @note out-of-band data is only processed during a scanf call + */ + void oob(const char *prefix, mbed::Callback<void()> func); + + /** + * Attach a callback for out-of-band data + * + * @param prefix string on when to initiate callback + * @param obj pointer to object to call member function on + * @param method callback to call when string is read + * @note out-of-band data is only processed during a scanf call + */ + template <typename T, typename M> + void oob(const char *prefix, T *obj, M method) { + return oob(prefix, mbed::Callback<void()>(obj, method)); + } + + /** + * Flushes the underlying stream + */ + void flush(); +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ATParser_os/BufferedSerial/Buffer/MyBuffer.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,76 @@ + +/** + * @file Buffer.cpp + * @brief Software Buffer - Templated Ring Buffer for most data types + * @author sam grove + * @version 1.0 + * @see + * + * Copyright (c) 2013 + * + * 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 "MyBuffer.h" + +template <class T> +MyBuffer<T>::MyBuffer(uint32_t size) +{ + _buf = new T [size]; + _size = size; + clear(); + + return; +} + +template <class T> +MyBuffer<T>::~MyBuffer() +{ + delete [] _buf; + + return; +} + +template <class T> +uint32_t MyBuffer<T>::getSize() +{ + return this->_size; +} + +template <class T> +void MyBuffer<T>::clear(void) +{ + _wloc = 0; + _rloc = 0; + memset(_buf, 0, _size); + + return; +} + +template <class T> +uint32_t MyBuffer<T>::peek(char c) +{ + return 1; +} + +// make the linker aware of some possible types +template class MyBuffer<uint8_t>; +template class MyBuffer<int8_t>; +template class MyBuffer<uint16_t>; +template class MyBuffer<int16_t>; +template class MyBuffer<uint32_t>; +template class MyBuffer<int32_t>; +template class MyBuffer<uint64_t>; +template class MyBuffer<int64_t>; +template class MyBuffer<char>; +template class MyBuffer<wchar_t>;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ATParser_os/BufferedSerial/Buffer/MyBuffer.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,163 @@ + +/** + * @file Buffer.h + * @brief Software Buffer - Templated Ring Buffer for most data types + * @author sam grove + * @version 1.0 + * @see + * + * Copyright (c) 2013 + * + * 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. + */ + +#ifndef MYBUFFER_H +#define MYBUFFER_H + +#include <stdint.h> +#include <string.h> + +/** A templated software ring buffer + * + * Example: + * @code + * #include "mbed.h" + * #include "MyBuffer.h" + * + * MyBuffer <char> buf; + * + * int main() + * { + * buf = 'a'; + * buf.put('b'); + * char *head = buf.head(); + * puts(head); + * + * char whats_in_there[2] = {0}; + * int pos = 0; + * + * while(buf.available()) + * { + * whats_in_there[pos++] = buf; + * } + * printf("%c %c\n", whats_in_there[0], whats_in_there[1]); + * buf.clear(); + * error("done\n\n\n"); + * } + * @endcode + */ + +template <typename T> +class MyBuffer +{ +private: + T *_buf; + volatile uint32_t _wloc; + volatile uint32_t _rloc; + uint32_t _size; + +public: + /** Create a Buffer and allocate memory for it + * @param size The size of the buffer + */ + MyBuffer(uint32_t size = 0x100); + + /** Get the size of the ring buffer + * @return the size of the ring buffer + */ + uint32_t getSize(); + + /** Destry a Buffer and release it's allocated memory + */ + ~MyBuffer(); + + /** Add a data element into the buffer + * @param data Something to add to the buffer + */ + void put(T data); + + /** Remove a data element from the buffer + * @return Pull the oldest element from the buffer + */ + T get(void); + + /** Get the address to the head of the buffer + * @return The address of element 0 in the buffer + */ + T *head(void); + + /** Reset the buffer to 0. Useful if using head() to parse packeted data + */ + void clear(void); + + /** Determine if anything is readable in the buffer + * @return 1 if something can be read, 0 otherwise + */ + uint32_t available(void); + + /** Overloaded operator for writing to the buffer + * @param data Something to put in the buffer + * @return + */ + MyBuffer &operator= (T data) + { + put(data); + return *this; + } + + /** Overloaded operator for reading from the buffer + * @return Pull the oldest element from the buffer + */ + operator int(void) + { + return get(); + } + + uint32_t peek(char c); + +}; + +template <class T> +inline void MyBuffer<T>::put(T data) +{ + _buf[_wloc++] = data; + _wloc %= (_size-1); + + return; +} + +template <class T> +inline T MyBuffer<T>::get(void) +{ + T data_pos = _buf[_rloc++]; + _rloc %= (_size-1); + + return data_pos; +} + +template <class T> +inline T *MyBuffer<T>::head(void) +{ + T *data_pos = &_buf[0]; + + return data_pos; +} + +template <class T> +inline uint32_t MyBuffer<T>::available(void) +{ + return (_wloc == _rloc) ? 0 : 1; +} + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ATParser_os/BufferedSerial/BufferedPrint.c Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014-2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "mbed_error.h" + +size_t BufferedSerialThunk(void *buf_serial, const void *s, size_t length); + +int BufferedPrintfC(void *stream, int size, const char* format, va_list arg) +{ + int r; + char buffer[512]; + if (size >= 512) { + return -1; + } + memset(buffer, 0, size); + r = vsprintf(buffer, format, arg); + // this may not hit the heap but should alert the user anyways + if(r > (int32_t) size) { + error("%s %d buffer overwrite (max_buf_size: %d exceeded: %d)!\r\n", __FILE__, __LINE__, size, r); + return 0; + } + if ( r > 0 ) { + BufferedSerialThunk(stream, buffer, r); + } + return r; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ATParser_os/BufferedSerial/BufferedSerial.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,166 @@ +/** + * @file BufferedSerial.cpp + * @brief Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX + * @author sam grove + * @version 1.0 + * @see + * + * Copyright (c) 2013 + * + * 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 "BufferedSerial.h" +#include <stdarg.h> + +extern "C" int BufferedPrintfC(void *stream, int size, const char* format, va_list arg); + +BufferedSerial::BufferedSerial(PinName tx, PinName rx, uint32_t buf_size, uint32_t tx_multiple, const char* name) + : RawSerial(tx, rx) , _rxbuf(buf_size), _txbuf((uint32_t)(tx_multiple*buf_size)) +{ + RawSerial::attach(this, &BufferedSerial::rxIrq, Serial::RxIrq); + this->_buf_size = buf_size; + this->_tx_multiple = tx_multiple; + return; +} + +BufferedSerial::~BufferedSerial(void) +{ + RawSerial::attach(NULL, RawSerial::RxIrq); + RawSerial::attach(NULL, RawSerial::TxIrq); + + return; +} + +int BufferedSerial::readable(void) +{ + return _rxbuf.available(); // note: look if things are in the buffer +} + +int BufferedSerial::writeable(void) +{ + return 1; // buffer allows overwriting by design, always true +} + +int BufferedSerial::getc(void) +{ + return _rxbuf; +} + +int BufferedSerial::putc(int c) +{ + _txbuf = (char)c; + BufferedSerial::prime(); + + return c; +} + +int BufferedSerial::puts(const char *s) +{ + if (s != NULL) { + const char* ptr = s; + + while(*(ptr) != 0) { + _txbuf = *(ptr++); + } + _txbuf = '\n'; // done per puts definition + BufferedSerial::prime(); + + return (ptr - s) + 1; + } + return 0; +} + +extern "C" size_t BufferedSerialThunk(void *buf_serial, const void *s, size_t length) +{ + BufferedSerial *buffered_serial = (BufferedSerial *)buf_serial; + return buffered_serial->write(s, length); +} + +int BufferedSerial::printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int r = BufferedPrintfC((void*)this, this->_buf_size, format, arg); + va_end(arg); + return r; +} + +ssize_t BufferedSerial::write(const void *s, size_t length) +{ + if (s != NULL && length > 0) { + const char* ptr = (const char*)s; + const char* end = ptr + length; + + while (ptr != end) { + _txbuf = *(ptr++); + } + BufferedSerial::prime(); + + return ptr - (const char*)s; + } + return 0; +} + + +void BufferedSerial::rxIrq(void) +{ + // read from the peripheral and make sure something is available + if(serial_readable(&_serial)) { + _rxbuf = serial_getc(&_serial); // if so load them into a buffer + // trigger callback if necessary + if (_cbs[RxIrq]) { + _cbs[RxIrq](); + } + } + + return; +} + +void BufferedSerial::txIrq(void) +{ + // see if there is room in the hardware fifo and if something is in the software fifo + while(serial_writable(&_serial)) { + if(_txbuf.available()) { + serial_putc(&_serial, (int)_txbuf.get()); + } else { + // disable the TX interrupt when there is nothing left to send + RawSerial::attach(NULL, RawSerial::TxIrq); + // trigger callback if necessary + if (_cbs[TxIrq]) { + _cbs[TxIrq](); + } + break; + } + } + + return; +} + +void BufferedSerial::prime(void) +{ + // if already busy then the irq will pick this up + if(serial_writable(&_serial)) { + RawSerial::attach(NULL, RawSerial::TxIrq); // make sure not to cause contention in the irq + BufferedSerial::txIrq(); // only write to hardware in one place + RawSerial::attach(this, &BufferedSerial::txIrq, RawSerial::TxIrq); + } + + return; +} + +void BufferedSerial::attach(Callback<void()> func, IrqType type) +{ + _cbs[type] = func; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ATParser_os/BufferedSerial/BufferedSerial.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,168 @@ + +/** + * @file BufferedSerial.h + * @brief Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX + * @author sam grove + * @version 1.0 + * @see + * + * Copyright (c) 2013 + * + * 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. + */ + +#ifndef BUFFEREDSERIAL_H +#define BUFFEREDSERIAL_H + +#include "mbed.h" +#include "MyBuffer.h" + +/** A serial port (UART) for communication with other serial devices + * + * Can be used for Full Duplex communication, or Simplex by specifying + * one pin as NC (Not Connected) + * + * Example: + * @code + * #include "mbed.h" + * #include "BufferedSerial.h" + * + * BufferedSerial pc(USBTX, USBRX); + * + * int main() + * { + * while(1) + * { + * Timer s; + * + * s.start(); + * pc.printf("Hello World - buffered\n"); + * int buffered_time = s.read_us(); + * wait(0.1f); // give time for the buffer to empty + * + * s.reset(); + * printf("Hello World - blocking\n"); + * int polled_time = s.read_us(); + * s.stop(); + * wait(0.1f); // give time for the buffer to empty + * + * pc.printf("printf buffered took %d us\n", buffered_time); + * pc.printf("printf blocking took %d us\n", polled_time); + * wait(0.5f); + * } + * } + * @endcode + */ + +/** + * @class BufferedSerial + * @brief Software buffers and interrupt driven tx and rx for Serial + */ +class BufferedSerial : public RawSerial +{ +private: + MyBuffer <char> _rxbuf; + MyBuffer <char> _txbuf; + uint32_t _buf_size; + uint32_t _tx_multiple; + + void rxIrq(void); + void txIrq(void); + void prime(void); + + Callback<void()> _cbs[2]; + +public: + /** Create a BufferedSerial port, connected to the specified transmit and receive pins + * @param tx Transmit pin + * @param rx Receive pin + * @param buf_size printf() buffer size + * @param tx_multiple amount of max printf() present in the internal ring buffer at one time + * @param name optional name + * @note Either tx or rx may be specified as NC if unused + */ + BufferedSerial(PinName tx, PinName rx, uint32_t buf_size = 256, uint32_t tx_multiple = 4,const char* name=NULL); + + /** Destroy a BufferedSerial port + */ + virtual ~BufferedSerial(void); + + /** Check on how many bytes are in the rx buffer + * @return 1 if something exists, 0 otherwise + */ + virtual int readable(void); + + /** Check to see if the tx buffer has room + * @return 1 always has room and can overwrite previous content if too small / slow + */ + virtual int writeable(void); + + /** Get a single byte from the BufferedSerial Port. + * Should check readable() before calling this. + * @return A byte that came in on the Serial Port + */ + virtual int getc(void); + + /** Write a single byte to the BufferedSerial Port. + * @param c The byte to write to the Serial Port + * @return The byte that was written to the Serial Port Buffer + */ + virtual int putc(int c); + + /** Write a string to the BufferedSerial Port. Must be NULL terminated + * @param s The string to write to the Serial Port + * @return The number of bytes written to the Serial Port Buffer + */ + virtual int puts(const char *s); + + /** Write a formatted string to the BufferedSerial Port. + * @param format The string + format specifiers to write to the Serial Port + * @return The number of bytes written to the Serial Port Buffer + */ + virtual int printf(const char* format, ...); + + /** Write data to the Buffered Serial Port + * @param s A pointer to data to send + * @param length The amount of data being pointed to + * @return The number of bytes written to the Serial Port Buffer + */ + virtual ssize_t write(const void *s, std::size_t length); + + /** Attach a function to call whenever a serial interrupt is generated + * @param func A pointer to a void function, or 0 to set as none + * @param type Which serial interrupt to attach the member function to (Serial::RxIrq for receive, TxIrq for transmit buffer empty) + */ + virtual void attach(Callback<void()> func, IrqType type=RxIrq); + + /** Attach a member function to call whenever a serial interrupt is generated + * @param obj pointer to the object to call the member function on + * @param method pointer to the member function to call + * @param type Which serial interrupt to attach the member function to (Serial::RxIrq for receive, TxIrq for transmit buffer empty) + */ + template <typename T> + void attach(T *obj, void (T::*method)(), IrqType type=RxIrq) { + attach(Callback<void()>(obj, method), type); + } + + /** Attach a member function to call whenever a serial interrupt is generated + * @param obj pointer to the object to call the member function on + * @param method pointer to the member function to call + * @param type Which serial interrupt to attach the member function to (Serial::RxIrq for receive, TxIrq for transmit buffer empty) + */ + template <typename T> + void attach(T *obj, void (*method)(T*), IrqType type=RxIrq) { + attach(Callback<void()>(obj, method), type); + } +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ESP32.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,838 @@ +/* ESP32 Example + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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 "ESP32.h" + +ESP32 * ESP32::instESP32 = NULL; + +ESP32 * ESP32::getESP32Inst(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) +{ + if (instESP32 == NULL) { + instESP32 = new ESP32(en, io0, tx, rx, debug, rts, cts, baudrate); + } else { + if (debug) { + instESP32->debugOn(debug); + } + } + return instESP32; +} + +ESP32::ESP32(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) + : wifi_en(en), wifi_io0(io0), init_end(false) + , _serial(tx, rx, 1024), _parser(_serial) + , _packets(0), _packets_end(&_packets) + , _id_bits(0), _id_bits_close(0),_server_act(false) +{ + _wifi_mode = 1; + _baudrate = baudrate; + memset(_ids, 0, sizeof(_ids)); + memset(_cbs, 0, sizeof(_cbs)); + + _rts = rts; + _cts = cts; + + if ((_rts != NC) && (_cts != NC)) { + _flow_control = 3; + } else if (_rts != NC) { + _flow_control = 1; + } else if (_cts != NC) { + _flow_control = 2; + } else { + _flow_control = 0; + } + + _serial.baud(115200); + _parser.debugOn(debug); + _serial.attach(Callback<void()>(this, &ESP32::event)); +} + +void ESP32::debugOn(bool debug) +{ + _parser.debugOn(debug); +} + +bool ESP32::startup() +{ + if (init_end) { + return true; + } + + wifi_io0 = 1; + wifi_en = 0; + Thread::wait(10); + wifi_en = 1; + + _parser.setTimeout(1500); + _parser.recv("ready"); + reset(); + _parser.setTimeout(5000); + bool success = _parser.send("AT+CWMODE=%d", _wifi_mode) + && _parser.recv("OK") + && _parser.send("AT+CIPMUX=1") + && _parser.recv("OK") + && _parser.send("AT+CWAUTOCONN=0") + && _parser.recv("OK") + && _parser.send("AT+CWQAP") + && _parser.recv("OK"); + + if (success) { + _parser.oob("+IPD", this, &ESP32::_packet_handler); + _parser.oob("0,CONNECT", this, &ESP32::_connect_handler_0); + _parser.oob("1,CONNECT", this, &ESP32::_connect_handler_1); + _parser.oob("2,CONNECT", this, &ESP32::_connect_handler_2); + _parser.oob("3,CONNECT", this, &ESP32::_connect_handler_3); + _parser.oob("4,CONNECT", this, &ESP32::_connect_handler_4); + _parser.oob("0,CLOSED", this, &ESP32::_closed_handler_0); + _parser.oob("1,CLOSED", this, &ESP32::_closed_handler_1); + _parser.oob("2,CLOSED", this, &ESP32::_closed_handler_2); + _parser.oob("3,CLOSED", this, &ESP32::_closed_handler_3); + _parser.oob("4,CLOSED", this, &ESP32::_closed_handler_4); + init_end = true; + } + + return success; +} + +bool ESP32::restart() +{ + bool success; + + _smutex.lock(); + if (!init_end) { + success = startup(); + } else { + reset(); + _parser.setTimeout(5000); + success = _parser.send("AT+CWMODE=%d", _wifi_mode) + && _parser.recv("OK") + && _parser.send("AT+CIPMUX=1") + && _parser.recv("OK"); + } + _smutex.unlock(); + + return success; +} + +bool ESP32::set_mode(int mode) +{ + //only 3 valid modes + if (mode < 1 || mode > 3) { + return false; + } + _wifi_mode = mode; + return restart(); +} + +bool ESP32::cre_server(int port) +{ + if (_server_act) { + return false; + } + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + if (!(_parser.send("AT+CIPSERVER=1,%d", port) + && _parser.recv("OK"))) { + _smutex.unlock(); + return false; + } + _server_act = true; + _smutex.unlock(); + return true; +} + +bool ESP32::del_server() +{ + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + if (!(_parser.send("AT+CIPSERVER=0") + && _parser.recv("OK"))) { + _smutex.unlock(); + return false; + } + _server_act = false; + _smutex.unlock(); + return true; +} + +void ESP32::socket_handler(bool connect, int id) +{ + _smutex.lock(); + startup(); + _cbs[id].Notified = 0; + if (connect) { + _id_bits |= (1 << id); + if (_server_act) { + _accept_id.push_back(id); + } + } else { + _id_bits &= ~(1 << id); + _id_bits_close |= (1 << id); + if (_server_act) { + for (size_t i = 0; i < _accept_id.size(); i++) { + if (id == _accept_id[i]) { + _accept_id.erase(_accept_id.begin() + i); + } + } + } + } + _smutex.unlock(); +} + +void ESP32::_connect_handler_0() { socket_handler(true, 0); } +void ESP32::_connect_handler_1() { socket_handler(true, 1); } +void ESP32::_connect_handler_2() { socket_handler(true, 2); } +void ESP32::_connect_handler_3() { socket_handler(true, 3); } +void ESP32::_connect_handler_4() { socket_handler(true, 4); } +void ESP32::_closed_handler_0() { socket_handler(false, 0); } +void ESP32::_closed_handler_1() { socket_handler(false, 1); } +void ESP32::_closed_handler_2() { socket_handler(false, 2); } +void ESP32::_closed_handler_3() { socket_handler(false, 3); } +void ESP32::_closed_handler_4() { socket_handler(false, 4); } + +bool ESP32::accept(int * p_id) +{ + bool ret = false; + + while (!ret) { + if (!_server_act) { + break; + } + _smutex.lock(); + startup(); + if (!_accept_id.empty()) { + ret = true; + } else { + _parser.setTimeout(2); + _parser.recv("%*,CONNECT"); + if (!_accept_id.empty()) { + ret = true; + } + } + if (ret) { + *p_id = _accept_id[0]; + _accept_id.erase(_accept_id.begin()); + } + _smutex.unlock(); + Thread::wait(1); + } + + if (ret) { + for (int i = 0; i < 50; i++) { + if ((_id_bits_close & (1 << *p_id)) == 0) { + break; + } + Thread::wait(10); + } + } + + return ret; +} + +bool ESP32::reset(void) +{ + for (int i = 0; i < 2; i++) { + _parser.setTimeout(1500); + if (_parser.send("AT+RST") + && _parser.recv("OK")) { + _serial.baud(115200); +#if DEVICE_SERIAL_FC + _serial.set_flow_control(SerialBase::Disabled); +#endif + _parser.recv("ready"); + + if (_parser.send("AT+UART_CUR=%d,8,1,0,%d", _baudrate, _flow_control) + && _parser.recv("OK")) { + _serial.baud(_baudrate); +#if DEVICE_SERIAL_FC + switch (_flow_control) { + case 1: + _serial.set_flow_control(SerialBase::RTS, _rts); + break; + case 2: + _serial.set_flow_control(SerialBase::CTS, _cts); + break; + case 3: + _serial.set_flow_control(SerialBase::RTSCTS, _rts, _cts); + break; + case 0: + default: + // do nothing + break; + } +#endif + } + + return true; + } + } + + return false; +} + +bool ESP32::dhcp(bool enabled, int mode) +{ + bool ret; + + //only 3 valid modes + if (mode < 0 || mode > 2) { + return false; + } + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CWDHCP=%d,%d", enabled?1:0, mode) + && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +bool ESP32::connect(const char *ap, const char *passPhrase) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + + ret = _parser.send("AT+CWAUTOCONN=1") + && _parser.recv("OK") + && _parser.send("AT+CWJAP=\"%s\",\"%s\"", ap, passPhrase) + && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +bool ESP32::config_soft_ap(const char *ap, const char *passPhrase, uint8_t chl, uint8_t ecn) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CWSAP=\"%s\",\"%s\",%hhu,%hhu", ap, passPhrase, chl, ecn) + && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +bool ESP32::get_ssid(const char *ap) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(500); + ret = _parser.send("AT+CWJAP?") + && _parser.recv("+CWJAP:\"%33[^\"]\",", ap) + && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +bool ESP32::disconnect(void) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CWQAP") && _parser.recv("OK"); + _smutex.unlock(); + return ret; +} + +const char *ESP32::getIPAddress(void) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:STAIP,\"%15[^\"]\"", _ip_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + if (!ret) { + return 0; + } + return _ip_buffer; +} + +const char *ESP32::getIPAddress_ap(void) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:APIP,\"%15[^\"]\"", _ip_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + if (!ret) { + return 0; + } + return _ip_buffer_ap; +} + +const char *ESP32::getMACAddress(void) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:STAMAC,\"%17[^\"]\"", _mac_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _mac_buffer; +} + +const char *ESP32::getMACAddress_ap(void) +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:APMAC,\"%17[^\"]\"", _mac_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _mac_buffer_ap; +} + +const char *ESP32::getGateway() +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIPSTA?") + && _parser.recv("+CIPSTA:gateway:\"%15[^\"]\"", _gateway_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _gateway_buffer; +} + +const char *ESP32::getGateway_ap() +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIPAP?") + && _parser.recv("+CIPAP:gateway:\"%15[^\"]\"", _gateway_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _gateway_buffer_ap; +} + +const char *ESP32::getNetmask() +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIPSTA?") + && _parser.recv("+CIPSTA:netmask:\"%15[^\"]\"", _netmask_buffer) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _netmask_buffer; +} + +const char *ESP32::getNetmask_ap() +{ + bool ret; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIPAP?") + && _parser.recv("+CIPAP:netmask:\"%15[^\"]\"", _netmask_buffer_ap) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + return _netmask_buffer_ap; +} + +int8_t ESP32::getRSSI() +{ + bool ret; + int8_t rssi; + char ssid[33]; + char bssid[18]; + + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CWJAP?") + && _parser.recv("+CWJAP:\"%32[^\"]\",\"%17[^\"]\"", ssid, bssid) + && _parser.recv("OK"); + if (!ret) { + _smutex.unlock(); + return 0; + } + + ret = _parser.send("AT+CWLAP=\"%s\",\"%s\"", ssid, bssid) + && _parser.recv("+CWLAP:(%*d,\"%*[^\"]\",%hhd,", &rssi) + && _parser.recv("OK"); + _smutex.unlock(); + + if (!ret) { + return 0; + } + + return rssi; +} + +bool ESP32::isConnected(void) +{ + return getIPAddress() != 0; +} + +int ESP32::scan(WiFiAccessPoint *res, unsigned limit) +{ + unsigned cnt = 0; + nsapi_wifi_ap_t ap; + + if (!init_end) { + _smutex.lock(); + startup(); + _smutex.unlock(); + Thread::wait(1500); + } + + _smutex.lock(); + _parser.setTimeout(5000); + if (!_parser.send("AT+CWLAP")) { + _smutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } + + while (recv_ap(&ap)) { + if (cnt < limit) { + res[cnt] = WiFiAccessPoint(ap); + } + + cnt++; + if ((limit != 0) && (cnt >= limit)) { + break; + } + _parser.setTimeout(500); + } + _parser.setTimeout(10); + _parser.recv("OK"); + _smutex.unlock(); + + return cnt; +} + +bool ESP32::open(const char *type, int id, const char* addr, int port) +{ + bool ret; + + //IDs only 0-4 + if (id > 4) { + return false; + } + _cbs[id].Notified = 0; + + _smutex.lock(); + startup(); + _parser.setTimeout(500); + ret = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port) + && _parser.recv("OK"); + _smutex.unlock(); + + return ret; +} + +bool ESP32::send(int id, const void *data, uint32_t amount) +{ + uint32_t send_size; + bool ret; + int error_cnt = 0; + int index = 0; + + _cbs[id].Notified = 0; + if (amount == 0) { + return true; + } + + //May take a second try if device is busy + while (error_cnt < 2) { + if (((_id_bits & (1 << id)) == 0) + || ((_id_bits_close & (1 << id)) != 0)) { + return false; + } + send_size = amount; + if (send_size > 2048) { + send_size = 2048; + } + _smutex.lock(); + startup(); + _parser.setTimeout(3000); + ret = _parser.send("AT+CIPSEND=%d,%d", id, send_size) + && _parser.recv(">") + && (_parser.write((char*)data + index, (int)send_size) >= 0) + && _parser.recv("SEND OK"); + _smutex.unlock(); + if (ret) { + amount -= send_size; + index += send_size; + error_cnt = 0; + if (amount == 0) { + return true; + } + } else { + error_cnt++; + } + } + + return false; +} + +void ESP32::_packet_handler() +{ + int id; + uint32_t amount; + int tmp_timeout; + + startup(); + // parse out the packet + if (!_parser.recv(",%d,%d:", &id, &amount)) { + return; + } + + struct packet *packet = (struct packet*)malloc( + sizeof(struct packet) + amount); + if (!packet) { + return; + } + + packet->id = id; + packet->len = amount; + packet->next = 0; + packet->index = 0; + + tmp_timeout = _parser.getTimeout(); + _parser.setTimeout(500); + if (!(_parser.read((char*)(packet + 1), amount))) { + free(packet); + _parser.setTimeout(tmp_timeout); + return; + } + _parser.setTimeout(tmp_timeout); + + // append to packet list + *_packets_end = packet; + _packets_end = &packet->next; +} + +int32_t ESP32::recv(int id, void *data, uint32_t amount) +{ + bool retry = false; + _cbs[id].Notified = 0; + + while (true) { + // check if any packets are ready for us + for (struct packet **p = &_packets; *p; p = &(*p)->next) { + if ((*p)->id == id) { + struct packet *q = *p; + + if (q->len <= amount) { // Return and remove full packet + memcpy(data, (uint8_t*)(q+1) + q->index, q->len); + + if (_packets_end == &(*p)->next) { + _packets_end = p; + } + *p = (*p)->next; + + uint32_t len = q->len; + free(q); + return len; + } else { // return only partial packet + memcpy(data, (uint8_t*)(q+1) + q->index, amount); + + q->len -= amount; + q->index += amount; + + return amount; + } + } + } + + // Wait for inbound packet + _smutex.lock(); + startup(); + _parser.setTimeout(2); + if (!_parser.recv("OK")) { + _smutex.unlock(); + if (retry) { + if (((_id_bits & (1 << id)) == 0) + || ((_id_bits_close & (1 << id)) != 0)) { + return -2; + } else { + return -1; + } + } + retry = true; + } else { + _smutex.unlock(); + } + } +} + +bool ESP32::close(int id, bool wait_close) +{ + if (wait_close) { + for (int j = 0; j < 50; j++) { + _smutex.lock(); + startup(); + _parser.setTimeout(5); + _parser.recv("%*,CLOSED"); + if (((_id_bits & (1 << id)) == 0) + || ((_id_bits_close & (1 << id)) != 0)) { + _smutex.unlock(); + _id_bits_close &= ~(1 << id); + _ids[id] = false; + return true; + } + _smutex.unlock(); + Thread::wait(10); + } + } + + //May take a second try if device is busy + for (unsigned i = 0; i < 2; i++) { + _smutex.lock(); + startup(); + _parser.setTimeout(500); + if ((_id_bits & (1 << id)) == 0) { + _smutex.unlock(); + _id_bits_close &= ~(1 << id); + _ids[id] = false; + return true; + } + if (_parser.send("AT+CIPCLOSE=%d", id) + && _parser.recv("OK")) { + _smutex.unlock(); + _id_bits_close &= ~(1 << id); + _ids[id] = false; + return true; + } + _smutex.unlock(); + } + + _ids[id] = false; + return false; +} + +void ESP32::setTimeout(uint32_t timeout_ms) +{ + _parser.setTimeout(timeout_ms); +} + +bool ESP32::readable() +{ + return _serial.readable(); +} + +bool ESP32::writeable() +{ + return _serial.writeable(); +} + +void ESP32::attach(int id, void (*callback)(void *), void *data) +{ + _cbs[id].callback = callback; + _cbs[id].data = data; + _cbs[id].Notified = 0; +} + +int ESP32::get_free_id() +{ + // Look for an unused socket + int id = -1; + + for (int i = 0; i < ESP32_SOCKET_COUNT; i++) { + if ((!_ids[i]) && ((_id_bits & (1 << i)) == 0)) { + id = i; + _ids[i] = true; + break; + } + } + + return id; +} + +void ESP32::event() { + for (int i = 0; i < ESP32_SOCKET_COUNT; i++) { + if ((_cbs[i].callback) && (_cbs[i].Notified == 0)) { + _cbs[i].callback(_cbs[i].data); + _cbs[i].Notified = 1; + } + } +} + +bool ESP32::recv_ap(nsapi_wifi_ap_t *ap) +{ + int sec; + bool ret; + + Thread::wait(5); + ret = _parser.recv("+CWLAP:(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%d)", &sec, ap->ssid, + &ap->rssi, &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], + &ap->bssid[5], &ap->channel); + + ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN; + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32/ESP32.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,257 @@ +/* ESP32Interface Example + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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. + */ + +#ifndef ESP32_H +#define ESP32_H + +#include "ATParser_os.h" + +#define ESP32_SOCKET_COUNT 5 + +/** ESP32Interface class. + This is an interface to a ESP32 radio. + */ +class ESP32 +{ +public: + /** + * Static method to create or retrieve the single ESP32 instance + */ + static ESP32 * getESP32Inst(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate); + + ESP32(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate); + + /** + * Sets the Wi-Fi Mode + * + * @param mode mode of WIFI 1-client, 2-host, 3-both + * @return true only if ESP32 was setup correctly + */ + bool set_mode(int mode); + + /** + * Enable/Disable DHCP + * + * @param enabled DHCP enabled when true + * @param mode mode of DHCP 0-softAP, 1-station, 2-both + * @return true only if ESP32 enables/disables DHCP successfully + */ + bool dhcp(bool enabled, int mode); + + /** + * Connect ESP32 to AP + * + * @param ap the name of the AP + * @param passPhrase the password of AP + * @return true only if ESP32 is connected successfully + */ + bool connect(const char *ap, const char *passPhrase); + + /** + * Disconnect ESP32 from AP + * + * @return true only if ESP32 is disconnected successfully + */ + bool disconnect(void); + + /** + * Get the IP address of ESP32 + * + * @return null-teriminated IP address or null if no IP address is assigned + */ + const char *getIPAddress(void); + const char *getIPAddress_ap(void); + + /** + * Get the MAC address of ESP32 + * + * @return null-terminated MAC address or null if no MAC address is assigned + */ + const char *getMACAddress(void); + const char *getMACAddress_ap(void); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been recieved + */ + const char *getGateway(); + const char *getGateway_ap(); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been recieved + */ + const char *getNetmask(); + const char *getNetmask_ap(); + + /* Return RSSI for active connection + * + * @return Measured RSSI + */ + int8_t getRSSI(); + + /** + * Check if ESP32 is conenected + * + * @return true only if the chip has an IP address + */ + bool isConnected(void); + + /** Scan for available networks + * + * @param ap Pointer to allocated array to store discovered AP + * @param limit Size of allocated @a res array, or 0 to only count available AP + * @return Number of entries in @a res, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + int scan(WiFiAccessPoint *res, unsigned limit); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "UDP" or "TCP" + * @param id id to give the new socket, valid 0-4 + * @param port port to open connection with + * @param addr the IP address of the destination + * @return true only if socket opened successfully + */ + bool open(const char *type, int id, const char* addr, int port); + + /** + * Sends data to an open socket + * + * @param id id of socket to send to + * @param data data to be sent + * @param amount amount of data to be sent - max 1024 + * @return true only if data sent successfully + */ + bool send(int id, const void *data, uint32_t amount); + + /** + * Receives data from an open socket + * + * @param id id to receive from + * @param data placeholder for returned information + * @param amount number of bytes to be received + * @return the number of bytes received + */ + int32_t recv(int id, void *data, uint32_t amount); + + /** + * Closes a socket + * + * @param id id of socket to close, valid only 0-4 + * @param wait_close + * @return true only if socket is closed successfully + */ + bool close(int id, bool wait_close); + + /** + * Allows timeout to be changed between commands + * + * @param timeout_ms timeout of the connection + */ + void setTimeout(uint32_t timeout_ms); + + /** + * Checks if data is available + */ + bool readable(); + + /** + * Checks if data can be written + */ + bool writeable(); + + void attach(int id, void (*callback)(void *), void *data); + int get_free_id(); + + bool config_soft_ap(const char *ap, const char *passPhrase, uint8_t chl, uint8_t ecn); + + bool restart(); + bool get_ssid(const char *ap); + bool cre_server(int port); + bool del_server(); + bool accept(int * p_id); + +private: + DigitalOut wifi_en; + DigitalOut wifi_io0; + bool init_end; + BufferedSerial _serial; + ATParser_os _parser; + struct packet { + struct packet *next; + int id; + uint32_t len; + uint32_t index; + // data follows + } *_packets, **_packets_end; + int _wifi_mode; + int _baudrate; + PinName _rts; + PinName _cts; + int _flow_control; + + std::vector<int> _accept_id; + uint32_t _id_bits; + uint32_t _id_bits_close; + bool _server_act; + Mutex _smutex; + static ESP32 * instESP32; + + bool _ids[ESP32_SOCKET_COUNT]; + struct { + void (*callback)(void *); + void *data; + int Notified; + } _cbs[ESP32_SOCKET_COUNT]; + + bool startup(); + bool reset(void); + void debugOn(bool debug); + void socket_handler(bool connect, int id); + void _connect_handler_0(); + void _connect_handler_1(); + void _connect_handler_2(); + void _connect_handler_3(); + void _connect_handler_4(); + void _closed_handler_0(); + void _closed_handler_1(); + void _closed_handler_2(); + void _closed_handler_3(); + void _closed_handler_4(); + void _packet_handler(); + void event(); + bool recv_ap(nsapi_wifi_ap_t *ap); + + char _ip_buffer[16]; + char _gateway_buffer[16]; + char _netmask_buffer[16]; + char _mac_buffer[18]; + + char _ip_buffer_ap[16]; + char _gateway_buffer_ap[16]; + char _netmask_buffer_ap[16]; + char _mac_buffer_ap[18]; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32Interface.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,133 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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 "ESP32Interface.h" + +// ESP32Interface implementation +ESP32Interface::ESP32Interface(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) + : ESP32Stack(en, io0, tx, rx, debug, rts, cts, baudrate) +{ +} + +int ESP32Interface::connect(const char *ssid, const char *pass, nsapi_security_t security, + uint8_t channel) +{ + if (channel != 0) { + return NSAPI_ERROR_UNSUPPORTED; + } + + set_credentials(ssid, pass, security); + return connect(); +} + +int ESP32Interface::connect() +{ + char wk_ssid[33]; + size_t len; + + if (!_esp->dhcp(true, 1)) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + _esp->disconnect(); + if (!_esp->connect(ap_ssid, ap_pass)) { + if (!_esp->restart()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + if (!_esp->dhcp(true, 1)) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + for (int i = 0; i < 10; i++) { + if (_esp->get_ssid(wk_ssid)) { + len = strlen(wk_ssid); + if ((strlen(ap_ssid) == len) && (memcmp(ap_ssid, wk_ssid, len) == 0)) { + break; + } else { + return NSAPI_ERROR_NO_CONNECTION; + } + } else { + Thread::wait(1000); + } + } + } + + if (!_esp->getIPAddress()) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + return NSAPI_ERROR_OK; +} + +int ESP32Interface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security) +{ + memset(ap_ssid, 0, sizeof(ap_ssid)); + strncpy(ap_ssid, ssid, sizeof(ap_ssid)); + + memset(ap_pass, 0, sizeof(ap_pass)); + strncpy(ap_pass, pass, sizeof(ap_pass)); + + ap_sec = security; + + return 0; +} + +int ESP32Interface::set_channel(uint8_t channel) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ESP32Interface::disconnect() +{ + if (!_esp->disconnect()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return NSAPI_ERROR_OK; +} + +const char *ESP32Interface::get_ip_address() +{ + return _esp->getIPAddress(); +} + +const char *ESP32Interface::get_mac_address() +{ + return _esp->getMACAddress(); +} + +const char *ESP32Interface::get_gateway() +{ + return _esp->getGateway(); +} + +const char *ESP32Interface::get_netmask() +{ + return _esp->getNetmask(); +} + +int8_t ESP32Interface::get_rssi() +{ + return _esp->getRSSI(); +} + +int ESP32Interface::scan(WiFiAccessPoint *res, unsigned count) +{ + return _esp->scan(res, count); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32Interface.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,169 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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. + */ + +#ifndef ESP32_INTERFACE_H +#define ESP32_INTERFACE_H + +#include "mbed.h" +#include "ESP32Stack.h" + +/** ESP32Interface class + * Implementation of the NetworkStack for the ESP32 + */ +class ESP32Interface : public ESP32Stack, public WiFiInterface +{ +public: + /** ESP32Interface lifetime + * @param en EN pin + * @param io0 IO0 pin + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + * @param rts RTS pin + * @param cts CTS pin + * @param baudrate The baudrate of the serial port (default = 230400). + */ + ESP32Interface(PinName en, PinName io0, PinName tx, PinName rx, bool debug = false, + PinName rts = NC, PinName cts = NC, int baudrate = 230400); + + /** Start the interface + * + * Attempts to connect to a WiFi network. Requires ssid and passphrase to be set. + * If passphrase is invalid, NSAPI_ERROR_AUTH_ERROR is returned. + * + * @return 0 on success, negative error code on failure + */ + virtual int connect(); + + /** Start the interface + * + * Attempts to connect to a WiFi network. + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE) + * @param channel This parameter is not supported, setting it to anything else than 0 will result in NSAPI_ERROR_UNSUPPORTED + * @return 0 on success, or error code on failure + */ + virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE, + uint8_t channel = 0); + + /** Set the WiFi network credentials + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * (defaults to NSAPI_SECURITY_NONE) + * @return 0 on success, or error code on failure + */ + virtual int set_credentials(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE); + + /** Set the WiFi network channel - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param channel Channel on which the connection is to be made, or 0 for any (Default: 0) + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual int set_channel(uint8_t channel); + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual int disconnect(); + + /** Get the internally stored IP address + * @return IP address of the interface or null if not yet connected + */ + virtual const char *get_ip_address(); + + /** Get the internally stored MAC address + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been recieved + */ + virtual const char *get_gateway(); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been recieved + */ + virtual const char *get_netmask(); + + /** Gets the current radio signal strength for active connection + * + * @return Connection strength in dBm (negative value) + */ + virtual int8_t get_rssi(); + + /** Scan for available networks + * + * This function will block. + * + * @param ap Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @param timeout Timeout in milliseconds; 0 for no timeout (Default: 0) + * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + virtual int scan(WiFiAccessPoint *res, unsigned count); + + /** Translates a hostname to an IP address with specific version + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param address Destination for the host SocketAddress + * @param host Hostname to resolve + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::gethostbyname; + + /** Add a domain name server to list of servers to query + * + * @param addr Destination for the host address + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::add_dns_server; + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() + { + return this; + } + +protected: + char ap_ssid[33]; /* 32 is what 802.11 defines as longest possible name; +1 for the \0 */ + nsapi_security_t ap_sec; + uint8_t ap_ch; + char ap_pass[64]; /* The longest allowed passphrase */ +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32InterfaceAP.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,134 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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 "ESP32InterfaceAP.h" + +// ESP32InterfaceAP implementation +ESP32InterfaceAP::ESP32InterfaceAP(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) + : ESP32Stack(en, io0, tx, rx, debug, rts, cts, baudrate) +{ + own_ch = 1; +} + +int ESP32InterfaceAP::connect(const char *ssid, const char *pass, nsapi_security_t security, + uint8_t channel) +{ + int ret; + + ret = set_credentials(ssid, pass, security); + if (ret != 0) { + return ret; + } + + ret = set_channel(channel); + if (ret != 0) { + return ret; + } + + return connect(); +} + +int ESP32InterfaceAP::connect() +{ + if (!_esp->set_mode(3)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + if (!_esp->dhcp(true, 1)) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + if (!_esp->config_soft_ap(own_ssid, own_pass, own_ch, (uint8_t)own_sec)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return NSAPI_ERROR_OK; +} + +int ESP32InterfaceAP::set_credentials(const char *ssid, const char *pass, nsapi_security_t security) +{ + switch (security) { + case NSAPI_SECURITY_NONE: + case NSAPI_SECURITY_WPA: + case NSAPI_SECURITY_WPA2: + case NSAPI_SECURITY_WPA_WPA2: + own_sec = security; + break; + case NSAPI_SECURITY_UNKNOWN: + case NSAPI_SECURITY_WEP: + default: + return NSAPI_ERROR_UNSUPPORTED; + } + + memset(own_ssid, 0, sizeof(own_ssid)); + strncpy(own_ssid, ssid, sizeof(own_ssid)); + + memset(own_pass, 0, sizeof(own_pass)); + strncpy(own_pass, pass, sizeof(own_pass)); + + return 0; +} + +int ESP32InterfaceAP::set_channel(uint8_t channel) +{ + if (channel != 0) { + own_ch = channel; + } + + return 0; +} + +int ESP32InterfaceAP::disconnect() +{ + if (!_esp->set_mode(1)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return NSAPI_ERROR_OK; +} + +const char *ESP32InterfaceAP::get_ip_address() +{ + return _esp->getIPAddress_ap(); +} + +const char *ESP32InterfaceAP::get_mac_address() +{ + return _esp->getMACAddress_ap(); +} + +const char *ESP32InterfaceAP::get_gateway() +{ + return _esp->getGateway_ap(); +} + +const char *ESP32InterfaceAP::get_netmask() +{ + return _esp->getNetmask_ap(); +} + +int8_t ESP32InterfaceAP::get_rssi() +{ + return 0; +} + +int ESP32InterfaceAP::scan(WiFiAccessPoint *res, unsigned count) +{ + return NSAPI_ERROR_UNSUPPORTED; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32InterfaceAP.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,170 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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. + */ + +#ifndef ESP32_INTERFACE_AP_H +#define ESP32_INTERFACE_AP_H + +#include "mbed.h" +#include "ESP32Stack.h" + + +/** ESP32Interface class + * Implementation of the NetworkStack for the ESP32 + */ +class ESP32InterfaceAP : public ESP32Stack, public WiFiInterface +{ +public: + /** ESP32InterfaceAP lifetime + * @param en EN pin + * @param io0 IO0 pin + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + * @param rts RTS pin + * @param cts CTS pin + * @param baudrate The baudrate of the serial port (default = 230400). + */ + ESP32InterfaceAP(PinName en, PinName io0, PinName tx, PinName rx, bool debug = false, + PinName rts = NC, PinName cts = NC, int baudrate = 230400); + + /** Start the interface + * + * Attempts to connect to a WiFi network. Requires ssid and passphrase to be set. + * If passphrase is invalid, NSAPI_ERROR_AUTH_ERROR is returned. + * + * @return 0 on success, negative error code on failure + */ + virtual int connect(); + + /** Start the interface + * + * Attempts to connect to a WiFi network. + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE) + * @param channel This parameter is not supported, setting it to anything else than 0 will result in NSAPI_ERROR_UNSUPPORTED + * @return 0 on success, or error code on failure + */ + virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE, + uint8_t channel = 0); + + /** Set the WiFi network credentials + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * (defaults to NSAPI_SECURITY_NONE) + * @return 0 on success, or error code on failure + */ + virtual int set_credentials(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE); + + /** Set the WiFi network channel - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param channel Channel on which the connection is to be made, or 0 for any (Default: 0) + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual int set_channel(uint8_t channel); + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual int disconnect(); + + /** Get the internally stored IP address + * @return IP address of the interface or null if not yet connected + */ + virtual const char *get_ip_address(); + + /** Get the internally stored MAC address + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been recieved + */ + virtual const char *get_gateway(); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been recieved + */ + virtual const char *get_netmask(); + + /** Gets the current radio signal strength for active connection + * + * @return Connection strength in dBm (negative value) + */ + virtual int8_t get_rssi(); + + /** Scan for available networks + * + * This function will block. + * + * @param ap Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @param timeout Timeout in milliseconds; 0 for no timeout (Default: 0) + * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + virtual int scan(WiFiAccessPoint *res, unsigned count); + + /** Translates a hostname to an IP address with specific version + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param address Destination for the host SocketAddress + * @param host Hostname to resolve + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::gethostbyname; + + /** Add a domain name server to list of servers to query + * + * @param addr Destination for the host address + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::add_dns_server; + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() + { + return this; + } + +private: + char own_ssid[33]; /* 32 is what 802.11 defines as longest possible name; +1 for the \0 */ + nsapi_security_t own_sec; + char own_pass[64]; /* The longest allowed passphrase */ + uint8_t own_ch; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32Stack.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,204 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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 "ESP32Stack.h" + +// ESP32Stack implementation +ESP32Stack::ESP32Stack(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate) +{ + _esp = ESP32::getESP32Inst(en, io0, tx, rx, debug, rts, cts, baudrate); +} + +struct esp32_socket { + int id; + nsapi_protocol_t proto; + bool connected; + SocketAddress addr; + bool accept_id; + bool tcp_server; +}; + +int ESP32Stack::socket_open(void **handle, nsapi_protocol_t proto) +{ + // Look for an unused socket + int id = _esp->get_free_id(); + + if (id == -1) { + return NSAPI_ERROR_NO_SOCKET; + } + + struct esp32_socket *socket = new struct esp32_socket; + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + socket->id = id; + socket->proto = proto; + socket->connected = false; + socket->accept_id = false; + socket->tcp_server = false; + *handle = socket; + return 0; +} + +int ESP32Stack::socket_close(void *handle) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + int err = 0; + + if (!_esp->close(socket->id, socket->accept_id)) { + err = NSAPI_ERROR_DEVICE_ERROR; + } + + if (socket->tcp_server) { + _esp->del_server(); + } + + delete socket; + return err; +} + +int ESP32Stack::socket_bind(void *handle, const SocketAddress &address) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + socket->addr = address; + return 0; +} + +int ESP32Stack::socket_listen(void *handle, int backlog) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + (void)backlog; + + if (socket->proto != NSAPI_TCP) { + return NSAPI_ERROR_UNSUPPORTED; + } + + if (!_esp->cre_server(socket->addr.get_port())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + socket->tcp_server = true; + return 0; +} + +int ESP32Stack::socket_connect(void *handle, const SocketAddress &addr) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + const char *proto = (socket->proto == NSAPI_UDP) ? "UDP" : "TCP"; + if (!_esp->open(proto, socket->id, addr.get_ip_address(), addr.get_port())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + socket->connected = true; + return 0; +} + +int ESP32Stack::socket_accept(void *server, void **socket, SocketAddress *addr) +{ + int id; + + if (!_esp->accept(&id)) { + return NSAPI_ERROR_NO_SOCKET; + } + + struct esp32_socket *socket_new = new struct esp32_socket; + if (!socket_new) { + return NSAPI_ERROR_NO_SOCKET; + } + + socket_new->id = id; + socket_new->proto = NSAPI_TCP; + socket_new->connected = true; + socket_new->accept_id = true; + socket_new->tcp_server = false; + *socket = socket_new; + + return 0; +} + +int ESP32Stack::socket_send(void *handle, const void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (!_esp->send(socket->id, data, size)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return size; +} + +int ESP32Stack::socket_recv(void *handle, void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + int32_t recv = _esp->recv(socket->id, data, size); + if (recv == -1) { + return NSAPI_ERROR_WOULD_BLOCK; + } else if (recv < 0) { + return NSAPI_ERROR_NO_SOCKET; + } else { + // do nothing + } + + return recv; +} + +int ESP32Stack::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + + if (socket->connected && socket->addr != addr) { + if (!_esp->close(socket->id, socket->accept_id)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + socket->connected = false; + } + + if (!socket->connected) { + int err = socket_connect(socket, addr); + if (err < 0) { + return err; + } + socket->addr = addr; + } + + return socket_send(socket, data, size); +} + +int ESP32Stack::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + int ret = socket_recv(socket, data, size); + if (ret >= 0 && addr) { + *addr = socket->addr; + } + + return ret; +} + +void ESP32Stack::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + struct esp32_socket *socket = (struct esp32_socket *)handle; + _esp->attach(socket->id, callback, data); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/ESP32Stack.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,144 @@ +/* ESP32 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 Renesas Electronics Corporation + * + * 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. + */ + +#ifndef ESP32_STACK_H +#define ESP32_STACK_H + +#include "mbed.h" +#include "ESP32.h" + +/** ESP32Stack class + * Implementation of the NetworkStack for the ESP32 + */ +class ESP32Stack : public NetworkStack +{ +protected: + /** ESP32Stack lifetime + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + * @param rts RTS pin + * @param cts CTS pin + * @param baudrate The baudrate of the serial port. + */ + ESP32Stack(PinName en, PinName io0, PinName tx, PinName rx, bool debug, + PinName rts, PinName cts, int baudrate); + +protected: + /** Open a socket + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative on failure + */ + virtual int socket_open(void **handle, nsapi_protocol_t proto); + + /** Close the socket + * @param handle Socket handle + * @return 0 on success, negative on failure + * @note On failure, any memory associated with the socket must still + * be cleaned up + */ + virtual int socket_close(void *handle); + + /** Bind a server socket to a specific port + * @param handle Socket handle + * @param address Local address to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + virtual int socket_bind(void *handle, const SocketAddress &address); + + /** Start listening for incoming connections + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return 0 on success, negative on failure + */ + virtual int socket_listen(void *handle, int backlog); + + /** Connects this TCP socket to the server + * @param handle Socket handle + * @param address SocketAddress to connect to + * @return 0 on success, negative on failure + */ + virtual int socket_connect(void *handle, const SocketAddress &address); + + /** Accept a new connection. + * @param handle Handle in which to store new socket + * @param server Socket handle to server to accept from + * @return 0 on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_accept(void *handle, void **socket, SocketAddress *address); + + /** Send data to the remote host + * @param handle Socket handle + * @param data The buffer to send to the host + * @param size The length of the buffer to send + * @return Number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_send(void *handle, const void *data, unsigned size); + + /** Receive data from the remote host + * @param handle Socket handle + * @param data The buffer in which to store the data received from the host + * @param size The maximum length of the buffer + * @return Number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recv(void *handle, void *data, unsigned size); + + /** Send a packet to a remote endpoint + * @param handle Socket handle + * @param address The remote SocketAddress + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return The number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + + /** Receive a packet from a remote endpoint + * @param handle Socket handle + * @param address Destination for the remote SocketAddress or null + * @param buffer The buffer for storing the incoming packet data + * If a packet is too long to fit in the supplied buffer, + * excess bytes are discarded + * @param size The length of the buffer + * @return The number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + * @note Callback may be called in an interrupt context. + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + +protected: + ESP32 *_esp; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp32-driver/README.md Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,8 @@ +# The ESP32 WiFi driver for mbed-os +The mbed OS driver for the ESP32 WiFi module + +## Firmware version +How to write mbed-os compatible firmware : https://github.com/d-kato/GR-LYCHEE_ESP32_Serial_Bridge + +## Restrictions +- Setting up an UDP server is not possible
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver.lib Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/esp8266-driver/#32148e373366222e4aff21d139cab88fbcc72269
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/ESP8266/ESP8266.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,641 @@ +/* ESP8266 Example + * Copyright (c) 2015 ARM Limited + * + * 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 "ESP8266.h" +#include "mbed_debug.h" +#include "nsapi_types.h" + +#include <cstring> + +#define ESP8266_DEFAULT_BAUD_RATE 115200 + +ESP8266::ESP8266(PinName tx, PinName rx, bool debug) + : _serial(tx, rx, ESP8266_DEFAULT_BAUD_RATE), + _parser(&_serial), + _packets(0), + _packets_end(&_packets), + _connect_error(0), + _fail(false), + _closed(false), + _socket_open(), + _connection_status(NSAPI_STATUS_DISCONNECTED) +{ + _serial.set_baud( ESP8266_DEFAULT_BAUD_RATE ); + _parser.debug_on(debug); + _parser.set_delimiter("\r\n"); + _parser.oob("+IPD", callback(this, &ESP8266::_packet_handler)); + //Note: espressif at command document says that this should be +CWJAP_CUR:<error code> + //but seems that at least current version is not sending it + //https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf + //Also seems that ERROR is not sent, but FAIL instead + _parser.oob("+CWJAP:", callback(this, &ESP8266::_connect_error_handler)); + _parser.oob("0,CLOSED", callback(this, &ESP8266::_oob_socket0_closed_handler)); + _parser.oob("1,CLOSED", callback(this, &ESP8266::_oob_socket1_closed_handler)); + _parser.oob("2,CLOSED", callback(this, &ESP8266::_oob_socket2_closed_handler)); + _parser.oob("3,CLOSED", callback(this, &ESP8266::_oob_socket3_closed_handler)); + _parser.oob("4,CLOSED", callback(this, &ESP8266::_oob_socket4_closed_handler)); + _parser.oob("WIFI ", callback(this, &ESP8266::_connection_status_handler)); + _parser.oob("UNLINK", callback(this, &ESP8266::_oob_socket_close_error)); +} + +int ESP8266::get_firmware_version() +{ + int version; + + _smutex.lock(); + bool done = _parser.send("AT+GMR") + && _parser.recv("SDK version:%d", &version) + && _parser.recv("OK\n"); + _smutex.unlock(); + + if(done) { + return version; + } else { + // Older firmware versions do not prefix the version with "SDK version: " + return -1; + } +} + +bool ESP8266::startup(int mode) +{ + if (!(mode == WIFIMODE_STATION || mode == WIFIMODE_SOFTAP + || mode == WIFIMODE_STATION_SOFTAP)) { + return false; + } + + _smutex.lock(); + setTimeout(ESP8266_CONNECT_TIMEOUT); + bool done = _parser.send("AT+CWMODE_CUR=%d", mode) + && _parser.recv("OK\n") + &&_parser.send("AT+CIPMUX=1") + && _parser.recv("OK\n"); + setTimeout(); //Restore default + _smutex.unlock(); + + return done; +} + +bool ESP8266::reset(void) +{ + _smutex.lock(); + setTimeout(ESP8266_CONNECT_TIMEOUT); + + for (int i = 0; i < 2; i++) { + if (_parser.send("AT+RST") + && _parser.recv("OK\n") + && _parser.recv("ready")) { + _smutex.unlock(); + return true; + } + } + setTimeout(); + _smutex.unlock(); + + return false; +} + +bool ESP8266::dhcp(bool enabled, int mode) +{ + //only 3 valid modes + if (mode < 0 || mode > 2) { + return false; + } + + _smutex.lock(); + bool done = _parser.send("AT+CWDHCP_CUR=%d,%d", mode, enabled?1:0) + && _parser.recv("OK\n"); + _smutex.unlock(); + + return done; +} + +nsapi_error_t ESP8266::connect(const char *ap, const char *passPhrase) +{ + _smutex.lock(); + setTimeout(ESP8266_CONNECT_TIMEOUT); + _connection_status = NSAPI_STATUS_CONNECTING; + if(_connection_status_cb) + _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connection_status); + + _parser.send("AT+CWJAP_CUR=\"%s\",\"%s\"", ap, passPhrase); + if (!_parser.recv("OK\n")) { + if (_fail) { + _smutex.unlock(); + nsapi_error_t ret; + if (_connect_error == 1) + ret = NSAPI_ERROR_CONNECTION_TIMEOUT; + else if (_connect_error == 2) + ret = NSAPI_ERROR_AUTH_FAILURE; + else if (_connect_error == 3) + ret = NSAPI_ERROR_NO_SSID; + else + ret = NSAPI_ERROR_NO_CONNECTION; + + _fail = false; + _connect_error = 0; + return ret; + } + } + setTimeout(); + _smutex.unlock(); + + return NSAPI_ERROR_OK; +} + +bool ESP8266::disconnect(void) +{ + _smutex.lock(); + bool done = _parser.send("AT+CWQAP") && _parser.recv("OK\n"); + _smutex.unlock(); + + return done; +} + +const char *ESP8266::getIPAddress(void) +{ + _smutex.lock(); + setTimeout(ESP8266_CONNECT_TIMEOUT); + if (!(_parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:STAIP,\"%15[^\"]\"", _ip_buffer) + && _parser.recv("OK\n"))) { + _smutex.unlock(); + return 0; + } + setTimeout(); + _smutex.unlock(); + + return _ip_buffer; +} + +const char *ESP8266::getMACAddress(void) +{ + _smutex.lock(); + if (!(_parser.send("AT+CIFSR") + && _parser.recv("+CIFSR:STAMAC,\"%17[^\"]\"", _mac_buffer) + && _parser.recv("OK\n"))) { + _smutex.unlock(); + return 0; + } + _smutex.unlock(); + + return _mac_buffer; +} + +const char *ESP8266::getGateway() +{ + _smutex.lock(); + if (!(_parser.send("AT+CIPSTA_CUR?") + && _parser.recv("+CIPSTA_CUR:gateway:\"%15[^\"]\"", _gateway_buffer) + && _parser.recv("OK\n"))) { + _smutex.unlock(); + return 0; + } + _smutex.unlock(); + + return _gateway_buffer; +} + +const char *ESP8266::getNetmask() +{ + _smutex.lock(); + if (!(_parser.send("AT+CIPSTA_CUR?") + && _parser.recv("+CIPSTA_CUR:netmask:\"%15[^\"]\"", _netmask_buffer) + && _parser.recv("OK\n"))) { + _smutex.unlock(); + return 0; + } + _smutex.unlock(); + + return _netmask_buffer; +} + +int8_t ESP8266::getRSSI() +{ + int8_t rssi; + char bssid[18]; + + _smutex.lock(); + setTimeout(ESP8266_CONNECT_TIMEOUT); + if (!(_parser.send("AT+CWJAP_CUR?") + && _parser.recv("+CWJAP_CUR:\"%*[^\"]\",\"%17[^\"]\"", bssid) + && _parser.recv("OK\n"))) { + _smutex.unlock(); + return 0; + } + setTimeout(); + _smutex.unlock(); + + _smutex.lock(); + setTimeout(ESP8266_CONNECT_TIMEOUT); + if (!(_parser.send("AT+CWLAP=\"\",\"%s\",", bssid) + && _parser.recv("+CWLAP:(%*d,\"%*[^\"]\",%hhd,", &rssi) + && _parser.recv("OK\n"))) { + _smutex.unlock(); + return 0; + } + setTimeout(); + _smutex.unlock(); + + return rssi; +} + +int ESP8266::scan(WiFiAccessPoint *res, unsigned limit) +{ + unsigned cnt = 0; + nsapi_wifi_ap_t ap; + + _smutex.lock(); + setTimeout(ESP8266_CONNECT_TIMEOUT); + + if (!_parser.send("AT+CWLAP")) { + _smutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } + + while (recv_ap(&ap)) { + if (cnt < limit) { + res[cnt] = WiFiAccessPoint(ap); + } + + cnt++; + if (limit != 0 && cnt >= limit) { + break; + } + } + setTimeout(); + _smutex.unlock(); + + return cnt; +} + +nsapi_error_t ESP8266::open_udp(int id, const char* addr, int port, int local_port) +{ + static const char *type = "UDP"; + bool done = false; + + if (id >= SOCKET_COUNT) { + return NSAPI_ERROR_PARAMETER; + } else if (_socket_open[id]) { + return NSAPI_ERROR_IS_CONNECTED; + } + + _smutex.lock(); + if(local_port) { + done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, local_port) + && _parser.recv("OK\n"); + } else { + done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port) + && _parser.recv("OK\n"); + } + + if (done) { + _socket_open[id] = 1; + } + + _smutex.unlock(); + + return done ? NSAPI_ERROR_OK : NSAPI_ERROR_DEVICE_ERROR; +} + +bool ESP8266::open_tcp(int id, const char* addr, int port, int keepalive) +{ + static const char *type = "TCP"; + bool done = false; + + if (id >= SOCKET_COUNT || _socket_open[id]) { + return false; + } + + _smutex.lock(); + if(keepalive) { + done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d,%d", id, type, addr, port, keepalive) + && _parser.recv("OK\n"); + } else { + done = _parser.send("AT+CIPSTART=%d,\"%s\",\"%s\",%d", id, type, addr, port) + && _parser.recv("OK\n"); + } + + if (done) { + _socket_open[id] = 1; + } + + _smutex.unlock(); + + return done; +} + +bool ESP8266::dns_lookup(const char* name, char* ip) +{ + _smutex.lock(); + bool done = _parser.send("AT+CIPDOMAIN=\"%s\"", name) && _parser.recv("+CIPDOMAIN:%s%*[\r]%*[\n]", ip); + _smutex.unlock(); + + return done; +} + +nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount) +{ + //May take a second try if device is busy + for (unsigned i = 0; i < 2; i++) { + _smutex.lock(); + setTimeout(ESP8266_SEND_TIMEOUT); + if (_parser.send("AT+CIPSEND=%d,%lu", id, amount) + && _parser.recv(">") + && _parser.write((char*)data, (int)amount) >= 0) { + while (_parser.process_oob()); // multiple sends in a row require this + _smutex.unlock(); + return NSAPI_ERROR_OK; + } + setTimeout(); + _smutex.unlock(); + } + + return NSAPI_ERROR_DEVICE_ERROR; +} + +void ESP8266::_packet_handler() +{ + int id; + int amount; + + // parse out the packet + if (!_parser.recv(",%d,%d:", &id, &amount)) { + return; + } + + struct packet *packet = (struct packet*)malloc( + sizeof(struct packet) + amount); + if (!packet) { + debug("ESP8266: could not allocate memory for RX data\n"); + return; + } + + packet->id = id; + packet->len = amount; + packet->next = 0; + + if (_parser.read((char*)(packet + 1), amount) < amount) { + free(packet); + return; + } + + // append to packet list + *_packets_end = packet; + _packets_end = &packet->next; +} + +int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout) +{ + _smutex.lock(); + setTimeout(timeout); + + // Poll for inbound packets + while (_parser.process_oob()) { + } + + setTimeout(); + + // check if any packets are ready for us + for (struct packet **p = &_packets; *p; p = &(*p)->next) { + if ((*p)->id == id) { + struct packet *q = *p; + + if (q->len <= amount) { // Return and remove full packet + memcpy(data, q+1, q->len); + + if (_packets_end == &(*p)->next) { + _packets_end = p; + } + *p = (*p)->next; + _smutex.unlock(); + + uint32_t len = q->len; + free(q); + return len; + } else { // return only partial packet + memcpy(data, q+1, amount); + + q->len -= amount; + memmove(q+1, (uint8_t*)(q+1) + amount, q->len); + + _smutex.unlock(); + return amount; + } + } + } + if(!_socket_open[id]) { + _smutex.unlock(); + return 0; + } + _smutex.unlock(); + + return NSAPI_ERROR_WOULD_BLOCK; +} + +int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount, uint32_t timeout) +{ + _smutex.lock(); + setTimeout(timeout); + + // Poll for inbound packets + while (_parser.process_oob()) { + } + + setTimeout(); + + // check if any packets are ready for us + for (struct packet **p = &_packets; *p; p = &(*p)->next) { + if ((*p)->id == id) { + struct packet *q = *p; + + // Return and remove packet (truncated if necessary) + uint32_t len = q->len < amount ? q->len : amount; + memcpy(data, q+1, len); + + if (_packets_end == &(*p)->next) { + _packets_end = p; + } + *p = (*p)->next; + _smutex.unlock(); + + free(q); + return len; + } + } + _smutex.unlock(); + + return NSAPI_ERROR_WOULD_BLOCK; +} + +bool ESP8266::close(int id) +{ + //May take a second try if device is busy + for (unsigned i = 0; i < 2; i++) { + _smutex.lock(); + if (_parser.send("AT+CIPCLOSE=%d", id)) { + if (!_parser.recv("OK\n")) { + if (_closed) { // UNLINK ERROR + _closed = false; + _socket_open[id] = 0; + _smutex.unlock(); + // ESP8266 has a habit that it might close a socket on its own. + //debug("ESP8266: socket %d already closed when calling close\n", id); + return true; + } + } else { + _smutex.unlock(); + return true; + } + } + _smutex.unlock(); + } + + return false; +} + +void ESP8266::setTimeout(uint32_t timeout_ms) +{ + _parser.set_timeout(timeout_ms); +} + +bool ESP8266::readable() +{ + return _serial.FileHandle::readable(); +} + +bool ESP8266::writeable() +{ + return _serial.FileHandle::writable(); +} + +void ESP8266::sigio(Callback<void()> func) +{ + _serial.sigio(func); +} + +void ESP8266::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb) +{ + _connection_status_cb = status_cb; +} + +bool ESP8266::recv_ap(nsapi_wifi_ap_t *ap) +{ + int sec; + int dummy; + bool ret = _parser.recv("+CWLAP:(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d)\n", + &sec, + ap->ssid, + &ap->rssi, + &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], + &ap->channel, + &dummy, + &dummy); + + ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN; + + return ret; +} + +void ESP8266::_connect_error_handler() +{ + _fail = false; + _connect_error = 0; + + if (_parser.recv("%d", &_connect_error) && _parser.recv("FAIL")) { + _fail = true; + _parser.abort(); + } +} + +void ESP8266::_oob_socket_close_error() +{ + if (_parser.recv("ERROR\n")) { + _closed = true; // Not possible to pinpoint to a certain socket + _parser.abort(); + } +} + +void ESP8266::_oob_socket0_closed_handler() +{ + _socket_open[0] = 0; +} + +void ESP8266::_oob_socket1_closed_handler() +{ + _socket_open[1] = 0; +} + +void ESP8266::_oob_socket2_closed_handler() +{ + _socket_open[2] = 0; +} + +void ESP8266::_oob_socket3_closed_handler() +{ + _socket_open[3] = 0; +} + +void ESP8266::_oob_socket4_closed_handler() +{ + _socket_open[4] = 0; +} + +void ESP8266::_connection_status_handler() +{ + char status[13]; + if (_parser.recv("%12[^\"]\n", status)) { + if (strcmp(status, "GOT IP\n") == 0) + _connection_status = NSAPI_STATUS_GLOBAL_UP; + else if (strcmp(status, "DISCONNECT\n") == 0) + _connection_status = NSAPI_STATUS_DISCONNECTED; + else + return; + + if(_connection_status_cb) + _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connection_status); + } +} + +int8_t ESP8266::get_default_wifi_mode() +{ + int8_t mode; + + _smutex.lock(); + if (_parser.send("AT+CWMODE_DEF?") + && _parser.recv("+CWMODE_DEF:%hhd", &mode) + && _parser.recv("OK\n")) { + _smutex.unlock(); + return mode; + } + _smutex.unlock(); + + return 0; +} + +bool ESP8266::set_default_wifi_mode(const int8_t mode) +{ + _smutex.lock(); + bool done = _parser.send("AT+CWMODE_DEF=%hhd", mode) + && _parser.recv("OK\n"); + _smutex.unlock(); + + return done; +} + +nsapi_connection_status_t ESP8266::get_connection_status() const +{ + return _connection_status; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/ESP8266/ESP8266.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,308 @@ +/* ESP8266Interface Example + * Copyright (c) 2015 ARM Limited + * + * 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. + */ + +#ifndef ESP8266_H +#define ESP8266_H + +#include "ATCmdParser.h" +#include "nsapi_types.h" +#include "rtos.h" + +// Various timeouts for different ESP8266 operations +#ifndef ESP8266_CONNECT_TIMEOUT +#define ESP8266_CONNECT_TIMEOUT 15000 +#endif +#ifndef ESP8266_SEND_TIMEOUT +#define ESP8266_SEND_TIMEOUT 2000 +#endif +#ifndef ESP8266_RECV_TIMEOUT +#define ESP8266_RECV_TIMEOUT 2000 +#endif +#ifndef ESP8266_MISC_TIMEOUT +#define ESP8266_MISC_TIMEOUT 2000 +#endif + +/** ESP8266Interface class. + This is an interface to a ESP8266 radio. + */ +class ESP8266 +{ +public: + ESP8266(PinName tx, PinName rx, bool debug=false); + + /** + * Check firmware version of ESP8266 + * + * @return integer firmware version or -1 if firmware query command gives outdated response + */ + int get_firmware_version(void); + + /** + * Startup the ESP8266 + * + * @param mode mode of WIFI 1-client, 2-host, 3-both + * @return true only if ESP8266 was setup correctly + */ + bool startup(int mode); + + /** + * Reset ESP8266 + * + * @return true only if ESP8266 resets successfully + */ + bool reset(void); + + /** + * Enable/Disable DHCP + * + * @param enabled DHCP enabled when true + * @param mode mode of DHCP 0-softAP, 1-station, 2-both + * @return true only if ESP8266 enables/disables DHCP successfully + */ + bool dhcp(bool enabled, int mode); + + /** + * Connect ESP8266 to AP + * + * @param ap the name of the AP + * @param passPhrase the password of AP + * @return NSAPI_ERROR_OK only if ESP8266 is connected successfully + */ + nsapi_error_t connect(const char *ap, const char *passPhrase); + + /** + * Disconnect ESP8266 from AP + * + * @return true only if ESP8266 is disconnected successfully + */ + bool disconnect(void); + + /** + * Get the IP address of ESP8266 + * + * @return null-teriminated IP address or null if no IP address is assigned + */ + const char *getIPAddress(void); + + /** + * Get the MAC address of ESP8266 + * + * @return null-terminated MAC address or null if no MAC address is assigned + */ + const char *getMACAddress(void); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been recieved + */ + const char *getGateway(); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been recieved + */ + const char *getNetmask(); + + /* Return RSSI for active connection + * + * @return Measured RSSI + */ + int8_t getRSSI(); + + /** Scan for available networks + * + * @param ap Pointer to allocated array to store discovered AP + * @param limit Size of allocated @a res array, or 0 to only count available AP + * @return Number of entries in @a res, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + int scan(WiFiAccessPoint *res, unsigned limit); + + /**Perform a dns query + * + * @param name Hostname to resolve + * @param ip Buffer to store IP address + * @return 0 true on success, false on failure + */ + bool dns_lookup(const char *name, char *ip); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "UDP" or "TCP" + * @param id id to give the new socket, valid 0-4 + * @param port port to open connection with + * @param addr the IP address of the destination + * @param port the port on the destination + * @param local_port UDP socket's local port, zero means any + * @return true only if socket opened successfully + */ + nsapi_error_t open_udp(int id, const char* addr, int port, int local_port = 0); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "UDP" or "TCP" + * @param id id to give the new socket, valid 0-4 + * @param port port to open connection with + * @param addr the IP address of the destination + * @param port the port on the destination + * @param tcp_keepalive TCP connection's keep alive time, zero means disabled + * @return true only if socket opened successfully + */ + bool open_tcp(int id, const char* addr, int port, int keepalive = 0); + + /** + * Sends data to an open socket + * + * @param id id of socket to send to + * @param data data to be sent + * @param amount amount of data to be sent - max 1024 + * @return NSAPI_ERROR_OK in success, negative error code in failure + */ + nsapi_error_t send(int id, const void *data, uint32_t amount); + + /** + * Receives datagram from an open UDP socket + * + * @param id id to receive from + * @param data placeholder for returned information + * @param amount number of bytes to be received + * @return the number of bytes received + */ + int32_t recv_udp(int id, void *data, uint32_t amount, uint32_t timeout=ESP8266_RECV_TIMEOUT); + + /** + * Receives stream data from an open TCP socket + * + * @param id id to receive from + * @param data placeholder for returned information + * @param amount number of bytes to be received + * @return the number of bytes received + */ + int32_t recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout=ESP8266_RECV_TIMEOUT); + + /** + * Closes a socket + * + * @param id id of socket to close, valid only 0-4 + * @return true only if socket is closed successfully + */ + bool close(int id); + + /** + * Allows timeout to be changed between commands + * + * @param timeout_ms timeout of the connection + */ + void setTimeout(uint32_t timeout_ms=ESP8266_MISC_TIMEOUT); + + /** + * Checks if data is available + */ + bool readable(); + + /** + * Checks if data can be written + */ + bool writeable(); + + /** + * Attach a function to call whenever sigio happens in the serial + * + * @param func A pointer to a void function, or 0 to set as none + */ + void sigio(Callback<void()> func); + + /** + * Attach a function to call whenever sigio happens in the serial + * + * @param obj pointer to the object to call the member function on + * @param method pointer to the member function to call + */ + template <typename T, typename M> + void sigio(T *obj, M method) { + sigio(Callback<void()>(obj, method)); + } + + /** + * Attach a function to call whenever network state has changed + * + * @param func A pointer to a void function, or 0 to set as none + */ + void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb); + + /** + * Read default Wifi mode from flash + * + * return Station, SoftAP or SoftAP+Station - 0 on failure + */ + int8_t get_default_wifi_mode(); + + /** + * Write default Wifi mode to flash + */ + bool set_default_wifi_mode(const int8_t mode); + + /** Get the connection status + * + * @return The connection status according to ConnectionStatusType + */ + nsapi_connection_status_t get_connection_status() const; + + static const int8_t WIFIMODE_STATION = 1; + static const int8_t WIFIMODE_SOFTAP = 2; + static const int8_t WIFIMODE_STATION_SOFTAP = 3; + static const int8_t SOCKET_COUNT = 5; + +private: + UARTSerial _serial; + ATCmdParser _parser; + Mutex _smutex; // Protect serial port access + + struct packet { + struct packet *next; + int id; + uint32_t len; + // data follows + } *_packets, **_packets_end; + void _packet_handler(); + void _connect_error_handler(); + bool recv_ap(nsapi_wifi_ap_t *ap); + void _oob_socket0_closed_handler(); + void _oob_socket1_closed_handler(); + void _oob_socket2_closed_handler(); + void _oob_socket3_closed_handler(); + void _oob_socket4_closed_handler(); + void _connection_status_handler(); + void _oob_socket_close_error(); + + char _ip_buffer[16]; + char _gateway_buffer[16]; + char _netmask_buffer[16]; + char _mac_buffer[18]; + + int _connect_error; + bool _fail; + bool _closed; + int _socket_open[SOCKET_COUNT]; + nsapi_connection_status_t _connection_status; + Callback<void(nsapi_event_t, intptr_t)> _connection_status_cb; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/ESP8266Interface.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,577 @@ +/* ESP8266 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 ARM Limited + * + * 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 <cstring> +#include "ESP8266.h" +#include "ESP8266Interface.h" +#include "mbed_debug.h" +#include "nsapi_types.h" + + +#ifndef MBED_CONF_ESP8266_TX +#ifdef TARGET_FF_ARDUINO +#define MBED_CONF_ESP8266_TX D1 +#else +#define MBED_CONF_ESP8266_TX NC +#endif +#endif + +#ifndef MBED_CONF_ESP8266_RX +#ifdef TARGET_FF_ARDUINO +#define MBED_CONF_ESP8266_RX D0 +#else +#define MBED_CONF_ESP8266_RX NC +#endif +#endif + +// Firmware version +#define ESP8266_VERSION 2 + +ESP8266Interface::ESP8266Interface() + : _esp(MBED_CONF_ESP8266_TX, MBED_CONF_ESP8266_RX, MBED_CONF_ESP8266_DEBUG), + _initialized(false), + _started(false) +{ + memset(_ids, 0, sizeof(_ids)); + memset(_cbs, 0, sizeof(_cbs)); + memset(ap_ssid, 0, sizeof(ap_ssid)); + memset(ap_pass, 0, sizeof(ap_pass)); + memset(_local_ports, 0, sizeof(_local_ports)); + ap_sec = NSAPI_SECURITY_UNKNOWN; + + _esp.sigio(this, &ESP8266Interface::event); + _esp.setTimeout(); +} + +// ESP8266Interface implementation +ESP8266Interface::ESP8266Interface(PinName tx, PinName rx, bool debug) + : _esp(tx, rx, debug), + _initialized(false), + _started(false) +{ + memset(_ids, 0, sizeof(_ids)); + memset(_cbs, 0, sizeof(_cbs)); + memset(ap_ssid, 0, sizeof(ap_ssid)); + memset(ap_pass, 0, sizeof(ap_pass)); + memset(_local_ports, 0, sizeof(_local_ports)); + ap_sec = NSAPI_SECURITY_UNKNOWN; + + _esp.sigio(this, &ESP8266Interface::event); + _esp.setTimeout(); +} + +int ESP8266Interface::connect(const char *ssid, const char *pass, nsapi_security_t security, + uint8_t channel) +{ + if (channel != 0) { + return NSAPI_ERROR_UNSUPPORTED; + } + + int err = set_credentials(ssid, pass, security); + if(err) { + return err; + } + + return connect(); +} + +int ESP8266Interface::connect() +{ + nsapi_error_t status; + + if (strlen(ap_ssid) == 0) { + return NSAPI_ERROR_NO_SSID; + } + + if (ap_sec != NSAPI_SECURITY_NONE) { + if (strlen(ap_pass) < ESP8266_PASSPHRASE_MIN_LENGTH) { + return NSAPI_ERROR_PARAMETER; + } + } + + status = _init(); + if(status != NSAPI_ERROR_OK) { + return status; + } + + if(get_ip_address()) { + return NSAPI_ERROR_IS_CONNECTED; + } + + status = _startup(ESP8266::WIFIMODE_STATION); + if(status != NSAPI_ERROR_OK) { + return status; + } + _started = true; + + if (!_esp.dhcp(true, 1)) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + int connect_error = _esp.connect(ap_ssid, ap_pass); + if (connect_error) { + return connect_error; + } + + if (!get_ip_address()) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + return NSAPI_ERROR_OK; +} + +int ESP8266Interface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security) +{ + ap_sec = security; + + if (!ssid) { + return NSAPI_ERROR_PARAMETER; + } + + int ssid_length = strlen(ssid); + + if (ssid_length > 0 + && ssid_length <= ESP8266_SSID_MAX_LENGTH) { + memset(ap_ssid, 0, sizeof(ap_ssid)); + strncpy(ap_ssid, ssid, sizeof(ap_ssid)); + } else { + return NSAPI_ERROR_PARAMETER; + } + + if (ap_sec != NSAPI_SECURITY_NONE) { + + if (!pass) { + return NSAPI_ERROR_PARAMETER; + } + + int pass_length = strlen(pass); + if (pass_length >= ESP8266_PASSPHRASE_MIN_LENGTH + && pass_length <= ESP8266_PASSPHRASE_MAX_LENGTH ) { + memset(ap_pass, 0, sizeof(ap_pass)); + strncpy(ap_pass, pass, sizeof(ap_pass)); + } else { + return NSAPI_ERROR_PARAMETER; + } + } else { + memset(ap_pass, 0, sizeof(ap_pass)); + } + + return NSAPI_ERROR_OK; +} + +int ESP8266Interface::set_channel(uint8_t channel) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + + +int ESP8266Interface::disconnect() +{ + _started = false; + _initialized = false; + + return _esp.disconnect() ? NSAPI_ERROR_OK : NSAPI_ERROR_DEVICE_ERROR; +} + +const char *ESP8266Interface::get_ip_address() +{ + if(!_started) { + return NULL; + } + + const char *ip_buff = _esp.getIPAddress(); + if(!ip_buff || std::strcmp(ip_buff, "0.0.0.0") == 0) { + return NULL; + } + + return ip_buff; +} + +const char *ESP8266Interface::get_mac_address() +{ + return _esp.getMACAddress(); +} + +const char *ESP8266Interface::get_gateway() +{ + return _started ? _esp.getGateway() : NULL; +} + +const char *ESP8266Interface::get_netmask() +{ + return _started ? _esp.getNetmask() : NULL; +} + +int8_t ESP8266Interface::get_rssi() +{ + return _started ? _esp.getRSSI() : 0; +} + +int ESP8266Interface::scan(WiFiAccessPoint *res, unsigned count) +{ + nsapi_error_t status; + + status = _init(); + if(status != NSAPI_ERROR_OK) { + return status; + } + + status = _startup(ESP8266::WIFIMODE_STATION); + if(status != NSAPI_ERROR_OK) { + return status; + } + + return _esp.scan(res, count); +} + +bool ESP8266Interface::_get_firmware_ok() +{ + if (_esp.get_firmware_version() != ESP8266_VERSION) { + debug("ESP8266: ERROR: Firmware incompatible with this driver.\ + \r\nUpdate to v%d - https://developer.mbed.org/teams/ESP8266/wiki/Firmware-Update\r\n",ESP8266_VERSION); + return false; + } + + return true; +} + +bool ESP8266Interface::_disable_default_softap() +{ + static int disabled = false; + + if (disabled || _esp.get_default_wifi_mode() == ESP8266::WIFIMODE_STATION) { + disabled = true; + return true; + } + if (_esp.set_default_wifi_mode(ESP8266::WIFIMODE_STATION)) { + disabled = true; + return true; + } + + return false; +} + +nsapi_error_t ESP8266Interface::_init(void) +{ + if (!_initialized) { + if (!_esp.reset()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + if (!_get_firmware_ok()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + if (_disable_default_softap() == false) { + return NSAPI_ERROR_DEVICE_ERROR; + } + _initialized = true; + } + return NSAPI_ERROR_OK; +} + +nsapi_error_t ESP8266Interface::_startup(const int8_t wifi_mode) +{ + if (!_started) { + if (!_esp.startup(wifi_mode)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + } + return NSAPI_ERROR_OK; +} + +struct esp8266_socket { + int id; + nsapi_protocol_t proto; + bool connected; + SocketAddress addr; + int keepalive; // TCP +}; + +int ESP8266Interface::socket_open(void **handle, nsapi_protocol_t proto) +{ + // Look for an unused socket + int id = -1; + + for (int i = 0; i < ESP8266_SOCKET_COUNT; i++) { + if (!_ids[i]) { + id = i; + _ids[i] = true; + break; + } + } + + if (id == -1) { + return NSAPI_ERROR_NO_SOCKET; + } + + struct esp8266_socket *socket = new struct esp8266_socket; + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + socket->id = id; + socket->proto = proto; + socket->connected = false; + socket->keepalive = 0; + *handle = socket; + return 0; +} + +int ESP8266Interface::socket_close(void *handle) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + int err = 0; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (socket->connected && !_esp.close(socket->id)) { + err = NSAPI_ERROR_DEVICE_ERROR; + } + + socket->connected = false; + _ids[socket->id] = false; + _local_ports[socket->id] = 0; + delete socket; + return err; +} + +int ESP8266Interface::socket_bind(void *handle, const SocketAddress &address) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (socket->proto == NSAPI_UDP) { + if(address.get_addr().version != NSAPI_UNSPEC) { + return NSAPI_ERROR_UNSUPPORTED; + } + + for(int id = 0; id < ESP8266_SOCKET_COUNT; id++) { + if(_local_ports[id] == address.get_port() && id != socket->id) { // Port already reserved by another socket + return NSAPI_ERROR_PARAMETER; + } else if (id == socket->id && socket->connected) { + return NSAPI_ERROR_PARAMETER; + } + } + _local_ports[socket->id] = address.get_port(); + return 0; + } + + return NSAPI_ERROR_UNSUPPORTED; +} + +int ESP8266Interface::socket_listen(void *handle, int backlog) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ESP8266Interface::socket_connect(void *handle, const SocketAddress &addr) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + nsapi_error_t ret; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (socket->proto == NSAPI_UDP) { + ret = _esp.open_udp(socket->id, addr.get_ip_address(), addr.get_port(), _local_ports[socket->id]); + if (ret != NSAPI_ERROR_OK) { + return ret; + } + } else { + if (!_esp.open_tcp(socket->id, addr.get_ip_address(), addr.get_port(), socket->keepalive)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + } + + socket->connected = true; + return 0; +} + +int ESP8266Interface::socket_accept(void *server, void **socket, SocketAddress *addr) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ESP8266Interface::socket_send(void *handle, const void *data, unsigned size) +{ + nsapi_error_t status; + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + status = _esp.send(socket->id, data, size); + + return status != NSAPI_ERROR_OK ? status : size; +} + +int ESP8266Interface::socket_recv(void *handle, void *data, unsigned size) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int32_t recv; + if (socket->proto == NSAPI_TCP) { + recv = _esp.recv_tcp(socket->id, data, size); + if (recv <= 0 && recv != NSAPI_ERROR_WOULD_BLOCK) { + socket->connected = false; + } + } else { + recv = _esp.recv_udp(socket->id, data, size); + } + + return recv; +} + +int ESP8266Interface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if((strcmp(addr.get_ip_address(), "0.0.0.0") == 0) || !addr.get_port()) { + return NSAPI_ERROR_DNS_FAILURE; + } + + if (socket->connected && socket->addr != addr) { + if (!_esp.close(socket->id)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + socket->connected = false; + } + + if (!socket->connected) { + int err = socket_connect(socket, addr); + if (err < 0) { + return err; + } + socket->addr = addr; + } + + return socket_send(socket, data, size); +} + +int ESP8266Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int ret = socket_recv(socket, data, size); + if (ret >= 0 && addr) { + *addr = socket->addr; + } + + return ret; +} + +void ESP8266Interface::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + _cbs[socket->id].callback = callback; + _cbs[socket->id].data = data; +} + +nsapi_error_t ESP8266Interface::setsockopt(nsapi_socket_t handle, int level, + int optname, const void *optval, unsigned optlen) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + + if (!optlen) { + return NSAPI_ERROR_PARAMETER; + } else if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (level == NSAPI_SOCKET && socket->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_KEEPALIVE: { + if(socket->connected) {// ESP8266 limitation, keepalive needs to be given before connecting + return NSAPI_ERROR_UNSUPPORTED; + } + + if (optlen == sizeof(int)) { + int secs = *(int *)optval; + if (secs >= 0 && secs <= 7200) { + socket->keepalive = secs; + return NSAPI_ERROR_OK; + } + } + return NSAPI_ERROR_PARAMETER; + } + } + } + + return NSAPI_ERROR_UNSUPPORTED; +} + +nsapi_error_t ESP8266Interface::getsockopt(nsapi_socket_t handle, int level, int optname, void *optval, unsigned *optlen) +{ + struct esp8266_socket *socket = (struct esp8266_socket *)handle; + + if (!optval || !optlen) { + return NSAPI_ERROR_PARAMETER; + } else if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + if (level == NSAPI_SOCKET && socket->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_KEEPALIVE: { + if(*optlen > sizeof(int)) { + *optlen = sizeof(int); + } + memcpy(optval, &(socket->keepalive), *optlen); + return NSAPI_ERROR_OK; + } + } + } + + return NSAPI_ERROR_UNSUPPORTED; +} + + +void ESP8266Interface::event() +{ + for (int i = 0; i < ESP8266_SOCKET_COUNT; i++) { + if (_cbs[i].callback) { + _cbs[i].callback(_cbs[i].data); + } + } +} + +void ESP8266Interface::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb) +{ + _esp.attach(status_cb); +} + +nsapi_connection_status_t ESP8266Interface::get_connection_status() const +{ + return _esp.get_connection_status(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/ESP8266Interface.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,346 @@ +/* ESP8266 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 ARM Limited + * + * 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. + */ + +#ifndef ESP8266_INTERFACE_H +#define ESP8266_INTERFACE_H + +#include "mbed.h" +#include "ESP8266.h" + + +#define ESP8266_SOCKET_COUNT 5 + +/** ESP8266Interface class + * Implementation of the NetworkStack for the ESP8266 + */ +class ESP8266Interface : public NetworkStack, public WiFiInterface +{ +public: + /** + * @brief ESP8266Interface default constructor + * Will use values defined in mbed_lib.json + */ + ESP8266Interface(); + + /** ESP8266Interface lifetime + * @param tx TX pin + * @param rx RX pin + * @param debug Enable debugging + */ + ESP8266Interface(PinName tx, PinName rx, bool debug = false); + + /** Start the interface + * + * Attempts to connect to a WiFi network. Requires ssid and passphrase to be set. + * If passphrase is invalid, NSAPI_ERROR_AUTH_ERROR is returned. + * + * @return 0 on success, negative error code on failure + */ + virtual int connect(); + + /** Start the interface + * + * Attempts to connect to a WiFi network. + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE) + * @param channel This parameter is not supported, setting it to anything else than 0 will result in NSAPI_ERROR_UNSUPPORTED + * @return 0 on success, or error code on failure + */ + virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE, + uint8_t channel = 0); + + /** Set the WiFi network credentials + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * (defaults to NSAPI_SECURITY_NONE) + * @return 0 on success, or error code on failure + */ + virtual int set_credentials(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE); + + /** Set the WiFi network channel - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param channel Channel on which the connection is to be made, or 0 for any (Default: 0) + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual int set_channel(uint8_t channel); + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual int disconnect(); + + /** Get the internally stored IP address + * @return IP address of the interface or null if not yet connected + */ + virtual const char *get_ip_address(); + + /** Get the internally stored MAC address + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been recieved + */ + virtual const char *get_gateway(); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been recieved + */ + virtual const char *get_netmask(); + + /** Gets the current radio signal strength for active connection + * + * @return Connection strength in dBm (negative value) + */ + virtual int8_t get_rssi(); + + /** Scan for available networks + * + * This function will block. + * + * @param ap Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @param timeout Timeout in milliseconds; 0 for no timeout (Default: 0) + * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + virtual int scan(WiFiAccessPoint *res, unsigned count); + + /** Translates a hostname to an IP address with specific version + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param address Destination for the host SocketAddress + * @param host Hostname to resolve + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::gethostbyname; + + /** Add a domain name server to list of servers to query + * + * @param addr Destination for the host address + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::add_dns_server; + + /** Set socket options + * + * The setsockopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * + * @param handle Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t setsockopt(nsapi_socket_t handle, int level, + int optname, const void *optval, unsigned optlen); + + /** Get socket options + * + * getsockopt allows an application to retrieve stack-specific options + * from the underlying stack using stack-specific level and option names, + * or to request generic options using levels from nsapi_socket_level_t. + * + * For unsupported options, NSAPI_ERROR_UNSUPPORTED is returned + * and the socket is unmodified. + * + * @param level Stack-specific protocol level or nsapi_socket_level_t + * @param optname Level-specific option name + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t getsockopt(nsapi_socket_t handle, int level, int optname, + void *optval, unsigned *optlen); + + /** Register callback for status reporting + * + * The specified status callback function will be called on status changes + * on the network. The parameters on the callback are the event type and + * event-type dependent reason parameter. + * + * In ESP8266 the callback will be called when processing OOB-messages via + * AT-parser. Do NOT call any ESP8266Interface -functions or do extensive + * processing in the callback. + * + * @param status_cb The callback for status changes + */ + virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb); + + /** Get the connection status + * + * @return The connection status according to ConnectionStatusType + */ + virtual nsapi_connection_status_t get_connection_status() const; + +protected: + /** Open a socket + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative on failure + */ + virtual int socket_open(void **handle, nsapi_protocol_t proto); + + /** Close the socket + * @param handle Socket handle + * @return 0 on success, negative on failure + * @note On failure, any memory associated with the socket must still + * be cleaned up + */ + virtual int socket_close(void *handle); + + /** Bind a server socket to a specific port + * @param handle Socket handle + * @param address Local address to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + virtual int socket_bind(void *handle, const SocketAddress &address); + + /** Start listening for incoming connections + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return 0 on success, negative on failure + */ + virtual int socket_listen(void *handle, int backlog); + + /** Connects this TCP socket to the server + * @param handle Socket handle + * @param address SocketAddress to connect to + * @return 0 on success, negative on failure + */ + virtual int socket_connect(void *handle, const SocketAddress &address); + + /** Accept a new connection. + * @param handle Handle in which to store new socket + * @param server Socket handle to server to accept from + * @return 0 on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_accept(void *handle, void **socket, SocketAddress *address); + + /** Send data to the remote host + * @param handle Socket handle + * @param data The buffer to send to the host + * @param size The length of the buffer to send + * @return Number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_send(void *handle, const void *data, unsigned size); + + /** Receive data from the remote host + * @param handle Socket handle + * @param data The buffer in which to store the data received from the host + * @param size The maximum length of the buffer + * @return Number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recv(void *handle, void *data, unsigned size); + + /** Send a packet to a remote endpoint + * @param handle Socket handle + * @param address The remote SocketAddress + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return The number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + + /** Receive a packet from a remote endpoint + * @param handle Socket handle + * @param address Destination for the remote SocketAddress or null + * @param buffer The buffer for storing the incoming packet data + * If a packet is too long to fit in the supplied buffer, + * excess bytes are discarded + * @param size The length of the buffer + * @return The number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + * @note Callback may be called in an interrupt context. + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() + { + return this; + } + +private: + static const int ESP8266_SSID_MAX_LENGTH = 32; /* 32 is what 802.11 defines as longest possible name */ + static const int ESP8266_PASSPHRASE_MAX_LENGTH = 63; /* The longest allowed passphrase */ + static const int ESP8266_PASSPHRASE_MIN_LENGTH = 8; /* The shortest allowed passphrase */ + + ESP8266 _esp; + bool _ids[ESP8266_SOCKET_COUNT]; + int _initialized; + int _started; + + char ap_ssid[ESP8266_SSID_MAX_LENGTH + 1]; /* 32 is what 802.11 defines as longest possible name; +1 for the \0 */ + nsapi_security_t ap_sec; + uint8_t ap_ch; + char ap_pass[ESP8266_PASSPHRASE_MAX_LENGTH + 1]; + uint16_t _local_ports[ESP8266_SOCKET_COUNT]; + + bool _disable_default_softap(); + void event(); + bool _get_firmware_ok(); + nsapi_error_t _init(void); + nsapi_error_t _startup(const int8_t wifi_mode); + + struct { + void (*callback)(void *); + void *data; + } _cbs[ESP8266_SOCKET_COUNT]; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/README.md Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,13 @@ +# ESP8266 WiFi driver for Mbed OS + +The Mbed OS driver for the ESP8266 WiFi module. + +## Firmware version + +ESP8266 modules come in different shapes and formats, but the most important factor is the firmware version in it. To make sure that the firmware in your module is compatible with Mbed OS, follow the [Update guide](https://developer.mbed.org/teams/ESP8266/wiki/Firmware-Update). + +## Restrictions + +- The ESP8266 WiFi module does not allow the TCP client to bind on a specific port. +- Setting up a UDP server is not possible. +- The serial port does not have hardware flow control enabled. The AT command set does not either have a way to limit the download rate. Therefore, downloading anything larger than the serial port input buffer is unreliable. An application should be able to read fast enough to stay ahead of the network. This affects mostly the TCP protocol where data would be lost with no notification. On UDP, this would lead to only packet losses which the higher layer protocol should recover from.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/.mbedignore Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,1 @@ +host_tests/* \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/connectivity/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,76 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "ESP8266Interface.h" + +using namespace utest::v1; + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +// Bringing the network up and down +template <int COUNT> +void test_bring_up_down() { + ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); + net.set_credentials(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + + for (int i = 0; i < COUNT; i++) { + int err = net.connect(); + TEST_ASSERT_EQUAL(0, err); + + printf("MBED: IP Address %s\r\n", net.get_ip_address()); + printf("MBED: Netmask %s\r\n", net.get_netmask()); + printf("MBED: Gateway %s\r\n", net.get_gateway()); + TEST_ASSERT(net.get_ip_address()); + TEST_ASSERT(net.get_netmask()); + TEST_ASSERT(net.get_gateway()); + + UDPSocket udp; + err = udp.open(&net); + TEST_ASSERT_EQUAL(0, err); + err = udp.close(); + TEST_ASSERT_EQUAL(0, err); + + TCPSocket tcp; + err = tcp.open(&net); + TEST_ASSERT_EQUAL(0, err); + err = tcp.close(); + TEST_ASSERT_EQUAL(0, err); + + err = net.disconnect(); + TEST_ASSERT_EQUAL(0, err); + } +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Bringing the network up and down", test_bring_up_down<1>), + Case("Bringing the network up and down twice", test_bring_up_down<2>), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/gethostbyname/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,118 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "ESP8266Interface.h" + +using namespace utest::v1; + +// Hostname for testing against +// Must have A and AAAA records +#ifndef MBED_DNS_TEST_HOST +#define MBED_DNS_TEST_HOST "connector.mbed.com" +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + +// Address info from stack +const char *ip_literal; +nsapi_version_t ip_pref; +const char *ip_pref_repr; + +// Network setup +ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); +void net_bringup() { + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + printf("MBED: Connected to network\n"); + printf("MBED: IP Address: %s\n", net.get_ip_address()); + + ip_literal = net.get_ip_address(); + ip_pref = SocketAddress(ip_literal).get_ip_version(); + ip_pref_repr = (ip_pref == NSAPI_IPv4) ? "ipv4" : + (ip_pref == NSAPI_IPv6) ? "ipv6" : "unspec"; +} + + +// DNS tests +void test_dns_query() { + SocketAddress addr; + int err = net.gethostbyname(MBED_DNS_TEST_HOST, &addr); + printf("DNS: query \"%s\" => \"%s\"\n", + MBED_DNS_TEST_HOST, addr.get_ip_address()); + + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT((bool)addr); + TEST_ASSERT(strlen(addr.get_ip_address()) > 1); +} + +void test_dns_query_pref() { + SocketAddress addr; + int err = net.gethostbyname(MBED_DNS_TEST_HOST, &addr, ip_pref); + printf("DNS: query %s \"%s\" => \"%s\"\n", + ip_pref_repr, MBED_DNS_TEST_HOST, addr.get_ip_address()); + + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT((bool)addr); + TEST_ASSERT(strlen(addr.get_ip_address()) > 1); + TEST_ASSERT_EQUAL(ip_pref, addr.get_ip_version()); +} + +void test_dns_literal() { + SocketAddress addr; + int err = net.gethostbyname(ip_literal, &addr); + printf("DNS: literal \"%s\" => \"%s\"\n", + ip_literal, addr.get_ip_address()); + + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT((bool)addr); + TEST_ASSERT(strlen(addr.get_ip_address()) > 1); + TEST_ASSERT(strcmp(ip_literal, addr.get_ip_address()) == 0); +} + +void test_dns_literal_pref() { + SocketAddress addr; + int err = net.gethostbyname(ip_literal, &addr, ip_pref); + printf("DNS: literal %s \"%s\" => \"%s\"\n", + ip_pref_repr, ip_literal, addr.get_ip_address()); + + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT((bool)addr); + TEST_ASSERT(strlen(addr.get_ip_address()) > 1); + TEST_ASSERT_EQUAL(ip_pref, addr.get_ip_version()); + TEST_ASSERT(strcmp(ip_literal, addr.get_ip_address()) == 0); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "default_auto"); + net_bringup(); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("DNS query", test_dns_query), + Case("DNS preference query", test_dns_query_pref), + Case("DNS literal", test_dns_literal), + Case("DNS preference literal", test_dns_literal_pref), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/host_tests/tcp_echo.py Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,195 @@ +# Copyright 2015 ARM Limited, All rights reserved +# +# 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. + +import sys +import select +import socket +import logging +from threading import Thread +from sys import stdout +from SocketServer import BaseRequestHandler, TCPServer +from mbed_host_tests import BaseHostTest, event_callback + + +class TCPEchoClientHandler(BaseRequestHandler): + def handle(self): + """ + Handles a connection. Test starts by client(i.e. mbed) connecting to server. + This connection handler receives data and echoes back to the client util + {{end}} is received. Then it sits on recv() for client to terminate the + connection. + + Note: reason for not echoing data back after receiving {{end}} is that send + fails raising a SocketError as client closes connection. + """ + while self.server.isrunning(): + try: + data = self.recv() + if not data: break + except Exception as e: + break + + try: + # echo data back to the client + self.send(data) + except Exception as e: + break + + def recv(self): + """ + Try to receive until server is shutdown + """ + while self.server.isrunning(): + rl, wl, xl = select.select([self.request], [], [], 1) + if len(rl): + return self.request.recv(1024) + + def send(self, data): + """ + Try to send until server is shutdown + """ + while self.server.isrunning(): + rl, wl, xl = select.select([], [self.request], [], 1) + if len(wl): + self.request.sendall(data) + break + + +class TCPServerWrapper(TCPServer): + """ + Wrapper over TCP server to implement server initiated shutdown. + Adds a flag:= running that a request handler can check and come out of + recv loop when shutdown is called. + """ + + def __init__(self, addr, request_handler): + # hmm, TCPServer is not sub-classed from object! + if issubclass(TCPServer, object): + super(TCPServerWrapper, self).__init__(addr, request_handler) + else: + TCPServer.__init__(self, addr, request_handler) + self.running = False + + def serve_forever(self): + self.running = True + if issubclass(TCPServer, object): + super(TCPServerWrapper, self).serve_forever() + else: + TCPServer.serve_forever(self) + + def shutdown(self): + self.running = False + if issubclass(TCPServer, object): + super(TCPServerWrapper, self).shutdown() + else: + TCPServer.shutdown(self) + + def isrunning(self): + return self.running + + +class TCPEchoClientTest(BaseHostTest): + + def __init__(self): + """ + Initialise test parameters. + + :return: + """ + BaseHostTest.__init__(self) + self.SERVER_IP = None # Will be determined after knowing the target IP + self.SERVER_PORT = 0 # Let TCPServer choose an arbitrary port + self.server = None + self.server_thread = None + self.target_ip = None + + @staticmethod + def find_interface_to_target_addr(target_ip): + """ + Finds IP address of the interface through which it is connected to the target. + + :return: + """ + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect((target_ip, 0)) # Target IP, any port + except socket.error: + s.connect((target_ip, 8000)) # Target IP, 'random' port + ip = s.getsockname()[0] + s.close() + return ip + + def setup_tcp_server(self): + """ + sets up a TCP server for target to connect and send test data. + + :return: + """ + # !NOTE: There should mechanism to assert in the host test + if self.SERVER_IP is None: + self.log("setup_tcp_server() called before determining server IP!") + self.notify_complete(False) + + # Returning none will suppress host test from printing success code + self.server = TCPServerWrapper((self.SERVER_IP, self.SERVER_PORT), TCPEchoClientHandler) + ip, port = self.server.server_address + self.SERVER_PORT = port + self.server.allow_reuse_address = True + self.log("HOST: Listening for TCP connections: " + self.SERVER_IP + ":" + str(self.SERVER_PORT)) + self.server_thread = Thread(target=TCPEchoClientTest.server_thread_func, args=(self,)) + self.server_thread.start() + + @staticmethod + def server_thread_func(this): + """ + Thread function to run TCP server forever. + + :param this: + :return: + """ + this.server.serve_forever() + + @event_callback("target_ip") + def _callback_target_ip(self, key, value, timestamp): + """ + Callback to handle reception of target's IP address. + + :param key: + :param value: + :param timestamp: + :return: + """ + self.target_ip = value + self.SERVER_IP = self.find_interface_to_target_addr(self.target_ip) + self.setup_tcp_server() + + @event_callback("host_ip") + def _callback_host_ip(self, key, value, timestamp): + """ + Callback for request for host IP Addr + + """ + self.send_kv("host_ip", self.SERVER_IP) + + @event_callback("host_port") + def _callback_host_port(self, key, value, timestamp): + """ + Callback for request for host port + """ + self.send_kv("host_port", self.SERVER_PORT) + + def teardown(self): + if self.server: + self.server.shutdown() + self.server_thread.join()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/host_tests/udp_echo.py Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,127 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +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. +""" + +import sys +import socket +from sys import stdout +from threading import Thread +from SocketServer import BaseRequestHandler, UDPServer +from mbed_host_tests import BaseHostTest, event_callback + + +class UDPEchoClientHandler(BaseRequestHandler): + def handle(self): + """ UDP packet handler. Echoes data back to sender's address. + """ + data, sock = self.request + sock.sendto(data, self.client_address) + + +class UDPEchoClientTest(BaseHostTest): + + def __init__(self): + """ + Initialise test parameters. + + :return: + """ + BaseHostTest.__init__(self) + self.SERVER_IP = None # Will be determined after knowing the target IP + self.SERVER_PORT = 0 # Let TCPServer choose an arbitrary port + self.server = None + self.server_thread = None + self.target_ip = None + + @staticmethod + def find_interface_to_target_addr(target_ip): + """ + Finds IP address of the interface through which it is connected to the target. + + :return: + """ + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect((target_ip, 0)) # Target IP, any port + except socket.error: + s.connect((target_ip, 8000)) # Target IP, 'random' port + ip = s.getsockname()[0] + s.close() + return ip + + def setup_udp_server(self): + """ + sets up a UDP server for target to connect and send test data. + + :return: + """ + # !NOTE: There should mechanism to assert in the host test + if self.SERVER_IP is None: + self.log("setup_udp_server() called before determining server IP!") + self.notify_complete(False) + + # Returning none will suppress host test from printing success code + self.server = UDPServer((self.SERVER_IP, self.SERVER_PORT), UDPEchoClientHandler) + ip, port = self.server.server_address + self.SERVER_PORT = port + self.server.allow_reuse_address = True + self.log("HOST: Listening for UDP packets: " + self.SERVER_IP + ":" + str(self.SERVER_PORT)) + self.server_thread = Thread(target=UDPEchoClientTest.server_thread_func, args=(self,)) + self.server_thread.start() + + @staticmethod + def server_thread_func(this): + """ + Thread function to run TCP server forever. + + :param this: + :return: + """ + this.server.serve_forever() + + @event_callback("target_ip") + def _callback_target_ip(self, key, value, timestamp): + """ + Callback to handle reception of target's IP address. + + :param key: + :param value: + :param timestamp: + :return: + """ + self.target_ip = value + self.SERVER_IP = self.find_interface_to_target_addr(self.target_ip) + self.setup_udp_server() + + @event_callback("host_ip") + def _callback_host_ip(self, key, value, timestamp): + """ + Callback for request for host IP Addr + + """ + self.send_kv("host_ip", self.SERVER_IP) + + @event_callback("host_port") + def _callback_host_port(self, key, value, timestamp): + """ + Callback for request for host port + """ + self.send_kv("host_port", self.SERVER_PORT) + + def teardown(self): + if self.server: + self.server.shutdown() + self.server_thread.join()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/host_tests/udp_shotgun.py Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,142 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +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. +""" + +import sys +import socket +import json +import random +import itertools +import time +from sys import stdout +from threading import Thread +from SocketServer import BaseRequestHandler, UDPServer +from mbed_host_tests import BaseHostTest, event_callback + + +class UDPEchoClientHandler(BaseRequestHandler): + def handle(self): + """ UDP packet handler. Responds with multiple simultaneous packets + """ + data, sock = self.request + pattern = [ord(d) << 4 for d in data] + + # Each byte in request indicates size of packet to recieve + # Each packet size is shifted over by 4 to fit in a byte, which + # avoids any issues with endianess or decoding + for packet in pattern: + data = [random.randint(0, 255) for _ in range(packet-1)] + data.append(reduce(lambda a,b: a^b, data)) + data = ''.join(map(chr, data)) + sock.sendto(data, self.client_address) + + # Sleep a tiny bit to compensate for local network + time.sleep(0.01) + + +class UDPEchoClientTest(BaseHostTest): + def __init__(self): + """ + Initialise test parameters. + + :return: + """ + BaseHostTest.__init__(self) + self.SERVER_IP = None # Will be determined after knowing the target IP + self.SERVER_PORT = 0 # Let TCPServer choose an arbitrary port + self.server = None + self.server_thread = None + self.target_ip = None + + @staticmethod + def find_interface_to_target_addr(target_ip): + """ + Finds IP address of the interface through which it is connected to the target. + + :return: + """ + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect((target_ip, 0)) # Target IP, any port + except socket.error: + s.connect((target_ip, 8000)) # Target IP, 'random' port + ip = s.getsockname()[0] + s.close() + return ip + + def setup_udp_server(self): + """ + sets up a UDP server for target to connect and send test data. + + :return: + """ + # !NOTE: There should mechanism to assert in the host test + if self.SERVER_IP is None: + self.log("setup_udp_server() called before determining server IP!") + self.notify_complete(False) + + # Returning none will suppress host test from printing success code + self.server = UDPServer((self.SERVER_IP, self.SERVER_PORT), UDPEchoClientHandler) + ip, port = self.server.server_address + self.SERVER_PORT = port + self.server.allow_reuse_address = True + self.log("HOST: Listening for UDP packets: " + self.SERVER_IP + ":" + str(self.SERVER_PORT)) + self.server_thread = Thread(target=UDPEchoClientTest.server_thread_func, args=(self,)) + self.server_thread.start() + + @staticmethod + def server_thread_func(this): + """ + Thread function to run TCP server forever. + + :param this: + :return: + """ + this.server.serve_forever() + + @event_callback("target_ip") + def _callback_target_ip(self, key, value, timestamp): + """ + Callback to handle reception of target's IP address. + + :param key: + :param value: + :param timestamp: + :return: + """ + self.target_ip = value + self.SERVER_IP = self.find_interface_to_target_addr(self.target_ip) + self.setup_udp_server() + + @event_callback("host_ip") + def _callback_host_ip(self, key, value, timestamp): + """ + Callback for request for host IP Addr + + """ + self.send_kv("host_ip", self.SERVER_IP) + + @event_callback("host_port") + def _callback_host_port(self, key, value, timestamp): + """ + Callback for request for host port + """ + self.send_kv("host_port", self.SERVER_PORT) + + def teardown(self): + if self.server: + self.server.shutdown() + self.server_thread.join()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/tcp_echo/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,112 @@ +#include "mbed.h" +#include "ESP8266Interface.h" +#include "TCPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE +#define MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE 256 +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + +namespace { + char tx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + char rx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + const char ASCII_MAX = '~' - ' '; +} + +void prep_buffer(char *tx_buffer, size_t tx_size) { + for (size_t i=0; i<tx_size; ++i) { + tx_buffer[i] = (rand() % 10) + '0'; + } +} + +void test_tcp_echo() { + ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + + if (err) { + printf("MBED: failed to connect with an error of %d\r\n", err); + TEST_ASSERT_EQUAL(0, err); + } + + printf("MBED: TCPClient IP address is '%s'\n", net.get_ip_address()); + printf("MBED: TCPClient waiting for server IP and port...\n"); + + greentea_send_kv("target_ip", net.get_ip_address()); + + bool result = false; + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: Server IP address received: %s:%d \n", ipbuf, port); + + TCPSocket sock(&net); + SocketAddress tcp_addr(ipbuf, port); + if (sock.connect(tcp_addr) == 0) { + printf("HTTP: Connected to %s:%d\r\n", ipbuf, port); + printf("tx_buffer buffer size: %u\r\n", sizeof(tx_buffer)); + printf("rx_buffer buffer size: %u\r\n", sizeof(rx_buffer)); + + prep_buffer(tx_buffer, sizeof(tx_buffer)); + sock.send(tx_buffer, sizeof(tx_buffer)); + printf("MBED: Finished sending\r\n"); + // Server will respond with HTTP GET's success code + const int ret = sock.recv(rx_buffer, sizeof(rx_buffer)); + printf("MBED: Finished receiving\r\n"); + + result = !memcmp(tx_buffer, rx_buffer, sizeof(tx_buffer)); + TEST_ASSERT_EQUAL(ret, sizeof(rx_buffer)); + TEST_ASSERT_EQUAL(true, result); + } + + sock.close(); + net.disconnect(); + TEST_ASSERT_EQUAL(true, result); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "tcp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("TCP echo", test_tcp_echo), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/tcp_echo_parallel/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,159 @@ +#ifndef MBED_EXTENDED_TESTS + #error [NOT_SUPPORTED] Parallel tests are not supported by default +#endif + +#include "mbed.h" +#include "ESP8266Interface.h" +#include "TCPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE +#define MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE 64 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_ECHO_THREADS +#define MBED_CFG_TCP_CLIENT_ECHO_THREADS 3 +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); +SocketAddress tcp_addr; +Mutex iomutex; + +void prep_buffer(char *tx_buffer, size_t tx_size) { + for (size_t i=0; i<tx_size; ++i) { + tx_buffer[i] = (rand() % 10) + '0'; + } +} + + +// Each echo class is in charge of one parallel transaction +class Echo { +private: + char tx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE]; + char rx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE]; + + TCPSocket sock; + Thread thread; + +public: + // Limiting stack size to 1k + Echo(): thread(osPriorityNormal, 1024) { + } + + void start() { + osStatus status = thread.start(callback(this, &Echo::echo)); + TEST_ASSERT_EQUAL(osOK, status); + } + + void join() { + osStatus status = thread.join(); + TEST_ASSERT_EQUAL(osOK, status); + } + + void echo() { + int err = sock.open(&net); + TEST_ASSERT_EQUAL(0, err); + + err = sock.connect(tcp_addr); + TEST_ASSERT_EQUAL(0, err); + + iomutex.lock(); + printf("HTTP: Connected to %s:%d\r\n", + tcp_addr.get_ip_address(), tcp_addr.get_port()); + printf("tx_buffer buffer size: %u\r\n", sizeof(tx_buffer)); + printf("rx_buffer buffer size: %u\r\n", sizeof(rx_buffer)); + iomutex.unlock(); + + prep_buffer(tx_buffer, sizeof(tx_buffer)); + sock.send(tx_buffer, sizeof(tx_buffer)); + + // Server will respond with HTTP GET's success code + const int ret = sock.recv(rx_buffer, sizeof(rx_buffer)); + bool result = !memcmp(tx_buffer, rx_buffer, sizeof(tx_buffer)); + TEST_ASSERT_EQUAL(ret, sizeof(rx_buffer)); + TEST_ASSERT_EQUAL(true, result); + + err = sock.close(); + TEST_ASSERT_EQUAL(0, err); + } +}; + +Echo *echoers[MBED_CFG_TCP_CLIENT_ECHO_THREADS]; + + +void test_tcp_echo_parallel() { + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + printf("MBED: TCPClient IP address is '%s'\n", net.get_ip_address()); + printf("MBED: TCPClient waiting for server IP and port...\n"); + + greentea_send_kv("target_ip", net.get_ip_address()); + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: Server IP address received: %s:%d \n", ipbuf, port); + tcp_addr.set_ip_address(ipbuf); + tcp_addr.set_port(port); + + // Startup echo threads in parallel + for (int i = 0; i < MBED_CFG_TCP_CLIENT_ECHO_THREADS; i++) { + echoers[i] = new Echo; + echoers[i]->start(); + } + + for (int i = 0; i < MBED_CFG_TCP_CLIENT_ECHO_THREADS; i++) { + echoers[i]->join(); + delete echoers[i]; + } + + net.disconnect(); +} + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "tcp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("TCP echo parallel", test_tcp_echo_parallel), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/tcp_hello_world/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,115 @@ +#include <algorithm> +#include "mbed.h" +#include "ESP8266Interface.h" +#include "TCPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +namespace { + // Test connection information + const char *HTTP_SERVER_NAME = "os.mbed.com"; + const char *HTTP_SERVER_FILE_PATH = "/media/uploads/mbed_official/hello.txt"; + const int HTTP_SERVER_PORT = 80; +#if defined(TARGET_VK_RZ_A1H) + const int RECV_BUFFER_SIZE = 300; +#else + const int RECV_BUFFER_SIZE = 512; +#endif + // Test related data + const char *HTTP_OK_STR = "200 OK"; + const char *HTTP_HELLO_STR = "Hello world!"; + + // Test buffers + char buffer[RECV_BUFFER_SIZE] = {0}; +} + +bool find_substring(const char *first, const char *last, const char *s_first, const char *s_last) { + const char *f = std::search(first, last, s_first, s_last); + return (f != last); +} + +void test_tcp_hello_world() { + bool result = false; + ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); + net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + printf("TCP client IP Address is %s\r\n", net.get_ip_address()); + + TCPSocket sock(&net); + printf("HTTP: Connection to %s:%d\r\n", HTTP_SERVER_NAME, HTTP_SERVER_PORT); + if (sock.connect(HTTP_SERVER_NAME, HTTP_SERVER_PORT) == 0) { + printf("HTTP: OK\r\n"); + + // We are constructing GET command like this: + // GET http://os.mbed.com/media/uploads/mbed_official/hello.txt HTTP/1.0\n\n + strcpy(buffer, "GET http://"); + strcat(buffer, HTTP_SERVER_NAME); + strcat(buffer, HTTP_SERVER_FILE_PATH); + strcat(buffer, " HTTP/1.0\n\n"); + // Send GET command + sock.send(buffer, strlen(buffer)); + + // Server will respond with HTTP GET's success code + const int ret = sock.recv(buffer, sizeof(buffer) - 1); + buffer[ret] = '\0'; + + // Find 200 OK HTTP status in reply + bool found_200_ok = find_substring(buffer, buffer + ret, HTTP_OK_STR, HTTP_OK_STR + strlen(HTTP_OK_STR)); + // Find "Hello World!" string in reply + bool found_hello = find_substring(buffer, buffer + ret, HTTP_HELLO_STR, HTTP_HELLO_STR + strlen(HTTP_HELLO_STR)); + + TEST_ASSERT_TRUE(found_200_ok); + TEST_ASSERT_TRUE(found_hello); + + if (found_200_ok && found_hello) result = true; + + printf("HTTP: Received %d chars from server\r\n", ret); + printf("HTTP: Received 200 OK status ... %s\r\n", found_200_ok ? "[OK]" : "[FAIL]"); + printf("HTTP: Received '%s' status ... %s\r\n", HTTP_HELLO_STR, found_hello ? "[OK]" : "[FAIL]"); + printf("HTTP: Received message:\r\n"); + printf("%s", buffer); + sock.close(); + } else { + printf("HTTP: ERROR\r\n"); + } + + net.disconnect(); + printf("Network disconnected\r\n"); + TEST_ASSERT_EQUAL(true, result); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("TCP hello world", test_tcp_hello_world), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/tcp_packet_pressure/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,253 @@ +#ifndef MBED_EXTENDED_TESTS + #error [NOT_SUPPORTED] Pressure tests are not supported by default +#endif + +#include "mbed.h" +#include "ESP8266Interface.h" +#include "TCPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN 64 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX 0x80000 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG false +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +// Simple xorshift pseudorandom number generator +class RandSeq { +private: + uint32_t x; + uint32_t y; + static const int A = 15; + static const int B = 18; + static const int C = 11; + +public: + RandSeq(uint32_t seed=MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED) + : x(seed), y(seed) {} + + uint32_t next(void) { + x ^= x << A; + x ^= x >> B; + x ^= y ^ (y >> C); + return x + y; + } + + void skip(size_t size) { + for (size_t i = 0; i < size; i++) { + next(); + } + } + + void buffer(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + buffer[i] = lookahead.next() & 0xff; + } + } + + int cmp(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + int diff = buffer[i] - (lookahead.next() & 0xff); + if (diff != 0) { + return diff; + } + } + return 0; + } +}; + +// Shared buffer for network transactions +uint8_t *buffer; +size_t buffer_size; + +// Tries to get the biggest buffer possible on the device. Exponentially +// grows a buffer until heap runs out of space, and uses half to leave +// space for the rest of the program +void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) { + size_t i = min; + while (i < max) { + void *b = malloc(i); + if (!b) { + i /= 4; + if (i < min) { + i = min; + } + break; + } + free(b); + i *= 2; + } + + *buffer = (uint8_t *)malloc(i); + *size = i; + TEST_ASSERT(buffer); +} + + +void test_tcp_packet_pressure() { + generate_buffer(&buffer, &buffer_size, + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN, + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX); + printf("MBED: Generated buffer %d\r\n", buffer_size); + + ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + printf("MBED: TCPClient IP address is '%s'\n", net.get_ip_address()); + printf("MBED: TCPClient waiting for server IP and port...\n"); + + greentea_send_kv("target_ip", net.get_ip_address()); + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: Server IP address received: %s:%d \n", ipbuf, port); + + TCPSocket sock; + SocketAddress tcp_addr(ipbuf, port); + + Timer timer; + timer.start(); + + // Tests exponentially growing sequences + for (size_t size = MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN; + size < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX; + size *= 2) { + err = sock.open(&net); + TEST_ASSERT_EQUAL(0, err); + err = sock.connect(tcp_addr); + TEST_ASSERT_EQUAL(0, err); + printf("TCP: %s:%d streaming %d bytes\r\n", ipbuf, port, size); + + sock.set_blocking(false); + + // Loop to send/recv all data + RandSeq tx_seq; + RandSeq rx_seq; + size_t rx_count = 0; + size_t tx_count = 0; + size_t window = buffer_size; + + while (tx_count < size || rx_count < size) { + // Send out data + if (tx_count < size) { + size_t chunk_size = size - tx_count; + if (chunk_size > window) { + chunk_size = window; + } + + tx_seq.buffer(buffer, chunk_size); + int td = sock.send(buffer, chunk_size); + + if (td > 0) { + if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) { + printf("TCP: tx -> %d\r\n", td); + } + tx_seq.skip(td); + tx_count += td; + } else if (td != NSAPI_ERROR_WOULD_BLOCK) { + // We may fail to send because of buffering issues, + // cut buffer in half + if (window > MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) { + window /= 2; + } + + if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) { + printf("TCP: Not sent (%d), window = %d\r\n", td, window); + } + } + } + + // Verify recieved data + while (rx_count < size) { + int rd = sock.recv(buffer, buffer_size); + TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK); + if (rd > 0) { + if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) { + printf("TCP: rx <- %d\r\n", rd); + } + int diff = rx_seq.cmp(buffer, rd); + TEST_ASSERT_EQUAL(0, diff); + rx_seq.skip(rd); + rx_count += rd; + } else if (rd == NSAPI_ERROR_WOULD_BLOCK) { + break; + } + } + } + + err = sock.close(); + TEST_ASSERT_EQUAL(0, err); + } + + timer.stop(); + printf("MBED: Time taken: %fs\r\n", timer.read()); + printf("MBED: Speed: %.3fkb/s\r\n", + 8*(2*MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX - + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read())); + + net.disconnect(); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "tcp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("TCP packet pressure", test_tcp_packet_pressure), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/tcp_packet_pressure_parallel/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,315 @@ +#ifndef MBED_EXTENDED_TESTS + #error [NOT_SUPPORTED] Parallel pressure tests are not supported by default +#endif + +#include "mbed.h" +#include "ESP8266Interface.h" +#include "TCPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN 64 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX 0x80000 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS 3 +#endif + +#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG +#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG false +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +// Simple xorshift pseudorandom number generator +class RandSeq { +private: + uint32_t x; + uint32_t y; + static const int A = 15; + static const int B = 18; + static const int C = 11; + +public: + RandSeq(uint32_t seed=MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED) + : x(seed), y(seed) {} + + uint32_t next(void) { + x ^= x << A; + x ^= x >> B; + x ^= y ^ (y >> C); + return x + y; + } + + void skip(size_t size) { + for (size_t i = 0; i < size; i++) { + next(); + } + } + + void buffer(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + buffer[i] = lookahead.next() & 0xff; + } + } + + int cmp(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + int diff = buffer[i] - (lookahead.next() & 0xff); + if (diff != 0) { + return diff; + } + } + return 0; + } +}; + + +// Tries to get the biggest buffer possible on the device. Exponentially +// grows a buffer until heap runs out of space, and uses half to leave +// space for the rest of the program +void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) { + size_t i = min; + while (i < max) { + void *b = malloc(i); + if (!b) { + i /= 4; + if (i < min) { + i = min; + } + break; + } + free(b); + i *= 2; + } + + *buffer = (uint8_t *)malloc(i); + *size = i; + TEST_ASSERT(buffer); +} + + +// Global variables shared between pressure tests +ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); +SocketAddress tcp_addr; +Timer timer; +Mutex iomutex; + +// Single instance of a pressure test +class PressureTest { +private: + uint8_t *buffer; + size_t buffer_size; + + TCPSocket sock; + Thread thread; + +public: + PressureTest(uint8_t *buffer, size_t buffer_size) + : buffer(buffer), buffer_size(buffer_size) { + } + + void start() { + osStatus status = thread.start(callback(this, &PressureTest::run)); + TEST_ASSERT_EQUAL(osOK, status); + } + + void join() { + osStatus status = thread.join(); + TEST_ASSERT_EQUAL(osOK, status); + } + + void run() { + // Tests exponentially growing sequences + for (size_t size = MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN; + size < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX; + size *= 2) { + int err = sock.open(&net); + TEST_ASSERT_EQUAL(0, err); + err = sock.connect(tcp_addr); + TEST_ASSERT_EQUAL(0, err); + iomutex.lock(); + printf("TCP: %s:%d streaming %d bytes\r\n", + tcp_addr.get_ip_address(), tcp_addr.get_port(), size); + iomutex.unlock(); + + sock.set_blocking(false); + + // Loop to send/recv all data + RandSeq tx_seq; + RandSeq rx_seq; + size_t rx_count = 0; + size_t tx_count = 0; + size_t window = buffer_size; + + while (tx_count < size || rx_count < size) { + // Send out data + if (tx_count < size) { + size_t chunk_size = size - tx_count; + if (chunk_size > window) { + chunk_size = window; + } + + tx_seq.buffer(buffer, chunk_size); + int td = sock.send(buffer, chunk_size); + + if (td > 0) { + if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) { + iomutex.lock(); + printf("TCP: tx -> %d\r\n", td); + iomutex.unlock(); + } + tx_seq.skip(td); + tx_count += td; + } else if (td != NSAPI_ERROR_WOULD_BLOCK) { + // We may fail to send because of buffering issues, + // cut buffer in half + if (window > MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) { + window /= 2; + } + + if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) { + iomutex.lock(); + printf("TCP: Not sent (%d), window = %d\r\n", td, window); + iomutex.unlock(); + } + } + } + + // Verify recieved data + while (rx_count < size) { + int rd = sock.recv(buffer, buffer_size); + TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK); + if (rd > 0) { + if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) { + iomutex.lock(); + printf("TCP: rx <- %d\r\n", rd); + iomutex.unlock(); + } + int diff = rx_seq.cmp(buffer, rd); + TEST_ASSERT_EQUAL(0, diff); + rx_seq.skip(rd); + rx_count += rd; + } else if (rd == NSAPI_ERROR_WOULD_BLOCK) { + break; + } + } + } + + err = sock.close(); + TEST_ASSERT_EQUAL(0, err); + } + } +}; + +PressureTest *pressure_tests[MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS]; + + +void test_tcp_packet_pressure_parallel() { + uint8_t *buffer; + size_t buffer_size; + generate_buffer(&buffer, &buffer_size, + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN, + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX); + + size_t buffer_subsize = buffer_size / MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS; + printf("MBED: Generated buffer %d\r\n", buffer_size); + printf("MBED: Split into %d buffers %d\r\n", + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS, + buffer_subsize); + + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + printf("MBED: TCPClient IP address is '%s'\n", net.get_ip_address()); + printf("MBED: TCPClient waiting for server IP and port...\n"); + + greentea_send_kv("target_ip", net.get_ip_address()); + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: Server IP address received: %s:%d \n", ipbuf, port); + tcp_addr.set_ip_address(ipbuf); + tcp_addr.set_port(port); + + timer.start(); + + // Startup pressure tests in parallel + for (int i = 0; i < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS; i++) { + pressure_tests[i] = new PressureTest(&buffer[i*buffer_subsize], buffer_subsize); + pressure_tests[i]->start(); + } + + for (int i = 0; i < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS; i++) { + pressure_tests[i]->join(); + delete pressure_tests[i]; + } + + timer.stop(); + printf("MBED: Time taken: %fs\r\n", timer.read()); + printf("MBED: Speed: %.3fkb/s\r\n", + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS* + 8*(2*MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX - + MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read())); + + net.disconnect(); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "tcp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("TCP packet pressure parallel", test_tcp_packet_pressure_parallel), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/udp_dtls_handshake/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,159 @@ +#include "mbed.h" +#include "ESP8266Interface.h" +#include "UDPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_UDP_DTLS_HANDSHAKE_BUFFER_SIZE +#define MBED_CFG_UDP_DTLS_HANDSHAKE_BUFFER_SIZE 512 +#endif + +#ifndef MBED_CFG_UDP_DTLS_HANDSHAKE_RETRIES +#define MBED_CFG_UDP_DTLS_HANDSHAKE_RETRIES 16 +#endif + +#ifndef MBED_CFG_UDP_DTLS_HANDSHAKE_PATTERN +#define MBED_CFG_UDP_DTLS_HANDSHAKE_PATTERN 112, 384, 200, 219, 25 +#endif + +#ifndef MBED_CFG_UDP_DTLS_HANDSHAKE_TIMEOUT +#define MBED_CFG_UDP_DTLS_HANDSHAKE_TIMEOUT 1500 +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + +uint8_t buffer[MBED_CFG_UDP_DTLS_HANDSHAKE_BUFFER_SIZE] = {0}; +int udp_dtls_handshake_pattern[] = {MBED_CFG_UDP_DTLS_HANDSHAKE_PATTERN}; +const int udp_dtls_handshake_count = sizeof(udp_dtls_handshake_pattern) / sizeof(int); + +void test_udp_dtls_handshake() { + ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + printf("MBED: UDPClient IP address is '%s'\n", net.get_ip_address()); + printf("MBED: UDPClient waiting for server IP and port...\n"); + + greentea_send_kv("target_ip", net.get_ip_address()); + + bool result = false; + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: UDP Server IP address received: %s:%d \n", ipbuf, port); + + // align each size to 4-bits + for (int i = 0; i < udp_dtls_handshake_count; i++) { + udp_dtls_handshake_pattern[i] = (~0xf & udp_dtls_handshake_pattern[i]) + 0x10; + } + + printf("MBED: DTLS pattern ["); + for (int i = 0; i < udp_dtls_handshake_count; i++) { + printf("%d", udp_dtls_handshake_pattern[i]); + if (i != udp_dtls_handshake_count-1) { + printf(", "); + } + } + printf("]\r\n"); + + UDPSocket sock; + SocketAddress udp_addr(ipbuf, port); + sock.set_timeout(MBED_CFG_UDP_DTLS_HANDSHAKE_TIMEOUT); + + for (int attempt = 0; attempt < MBED_CFG_UDP_DTLS_HANDSHAKE_RETRIES; attempt++) { + err = sock.open(&net); + TEST_ASSERT_EQUAL(0, err); + + for (int i = 0; i < udp_dtls_handshake_count; i++) { + buffer[i] = udp_dtls_handshake_pattern[i] >> 4; + } + + err = sock.sendto(udp_addr, buffer, udp_dtls_handshake_count); + printf("UDP: tx -> %d\r\n", err); + TEST_ASSERT_EQUAL(udp_dtls_handshake_count, err); + + int step = 0; + while (step < udp_dtls_handshake_count) { + err = sock.recvfrom(NULL, buffer, sizeof(buffer)); + printf("UDP: rx <- %d ", err); + + // check length + if (err != udp_dtls_handshake_pattern[step]) { + printf("x (expected %d)\r\n", udp_dtls_handshake_pattern[step]); + break; + } + + // check quick xor of packet + uint8_t check = 0; + for (int j = 0; j < udp_dtls_handshake_pattern[step]; j++) { + check ^= buffer[j]; + } + + if (check != 0) { + printf("x (checksum 0x%02x)\r\n", check); + break; + } + + // successfully got a packet + printf("\r\n"); + step += 1; + } + + err = sock.close(); + TEST_ASSERT_EQUAL(0, err); + + // got through all steps, test passed + if (step == udp_dtls_handshake_count) { + result = true; + break; + } + } + + net.disconnect(); + TEST_ASSERT_EQUAL(true, result); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "udp_shotgun"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("UDP DTLS handshake", test_udp_dtls_handshake), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/udp_echo/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,151 @@ +#include "mbed.h" +#include "ESP8266Interface.h" +#include "UDPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE +#define MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE 64 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT +#define MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT 500 +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +namespace { + char tx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + char rx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + const char ASCII_MAX = '~' - ' '; + const int ECHO_LOOPS = 16; + char uuid[48] = {0}; +} + +void prep_buffer(char *uuid, char *tx_buffer, size_t tx_size) { + size_t i = 0; + + memcpy(tx_buffer, uuid, strlen(uuid)); + i += strlen(uuid); + + tx_buffer[i++] = ' '; + + for (; i<tx_size; ++i) { + tx_buffer[i] = (rand() % 10) + '0'; + } +} + +void test_udp_echo() { + ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); + + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + if (err) { + printf("MBED: failed to connect with an error of %d\r\n", err); + TEST_ASSERT_EQUAL(0, err); + } + + printf("UDP client IP Address is %s\n", net.get_ip_address()); + + greentea_send_kv("target_ip", net.get_ip_address()); + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + UDPSocket sock; + sock.open(&net); + sock.set_timeout(MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT); + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: UDP Server IP address received: %s:%d \n", ipbuf, port); + SocketAddress udp_addr(ipbuf, port); + + int success = 0; + for (int i = 0; success < ECHO_LOOPS; i++) { + prep_buffer(uuid, tx_buffer, sizeof(tx_buffer)); + const int ret = sock.sendto(udp_addr, tx_buffer, sizeof(tx_buffer)); + if (ret >= 0) { + printf("[%02d] sent %d bytes - %.*s \n", i, ret, ret, tx_buffer); + } else { + printf("[%02d] Network error %d\n", i, ret); + continue; + } + + SocketAddress temp_addr; + const int n = sock.recvfrom(&temp_addr, rx_buffer, sizeof(rx_buffer)); + if (n >= 0) { + printf("[%02d] recv %d bytes - %.*s \n", i, n, n, tx_buffer); + } else { + printf("[%02d] Network error %d\n", i, n); + continue; + } + + if ((temp_addr == udp_addr && + n == sizeof(tx_buffer) && + memcmp(rx_buffer, tx_buffer, sizeof(rx_buffer)) == 0)) { + success += 1; + + printf("[%02d] success #%d\n", i, success); + continue; + } + + // failed, clean out any remaining bad packets + sock.set_timeout(0); + while (true) { + err = sock.recvfrom(NULL, NULL, 0); + if (err == NSAPI_ERROR_WOULD_BLOCK) { + break; + } + } + sock.set_timeout(MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT); + } + + sock.close(); + net.disconnect(); + TEST_ASSERT_EQUAL(ECHO_LOOPS, success); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "udp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("UDP echo", test_udp_echo), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/udp_echo_parallel/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,231 @@ +#ifndef MBED_EXTENDED_TESTS + #error [NOT_SUPPORTED] Parallel tests are not supported by default +#endif + +#include "mbed.h" +#include "ESP8266Interface.h" +#include "UDPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE +#define MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE 64 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT +#define MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT 500 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_ECHO_THREADS +#define MBED_CFG_UDP_CLIENT_ECHO_THREADS 3 +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +const int ECHO_LOOPS = 16; +ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); +SocketAddress udp_addr; +Mutex iomutex; +char uuid[48] = {0}; + +// NOTE: assuming that "id" stays in the single digits +void prep_buffer(int id, char *uuid, char *tx_buffer, size_t tx_size) { + size_t i = 0; + + tx_buffer[i++] = '0' + id; + tx_buffer[i++] = ' '; + + memcpy(tx_buffer+i, uuid, strlen(uuid)); + i += strlen(uuid); + + tx_buffer[i++] = ' '; + + for (; i<tx_size; ++i) { + tx_buffer[i] = (rand() % 10) + '0'; + } +} + + +// Each echo class is in charge of one parallel transaction +class Echo { +private: + char tx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE]; + char rx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE]; + + UDPSocket sock; + Thread thread; + bool result; + int id; + char *uuid; + +public: + // Limiting stack size to 1k + Echo(): thread(osPriorityNormal, 1024), result(false) { + } + + void start(int id, char *uuid) { + this->id = id; + this->uuid = uuid; + osStatus status = thread.start(callback(this, &Echo::echo)); + } + + void join() { + osStatus status = thread.join(); + TEST_ASSERT_EQUAL(osOK, status); + } + + void echo() { + int success = 0; + + int err = sock.open(&net); + TEST_ASSERT_EQUAL(0, err); + + sock.set_timeout(MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT); + + for (int i = 0; success < ECHO_LOOPS; i++) { + prep_buffer(id, uuid, tx_buffer, sizeof(tx_buffer)); + const int ret = sock.sendto(udp_addr, tx_buffer, sizeof(tx_buffer)); + if (ret >= 0) { + iomutex.lock(); + printf("[ID:%01d][%02d] sent %d bytes - %.*s \n", id, i, ret, ret, tx_buffer); + iomutex.unlock(); + } else { + iomutex.lock(); + printf("[ID:%01d][%02d] Network error %d\n", id, i, ret); + iomutex.unlock(); + continue; + } + + SocketAddress temp_addr; + const int n = sock.recvfrom(&temp_addr, rx_buffer, sizeof(rx_buffer)); + if (n >= 0) { + iomutex.lock(); + printf("[ID:%01d][%02d] recv %d bytes - %.*s \n", id, i, n, n, tx_buffer); + iomutex.unlock(); + } else { + iomutex.lock(); + printf("[ID:%01d][%02d] Network error %d\n", id, i, n); + iomutex.unlock(); + continue; + } + + if ((temp_addr == udp_addr && + n == sizeof(tx_buffer) && + memcmp(rx_buffer, tx_buffer, sizeof(rx_buffer)) == 0)) { + success += 1; + iomutex.lock(); + printf("[ID:%01d][%02d] success #%d\n", id, i, success); + iomutex.unlock(); + continue; + } + + // failed, clean out any remaining bad packets + sock.set_timeout(0); + while (true) { + err = sock.recvfrom(NULL, NULL, 0); + if (err == NSAPI_ERROR_WOULD_BLOCK) { + break; + } + } + sock.set_timeout(MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT); + } + + result = success == ECHO_LOOPS; + + err = sock.close(); + TEST_ASSERT_EQUAL(0, err); + if (err) { + result = false; + } + } + + bool get_result() { + return result; + } +}; + +Echo *echoers[MBED_CFG_UDP_CLIENT_ECHO_THREADS]; + + +void test_udp_echo_parallel() { + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + if (err) { + printf("MBED: failed to connect with an error of %d\r\n", err); + GREENTEA_TESTSUITE_RESULT(false); + } else { + printf("UDP client IP Address is %s\n", net.get_ip_address()); + + greentea_send_kv("target_ip", net.get_ip_address()); + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: UDP Server IP address received: %s:%d \n", ipbuf, port); + udp_addr.set_ip_address(ipbuf); + udp_addr.set_port(port); + + // Startup echo threads in parallel + for (int i = 0; i < MBED_CFG_UDP_CLIENT_ECHO_THREADS; i++) { + echoers[i] = new Echo; + echoers[i]->start(i, uuid); + } + + bool result = true; + + for (int i = 0; i < MBED_CFG_UDP_CLIENT_ECHO_THREADS; i++) { + echoers[i]->join(); + result = result && echoers[i]->get_result(); + delete echoers[i]; + } + + net.disconnect(); + TEST_ASSERT_EQUAL(true, result); + } +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "udp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("UDP echo parallel", test_udp_echo_parallel), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/udp_packet_pressure/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,276 @@ +#ifndef MBED_EXTENDED_TESTS + #error [NOT_SUPPORTED] Pressure tests are not supported by default +#endif + +#include "mbed.h" +#include "ESP8266Interface.h" +#include "UDPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN 64 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX 0x80000 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT 100 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG false +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +// Simple xorshift pseudorandom number generator +class RandSeq { +private: + uint32_t x; + uint32_t y; + static const int A = 15; + static const int B = 18; + static const int C = 11; + +public: + RandSeq(uint32_t seed=MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED) + : x(seed), y(seed) {} + + uint32_t next(void) { + x ^= x << A; + x ^= x >> B; + x ^= y ^ (y >> C); + return x + y; + } + + void skip(size_t size) { + for (size_t i = 0; i < size; i++) { + next(); + } + } + + void buffer(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + buffer[i] = lookahead.next() & 0xff; + } + } + + int cmp(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + int diff = buffer[i] - (lookahead.next() & 0xff); + if (diff != 0) { + return diff; + } + } + return 0; + } +}; + +// Shared buffer for network transactions +uint8_t *buffer; +size_t buffer_size; + +// Tries to get the biggest buffer possible on the device. Exponentially +// grows a buffer until heap runs out of space, and uses half to leave +// space for the rest of the program +void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) { + size_t i = min; + while (i < max) { + void *b = malloc(i); + if (!b) { + i /= 4; + if (i < min) { + i = min; + } + break; + } + free(b); + i *= 2; + } + + *buffer = (uint8_t *)malloc(i); + *size = i; + TEST_ASSERT(buffer); +} + +void test_udp_packet_pressure() { + generate_buffer(&buffer, &buffer_size, + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN, + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX); + printf("MBED: Generated buffer %d\r\n", buffer_size); + + ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + printf("MBED: UDPClient IP address is '%s'\n", net.get_ip_address()); + printf("MBED: UDPClient waiting for server IP and port...\n"); + + greentea_send_kv("target_ip", net.get_ip_address()); + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: Server IP address received: %s:%d \n", ipbuf, port); + + UDPSocket sock; + SocketAddress udp_addr(ipbuf, port); + + Timer timer; + timer.start(); + + // Tests exponentially growing sequences + for (size_t size = MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN; + size < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX; + size *= 2) { + err = sock.open(&net); + TEST_ASSERT_EQUAL(0, err); + printf("UDP: %s:%d streaming %d bytes\r\n", ipbuf, port, size); + + sock.set_blocking(false); + + // Loop to send/recv all data + RandSeq tx_seq; + RandSeq rx_seq; + size_t rx_count = 0; + size_t tx_count = 0; + int known_time = timer.read_ms(); + size_t window = buffer_size; + + while (tx_count < size || rx_count < size) { + // Send out packets + if (tx_count < size) { + size_t chunk_size = size - tx_count; + if (chunk_size > window) { + chunk_size = window; + } + + tx_seq.buffer(buffer, chunk_size); + int td = sock.sendto(udp_addr, buffer, chunk_size); + + if (td > 0) { + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + printf("UDP: tx -> %d\r\n", td); + } + tx_seq.skip(td); + tx_count += td; + } else if (td != NSAPI_ERROR_WOULD_BLOCK) { + // We may fail to send because of buffering issues, revert to + // last good sequence and cut buffer in half + if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) { + window /= 2; + } + + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + printf("UDP: Not sent (%d), window = %d\r\n", td, window); + } + } + } + + // Prioritize recieving over sending packets to avoid flooding + // the network while handling erronous packets + while (rx_count < size) { + int rd = sock.recvfrom(NULL, buffer, buffer_size); + TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK); + + if (rd > 0) { + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + printf("UDP: rx <- %d\r\n", rd); + } + + if (rx_seq.cmp(buffer, rd) == 0) { + rx_seq.skip(rd); + rx_count += rd; + known_time = timer.read_ms(); + if (window < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX) { + window += MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN; + } + } + } else if (timer.read_ms() - known_time > + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT) { + // Dropped packet or out of order, revert to last good sequence + // and cut buffer in half + tx_seq = rx_seq; + tx_count = rx_count; + known_time = timer.read_ms(); + if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) { + window /= 2; + } + + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + printf("UDP: Dropped, window = %d\r\n", window); + } + } else if (rd == NSAPI_ERROR_WOULD_BLOCK) { + break; + } + } + } + + err = sock.close(); + TEST_ASSERT_EQUAL(0, err); + } + + timer.stop(); + printf("MBED: Time taken: %fs\r\n", timer.read()); + printf("MBED: Speed: %.3fkb/s\r\n", + 8*(2*MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX - + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read())); + + net.disconnect(); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "udp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("UDP packet pressure", test_udp_packet_pressure), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/TESTS/net/udp_packet_pressure_parallel/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,341 @@ +#ifndef MBED_EXTENDED_TESTS + #error [NOT_SUPPORTED] Parallel pressure tests are not supported by default +#endif + +#include "mbed.h" +#include "ESP8266Interface.h" +#include "UDPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest.h" + +using namespace utest::v1; + + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN 64 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX 0x80000 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT 100 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS 3 +#endif + +#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG +#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG false +#endif + +#ifndef MBED_CFG_ESP8266_TX +#define MBED_CFG_ESP8266_TX D1 +#endif + +#ifndef MBED_CFG_ESP8266_RX +#define MBED_CFG_ESP8266_RX D0 +#endif + +#ifndef MBED_CFG_ESP8266_DEBUG +#define MBED_CFG_ESP8266_DEBUG false +#endif + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x + + +// Simple xorshift pseudorandom number generator +class RandSeq { +private: + uint32_t x; + uint32_t y; + static const int A = 15; + static const int B = 18; + static const int C = 11; + +public: + RandSeq(uint32_t seed=MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED) + : x(seed), y(seed) {} + + uint32_t next(void) { + x ^= x << A; + x ^= x >> B; + x ^= y ^ (y >> C); + return x + y; + } + + void skip(size_t size) { + for (size_t i = 0; i < size; i++) { + next(); + } + } + + void buffer(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + buffer[i] = lookahead.next() & 0xff; + } + } + + int cmp(uint8_t *buffer, size_t size) { + RandSeq lookahead = *this; + + for (size_t i = 0; i < size; i++) { + int diff = buffer[i] - (lookahead.next() & 0xff); + if (diff != 0) { + return diff; + } + } + return 0; + } +}; + +// Tries to get the biggest buffer possible on the device. Exponentially +// grows a buffer until heap runs out of space, and uses half to leave +// space for the rest of the program +void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) { + size_t i = min; + while (i < max) { + void *b = malloc(i); + if (!b) { + i /= 8; + if (i < min) { + i = min; + } + break; + } + free(b); + i *= 2; + } + + *buffer = (uint8_t *)malloc(i); + *size = i; + TEST_ASSERT(buffer); +} + + +// Global variables shared between pressure tests +ESP8266Interface net(MBED_CFG_ESP8266_TX, MBED_CFG_ESP8266_RX, MBED_CFG_ESP8266_DEBUG); +SocketAddress udp_addr; +Timer timer; +Mutex iomutex; + +// Single instance of a pressure test +class PressureTest { +private: + uint8_t *buffer; + size_t buffer_size; + + UDPSocket sock; + Thread thread; + +public: + PressureTest(uint8_t *buffer, size_t buffer_size) + : buffer(buffer), buffer_size(buffer_size) { + } + + void start() { + osStatus status = thread.start(callback(this, &PressureTest::run)); + TEST_ASSERT_EQUAL(osOK, status); + } + + void join() { + osStatus status = thread.join(); + TEST_ASSERT_EQUAL(osOK, status); + } + + void run() { + // Tests exponentially growing sequences + for (size_t size = MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN; + size < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX; + size *= 2) { + int err = sock.open(&net); + TEST_ASSERT_EQUAL(0, err); + iomutex.lock(); + printf("UDP: %s:%d streaming %d bytes\r\n", + udp_addr.get_ip_address(), udp_addr.get_port(), size); + iomutex.unlock(); + + sock.set_blocking(false); + + // Loop to send/recv all data + RandSeq tx_seq; + RandSeq rx_seq; + size_t rx_count = 0; + size_t tx_count = 0; + int known_time = timer.read_ms(); + size_t window = buffer_size; + + while (tx_count < size || rx_count < size) { + // Send out packets + if (tx_count < size) { + size_t chunk_size = size - tx_count; + if (chunk_size > window) { + chunk_size = window; + } + + tx_seq.buffer(buffer, chunk_size); + int td = sock.sendto(udp_addr, buffer, chunk_size); + + if (td > 0) { + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + iomutex.lock(); + printf("UDP: tx -> %d\r\n", td); + iomutex.unlock(); + } + tx_seq.skip(td); + tx_count += td; + } else if (td != NSAPI_ERROR_WOULD_BLOCK) { + // We may fail to send because of buffering issues, revert to + // last good sequence and cut buffer in half + if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) { + window /= 2; + } + + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + iomutex.lock(); + printf("UDP: Not sent (%d), window = %d\r\n", td, window); + iomutex.unlock(); + } + } + } + + // Prioritize recieving over sending packets to avoid flooding + // the network while handling erronous packets + while (rx_count < size) { + int rd = sock.recvfrom(NULL, buffer, buffer_size); + TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK); + + if (rd > 0) { + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + iomutex.lock(); + printf("UDP: rx <- %d\r\n", rd); + iomutex.unlock(); + } + + if (rx_seq.cmp(buffer, rd) == 0) { + rx_seq.skip(rd); + rx_count += rd; + known_time = timer.read_ms(); + if (window < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX) { + window += MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN; + } + } + } else if (timer.read_ms() - known_time > + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT) { + // Dropped packet or out of order, revert to last good sequence + // and cut buffer in half + tx_seq = rx_seq; + tx_count = rx_count; + known_time = timer.read_ms(); + if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) { + window /= 2; + } + + if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) { + iomutex.lock(); + printf("UDP: Dropped, window = %d\r\n", window); + iomutex.unlock(); + } + } else if (rd == NSAPI_ERROR_WOULD_BLOCK) { + break; + } + } + } + + err = sock.close(); + TEST_ASSERT_EQUAL(0, err); + } + } +}; + +PressureTest *pressure_tests[MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS]; + + +void test_udp_packet_pressure_parallel() { + uint8_t *buffer; + size_t buffer_size; + generate_buffer(&buffer, &buffer_size, + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN, + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX); + + size_t buffer_subsize = buffer_size / MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS; + printf("MBED: Generated buffer %d\r\n", buffer_size); + printf("MBED: Split into %d buffers %d\r\n", + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS, + buffer_subsize); + + int err = net.connect(STRINGIZE(MBED_CFG_ESP8266_SSID), STRINGIZE(MBED_CFG_ESP8266_PASS)); + TEST_ASSERT_EQUAL(0, err); + + printf("MBED: UDPClient IP address is '%s'\n", net.get_ip_address()); + printf("MBED: UDPClient waiting for server IP and port...\n"); + + greentea_send_kv("target_ip", net.get_ip_address()); + + char recv_key[] = "host_port"; + char ipbuf[60] = {0}; + char portbuf[16] = {0}; + unsigned int port = 0; + + greentea_send_kv("host_ip", " "); + greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); + + greentea_send_kv("host_port", " "); + greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); + sscanf(portbuf, "%u", &port); + + printf("MBED: Server IP address received: %s:%d \n", ipbuf, port); + udp_addr.set_ip_address(ipbuf); + udp_addr.set_port(port); + + timer.start(); + + // Startup pressure tests in parallel + for (int i = 0; i < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS; i++) { + pressure_tests[i] = new PressureTest(&buffer[i*buffer_subsize], buffer_subsize); + pressure_tests[i]->start(); + } + + for (int i = 0; i < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS; i++) { + pressure_tests[i]->join(); + delete pressure_tests[i]; + } + + timer.stop(); + printf("MBED: Time taken: %fs\r\n", timer.read()); + printf("MBED: Speed: %.3fkb/s\r\n", + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS* + 8*(2*MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX - + MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read())); + + net.disconnect(); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "udp_echo"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("UDP packet pressure parallel", test_udp_packet_pressure_parallel), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/esp8266-driver/mbed_lib.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,31 @@ +{ + "name": "esp8266", + "config": { + "tx": { + "help": "TX pin for serial connection", + "value": null + }, + "rx": { + "help": "RX pin for serial connection", + "value": null + }, + "debug": { + "help": "Enable debug logs", + "value": false + } + }, + "target_overrides": { + "HEXIWEAR": { + "tx": "PTD3", + "rx": "PTD2" + }, + "NUCLEO_F401RE": { + "tx": "D8", + "rx": "D2" + }, + "NUCLEO_F411RE": { + "tx": "D8", + "rx": "D2" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,174 @@ +/* WiFi Example + * Copyright (c) 2016 ARM Limited + * + * 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 "mbed.h" +#include "TCPSocket.h" + +#define WIFI_ESP8266 1 +#define WIFI_IDW0XX1 2 +#define WIFI_ESP32 3 + +#if TARGET_UBLOX_EVK_ODIN_W2 +#include "OdinWiFiInterface.h" +OdinWiFiInterface wifi; + +#elif TARGET_REALTEK_RTL8195AM +#include "RTWInterface.h" +RTWInterface wifi; + +#elif TARGET_GR_LYCHEE +#include "ESP32Interface.h" +ESP32Interface wifi(P5_3, P3_14, P7_1, P0_1); + +#else // External WiFi modules + +#if MBED_CONF_APP_WIFI_SHIELD == WIFI_ESP8266 +#include "ESP8266Interface.h" +ESP8266Interface wifi(MBED_CONF_APP_WIFI_TX, MBED_CONF_APP_WIFI_RX); +#elif MBED_CONF_APP_WIFI_SHIELD == WIFI_IDW0XX1 +#include "SpwfSAInterface.h" +SpwfSAInterface wifi(MBED_CONF_APP_WIFI_TX, MBED_CONF_APP_WIFI_RX); +#elif MBED_CONF_APP_WIFI_SHIELD == WIFI_ESP32 +#include "ESP32Interface.h" +ESP32Interface wifi(MBED_CONF_APP_WIFI_EN, MBED_CONF_APP_WIFI_IO0, MBED_CONF_APP_WIFI_TX, MBED_CONF_APP_WIFI_RX); +#endif + +#endif + +const char *sec2str(nsapi_security_t sec) +{ + switch (sec) { + case NSAPI_SECURITY_NONE: + return "None"; + case NSAPI_SECURITY_WEP: + return "WEP"; + case NSAPI_SECURITY_WPA: + return "WPA"; + case NSAPI_SECURITY_WPA2: + return "WPA2"; + case NSAPI_SECURITY_WPA_WPA2: + return "WPA/WPA2"; + case NSAPI_SECURITY_UNKNOWN: + default: + return "Unknown"; + } +} + +int scan_demo(WiFiInterface *wifi) +{ + WiFiAccessPoint *ap; + + printf("Scan:\n"); + + int count = wifi->scan(NULL,0); + + /* Limit number of network arbitrary to 15 */ + count = count < 15 ? count : 15; + + ap = new WiFiAccessPoint[count]; + count = wifi->scan(ap, count); + for (int i = 0; i < count; i++) + { + printf("Network: %s secured: %s BSSID: %hhX:%hhX:%hhX:%hhx:%hhx:%hhx RSSI: %hhd Ch: %hhd\n", ap[i].get_ssid(), + sec2str(ap[i].get_security()), ap[i].get_bssid()[0], ap[i].get_bssid()[1], ap[i].get_bssid()[2], + ap[i].get_bssid()[3], ap[i].get_bssid()[4], ap[i].get_bssid()[5], ap[i].get_rssi(), ap[i].get_channel()); + } + printf("%d networks available.\n", count); + + delete[] ap; + return count; +} + +void http_demo(NetworkInterface *net) +{ + TCPSocket socket; + nsapi_error_t response; + + printf("Sending HTTP request to www.arm.com...\n"); + + // Open a socket on the network interface, and create a TCP connection to www.arm.com + socket.open(net); + + response = socket.connect("www.arm.com", 80); + if(0 != response) { + printf("Error connecting: %d\n", response); + socket.close(); + return; + } + + // Send a simple http request + char sbuffer[] = "GET / HTTP/1.1\r\nHost: www.arm.com\r\n\r\n"; + nsapi_size_t size = strlen(sbuffer); + response = 0; + while(size) + { + response = socket.send(sbuffer+response, size); + if (response < 0) { + printf("Error sending data: %d\n", response); + socket.close(); + return; + } else { + size -= response; + // Check if entire message was sent or not + printf("sent %d [%.*s]\n", response, strstr(sbuffer, "\r\n")-sbuffer, sbuffer); + } + } + + // Recieve a simple http response and print out the response line + char rbuffer[64]; + response = socket.recv(rbuffer, sizeof rbuffer); + if (response < 0) { + printf("Error receiving data: %d\n", response); + } else { + printf("recv %d [%.*s]\n", response, strstr(rbuffer, "\r\n")-rbuffer, rbuffer); + } + + // Close the socket to return its memory and bring down the network interface + socket.close(); +} + +int main() +{ + int count = 0; + + printf("WiFi example\n\n"); + + count = scan_demo(&wifi); + if (count == 0) { + printf("No WIFI APNs found - can't continue further.\n"); + return -1; + } + + printf("\nConnecting to %s...\n", MBED_CONF_APP_WIFI_SSID); + int ret = wifi.connect(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, NSAPI_SECURITY_WPA_WPA2); + if (ret != 0) { + printf("\nConnection error\n"); + return -1; + } + + printf("Success\n\n"); + printf("MAC: %s\n", wifi.get_mac_address()); + printf("IP: %s\n", wifi.get_ip_address()); + printf("Netmask: %s\n", wifi.get_netmask()); + printf("Gateway: %s\n", wifi.get_gateway()); + printf("RSSI: %d\n\n", wifi.get_rssi()); + + http_demo(&wifi); + + wifi.disconnect(); + + printf("\nDone\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#569159b784f70feaa32ce226aaca896fb83452f7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,48 @@ +{ + "config": { + "wifi-shield": { + "help": "Options are internal, WIFI_ESP8266, WIFI_IDW0XX1, WIFI_ESP32", + "value": "internal" + }, + "wifi-ssid": { + "help": "WiFi SSID", + "value": "\"SSID\"" + }, + "wifi-password": { + "help": "WiFi Password", + "value": "\"PASSWORD\"" + }, + "wifi-tx": { + "help": "TX pin for serial connection to external device", + "value": "D1" + }, + "wifi-rx": { + "help": "RX pin for serial connection to external device", + "value": "D0" + }, + "wifi-en": { + "help": "EN pin for ESP32", + "value": "NC" + }, + "wifi-io0": { + "help": "IO0 pin for ESP32", + "value": "NC" + } + }, + "target_overrides": { + "*": { + "platform.stdio-convert-newlines": true + }, + "UBLOX_EVK_ODIN_W2": { + "target.device_has": ["EMAC"] + }, + "NUCLEO_L476RG": { + "wifi-tx": "D8", + "wifi-rx": "D2" + }, + "NUCLEO_F401RE": { + "wifi-tx": "D8", + "wifi-rx": "D2" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app_esp32.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,44 @@ +{ + "config": { + "wifi-shield": { + "help": "Options are internal, WIFI_ESP32", + "value": "WIFI_ESP32" + }, + "wifi-ssid": { + "help": "WiFi SSID", + "value": "\"SSID\"" + }, + "wifi-password": { + "help": "WiFi Password", + "value": "\"PASSWORD\"" + }, + "wifi-tx": { + "help": "TX pin for serial connection to external device", + "value": "D1" + }, + "wifi-rx": { + "help": "RX pin for serial connection to external device", + "value": "D0" + }, + "wifi-en": { + "help": "EN pin for ESP32", + "value": "NC" + }, + "wifi-io0": { + "help": "IO0 pin for ESP32", + "value": "NC" + } + }, + "target_overrides": { + "*": { + "platform.stdio-convert-newlines": true + }, + "GR_LYCHEE": { + "wifi-en": "P5_3", + "wifi-io0": "P3_14", + "wifi-tx": "P7_1", + "wifi-rx": "P0_1" + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app_esp8266.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,40 @@ +{ + "config": { + "wifi-shield": { + "help": "Options are internal, WIFI_ESP8266, WIFI_IDW0XX1", + "value": "WIFI_ESP8266" + }, + "wifi-ssid": { + "help": "WiFi SSID", + "value": "\"SSID\"" + }, + "wifi-password": { + "help": "WiFi Password", + "value": "\"PASSWORD\"" + }, + "wifi-tx": { + "help": "TX pin for serial connection to external device", + "value": "D1" + }, + "wifi-rx": { + "help": "RX pin for serial connection to external device", + "value": "D0" + } + }, + "target_overrides": { + "*": { + "platform.stdio-convert-newlines": true + }, + "UBLOX_EVK_ODIN_W2": { + "target.device_has": ["EMAC"] + }, + "NUCLEO_L476RG": { + "wifi-tx": "D8", + "wifi-rx": "D2" + }, + "NUCLEO_F401RE": { + "wifi-tx": "D8", + "wifi-rx": "D2" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app_idw01m1.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,35 @@ +{ + "config": { + "wifi-shield": { + "help": "Options are internal, WIFI_ESP8266, WIFI_IDW0XX1", + "value": "WIFI_IDW0XX1" + }, + "wifi-ssid": { + "help": "WiFi SSID", + "value": "\"SSID\"" + }, + "wifi-password": { + "help": "WiFi Password", + "value": "\"PASSWORD\"" + }, + "wifi-tx": { + "help": "TX pin for serial connection to external device", + "value": "PA_9" + }, + "wifi-rx": { + "help": "RX pin for serial connection to external device", + "value": "PA_10" + } + }, + "target_overrides": { + "*": { + "platform.stdio-convert-newlines": true, + "idw0xx1.expansion-board": "IDW01M1", + "drivers.uart-serial-txbuf-size": 730, + "drivers.uart-serial-rxbuf-size": 730 + }, + "UBLOX_EVK_ODIN_W2": { + "target.device_has": ["EMAC"] + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app_idw04a1.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,36 @@ +{ + "config": { + "wifi-shield": { + "help": "Options are internal, WIFI_ESP8266, WIFI_IDW0XX1", + "value": "WIFI_IDW0XX1" + }, + "wifi-ssid": { + "help": "WiFi SSID", + "value": "\"SSID\"" + }, + "wifi-password": { + "help": "WiFi Password", + "value": "\"PASSWORD\"" + }, + "wifi-tx": { + "help": "TX pin for serial connection to external device", + "value": "D8" + }, + "wifi-rx": { + "help": "RX pin for serial connection to external device", + "value": "D2" + } + }, + "target_overrides": { + "*": { + "platform.stdio-convert-newlines": true, + "idw0xx1.expansion-board": "IDW04A1", + "drivers.uart-serial-txbuf-size": 750, + "drivers.uart-serial-rxbuf-size": 750 + }, + "UBLOX_EVK_ODIN_W2": { + "target.device_has": ["EMAC"] + } + }, + "macros": ["IDW04A1_WIFI_HW_BUG_WA"] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1.lib Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/#974ecf7ac236db300e30981f2e00b802007795b7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/.gitignore Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,3 @@ +BUILD +TESTS +.mbed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/BlockExecuter.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,22 @@ +#ifndef BLOCK_EXEC_H +#define BLOCK_EXEC_H + +#include "mbed.h" + +/* Helper class to execute something whenever entering/leaving a basic block */ +class BlockExecuter { +public: + BlockExecuter(Callback<void()> exit_cb, Callback<void()> enter_cb = Callback<void()>()) : + _exit_cb(exit_cb) { + if((bool)enter_cb) enter_cb(); + } + + ~BlockExecuter(void) { + _exit_cb(); + } + +private: + Callback<void()> _exit_cb; +}; + +#endif //BLOCK_EXEC_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/README.md Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,61 @@ +# Prototype Driver for STM Wi-Fi Expansion Boards based on the SPWFxx Module for STM32 Nucleo # + +## Currently supported expansion boards + * [X-NUCLEO-IDW01M1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw01m1.html), by setting `mbed` configuration variable `idw0xx1.expansion-board` to value `IDW01M1` + * [X-NUCLEO-IDW04A1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw04a1.html), by setting `mbed` configuration variable `idw0xx1.expansion-board` to value `IDW04A1`. You might also need to define macro `IDW04A1_WIFI_HW_BUG_WA` _(see beyond)_. + +## Configuration examples + +### Generic concepts + +For the ones, which might be less familiar with the **"The mbed configuration system"** in general, here is a [link](https://docs.mbed.com/docs/mbed-os-handbook/en/latest/advanced/config_system/) which points to the latest version of the respective _mbed OS 5 handbook tutorial_. + +Furthermore, with respect to this driver, pls. refer to files [`mbed_app_idw01m1.json`](https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/blob/master/mbed_app_idw01m1.json) and [`mbed_app_idw04a1.json`](https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/blob/master/mbed_app_idw04a1.json) regarding additional reference for what is explained beyond. + +### IDW01M1 + +Add the following lines to the `target_overrides`-section of your `mbed_app.json` file. + +``` json + "idw0xx1.expansion-board": "IDW01M1", + "drivers.uart-serial-txbuf-size": 512, + "drivers.uart-serial-rxbuf-size": 512 +``` + +`IDW01M1` is the default value in the [`mbed_lib.json`](https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/blob/master/mbed_lib.json) file, so setting the expansion board is not mandatory for `IDW01M1`, while setting the TX & RX buffer sizes is highly recommended. + +### IDW04A1 + +Add the following lines to the `target_overrides`-section of your `mbed_app.json` file. + +``` json + "idw0xx1.expansion-board": "IDW04A1", + "drivers.uart-serial-txbuf-size": 512, + "drivers.uart-serial-rxbuf-size": 512 +``` + +### Further configuration macros + +All configuration options mentioned in this section are optional and when required have to be added to the `macros`-section of your `mbed_app.json` file, e.g.: + +``` json + "macros": [..., "IDW04A1_WIFI_HW_BUG_WA", "SPWFSAXX_RESET_PIN=D7"] +``` + +Beyond you can find the list of available configuration macros each with a short explanation: + * `IDW04A1_WIFI_HW_BUG_WA`: activates the HW bug workaround for `IDW04A1` + * `SPWFSAXX_WAKEUP_PIN`: defines module wakeup pin _(requires value)_ + * `SPWFSAXX_RESET_PIN`: defines module reset pin _(requires value)_ + * `SPWFSAXX_RTS_PIN`: defines RTS pin of the UART device used _(requires value)_ + * `SPWFSAXX_CTS_PIN`: defines CTS pin of the UART device used _(requires value)_ + +**Note**: if the values of both `SPWFSAXX_RTS_PIN` and `SPWFSAXX_CTS_PIN` are different from `NC`, hardware flow control - if available on your development board - will be enabled on the used UART device (provided you are using `mbed-os` version greater than or equal to `v5.7.0`). + + +## Firmware upgrade + +Please make sure that you are using the latest firmware available for the expansion boards. For information on how to perform a FW upgrade you may refer to [X-CUBE-WIFI1](http://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32cube-embedded-software-expansion/x-cube-wifi1.html), especially to document **"X-NUCLEO-IDW0xx1- FW upgrading over UART_v1.2.pdf"** which is contained within folder **"Documentation"** of the X-CUBE-WIFI1 software archive you need to download. + +The actual firmware `.bin` or `.hex` files can be found under +- [STSW-WIFI001](http://www.st.com/content/st_com/en/products/embedded-software/wireless-connectivity-software/stsw-wifi001.html) _for what concerns expansion board_ X-NUCLEO-IDW01M1 _and under_ +- [STSW-WIFI004](http://www.st.com/content/st_com/en/products/embedded-software/wireless-connectivity-software/stsw-wifi004.html) _when considering_ X-NUCLEO-IDW04A1.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSA01/SPWFSA01.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,294 @@ +/* SPWFSA01 Device + * Copyright (c) 2015 ARM Limited + * + * 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 "SPWFSA01.h" +#include "SpwfSAInterface.h" +#include "mbed_debug.h" + +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1 + +SPWFSA01::SPWFSA01(PinName tx, PinName rx, + PinName rts, PinName cts, + SpwfSAInterface &ifce, bool debug, + PinName wakeup, PinName reset) +: SPWFSAxx(tx, rx, rts, cts, ifce, debug, wakeup, reset) { +} + +bool SPWFSA01::open(const char *type, int* spwf_id, const char* addr, int port) +{ + int socket_id; + int value; + int trials; + + if(!_parser.send("AT+S.SOCKON=%s,%d,%s,ind", addr, port, type)) + { + debug_if(_dbg_on, "\r\nSPWF> `SPWFSA01::open`: error opening socket (%d)\r\n", __LINE__); + return false; + } + + /* handle both response possibilities here before returning + * otherwise module seems to remain in inconsistent state. + */ + + /* wait for first character */ + trials = 0; + while((value = _parser.getc()) < 0) { + if(trials++ > SPWFXX_MAX_TRIALS) { + debug("\r\nSPWF> error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + return false; + } + } + + if(value != _cr_) { // Note: this is different to what the spec exactly says + debug_if(_dbg_on, "\r\nSPWF> error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + return false; + } + + if(!_recv_delim_lf()) { // Note: this is different to what the spec exactly says + debug_if(_dbg_on, "\r\nSPWF> error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + return false; + } + + value = _parser.getc(); + switch(value) { + case ' ': + if(_parser.recv("ID: %d\n", &socket_id) + && _recv_ok()) { + debug_if(_dbg_on, "AT^ ID: %d\r\n", socket_id); + + *spwf_id = socket_id; + return true; + } else { + empty_rx_buffer(); + } + break; + case 'E': + if(_parser.recv("RROR: %[^\n]\n", _msg_buffer) && _recv_delim_lf()) { + debug_if(_dbg_on, "AT^ ERROR: %s (%d)\r\n", _msg_buffer, __LINE__); + } else { + debug_if(_dbg_on, "\r\nSPWF> error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + } + break; + default: + debug_if(_dbg_on, "\r\nSPWF> error opening socket (value=%d, %d)\r\n", value, __LINE__); + break; + } + + return false; +} + +int SPWFSA01::_read_in(char* buffer, int spwf_id, uint32_t amount) { + int ret = -1; + + MBED_ASSERT(buffer != NULL); + + /* block asynchronous indications */ + if(!_winds_off()) { + return -1; + } + + /* read in data */ + if(_parser.send("AT+S.SOCKR=%d,%u", spwf_id, (unsigned int)amount)) { + /* set high timeout */ + _parser.set_timeout(SPWF_READ_BIN_TIMEOUT); + /* read in binary data */ + int read = _parser.read(buffer, amount); + /* reset timeout value */ + _parser.set_timeout(_timeout); + if(read > 0) { + if(_recv_ok()) { + ret = amount; + + /* remove from pending sizes + * (MUST be done before next async indications handling (e.g. `_winds_on()`)) */ + _remove_pending_pkt_size(spwf_id, amount); + } else { + debug_if(_dbg_on, "%s(%d): failed to receive OK\r\n", __func__, __LINE__); + empty_rx_buffer(); + } + } else { + debug_if(_dbg_on, "%s(%d): failed to read binary data\r\n", __func__, __LINE__); + empty_rx_buffer(); + } + } else { + debug_if(_dbg_on, "%s(%d): failed to send SOCKR\r\n", __func__, __LINE__); + } + + debug_if(_dbg_on, "%s():\t%d:%d\r\n", __func__, spwf_id, amount); + + /* unblock asynchronous indications */ + _winds_on(); + + return ret; +} + +/* betzw - TODO: improve performance! */ +bool SPWFSA01::_recv_ap(nsapi_wifi_ap_t *ap) +{ + bool ret; + unsigned int channel; + int trials; + + ap->security = NSAPI_SECURITY_UNKNOWN; + + /* check for end of list */ + if(_recv_delim_cr_lf()) { + return false; + } + + /* run to 'horizontal tab' */ + trials = 0; + while(_parser.getc() != '\x09') { + if(trials++ > SPWFXX_MAX_TRIALS) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + return false; + } + } + + /* read in next line */ + ret = _parser.recv(" %*s %hhx:%hhx:%hhx:%hhx:%hhx:%hhx CHAN: %u RSSI: %hhd SSID: \'%255[^\']\'", + &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], + &channel, &ap->rssi, ssid_buf); + + if(ret) { // ret == true + char value; + char *rest; + int val_getc; + unsigned int i; + size_t first_half; + + /* read in rest of line */ + first_half = strlen(ssid_buf); + for(i = first_half; i < sizeof(ssid_buf); i++) { + val_getc = _parser.getc(); + ssid_buf[i] = (char)val_getc; + if(val_getc < 0) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + return false; + } else if(val_getc == _cr_) { // '\r' + val_getc = _parser.getc(); + if('\n' != (char)val_getc) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + return false; + } + break; + } else if(val_getc == _lf_) { // '\n'; betzw: WORK-AROUND module FW issues + break; + } + } + ssid_buf[i] = '\0'; + + /* decide about position of `CAPS:` */ + rest = strstr(&ssid_buf[first_half], "CAPS:"); + if(rest == NULL) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + return false; + } + + /* substitute <space> with '\0' */ + MBED_ASSERT(&rest[-1] >= &ssid_buf[0]); + rest[-1] = '\0'; + + /* eventually substitute '\'' with '\0' */ + if((&rest[-2] >= &ssid_buf[0]) && (rest[-2] == '\'')) { + rest[-2] = '\0'; + } + + /* copy values */ + memcpy(&ap->ssid, ssid_buf, 32); + ap->ssid[32] = '\0'; + ap->channel = channel; + + /* skip `CAPS: 0421 ` */ + if(strlen(rest) < 11) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + return false; + } + rest += 11; + + /* get next character */ + value = *rest++; + if(value != 'W') { // no security + ap->security = NSAPI_SECURITY_NONE; + return true; + } + + /* determine security */ + { + char buffer[10]; + + if(!(sscanf(rest, "%s%*[\x20]", (char*)&buffer) > 0)) { // '\0x20' == <space> + return true; + } else if(strncmp("EP", buffer, 10) == 0) { + ap->security = NSAPI_SECURITY_WEP; + return true; + } else if(strncmp("PA2", buffer, 10) == 0) { + ap->security = NSAPI_SECURITY_WPA2; + return true; + } else if(strncmp("PA", buffer, 10) != 0) { + return true; + } + + /* got a "WPA", check for "WPA2" */ + rest += strlen(buffer); + value = *rest++; + if(value == '\0') { // no further protocol + ap->security = NSAPI_SECURITY_WPA; + return true; + } else { // assume "WPA2" + ap->security = NSAPI_SECURITY_WPA_WPA2; + return true; + } + } + } else { // ret == false + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + } + + return ret; +} + +int SPWFSA01::scan(WiFiAccessPoint *res, unsigned limit) +{ + unsigned cnt = 0; + nsapi_wifi_ap_t ap; + + if (!_parser.send("AT+S.SCAN=a,s")) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + while (_recv_ap(&ap)) { + if (cnt < limit) { + res[cnt] = WiFiAccessPoint(ap); + } + + cnt++; + if (limit != 0 && cnt >= limit) { + break; + } + } + + if(!_recv_ok()) { + empty_rx_buffer(); + } + + return cnt; +} + +#endif // MBED_CONF_IDW0XX1_EXPANSION_BOARD
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSA01/SPWFSA01.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,66 @@ +/* SPWFSA01 Device + * Copyright (c) 2015 ARM Limited + * + * 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. + */ + +#ifndef SPWFSA01_H +#define SPWFSA01_H + +#include "mbed.h" +#include "ATCmdParser.h" +#include "BlockExecuter.h" + +#include "./spwfsa01_at_strings.h" +#include "../SPWFSAxx.h" + +class SpwfSAInterface; + +/** SPWFSA01 Interface class. + This is an interface to a SPWFSA01 module. + */ +class SPWFSA01 : public SPWFSAxx +{ +public: + SPWFSA01(PinName tx, PinName rx, + PinName rts, PinName cts, + SpwfSAInterface &ifce, bool debug, + PinName wakeup, PinName reset); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "u" (UDP) or "t" (TCP) + * @param id id to get the new socket number, valid 0-7 + * @param port port to open connection with + * @param addr the IP address of the destination + * @return true only if socket opened successfully + */ + bool open(const char *type, int* id, const char* addr, int port); + + /** Scan for available networks + * + * @param ap Pointer to allocated array to store discovered AP + * @param limit Size of allocated @a res array, or 0 to only count available AP + * @return Number of entries in @a res, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + nsapi_size_or_error_t scan(WiFiAccessPoint *res, unsigned limit); + +private: + bool _recv_ap(nsapi_wifi_ap_t *ap); + + virtual int _read_in(char*, int, uint32_t); +}; + +#endif // SPWFSA01_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSA01/spwfsa01_at_strings.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,58 @@ +#ifndef SPWFSAXX_AT_STRINGS_H +#define SPWFSAXX_AT_STRINGS_H + +#if defined(TARGET_FF_MORPHO) + +#if !defined(SPWFSAXX_WAKEUP_PIN) +#define SPWFSAXX_WAKEUP_PIN PC_8 // A3 +#endif // !defined(SPWFSAXX_WAKEUP_PIN) +#if !defined(SPWFSAXX_RESET_PIN) +#define SPWFSAXX_RESET_PIN PC_12 // D7 / NC +#endif // !defined(SPWFSAXX_RESET_PIN) + +#else // !defined(TARGET_FF_MORPHO) + +#if !defined(SPWFSAXX_WAKEUP_PIN) +#define SPWFSAXX_WAKEUP_PIN NC // A3 +#endif // !defined(SPWFSAXX_WAKEUP_PIN) +#if !defined(SPWFSAXX_RESET_PIN) +#define SPWFSAXX_RESET_PIN NC // D7 / NC +#endif // !defined(SPWFSAXX_RESET_PIN) + +#endif // !defined(TARGET_FF_MORPHO) + +#define SPWFXX_SEND_RECV_PKTSIZE (730) + +#define SPWFXX_OOB_ERROR "ERROR:" // "AT-S.ERROR:" + +#define SPWFXX_RECV_OK "OK\n" // "AT-S.OK\n" +#define SPWFXX_RECV_WIFI_UP "+WIND:24:WiFi Up:%u.%u.%u.%u\n" // "+WIND:24:WiFi Up:%*u:%u.%u.%u.%u\n" +#define SPWFXX_RECV_IP_ADDR "# ip_ipaddr = %u.%u.%u.%u\n" // "AT-S.Var:ip_ipaddr=%u.%u.%u.%u\n" +#define SPWFXX_RECV_GATEWAY "# ip_gw = %u.%u.%u.%u\n" // "AT-S.Var:ip_gw=%u.%u.%u.%u\n" +#define SPWFXX_RECV_NETMASK "# ip_netmask = %u.%u.%u.%u\n" // "AT-S.Var:ip_netmask=%u.%u.%u.%u\n" +#define SPWFXX_RECV_RX_RSSI "# 0.rx_rssi = %d\n" // "AT-S.Var:0.rx_rssi=%d\n" +#define SPWFXX_RECV_MAC_ADDR "# nv_wifi_macaddr = %x:%x:%x:%x:%x:%x\n" // "AT-S.Var:nv_wifi_macaddr=%x:%x:%x:%x:%x:%x\n" +#define SPWFXX_RECV_DATALEN " DATALEN: %u\n" // "AT-S.Query:%u\n" +#define SPWFXX_RECV_PENDING_DATA ":%d:%d\n" // "::%u:%*u:%u\n" +#define SPWFXX_RECV_SOCKET_CLOSED ":%d\n" // ":%u:%*u\n" + +#define SPWFXX_SEND_FWCFG "AT&F" // "AT+S.FCFG" +#define SPWFXX_SEND_DISABLE_LE "AT+S.SCFG=localecho1,0" // "AT+S.SCFG=console_echo,0" +#define SPWFXX_SEND_DSPLY_CFGV "AT&V" // "AT+S.GCFG" +#define SPWFXX_SEND_GET_CONS_STATE "AT+S.GCFG=console1_enabled" // "AT+S.GCFG=console_enabled" +#define SPWFXX_SEND_GET_CONS_SPEED "AT+S.GCFG=console1_speed" // "AT+S.GCFG=console_speed" +#define SPWFXX_SEND_GET_HWFC_STATE "AT+S.GCFG=console1_hwfc" // "AT+S.GCFG=console_hwfc" +#define SPWFXX_SEND_GET_CONS_DELIM "AT+S.GCFG=console1_delimiter" // "AT+S.GCFG=console_delimiter" +#define SPWFXX_SEND_GET_CONS_ERRS "AT+S.GCFG=console1_errs" // "AT+S.GCFG=console_errs" +#define SPWFXX_SEND_DISABLE_FC "AT+S.SCFG=console1_hwfc,0" // "AT+S.SCFG=console_hwfc,0" +#define SPWFXX_SEND_ENABLE_FC "AT+S.SCFG=console1_hwfc,1" // "AT+S.SCFG=console_hwfc,1" +#define SPWFXX_SEND_SW_RESET "AT+CFUN=1" // "AT+S.RESET" +#define SPWFXX_SEND_SAVE_SETTINGS "AT&W" // "AT+S.WCFG" +#define SPWFXX_SEND_WIND_OFF_HIGH "AT+S.SCFG=wind_off_high," // "AT+S.SCFG=console_wind_off_high," +#define SPWFXX_SEND_WIND_OFF_MEDIUM "AT+S.SCFG=wind_off_medium," // "AT+S.SCFG=console_wind_off_medium," +#define SPWFXX_SEND_WIND_OFF_LOW "AT+S.SCFG=wind_off_low," // "AT+S.SCFG=console_wind_off_low," + +#define SPWFXX_WINDS_HIGH_ON "0x00000000" // "0x00100000" +#define SPWFXX_WINDS_MEDIUM_ON "0x00000000" // "0x80000000" + +#endif // SPWFSAXX_AT_STRINGS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSA04/SPWFSA04.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,328 @@ +/* SPWFSA04 Device + * Copyright (c) 2015 ARM Limited + * + * 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 "SPWFSA04.h" +#include "SpwfSAInterface.h" +#include "mbed_debug.h" + +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 + +SPWFSA04::SPWFSA04(PinName tx, PinName rx, + PinName rts, PinName cts, + SpwfSAInterface &ifce, bool debug, + PinName wakeup, PinName reset) +: SPWFSAxx(tx, rx, rts, cts, ifce, debug, wakeup, reset) { +} + +bool SPWFSA04::open(const char *type, int* spwf_id, const char* addr, int port) +{ + int socket_id; + int value; + int trials; + + if(!_parser.send("AT+S.SOCKON=%s,%d,NULL,%s", addr, port, type)) + { + debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__); + return false; + } + + /* handle both response possibilities here before returning + * otherwise module seems to remain in inconsistent state. + */ + + if(!_parser.recv("AT-S.")) { // get prefix + debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + return false; + } + + /* wait for next character */ + trials = 0; + while((value = _parser.getc()) < 0) { + if(++trials >= SPWFXX_MAX_TRIALS) { + debug("\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + return false; + } + } + + switch(value) { + case 'O': + /* get next character */ + value = _parser.getc(); + if(value != 'n') { + debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d) (%d, \'%c\')\r\n", + __LINE__, value, value); + empty_rx_buffer(); + return false; + } + + /* get socket id */ + if(!(_parser.recv(":%*u.%*u.%*u.%*u:%d\n", &socket_id) + && _recv_delim_lf() + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + return false; + } + debug_if(_dbg_on, "AT^ AT-S.On:%s:%d\r\n", addr, socket_id); + + *spwf_id = socket_id; + return true; + case 'E': + int err_nr; + if(_parser.recv("RROR:%d:%[^\n]\n", &err_nr, _msg_buffer) && _recv_delim_lf()) { + debug_if(_dbg_on, "AT^ AT-S.ERROR:%d:%s (%d)\r\n", err_nr, _msg_buffer, __LINE__); + } else { + debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__); + empty_rx_buffer(); + } + break; + default: + debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d) (%d, \'%c\')\r\n", + __LINE__, value, value); + empty_rx_buffer(); + break; + } + + return false; +} + +int SPWFSA04::_read_in(char* buffer, int spwf_id, uint32_t amount) { + int ret = -1; + int received, cumulative; + + MBED_ASSERT(buffer != NULL); + + /* block asynchronous indications */ + if(!_winds_off()) { + return -1; + } + + /* read in data */ + if(_parser.send("AT+S.SOCKR=%d,%d", spwf_id, (unsigned int)amount)) { + if(!(_parser.recv("AT-S.Reading:%d:%d\n", &received, &cumulative) && + _recv_delim_lf())) { + debug_if(_dbg_on, "%s(%d): failed to receive AT-S.Reading\r\n", __func__, __LINE__); + empty_rx_buffer(); + } else { + /* set high timeout */ + _parser.set_timeout(SPWF_READ_BIN_TIMEOUT); + /* read in binary data */ + int read = _parser.read(buffer, amount); + /* reset timeout value */ + _parser.set_timeout(_timeout); + if(read > 0) { + if(_recv_ok()) { + ret = amount; + + /* remove from pending sizes + * (MUST be done before next async indications handling (e.g. `_winds_on()`)) */ + _remove_pending_pkt_size(spwf_id, amount); + } else { + debug_if(_dbg_on, "%s(%d): failed to receive OK\r\n", __func__, __LINE__); + empty_rx_buffer(); + } + } else { + debug_if(_dbg_on, "%s(%d): failed to read binary data\r\n", __func__, __LINE__); + empty_rx_buffer(); + } + } + } else { + debug_if(_dbg_on, "%s(%d): failed to send SOCKR\r\n", __func__, __LINE__); + } + + debug_if(_dbg_on, "%s():\t%d:%d\r\n", __func__, spwf_id, amount); + + /* unblock asynchronous indications */ + _winds_on(); + + return ret; +} + +/* betzw - TODO: improve performance! */ +bool SPWFSA04::_recv_ap(nsapi_wifi_ap_t *ap) +{ + bool ret; + int curr; + unsigned int channel; + int trials; + + ap->security = NSAPI_SECURITY_UNKNOWN; + + /* determine list end */ + curr = _parser.getc(); + if(curr == 'A') { // assume end of list ("AT-S.OK") + if(!(_parser.recv("T-S.OK\n") && _recv_delim_lf())) { + empty_rx_buffer(); + } + return false; + } + + /* run to 'horizontal tab' */ + trials = 0; + while(_parser.getc() != '\x09') { + if(trials++ > SPWFXX_MAX_TRIALS) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } + } + + /* read in next line */ + ret = _parser.recv(" %*s %hhx:%hhx:%hhx:%hhx:%hhx:%hhx CHAN: %u RSSI: %hhd SSID: \'%255[^\']\'", + &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], + &channel, &ap->rssi, ssid_buf); + + if(ret) { // ret == true + char value; + char *rest; + int val_getc; + unsigned int i; + size_t first_half; + + /* read in rest of line */ + first_half = strlen(ssid_buf); + for(i = first_half; i < sizeof(ssid_buf); i++) { + val_getc = _parser.getc(); + ssid_buf[i] = (char)val_getc; + if(val_getc < 0) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } else if(val_getc == _cr_) { // '\r' + val_getc = _parser.getc(); + if('\n' != (char)val_getc) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } + break; + } else if(val_getc == _lf_) { // '\n'; betzw: WORK-AROUND module FW issues + break; + } + } + ssid_buf[i] = '\0'; + + /* decide about position of `CAPS:` */ + rest = strstr(&ssid_buf[first_half], "CAPS:"); + if(rest == NULL) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } + + /* substitute <space> with '\0' */ + MBED_ASSERT(&rest[-1] >= &ssid_buf[0]); + rest[-1] = '\0'; + + /* eventually substitute '\'' with '\0' */ + if((&rest[-2] >= &ssid_buf[0]) && (rest[-2] == '\'')) { + rest[-2] = '\0'; + } + + /* copy values */ + memcpy(&ap->ssid, ssid_buf, 32); + ap->ssid[32] = '\0'; + ap->channel = channel; + + /* skip `CAPS: 0421 ` */ + if(strlen(rest) < 11) { + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } + rest += 11; + + /* get next character */ + value = *rest++; + if(value != 'W') { // no security + ap->security = NSAPI_SECURITY_NONE; + return true; + } + + /* determine security */ + { + char buffer[10]; + + if(!(sscanf(rest, "%s%*[\x20]", (char*)&buffer) > 0)) { // '\0x20' == <space> + return true; + } else if(strncmp("EP", buffer, 10) == 0) { + ap->security = NSAPI_SECURITY_WEP; + return true; + } else if(strncmp("PA2", buffer, 10) == 0) { + ap->security = NSAPI_SECURITY_WPA2; + return true; + } else if(strncmp("PA", buffer, 10) != 0) { + return true; + } + + /* got a "WPA", check for "WPA2" */ + rest += strlen(buffer); + value = *rest++; + if(value == '\0') { // no further protocol + ap->security = NSAPI_SECURITY_WPA; + return true; + } else { // assume "WPA2" + ap->security = NSAPI_SECURITY_WPA_WPA2; + return true; + } + } + } else { // ret == false + debug("%s (%d) - WARNING: might happen in case of RX buffer overflow!\r\n", __func__, __LINE__); + empty_rx_buffer(); + } + + return ret; +} + +nsapi_size_or_error_t SPWFSA04::scan(WiFiAccessPoint *res, unsigned limit) +{ + unsigned int cnt = 0, found; + nsapi_wifi_ap_t ap; + + if (!_parser.send("AT+S.SCAN=s,")) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + if(!(_parser.recv("AT-S.Parsing Networks:%u\n", &found) && _recv_delim_lf())) { + debug_if(_dbg_on, "SPWF> error start network scanning\r\n"); + empty_rx_buffer(); + return NSAPI_ERROR_DEVICE_ERROR; + } + + debug_if(_dbg_on, "AT^ AT-S.Parsing Networks:%u\r\n", found); + + if(found > 0) { + while (_recv_ap(&ap)) { + if (cnt < limit) { + res[cnt] = WiFiAccessPoint(ap); + } + + if (!((limit != 0) && ((cnt + 1) > limit))) { + cnt++; + } + } + } else { + if(!_recv_ok()) { + empty_rx_buffer(); + } + } + + return cnt; +} + +#endif // MBED_CONF_IDW0XX1_EXPANSION_BOARD
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSA04/SPWFSA04.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,66 @@ +/* SPWFSA04 Device + * Copyright (c) 2015 ARM Limited + * + * 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. + */ + +#ifndef SPWFSA04_H +#define SPWFSA04_H + +#include "mbed.h" +#include "ATCmdParser.h" +#include "BlockExecuter.h" + +#include "./spwfsa04_at_strings.h" +#include "../SPWFSAxx.h" + +class SpwfSAInterface; + +/** SPWFSA04 Interface class. + This is an interface to a SPWFSA04 module. + */ +class SPWFSA04 : public SPWFSAxx +{ +public: + SPWFSA04(PinName tx, PinName rx, + PinName rts, PinName cts, + SpwfSAInterface &ifce, bool debug, + PinName wakeup, PinName reset); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "u" (UDP) or "t" (TCP) + * @param id id to get the new socket number, valid 0-7 + * @param port port to open connection with + * @param addr the IP address of the destination + * @return true only if socket opened successfully + */ + bool open(const char *type, int* id, const char* addr, int port); + + /** Scan for available networks + * + * @param ap Pointer to allocated array to store discovered AP + * @param limit Size of allocated @a res array, or 0 to only count available AP + * @return Number of entries in @a res, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + nsapi_size_or_error_t scan(WiFiAccessPoint *res, unsigned limit); + +private: + bool _recv_ap(nsapi_wifi_ap_t *ap); + + virtual int _read_in(char*, int, uint32_t); +}; + +#endif // SPWFSA04_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSA04/spwfsa04_at_strings.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,65 @@ +#ifndef SPWFSAXX_AT_STRINGS_H +#define SPWFSAXX_AT_STRINGS_H + +/* Define beyond macro if your X-NUCLEO-IDW04A1 expansion board has NOT the `WIFI_RST` HW patch applied on it */ +// #define IDW04A1_WIFI_HW_BUG_WA // delegated to mbed config system + +#if defined(TARGET_FF_ARDUINO) + +#if !defined(SPWFSAXX_WAKEUP_PIN) +#define SPWFSAXX_WAKEUP_PIN A3 +#endif +#if !defined(SPWFSAXX_RESET_PIN) +#ifndef IDW04A1_WIFI_HW_BUG_WA +#define SPWFSAXX_RESET_PIN D7 +#else // IDW04A1_WIFI_HW_PATCH +#define SPWFSAXX_RESET_PIN NC +#endif // !IDW04A1_WIFI_HW_PATCH +#endif + +#else // !defined(TARGET_FF_ARDUINO) + +#if !defined(SPWFSAXX_WAKEUP_PIN) +#define SPWFSAXX_WAKEUP_PIN NC +#endif +#if !defined(SPWFSAXX_RESET_PIN) +#define SPWFSAXX_RESET_PIN NC +#endif + +#endif // !defined(TARGET_FF_ARDUINO) + +#define SPWFXX_SEND_RECV_PKTSIZE (730) + +#define SPWFXX_OOB_ERROR "AT-S.ERROR:" // "ERROR:" + +#define SPWFXX_RECV_OK "AT-S.OK\n" // "OK\n" +#define SPWFXX_RECV_WIFI_UP "+WIND:24:WiFi Up:%*u:%u.%u.%u.%u\n" // "+WIND:24:WiFi Up:%u.%u.%u.%u\n" +#define SPWFXX_RECV_IP_ADDR "AT-S.Var:ip_ipaddr=%u.%u.%u.%u\n" // "# ip_ipaddr = %u.%u.%u.%u\n" +#define SPWFXX_RECV_GATEWAY "AT-S.Var:ip_gw=%u.%u.%u.%u\n" // "# ip_gw = %u.%u.%u.%u\n" +#define SPWFXX_RECV_NETMASK "AT-S.Var:ip_netmask=%u.%u.%u.%u\n" // "# ip_netmask = %u.%u.%u.%u\n" +#define SPWFXX_RECV_RX_RSSI "AT-S.Var:0.rx_rssi=%d\n" // "# 0.rx_rssi = %d\n" +#define SPWFXX_RECV_MAC_ADDR "AT-S.Var:nv_wifi_macaddr=%x:%x:%x:%x:%x:%x\n" // "# nv_wifi_macaddr = %x:%x:%x:%x:%x:%x\n" +#define SPWFXX_RECV_DATALEN "AT-S.Query:%u\n" // " DATALEN: %u\n" +#define SPWFXX_RECV_PENDING_DATA "::%u:%*u:%u\n" // ":%d:%d\n" +#define SPWFXX_RECV_SOCKET_CLOSED ":%u:%*u\n" // ":%d\n" + +#define SPWFXX_SEND_FWCFG "AT+S.FCFG" // "AT&F" +#define SPWFXX_SEND_DISABLE_LE "AT+S.SCFG=console_echo,0" // "AT+S.SCFG=localecho1,0" +#define SPWFXX_SEND_DSPLY_CFGV "AT+S.GCFG" // "AT&V" +#define SPWFXX_SEND_GET_CONS_STATE "AT+S.GCFG=console_enabled" // "AT+S.GCFG=console1_enabled" +#define SPWFXX_SEND_GET_CONS_SPEED "AT+S.GCFG=console_speed" // "AT+S.GCFG=console1_speed" +#define SPWFXX_SEND_GET_HWFC_STATE "AT+S.GCFG=console_hwfc" // "AT+S.GCFG=console1_hwfc" +#define SPWFXX_SEND_GET_CONS_DELIM "AT+S.GCFG=console_delimiter" // "AT+S.GCFG=console1_delimiter" +#define SPWFXX_SEND_GET_CONS_ERRS "AT+S.GCFG=console_errs" // "AT+S.GCFG=console1_errs" +#define SPWFXX_SEND_DISABLE_FC "AT+S.SCFG=console_hwfc,0" // "AT+S.SCFG=console1_hwfc,0" +#define SPWFXX_SEND_ENABLE_FC "AT+S.SCFG=console_hwfc,1" // "AT+S.SCFG=console1_hwfc,1" +#define SPWFXX_SEND_SW_RESET "AT+S.RESET" // "AT+CFUN=1" +#define SPWFXX_SEND_SAVE_SETTINGS "AT+S.WCFG" // "AT&W" +#define SPWFXX_SEND_WIND_OFF_HIGH "AT+S.SCFG=console_wind_off_high," // "AT+S.SCFG=wind_off_high," +#define SPWFXX_SEND_WIND_OFF_MEDIUM "AT+S.SCFG=console_wind_off_medium," // "AT+S.SCFG=wind_off_medium," +#define SPWFXX_SEND_WIND_OFF_LOW "AT+S.SCFG=console_wind_off_low," // "AT+S.SCFG=wind_off_low," + +#define SPWFXX_WINDS_HIGH_ON "0x00100000" // "0x00000000" +#define SPWFXX_WINDS_MEDIUM_ON "0x80000000" // "0x00000000" + +#endif // SPWFSAXX_AT_STRINGS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSAxx.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,1206 @@ +/* SPWFSAxx Devices + * Copyright (c) 2015 ARM Limited + * + * 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 "mbed_debug.h" + +#include "SpwfSAInterface.h" /* must be included first */ +#include "SPWFSAxx.h" + +static const char out_delim[] = {SPWFSAxx::_cr_, '\0'}; + +SPWFSAxx::SPWFSAxx(PinName tx, PinName rx, + PinName rts, PinName cts, + SpwfSAInterface &ifce, bool debug, + PinName wakeup, PinName reset) +: _serial(tx, rx, SPWFXX_DEFAULT_BAUD_RATE), _parser(&_serial, out_delim), + _wakeup(wakeup, 1), _reset(reset, 1), + _rts(rts), _cts(cts), + _timeout(SPWF_INIT_TIMEOUT), _dbg_on(debug), + _pending_sockets_bitmap(0), + _network_lost_flag(false), + _associated_interface(ifce), + _call_event_callback_blocked(false), + _callback_func(), + _packets(0), _packets_end(&_packets), + _msg_buffer(ssid_buf) +{ + memset(_pending_pkt_sizes, 0, sizeof(_pending_pkt_sizes)); + + _serial.set_baud(SPWFXX_DEFAULT_BAUD_RATE); + _serial.sigio(Callback<void()>(this, &SPWFSAxx::_event_handler)); + _parser.debug_on(debug); + + _parser.oob("+WIND:55:Pending Data", callback(this, &SPWFSAxx::_packet_handler_th)); + _parser.oob("+WIND:58:Socket Closed", callback(this, &SPWFSAxx::_server_gone_handler)); + _parser.oob("+WIND:33:WiFi Network Lost", callback(this, &SPWFSAxx::_network_lost_handler_th)); + _parser.oob("+WIND:8:Hard Fault", callback(this, &SPWFSAxx::_hard_fault_handler)); + _parser.oob("+WIND:5:WiFi Hardware Failure", callback(this, &SPWFSAxx::_wifi_hwfault_handler)); + _parser.oob(SPWFXX_OOB_ERROR, callback(this, &SPWFSAxx::_error_handler)); +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 + _parser.oob("+WIND:24:WiFi Up::", callback(this, &SPWFSAxx::_skip_oob)); +#endif +} + +bool SPWFSAxx::startup(int mode) +{ + /*Reset module*/ + if(!hw_reset()) { + debug_if(_dbg_on, "\r\nSPWF> HW reset failed\r\n"); + return false; + } + + /* factory reset */ + if(!(_parser.send(SPWFXX_SEND_FWCFG) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error restore factory default settings\r\n"); + return false; + } + + /*switch off led*/ + if(!(_parser.send("AT+S.SCFG=blink_led,0") && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error stop blinking led (%d)\r\n", __LINE__); + return false; + } + + /*set local echo to 0*/ + if(!(_parser.send(SPWFXX_SEND_DISABLE_LE) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error local echo set\r\n"); + return false; + } + + /*set Wi-Fi mode and rate to b/g/n*/ + if(!(_parser.send("AT+S.SCFG=wifi_ht_mode,1") && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error setting ht_mode\r\n"); + return false; + } + + /*set the operational rate*/ + if(!(_parser.send("AT+S.SCFG=wifi_opr_rate_mask,0x003FFFCF") && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error setting operational rates\r\n"); + return false; + } + + /*set idle mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ + if(!(_parser.send("AT+S.SCFG=wifi_mode,%d", mode) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error WiFi mode set idle (%d)\r\n", __LINE__); + return false; + } + +#if defined(MBED_MAJOR_VERSION) +#if !DEVICE_SERIAL_FC || (MBED_VERSION < MBED_ENCODE_VERSION(5, 7, 0)) + /*disable HW flow control*/ + if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); + return false; + } +#else // DEVICE_SERIAL_FC && (MBED_VERSION >= MBED_ENCODE_VERSION(5, 7, 0)) + if((_rts != NC) && (_cts != NC)) { + /*enable HW flow control*/ + if(!(_parser.send(SPWFXX_SEND_ENABLE_FC) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error enabling HW flow control\r\n"); + return false; + } + + /*configure pins for HW flow control*/ + _serial.set_flow_control(SerialBase::RTSCTS, _rts, _cts); + } else { + /*disable HW flow control*/ + if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); + return false; + } + } +#endif // DEVICE_SERIAL_FC && (MBED_VERSION >= MBED_ENCODE_VERSION(5, 7, 0)) +#else // !defined(MBED_MAJOR_VERSION) - Assuming `master` branch +#if !DEVICE_SERIAL_FC + /*disable HW flow control*/ + if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); + return false; + } +#else // DEVICE_SERIAL_FC + if((_rts != NC) && (_cts != NC)) { + /*enable HW flow control*/ + if(!(_parser.send(SPWFXX_SEND_ENABLE_FC) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error enabling HW flow control\r\n"); + return false; + } + + /*configure pins for HW flow control*/ + _serial.set_flow_control(SerialBase::RTSCTS, _rts, _cts); + } else { + /*disable HW flow control*/ + if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); + return false; + } + } +#endif // DEVICE_SERIAL_FC +#endif // !defined(MBED_MAJOR_VERSION) + + /* Disable selected WINDs */ + _winds_on(); + + /* sw reset */ + if(!reset()) { + debug_if(_dbg_on, "\r\nSPWF> SW reset failed (%s, %d)\r\n", __func__, __LINE__); + return false; + } + +#ifndef NDEBUG + if (!(_parser.send(SPWFXX_SEND_GET_CONS_STATE) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting console state\r\n"); + return false; + } + + if (!(_parser.send(SPWFXX_SEND_GET_CONS_SPEED) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting console speed\r\n"); + return false; + } + + if (!(_parser.send(SPWFXX_SEND_GET_HWFC_STATE) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting hwfc state\r\n"); + return false; + } + +#if (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) || defined(IDW01M1_FW_REL_35X) + /* betzw: IDW01M1 FW versions <3.5 seem to have problems with the following two commands. + * For the sake of simplicity, just excluding them for IDW01M1 in general. + */ + if (!(_parser.send(SPWFXX_SEND_GET_CONS_DELIM) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting console delimiter\r\n"); + return false; + } + + if (!(_parser.send(SPWFXX_SEND_GET_CONS_ERRS) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting console error setting\r\n"); + return false; + } +#endif // (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) || defined(IDW01M1_FW_REL_35X) + + if (!(_parser.send("AT+S.GCFG=sleep_enabled") + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting sleep state enabled\r\n"); + return false; + } + + if (!(_parser.send("AT+S.GCFG=wifi_powersave") + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting powersave mode\r\n"); + return false; + } + + if (!(_parser.send("AT+S.GCFG=standby_enabled") + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> error getting standby state enabled\r\n"); + return false; + } +#endif + + return true; +} + +bool SPWFSAxx::_wait_console_active(void) { + int trials = 0; + + while(true) { + if (_parser.recv("+WIND:0:Console active\n") && _recv_delim_lf()) { + debug_if(_dbg_on, "AT^ +WIND:0:Console active\r\n"); + return true; + } + if(++trials >= SPWFXX_MAX_TRIALS) { + debug("%s (%d) - ERROR: Should never happen!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } + } +} + +bool SPWFSAxx::_wait_wifi_hw_started(void) { + int trials = 0; + + while(true) { + if (_parser.recv("+WIND:32:WiFi Hardware Started\n") && _recv_delim_lf()) { + debug_if(_dbg_on, "AT^ +WIND:32:WiFi Hardware Started\r\n"); + return true; + } + if(++trials >= SPWFXX_MAX_TRIALS) { + debug("%s (%d) - ERROR: Should never happen!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } + } +} + +bool SPWFSAxx::hw_reset(void) +{ +#if (MBED_CONF_IDW0XX1_EXPANSION_BOARD != IDW04A1) || !defined(IDW04A1_WIFI_HW_BUG_WA) // betzw: HW reset doesn't work as expected on unmodified X_NUCLEO_IDW04A1 expansion boards + _reset.write(0); + wait_ms(200); + _reset.write(1); +#else // (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) && defined(IDW04A1_WIFI_HW_BUG_WA): substitute with SW reset + _parser.send(SPWFXX_SEND_SW_RESET); +#endif // (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) && defined(IDW04A1_WIFI_HW_BUG_WA) + return _wait_console_active(); +} + +bool SPWFSAxx::reset(void) +{ + bool ret; + + /* save current setting in flash */ + if(!(_parser.send(SPWFXX_SEND_SAVE_SETTINGS) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error saving configuration to flash\r\n"); + return false; + } + + if(!_parser.send(SPWFXX_SEND_SW_RESET)) return false; /* betzw - NOTE: "keep the current state and reset the device". + We assume that the module informs us about the + eventual closing of sockets via "WIND" asynchronous + indications! So everything regarding the clean-up + of these situations is handled there. */ + ret = _wait_wifi_hw_started(); + + return ret; +} + +/* Security Mode + None = 0, + WEP = 1, + WPA_Personal = 2, + */ +bool SPWFSAxx::connect(const char *ap, const char *passPhrase, int securityMode) +{ + int trials; + + //AT+S.SCFG=wifi_wpa_psk_text,%s + if(!(_parser.send("AT+S.SCFG=wifi_wpa_psk_text,%s", passPhrase) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error pass set\r\n"); + return false; + } + + //AT+S.SSIDTXT=%s + if(!(_parser.send("AT+S.SSIDTXT=%s", ap) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error ssid set\r\n"); + return false; + } + + //AT+S.SCFG=wifi_priv_mode,%d + if(!(_parser.send("AT+S.SCFG=wifi_priv_mode,%d", securityMode) && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error security mode set\r\n"); + return false; + } + + /*set STA mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ + if(!(_parser.send("AT+S.SCFG=wifi_mode,1") && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error WiFi mode set 1 (STA)\r\n"); + return false; + } + + /* sw reset */ + if(!reset()) { + debug_if(_dbg_on, "\r\nSPWF> SW reset failed (%s, %d)\r\n", __func__, __LINE__); + return false; + } + + trials = 0; + while(true) { + if(_parser.recv("%[^\n]\n", _msg_buffer) && _recv_delim_lf()) + { + if(strstr(_msg_buffer, ":24:") != NULL) { // WiFi Up + debug_if(_dbg_on, "AT^ %s\n", _msg_buffer); + if(strchr(_msg_buffer, '.') != NULL) { // IPv4 address + break; + } else { + continue; + } + } + if(strstr(_msg_buffer, ":40:") != NULL) { // Deauthentication + debug_if(_dbg_on, "AT~ %s\n", _msg_buffer); + if(++trials < 3) { // give it three trials + continue; + } + empty_rx_buffer(); + disconnect(); + return false; + } else { + debug_if(_dbg_on, "AT] %s\n", _msg_buffer); + } + continue; + } + if(++trials >= SPWFXX_MAX_TRIALS) { + debug("%s (%d) - ERROR: Should never happen!\r\n", __func__, __LINE__); + empty_rx_buffer(); + return false; + } + } + + return true; +} + +bool SPWFSAxx::disconnect(void) +{ +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 + /*disable Wi-Fi device*/ + if(!(_parser.send("AT+S.WIFI=0") && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error disabling WiFi\r\n"); + return false; + } +#endif // IDW04A1 + + /*set idle mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ + if(!(_parser.send("AT+S.SCFG=wifi_mode,0") && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error WiFi mode set idle (%d)\r\n", __LINE__); + return false; + } + +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 + /*enable Wi-Fi device*/ + if(!(_parser.send("AT+S.WIFI=1") && _recv_ok())) + { + debug_if(_dbg_on, "\r\nSPWF> error enabling WiFi\r\n"); + return false; + } +#endif // IDW04A1 + + // reset module + if(!reset()) { + debug_if(_dbg_on, "\r\nSPWF> SW reset failed (%s, %d)\r\n", __func__, __LINE__); + return false; + } + + return true; +} + +const char *SPWFSAxx::getIPAddress(void) +{ + unsigned int n1, n2, n3, n4; + + if (!(_parser.send("AT+S.STS=ip_ipaddr") + && _parser.recv(SPWFXX_RECV_IP_ADDR, &n1, &n2, &n3, &n4) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> get IP address error\r\n"); + return NULL; + } + + debug_if(_dbg_on, "AT^ ip_ipaddr = %u.%u.%u.%u\r\n", n1, n2, n3, n4); + + sprintf((char*)_ip_buffer,"%u.%u.%u.%u", n1, n2, n3, n4); + return _ip_buffer; +} + +const char *SPWFSAxx::getGateway(void) +{ + unsigned int n1, n2, n3, n4; + + if (!(_parser.send("AT+S.STS=ip_gw") + && _parser.recv(SPWFXX_RECV_GATEWAY, &n1, &n2, &n3, &n4) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> get gateway error\r\n"); + return NULL; + } + + debug_if(_dbg_on, "AT^ ip_gw = %u.%u.%u.%u\r\n", n1, n2, n3, n4); + + sprintf((char*)_gateway_buffer,"%u.%u.%u.%u", n1, n2, n3, n4); + return _gateway_buffer; +} + +const char *SPWFSAxx::getNetmask(void) +{ + unsigned int n1, n2, n3, n4; + + if (!(_parser.send("AT+S.STS=ip_netmask") + && _parser.recv(SPWFXX_RECV_NETMASK, &n1, &n2, &n3, &n4) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> get netmask error\r\n"); + return NULL; + } + + debug_if(_dbg_on, "AT^ ip_netmask = %u.%u.%u.%u\r\n", n1, n2, n3, n4); + + sprintf((char*)_netmask_buffer,"%u.%u.%u.%u", n1, n2, n3, n4); + return _netmask_buffer; +} + +int8_t SPWFSAxx::getRssi(void) +{ + int ret; + + if (!(_parser.send("AT+S.PEERS=0,rx_rssi") + && _parser.recv(SPWFXX_RECV_RX_RSSI, &ret) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> get RX rssi error\r\n"); + return 0; + } + + return (int8_t)ret; +} + +const char *SPWFSAxx::getMACAddress(void) +{ + unsigned int n1, n2, n3, n4, n5, n6; + + if (!(_parser.send("AT+S.GCFG=nv_wifi_macaddr") + && _parser.recv(SPWFXX_RECV_MAC_ADDR, &n1, &n2, &n3, &n4, &n5, &n6) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> get MAC address error\r\n"); + return 0; + } + + debug_if(_dbg_on, "AT^ nv_wifi_macaddr = %x:%x:%x:%x:%x:%x\r\n", n1, n2, n3, n4, n5, n6); + + sprintf((char*)_mac_buffer,"%02X:%02X:%02X:%02X:%02X:%02X", n1, n2, n3, n4, n5, n6); + return _mac_buffer; +} + +bool SPWFSAxx::isConnected(void) +{ + return _associated_interface._connected_to_network; +} + +bool SPWFSAxx::send(int spwf_id, const void *data, uint32_t amount) +{ + uint32_t sent = 0U, to_send; + bool ret = true; + + /* betzw - WORK AROUND module FW issues: split up big packages in smaller ones */ + for(to_send = (amount > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : amount; + sent < amount; + to_send = ((amount - sent) > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : (amount - sent)) { + { + BlockExecuter bh_handler(Callback<void()>(this, &SPWFSAxx::_execute_bottom_halves)); + + if (!(_parser.send("AT+S.SOCKW=%d,%d", spwf_id, (unsigned int)to_send) + && (_parser.write(((char*)data)+sent, (int)to_send) == (int)to_send) + && _recv_ok())) { + // betzw - TODO: handle different errors more accurately! + ret = false; + break; + } + } + + sent += to_send; + } + + return ret; +} + +int SPWFSAxx::_read_len(int spwf_id) { + unsigned int amount; + + if (!(_parser.send("AT+S.SOCKQ=%d", spwf_id) + && _parser.recv(SPWFXX_RECV_DATALEN, &amount) + && _recv_ok())) { + debug_if(_dbg_on, "\r\nSPWF> %s failed\r\n", __func__); + return SPWFXX_ERR_LEN; + } + + if(amount > 0) { + debug_if(_dbg_on, "%s():\t\t%d:%d\r\n", __func__, spwf_id, amount); + } + + MBED_ASSERT(((int)amount) >= 0); + + return (int)amount; +} + +#define SPWFXX_WINDS_OFF "0xFFFFFFFF" + +void SPWFSAxx::_winds_on(void) { + if(!(_parser.send(SPWFXX_SEND_WIND_OFF_HIGH SPWFXX_WINDS_HIGH_ON) && _recv_ok())) { + debug_if(_dbg_on, "%s: failed at line #%d\r\n", __func__, __LINE__); + } + if(!(_parser.send(SPWFXX_SEND_WIND_OFF_MEDIUM SPWFXX_WINDS_MEDIUM_ON) && _recv_ok())) { + debug_if(_dbg_on, "%s: failed at line #%d\r\n", __func__, __LINE__); + } + if(!(_parser.send(SPWFXX_SEND_WIND_OFF_LOW SPWFXX_WINDS_LOW_ON) && _recv_ok())) { + debug_if(_dbg_on, "%s: failed at line #%d\r\n", __func__, __LINE__); + } +} + +/* Note: in case of error blocking has been (tried to be) lifted */ +bool SPWFSAxx::_winds_off(void) { + if (!(_parser.send(SPWFXX_SEND_WIND_OFF_LOW SPWFXX_WINDS_OFF) + && _recv_ok())) { + debug_if(_dbg_on, "%s: failed at line #%d\r\n", __func__, __LINE__); + _winds_on(); + return false; + } + + if (!(_parser.send(SPWFXX_SEND_WIND_OFF_MEDIUM SPWFXX_WINDS_OFF) + && _recv_ok())) { + debug_if(_dbg_on, "%s: failed at line #%d\r\n", __func__, __LINE__); + _winds_on(); + return false; + } + + if (!(_parser.send(SPWFXX_SEND_WIND_OFF_HIGH SPWFXX_WINDS_OFF) + && _recv_ok())) { + debug_if(_dbg_on, "%s: failed at line #%d\r\n", __func__, __LINE__); + _winds_on(); + return false; + } + + return true; +} + +void SPWFSAxx::_execute_bottom_halves(void) { + _network_lost_handler_bh(); + _packet_handler_bh(); +} + +void SPWFSAxx::_read_in_pending(void) { + static int internal_id_cnt = 0; + + while(_is_data_pending()) { + if(_associated_interface._socket_has_connected(internal_id_cnt)) { + int spwf_id = _associated_interface._ids[internal_id_cnt].spwf_id; + + if(_is_data_pending(spwf_id)) { + int amount; + + amount = _read_in_pkt(spwf_id, false); + if(amount == SPWFXX_ERR_OOM) { /* consider only 'SPWFXX_ERR_OOM' as non recoverable */ + return; + } + } + + if(!_is_data_pending(spwf_id)) { + internal_id_cnt++; + internal_id_cnt %= SPWFSA_SOCKET_COUNT; + } + } else { + internal_id_cnt++; + internal_id_cnt %= SPWFSA_SOCKET_COUNT; + } + } +} + +/* Note: returns + * 'SPWFXX_ERR_OK' in case of success + * 'SPWFXX_ERR_OOM' in case of "out of memory" + * 'SPWFXX_ERR_READ' in case of `_read_in()` error + */ +int SPWFSAxx::_read_in_packet(int spwf_id, uint32_t amount) { + struct packet *packet = (struct packet*)malloc(sizeof(struct packet) + amount); + if (!packet) { +#ifndef NDEBUG + error("%s(%d): Out of memory!", __func__, __LINE__); +#else // NDEBUG + debug("%s(%d): Out of memory!", __func__, __LINE__); +#endif + debug_if(_dbg_on, "\r\nSPWF> %s failed (%d)\r\n", __func__, __LINE__); + return SPWFXX_ERR_OOM; /* out of memory: give up here! */ + } + + /* init packet */ + packet->id = spwf_id; + packet->len = amount; + packet->next = 0; + + /* read data in */ + if(!(_read_in((char*)(packet + 1), spwf_id, amount) > 0)) { + free(packet); + debug_if(_dbg_on, "\r\nSPWF> %s failed (%d)\r\n", __func__, __LINE__); + return SPWFXX_ERR_READ; + } else { + debug_if(_dbg_on, "%s():\t%d:%d\r\n", __func__, spwf_id, amount); + + /* append to packet list */ + *_packets_end = packet; + _packets_end = &packet->next; + + /* force call of (external) callback */ + _call_callback(); + } + + return SPWFXX_ERR_OK; +} + +void SPWFSAxx::_free_packets(int spwf_id) { + // check if any packets are ready for `spwf_id` + for(struct packet **p = &_packets; *p;) { + if ((*p)->id == spwf_id) { + struct packet *q = *p; + if (_packets_end == &(*p)->next) { + _packets_end = p; + } + *p = (*p)->next; + free(q); + } else { + p = &(*p)->next; + } + } +} + +void SPWFSAxx::_free_all_packets() { + for (int spwf_id = 0; spwf_id < SPWFSA_SOCKET_COUNT; spwf_id++) { + _free_packets(spwf_id); + } +} + +bool SPWFSAxx::close(int spwf_id) +{ + bool ret = false; + + if(((unsigned int)spwf_id) >= ((unsigned int)SPWFSA_SOCKET_COUNT)) { + goto close_bh_handling; // `ret == false` + } + + for(int retry_cnt = 0; retry_cnt < SPWFXX_MAX_TRIALS; retry_cnt++) { + // Flush out pending data + while(true) { + int amount = _read_in_pkt(spwf_id, true); + if(amount < 0) { // SPWFXX error + /* empty RX buffer & try to close */ + empty_rx_buffer(); + break; + } + if(amount == 0) break; // no more data to be read + + /* immediately free packet (to avoid "out of memory") */ + _free_packets(spwf_id); + + /* interleave bottom halves */ + _execute_bottom_halves(); + } + + // Close socket + if (_parser.send("AT+S.SOCKC=%d", spwf_id) + && _recv_ok()) { + ret = true; + break; // finish closing + } else { // close failed + debug_if(_dbg_on, "\r\nSPWF> %s failed (%d)\r\n", __func__, __LINE__); + /* interleave bottom halves */ + _execute_bottom_halves(); + } + } + +close_bh_handling: + /* anticipate bottom halves */ + _execute_bottom_halves(); + + if(ret) { + /* clear pending data flag (should be redundant) */ + _clear_pending_data(spwf_id); + + /* free packets for this socket */ + _free_packets(spwf_id); + + /* reset pending data sizes */ + _reset_pending_pkt_sizes(spwf_id); + } else { + debug_if(_dbg_on, "\r\nSPWF> SPWFSAxx::close failed (%d)\r\n", __LINE__); + + int internal_id = _associated_interface.get_internal_id(spwf_id); + if(!_associated_interface._socket_is_still_connected(internal_id)) { + /* clear pending data flag (should be redundant) */ + _clear_pending_data(spwf_id); + + /* free packets for this socket */ + _free_packets(spwf_id); + + /* reset pending data sizes */ + _reset_pending_pkt_sizes(spwf_id); + + ret = true; + } + } + + return ret; +} + +/* + * Buffered serial event handler + * + * Note: executed in IRQ context! + * Note: call (external) callback only while not receiving + */ +void SPWFSAxx::_event_handler(void) +{ + if(!_is_event_callback_blocked()) { + _call_callback(); + } +} + +/* + * Common error handler + */ +void SPWFSAxx::_error_handler(void) +{ + if(_parser.recv("%[^\n]\n", _msg_buffer) && _recv_delim_lf()) { + debug_if(_dbg_on, "AT^ ERROR:%s (%d)\r\n", _msg_buffer, __LINE__); + } else { + debug_if(_dbg_on, "\r\nSPWF> Unknown ERROR string in SPWFSAxx::_error_handler (%d)\r\n", __LINE__); + } + + /* force call of (external) callback */ + _call_callback(); +} + +/* + * Handling oob ("+WIND:33:WiFi Network Lost") + */ +void SPWFSAxx::_network_lost_handler_th(void) +{ +#ifndef NDEBUG + static unsigned int net_loss_cnt = 0; + net_loss_cnt++; +#endif + + _recv_delim_cr_lf(); + + debug_if(_dbg_on, "AT^ +WIND:33:WiFi Network Lost\r\n"); + +#ifndef NDEBUG + debug_if(_dbg_on, "Getting out of SPWFSAxx::_network_lost_handler_th: %d\r\n", net_loss_cnt); +#else // NDEBUG + debug_if(_dbg_on, "Getting out of SPWFSAxx::_network_lost_handler_th: %d\r\n", __LINE__); +#endif // NDEBUG + + /* set flag to signal network loss */ + _network_lost_flag = true; + + /* force call of (external) callback */ + _call_callback(); + + return; +} + +/* betzw - WORK AROUND module FW issues: split up big packages in smaller ones */ +void SPWFSAxx::_add_pending_packet_sz(int spwf_id, uint32_t size) { + uint32_t to_add; + uint32_t added = _get_cumulative_size(spwf_id); + + if(size <= added) { // might happen due to delayed WIND delivery + debug_if(_dbg_on, "%s: failed at line #%d\r\n", __func__, __LINE__); + return; + } + + for(to_add = ((size - added) > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : (size - added); + added < size; + to_add = ((size - added) > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : (size - added)) { + _add_pending_pkt_size(spwf_id, added + to_add); + added += to_add; + } + + /* force call of (external) callback */ + _call_callback(); + + /* set that data is pending */ + _set_pending_data(spwf_id); +} + +/* + * Handling oob ("+WIND:55:Pending Data") + */ +void SPWFSAxx::_packet_handler_th(void) +{ + int internal_id, spwf_id; + int amount; + + /* parse out the socket id & amount */ + if (!(_parser.recv(SPWFXX_RECV_PENDING_DATA, &spwf_id, &amount) && _recv_delim_lf())) { +#ifndef NDEBUG + error("\r\nSPWFSAxx::%s failed!\r\n", __func__); +#endif + return; + } + + debug_if(_dbg_on, "AT^ +WIND:55:Pending Data:%d:%d\r\n", spwf_id, amount); + + /* check for the module to report a valid id */ + MBED_ASSERT(((unsigned int)spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)); + + /* set that there is pending data for socket */ + /* NOTE: it seems as if asynchronous indications might report not up-to-date data length values + * therefore we just record the socket id without considering the `amount` of data reported! + */ + internal_id = _associated_interface.get_internal_id(spwf_id); + if(internal_id != SPWFSA_SOCKET_COUNT) { + debug_if(_dbg_on, "AT^ +WIND:55:Pending Data:%d:%d - #2\r\n", spwf_id, amount); + _add_pending_packet_sz(spwf_id, amount); + + MBED_ASSERT(_get_pending_pkt_size(spwf_id) != 0); + } else { + debug_if(_dbg_on, "\r\nSPWFSAxx::%s got invalid id %d\r\n", __func__, spwf_id); + } +} + +void SPWFSAxx::_network_lost_handler_bh(void) +{ + if(!_network_lost_flag) return; + _network_lost_flag = false; + + { + bool were_connected; + BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_and_callback), + Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* call (external) callback only while not receiving */ + Timer timer; + timer.start(); + + _parser.set_timeout(SPWF_NETLOST_TIMEOUT); + + were_connected = isConnected(); + _associated_interface._connected_to_network = false; + + if(were_connected) { + unsigned int n1, n2, n3, n4; + + while(true) { + if (timer.read_ms() > SPWF_CONNECT_TIMEOUT) { + debug_if(_dbg_on, "\r\nSPWFSAxx::_network_lost_handler_bh() #%d\r\n", __LINE__); + disconnect(); + goto nlh_get_out; + } + + if((_parser.recv(SPWFXX_RECV_WIFI_UP, &n1, &n2, &n3, &n4)) && _recv_delim_lf()) { + debug_if(_dbg_on, "Re-connected (%u.%u.%u.%u)!\r\n", n1, n2, n3, n4); + + _associated_interface._connected_to_network = true; + goto nlh_get_out; + } + } + } else { + debug_if(_dbg_on, "Leaving SPWFSAxx::_network_lost_handler_bh\r\n"); + goto nlh_get_out; + } + + nlh_get_out: + debug_if(_dbg_on, "Getting out of SPWFSAxx::_network_lost_handler_bh\r\n"); + _parser.set_timeout(_timeout); + + return; + } +} + +void SPWFSAxx::_recover_from_hard_faults(void) { + disconnect(); + _associated_interface.inner_constructor(); + _free_all_packets(); + empty_rx_buffer(); + + /* force call of (external) callback */ + _call_callback(); +} + +/* + * Handling oob ("+WIND:8:Hard Fault") + */ +void SPWFSAxx::_hard_fault_handler(void) +{ + _parser.set_timeout(SPWF_RECV_TIMEOUT); + if(_parser.recv("%[^\n]\n", _msg_buffer) && _recv_delim_lf()) {} + +#ifndef NDEBUG + error("\r\nSPWFSAXX hard fault error:\r\n%s\r\n", _msg_buffer); +#else // NDEBUG + debug("\r\nSPWFSAXX hard fault error:\r\n%s\r\n", _msg_buffer); + + // This is most likely the best we can do to recover from this module hard fault + _parser.set_timeout(SPWF_HF_TIMEOUT); + _recover_from_hard_faults(); + _parser.set_timeout(_timeout); +#endif // NDEBUG + + /* force call of (external) callback */ + _call_callback(); +} + +/* + * Handling oob ("+WIND:5:WiFi Hardware Failure") + */ +void SPWFSAxx::_wifi_hwfault_handler(void) +{ + unsigned int failure_nr; + + /* parse out the socket id & amount */ + _parser.recv(":%u\n", &failure_nr); + _recv_delim_lf(); + +#ifndef NDEBUG + error("\r\nSPWFSAXX WiFi HW fault error: %u\r\n", failure_nr); +#else // NDEBUG + debug("\r\nSPWFSAXX WiFi HW fault error: %u\r\n", failure_nr); + + // This is most likely the best we can do to recover from this module hard fault + _parser.set_timeout(SPWF_HF_TIMEOUT); + _recover_from_hard_faults(); + _parser.set_timeout(_timeout); +#endif // NDEBUG + + /* force call of (external) callback */ + _call_callback(); +} + +/* + * Handling oob ("+WIND:58:Socket Closed") + * when server closes a client connection + * + * NOTE: When a socket client receives an indication about socket server gone (only for TCP sockets, WIND:58), + * the socket connection is NOT automatically closed! + */ +void SPWFSAxx::_server_gone_handler(void) +{ + int spwf_id, internal_id; + + if(!(_parser.recv(SPWFXX_RECV_SOCKET_CLOSED, &spwf_id) && _recv_delim_lf())) { +#ifndef NDEBUG + error("\r\nSPWFSAxx::%s failed!\r\n", __func__); +#endif + goto _get_out; + } + + debug_if(_dbg_on, "AT^ +WIND:58:Socket Closed:%d\r\n", spwf_id); + + /* check for the module to report a valid id */ + MBED_ASSERT(((unsigned int)spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)); + + /* only set `server_gone` + * user still can receive data & must still explicitly close the socket + */ + internal_id = _associated_interface.get_internal_id(spwf_id); + if(internal_id != SPWFSA_SOCKET_COUNT) { + _associated_interface._ids[internal_id].server_gone = true; + } + +_get_out: + /* force call of (external) callback */ + _call_callback(); +} + +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 +/* + * Handling oob (currently only for "+WIND:24:WiFi Up::") + */ +void SPWFSAxx::_skip_oob(void) +{ + if(_parser.recv("%[^\n]\n", _msg_buffer) && _recv_delim_lf()) { + debug_if(_dbg_on, "AT^ +WIND:24:WiFi Up::%s\r\n", _msg_buffer); + } else { + debug_if(_dbg_on, "\r\nSPWF> Invalid string in SPWFSAxx::_skip_oob (%d)\r\n", __LINE__); + } +} +#endif + +void SPWFSAxx::setTimeout(uint32_t timeout_ms) +{ + _timeout = timeout_ms; + _parser.set_timeout(timeout_ms); +} + +void SPWFSAxx::attach(Callback<void()> func) +{ + _callback_func = func; /* call (external) callback only while not receiving */ +} + +/** + * Recv Function + */ +int32_t SPWFSAxx::recv(int spwf_id, void *data, uint32_t amount, bool datagram) +{ + BlockExecuter bh_handler(Callback<void()>(this, &SPWFSAxx::_execute_bottom_halves)); + + while (true) { + /* check if any packets are ready for us */ + for (struct packet **p = &_packets; *p; p = &(*p)->next) { + if ((*p)->id == spwf_id) { + debug_if(_dbg_on, "\r\nRead done on ID %d and length of packet is %d\r\n",spwf_id,(*p)->len); + struct packet *q = *p; + + MBED_ASSERT(q->len > 0); + + if(datagram) { // UDP => always remove pkt size + // will always consume a whole pending size + uint32_t ret; + + debug_if(_dbg_on, "%s():\t\t\t%d:%d (datagram)\r\n", __func__, spwf_id, q->len); + + ret = (amount < q->len) ? amount : q->len; + memcpy(data, q+1, ret); + + if (_packets_end == &(*p)->next) { + _packets_end = p; + } + *p = (*p)->next; + free(q); + + return ret; + } else { // TCP + if (q->len <= amount) { // return and remove full packet + memcpy(data, q+1, q->len); + + if (_packets_end == &(*p)->next) { + _packets_end = p; + } + *p = (*p)->next; + uint32_t len = q->len; + free(q); + + return len; + } else { // `q->len > amount`, return only partial packet + if(amount > 0) { + memcpy(data, q+1, amount); + q->len -= amount; + memmove(q+1, (uint8_t*)(q+1) + amount, q->len); + } + + return amount; + } + } + } + } + + /* check for pending data on module */ + { + int len; + + len = _read_in_pkt(spwf_id, false); + if(len <= 0) { /* SPWFXX error or no more data to be read */ + return -1; + } + } + } +} + +void SPWFSAxx::_process_winds(void) { + do { + if(readable()) { +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1 + if(_recv_delim_cr_lf()) // betzw: only necessary for `IDW01M1` +#endif + { + if(_parser.process_oob()) { + /* something to do? */; + } else { + debug_if(_dbg_on, "%s():\t\tNo oob's found!\r\n", __func__); + return; // no oob's found + } + } +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1 + else { + debug_if(_dbg_on, "%s():\t\tNo delimiters found!\r\n", __func__); + return; // no leading delimiters + } +#endif + } else { + return; // no more data in buffer + } + } while(true); +} + +/* Note: returns + * '>=0' in case of success, amount of read in data (in bytes) + * 'SPWFXX_ERR_OOM' in case of "out of memory" + * 'SPWFXX_ERR_READ' in case of other `_read_in_packet()` error + * 'SPWFXX_ERR_LEN' in case of `_read_len()` error + */ +int SPWFSAxx::_read_in_pkt(int spwf_id, bool close) { + int pending; + uint32_t wind_pending; + BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), + Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* call (external) callback only while not receiving */ + + _process_winds(); // perform async indication handling + + if(close) { // read in all data + wind_pending = pending = _read_len(spwf_id); // triggers also async indication handling! + + /* reset pending data sizes */ + _reset_pending_pkt_sizes(spwf_id); + /* set new entry for pending size */ + _add_pending_pkt_size(spwf_id, (uint32_t)pending); + } else { // only read in already notified data + pending = wind_pending = _get_pending_pkt_size(spwf_id); + if(pending == 0) { // special handling for no packets pending (to WORK AROUND missing WINDs)! + pending = _read_len(spwf_id); // triggers also async indication handling! + + if(pending != 0) { + _process_winds(); // perform async indication handling (again) + wind_pending = _get_pending_pkt_size(spwf_id); + + if(wind_pending == 0) { + /* betzw - WORK AROUND module FW issues: set new entry for pending size */ + debug_if(_dbg_on, "%s():\t\tAdd packet w/o WIND (%d)!\r\n", __func__, pending); + _add_pending_packet_sz(spwf_id, (uint32_t)pending); + + wind_pending = pending = _get_pending_pkt_size(spwf_id); + MBED_ASSERT(wind_pending > 0); + } + } + } + } + + if((pending > 0) && (wind_pending > 0)) { + if(pending <= (int)wind_pending) { + _clear_pending_data(spwf_id); + } + + int ret = _read_in_packet(spwf_id, wind_pending); + MBED_ASSERT(ret != 0); + if(ret < 0) { /* "out of memory" or `_read_in_packet()` error */ + /* we do not know if data is still pending at this point + but leaving the pending data bit set might lead to an endless loop */ + _clear_pending_data(spwf_id); + /* also reset pending data sizes */ + _reset_pending_pkt_sizes(spwf_id); + + return ret; + } + } else if(pending < 0) { /* 'SPWFXX_ERR_LEN' error */ + MBED_ASSERT(pending == SPWFXX_ERR_LEN); + /* we do not know if data is still pending at this point + but leaving the pending data bit set might lead to an endless loop */ + _clear_pending_data(spwf_id); + /* also reset pending data sizes */ + _reset_pending_pkt_sizes(spwf_id); + + return pending; + } else if(pending == 0) { + MBED_ASSERT(wind_pending == 0); + _clear_pending_data(spwf_id); + } else if(wind_pending == 0) { // `pending > 0` + /* betzw: should never happen! */ + MBED_ASSERT(false); + } + + return (int)wind_pending; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SPWFSAxx.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,452 @@ +/* SPWFSAxx Devices + * Copyright (c) 2015 ARM Limited + * + * 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. + */ + +#ifndef SPWFSAXX_H +#define SPWFSAXX_H + +#include "mbed.h" +#include "ATCmdParser.h" +#include "BlockExecuter.h" + +/* Common SPWFSAxx macros */ +#define SPWFXX_WINDS_LOW_ON "0x00000000" +#define SPWFXX_DEFAULT_BAUD_RATE 115200 +#define SPWFXX_MAX_TRIALS 3 + +#if !defined(SPWFSAXX_RTS_PIN) +#define SPWFSAXX_RTS_PIN NC +#endif // !defined(SPWFSAXX_RTS_PIN) +#if !defined(SPWFSAXX_CTS_PIN) +#define SPWFSAXX_CTS_PIN NC +#endif // !defined(SPWFSAXX_CTS_PIN) + +#define SPWFXX_ERR_OK (+1) +#define SPWFXX_ERR_OOM (-1) +#define SPWFXX_ERR_READ (-2) +#define SPWFXX_ERR_LEN (-3) + + +/* Max number of sockets & packets */ +#define SPWFSA_SOCKET_COUNT (8) +#define SPWFSA_MAX_PACKETS (4) + +#define PENDING_DATA_SLOTS (13) + +/* Pending data packets size buffer */ +class SpwfRealPendingPackets { +public: + SpwfRealPendingPackets() { + reset(); + } + + void add(uint32_t new_cum_size) { + MBED_ASSERT(new_cum_size >= cumulative_size); + + if(new_cum_size == cumulative_size) { + /* nothing to do */ + return; + } + + /* => `new_cum_size > cumulative_size` */ + real_pkt_sizes[last_pkt_ptr] = (uint16_t)(new_cum_size - cumulative_size); + cumulative_size = new_cum_size; + + last_pkt_ptr = (last_pkt_ptr + 1) % PENDING_DATA_SLOTS; + + MBED_ASSERT(first_pkt_ptr != last_pkt_ptr); + } + + uint32_t get(void) { + if(empty()) return 0; + + return real_pkt_sizes[first_pkt_ptr]; + } + + uint32_t cumulative(void) { + return cumulative_size; + } + + uint32_t remove(uint32_t size) { + MBED_ASSERT(!empty()); + + uint32_t ret = real_pkt_sizes[first_pkt_ptr]; + first_pkt_ptr = (first_pkt_ptr + 1) % PENDING_DATA_SLOTS; + + MBED_ASSERT(ret == size); + MBED_ASSERT(ret <= cumulative_size); + cumulative_size -= ret; + + return ret; + } + + void reset(void) { + memset(this, 0, sizeof(*this)); + } + +private: + bool empty(void) { + if(first_pkt_ptr == last_pkt_ptr) { + MBED_ASSERT(cumulative_size == 0); + return true; + } + return false; + } + + uint16_t real_pkt_sizes[PENDING_DATA_SLOTS]; + uint8_t first_pkt_ptr; + uint8_t last_pkt_ptr; + uint32_t cumulative_size; +}; + +class SpwfSAInterface; + +/** SPWFSAxx Interface class. + This is an interface to a SPWFSAxx module. + */ +class SPWFSAxx +{ +private: + /* abstract class*/ + SPWFSAxx(PinName tx, PinName rx, PinName rts, PinName cts, + SpwfSAInterface &ifce, bool debug, + PinName wakeup, PinName reset); + +public: + /** + * Init the SPWFSAxx + * + * @param mode mode in which to startup + * @return true only if SPWFSAxx has started up correctly + */ + bool startup(int mode); + + /** + * Connect SPWFSAxx to AP + * + * @param ap the name of the AP + * @param passPhrase the password of AP + * @param securityMode the security mode of AP (WPA/WPA2, WEP, Open) + * @return true only if SPWFSAxx is connected successfully + */ + bool connect(const char *ap, const char *passPhrase, int securityMode); + + /** + * Disconnect SPWFSAxx from AP + * + * @return true only if SPWFSAxx is disconnected successfully + */ + bool disconnect(void); + + /** + * Get the IP address of SPWFSAxx + * + * @return null-terminated IP address or null if no IP address is assigned + */ + const char *getIPAddress(void); + + /** + * Get the MAC address of SPWFSAxx + * + * @return null-terminated MAC address or null if no MAC address is assigned + */ + const char *getMACAddress(void); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been received + */ + const char *getGateway(void); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been received + */ + const char *getNetmask(void); + + /** Gets the current radio signal strength for active connection + * + * @return Connection strength in dBm (negative value) + */ + int8_t getRssi(); + + /** + * Sends data to an open socket + * + * @param id id of socket to send to + * @param data data to be sent + * @param amount amount of data to be sent - max 1024 + * @return true only if data sent successfully + */ + bool send(int id, const void *data, uint32_t amount); + + /** + * Receives data from an open socket + * + * @param id id to receive from + * @param data placeholder for returned information + * @param amount number of bytes to be received + * @param datagram receive a datagram packet + * @return the number of bytes received + */ + int32_t recv(int id, void *data, uint32_t amount, bool datagram); + + /** + * Closes a socket + * + * @param id id of socket to close, valid only 0-4 + * @return true only if socket is closed successfully + */ + bool close(int id); + + /** + * Allows timeout to be changed between commands + * + * @param timeout_ms timeout of the connection + */ + void setTimeout(uint32_t timeout_ms); + + /** + * Attach a function to call whenever network state has changed + * + * @param func A pointer to a void function, or 0 to set as none + */ + void attach(Callback<void()> func); + + /** + * Attach a function to call whenever network state has changed + * + * @param obj pointer to the object to call the member function on + * @param method pointer to the member function to call + */ + template <typename T, typename M> + void attach(T *obj, M method) { + attach(Callback<void()>(obj, method)); + } + + static const char _cr_ = '\x0d'; // '\r' carriage return + static const char _lf_ = '\x0a'; // '\n' line feed + +private: + UARTSerial _serial; + ATCmdParser _parser; + + DigitalOut _wakeup; + DigitalOut _reset; + PinName _rts; + PinName _cts; + + int _timeout; + bool _dbg_on; + + int _pending_sockets_bitmap; + SpwfRealPendingPackets _pending_pkt_sizes[SPWFSA_SOCKET_COUNT]; + + bool _network_lost_flag; + SpwfSAInterface &_associated_interface; + + /** + * Reset SPWFSAxx + * + * @return true only if SPWFSAxx resets successfully + */ + bool hw_reset(void); + bool reset(void); + + /** + * Check if SPWFSAxx is connected + * + * @return true only if the chip has an IP address + */ + bool isConnected(void); + + /** + * Checks if data is available + */ + bool readable(void) { + return _serial.FileHandle::readable(); + } + + /** + * Checks if data can be written + */ + bool writeable(void) { + return _serial.FileHandle::writable(); + } + + /** + * Try to empty RX buffer + * Can be used when commands fail receiving expected response to try to recover situation + * @note Gives no guarantee that situation improves + */ + void empty_rx_buffer(void) { + while(readable()) _parser.getc(); + } + + /* call (external) callback only while not receiving */ + volatile bool _call_event_callback_blocked; + Callback<void()> _callback_func; + + struct packet { + struct packet *next; + int id; + uint32_t len; + // data follows + } *_packets, **_packets_end; + + void _packet_handler_th(void); + void _execute_bottom_halves(void); + void _network_lost_handler_th(void); + void _network_lost_handler_bh(void); + void _hard_fault_handler(void); + void _wifi_hwfault_handler(void); + void _server_gone_handler(void); +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 + void _skip_oob(void); +#endif + bool _wait_wifi_hw_started(void); + bool _wait_console_active(void); + int _read_len(int); + int _flush_in(char*, int); + bool _winds_off(void); + void _winds_on(void); + void _read_in_pending(void); + int _read_in_pkt(int spwf_id, bool close); + int _read_in_packet(int spwf_id, uint32_t amount); + void _recover_from_hard_faults(void); + void _free_packets(int spwf_id); + void _free_all_packets(void); + void _process_winds(); + + virtual int _read_in(char*, int, uint32_t) = 0; + + bool _recv_delim_lf(void) { + return (_parser.getc() == _lf_); + } + + bool _recv_delim_cr(void) { + return (_parser.getc() == _cr_); + } + + bool _recv_delim_cr_lf(void) { + return _recv_delim_cr() && _recv_delim_lf(); + } + + bool _recv_ok(void) { + return _parser.recv(SPWFXX_RECV_OK) && _recv_delim_lf(); + } + + void _add_pending_packet_sz(int spwf_id, uint32_t size); + void _add_pending_pkt_size(int spwf_id, uint32_t size) { + _pending_pkt_sizes[spwf_id].add(size); + } + + uint32_t _get_cumulative_size(int spwf_id) { + return _pending_pkt_sizes[spwf_id].cumulative(); + } + + uint32_t _remove_pending_pkt_size(int spwf_id, uint32_t size) { + return _pending_pkt_sizes[spwf_id].remove(size); + } + + uint32_t _get_pending_pkt_size(int spwf_id) { + return _pending_pkt_sizes[spwf_id].get(); + } + + void _reset_pending_pkt_sizes(int spwf_id) { + _pending_pkt_sizes[spwf_id].reset(); + } + + void _set_pending_data(int spwf_id) { + _pending_sockets_bitmap |= (1 << spwf_id); + } + + void _clear_pending_data(int spwf_id) { + _pending_sockets_bitmap &= ~(1 << spwf_id); + } + + bool _is_data_pending(int spwf_id) { + return (_pending_sockets_bitmap & (1 << spwf_id)) ? true : false; + } + + bool _is_data_pending(void) { + if(_pending_sockets_bitmap != 0) return true; + else return false; + } + + void _packet_handler_bh(void) { + /* read in other eventually pending packages */ + _read_in_pending(); + } + + /* call (external) callback only while not receiving */ + void _event_handler(void); + + void _error_handler(void); + + void _call_callback(void) { + if((bool)_callback_func) { + _callback_func(); + } + } + + bool _is_event_callback_blocked(void) { + return _call_event_callback_blocked; + } + + void _block_event_callback(void) { + MBED_ASSERT(!_call_event_callback_blocked); + _call_event_callback_blocked = true; + } + + void _unblock_event_callback(void) { + MBED_ASSERT(_call_event_callback_blocked); + _call_event_callback_blocked = false; + _trigger_event_callback(); + } + + /* unblock & force call of (external) callback */ + void _unblock_and_callback(void) { + MBED_ASSERT(_call_event_callback_blocked); + _call_event_callback_blocked = false; + _call_callback(); + } + + /* trigger call of (external) callback in case there is still data */ + void _trigger_event_callback(void) { + MBED_ASSERT(!_call_event_callback_blocked); + /* if still data available */ + if(readable()) { + _call_callback(); + } + } + + char _ip_buffer[16]; + char _gateway_buffer[16]; + char _netmask_buffer[16]; + char _mac_buffer[18]; + + char ssid_buf[256]; /* required to handle not 802.11 compliant ssid's */ + char *_msg_buffer; + +private: + friend class SPWFSA01; + friend class SPWFSA04; + friend class SpwfSAInterface; +}; + +#endif // SPWFSAXX_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SpwfSAInterface.cpp Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,594 @@ +/* mbed Microcontroller Library +* Copyright (c) 20015 ARM Limited +* +* 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. +*/ + +/** + ****************************************************************************** + * @file SpwfSAInterface.cpp + * @author STMicroelectronics + * @brief Implementation of the NetworkStack for the SPWF Device + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2> + ****************************************************************************** + */ + +#include "SpwfSAInterface.h" +#include "mbed_debug.h" +#include "BlockExecuter.h" + +#if MBED_CONF_RTOS_PRESENT +static Mutex _spwf_mutex; // assuming a recursive mutex +static void _spwf_lock() { + (void)(_spwf_mutex.lock()); +} +static Callback<void()> _callback_spwf_lock(&_spwf_lock); + +static void _spwf_unlock() { + (void)(_spwf_mutex.unlock()); +} +static Callback<void()> _callback_spwf_unlock(&_spwf_unlock); + +#define SYNC_HANDLER \ + BlockExecuter sync_handler(_callback_spwf_unlock, _callback_spwf_lock) +#else +#define SYNC_HANDLER +#endif + + +/** + * @brief SpwfSAInterface constructor + * @param tx: Pin USART TX + * rx: Pin USART RX + * rts: Pin USART RTS + * cts: Pin USART RTS + * debug : not used + * @retval none + */ +SpwfSAInterface::SpwfSAInterface(PinName tx, PinName rx, + PinName rts, PinName cts, bool debug, + PinName wakeup, PinName reset) +: _spwf(tx, rx, rts, cts, *this, debug, wakeup, reset), + _dbg_on(debug) +{ + inner_constructor(); + reset_credentials(); +} + +/** + * @brief init function + * initializes SPWF FW and module + * @param none + * @retval error value + */ +nsapi_error_t SpwfSAInterface::init(void) +{ + _spwf.setTimeout(SPWF_INIT_TIMEOUT); + + if(_spwf.startup(0)) { + return NSAPI_ERROR_OK; + } + else return NSAPI_ERROR_DEVICE_ERROR; +} + +nsapi_error_t SpwfSAInterface::connect(void) +{ + int mode; + char *pass_phrase = ap_pass; + SYNC_HANDLER; + + // check for valid SSID + if(ap_ssid[0] == '\0') { + return NSAPI_ERROR_PARAMETER; + } + + switch(ap_sec) + { + case NSAPI_SECURITY_NONE: + mode = 0; + pass_phrase = NULL; + break; + case NSAPI_SECURITY_WEP: + mode = 1; + break; + case NSAPI_SECURITY_WPA: + case NSAPI_SECURITY_WPA2: + mode = 2; + break; + default: + mode = 2; + break; + } + + // First: disconnect + if(_connected_to_network) { + if(!disconnect()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + } + + //initialize the device before connecting + if(!_isInitialized) + { + if(init() != NSAPI_ERROR_OK) return NSAPI_ERROR_DEVICE_ERROR; + _isInitialized=true; + } + + // Then: (re-)connect + _spwf.setTimeout(SPWF_CONNECT_TIMEOUT); + + if (!_spwf.connect(ap_ssid, pass_phrase, mode)) { + return NSAPI_ERROR_AUTH_FAILURE; + } + + if (!_spwf.getIPAddress()) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + _connected_to_network = true; + return NSAPI_ERROR_OK; +} + +/** + * @brief network connect + * connects to Access Point + * @param ap: Access Point (AP) Name String + * pass_phrase: Password String for AP + * security: type of NSAPI security supported + * @retval NSAPI Error Type + */ +nsapi_error_t SpwfSAInterface::connect(const char *ssid, const char *pass, nsapi_security_t security, + uint8_t channel) +{ + nsapi_error_t ret; + SYNC_HANDLER; + + if (channel != 0) { + return NSAPI_ERROR_UNSUPPORTED; + } + + ret = set_credentials(ssid, pass, security); + if(ret != NSAPI_ERROR_OK) return ret; + + return connect(); +} + +/** + * @brief network disconnect + * disconnects from Access Point + * @param none + * @retval NSAPI Error Type + */ +nsapi_error_t SpwfSAInterface::disconnect(void) +{ + SYNC_HANDLER; + + _spwf.setTimeout(SPWF_DISCONNECT_TIMEOUT); + + if (!_spwf.disconnect()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + /* NOTE: all sockets are gone */ + inner_constructor(); + + return NSAPI_ERROR_OK; +} + +/** + * @brief Get the local IP address + * @param none + * @retval Null-terminated representation of the local IP address + * or null if not yet connected + */ +const char *SpwfSAInterface::get_ip_address(void) +{ + SYNC_HANDLER; + + _spwf.setTimeout(SPWF_MISC_TIMEOUT); + return _spwf.getIPAddress(); +} + +/** + * @brief Get the MAC address + * @param none + * @retval Null-terminated representation of the MAC address + * or null if not yet connected + */ +const char *SpwfSAInterface::get_mac_address(void) +{ + SYNC_HANDLER; + + _spwf.setTimeout(SPWF_MISC_TIMEOUT); + return _spwf.getMACAddress(); +} + +const char *SpwfSAInterface::get_gateway(void) +{ + SYNC_HANDLER; + + if(!_connected_to_network) return NULL; + + _spwf.setTimeout(SPWF_MISC_TIMEOUT); + return _spwf.getGateway(); +} + +const char *SpwfSAInterface::get_netmask(void) +{ + SYNC_HANDLER; + + if(!_connected_to_network) return NULL; + + _spwf.setTimeout(SPWF_MISC_TIMEOUT); + return _spwf.getNetmask(); +} + +/** + * @brief open a socket handle + * @param handle: Pointer to handle + * proto: TCP/UDP protocol + * @retval NSAPI Error Type + */ +nsapi_error_t SpwfSAInterface::socket_open(void **handle, nsapi_protocol_t proto) +{ + int internal_id; + SYNC_HANDLER; + + for (internal_id = 0; internal_id < SPWFSA_SOCKET_COUNT; internal_id++) { + if(_ids[internal_id].internal_id == SPWFSA_SOCKET_COUNT) break; + } + + if(internal_id == SPWFSA_SOCKET_COUNT) { + debug_if(_dbg_on, "NO Socket ID Error\r\n"); + return NSAPI_ERROR_NO_SOCKET; + } + + spwf_socket_t *socket = &_ids[internal_id]; + socket->internal_id = internal_id; + socket->spwf_id = SPWFSA_SOCKET_COUNT; + socket->server_gone = false; + socket->no_more_data = false; + socket->proto = proto; + socket->addr = SocketAddress(); + + *handle = socket; + return NSAPI_ERROR_OK; +} + +/** + * @brief connect to a remote socket + * @param handle: Pointer to socket handle + * addr: Address to connect to + * @retval NSAPI Error Type + */ +nsapi_error_t SpwfSAInterface::socket_connect(void *handle, const SocketAddress &addr) +{ + spwf_socket_t *socket = (spwf_socket_t*)handle; + SYNC_HANDLER; + + MBED_ASSERT(((unsigned int)socket->internal_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)); + + if(_socket_has_connected(socket->internal_id)) { + return NSAPI_ERROR_IS_CONNECTED; + } + + _spwf.setTimeout(SPWF_OPEN_TIMEOUT); + + const char *proto = (socket->proto == NSAPI_UDP) ? "u" : "t"; //"s" for secure socket? + + if(addr.get_ip_version() != NSAPI_IPv4) { // IPv6 not supported (yet) + return NSAPI_ERROR_UNSUPPORTED; + } + + /* block asynchronous indications */ + if(!_spwf._winds_off()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + { + BlockExecuter bh_handler(Callback<void()>(&_spwf, &SPWFSAxx::_execute_bottom_halves)); + { + BlockExecuter winds_enabler(Callback<void()>(&_spwf, &SPWFSAxx::_winds_on)); + + if(!_spwf.open(proto, &socket->spwf_id, addr.get_ip_address(), addr.get_port())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + /* check for the module to report a valid id */ + MBED_ASSERT(((unsigned int)socket->spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)); + + _internal_ids[socket->spwf_id] = socket->internal_id; + socket->addr = addr; + return NSAPI_ERROR_OK; + } + } +} + +nsapi_error_t SpwfSAInterface::socket_bind(void *handle, const SocketAddress &address) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +nsapi_error_t SpwfSAInterface::socket_listen(void *handle, int backlog) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +nsapi_error_t SpwfSAInterface::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +nsapi_error_t SpwfSAInterface::socket_close(void *handle) +{ + spwf_socket_t *socket = (spwf_socket_t*)handle; + int internal_id = socket->internal_id; + SYNC_HANDLER; + + if(!_socket_is_open(internal_id)) return NSAPI_ERROR_NO_SOCKET; + + if(this->_socket_has_connected(socket)) { + _spwf.setTimeout(SPWF_CLOSE_TIMEOUT); + if (!_spwf.close(socket->spwf_id)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + _internal_ids[socket->spwf_id] = SPWFSA_SOCKET_COUNT; + } + + _ids[internal_id].internal_id = SPWFSA_SOCKET_COUNT; + _ids[internal_id].spwf_id = SPWFSA_SOCKET_COUNT; + + return NSAPI_ERROR_OK; +} + +/** + * @brief write to a socket + * @param handle: Pointer to handle + * data: pointer to data + * size: size of data + * @retval number of bytes sent + */ +nsapi_size_or_error_t SpwfSAInterface::socket_send(void *handle, const void *data, unsigned size) +{ + spwf_socket_t *socket = (spwf_socket_t*)handle; + SYNC_HANDLER; + + CHECK_NOT_CONNECTED_ERR(); + + _spwf.setTimeout(SPWF_SEND_TIMEOUT); + + if(!_socket_is_still_connected(socket)) { + return NSAPI_ERROR_CONNECTION_LOST; + } + + if (!_spwf.send(socket->spwf_id, data, size)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return size; +} + +/** + * @brief receive data on a socket + * @param handle: Pointer to handle + * data: pointer to data + * size: size of data + * @retval number of bytes read or negative error code in case of error + */ +nsapi_size_or_error_t SpwfSAInterface::socket_recv(void *handle, void *data, unsigned size) +{ + SYNC_HANDLER; + + return _socket_recv(handle, data, size, false); +} + +nsapi_size_or_error_t SpwfSAInterface::_socket_recv(void *handle, void *data, unsigned size, bool datagram) +{ + spwf_socket_t *socket = (spwf_socket_t*)handle; + + CHECK_NOT_CONNECTED_ERR(); + + if(!_socket_might_have_data(socket)) { + return 0; + } + + _spwf.setTimeout(SPWF_RECV_TIMEOUT); + + int32_t recv = _spwf.recv(socket->spwf_id, (char*)data, (uint32_t)size, datagram); + + MBED_ASSERT((recv != 0) || (size == 0)); + + if (recv < 0) { + if(!_socket_is_still_connected(socket)) { + socket->no_more_data = true; + return 0; + } + + return NSAPI_ERROR_WOULD_BLOCK; + } + + return recv; +} + +/** + * @brief send data to a udp socket + * @param handle: Pointer to handle + * addr: address of udp socket + * data: pointer to data + * size: size of data + * @retval number of bytes sent + */ +nsapi_size_or_error_t SpwfSAInterface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + spwf_socket_t *socket = (spwf_socket_t*)handle; + SYNC_HANDLER; + + CHECK_NOT_CONNECTED_ERR(); + + if ((this->_socket_has_connected(socket)) && (socket->addr != addr)) { + _spwf.setTimeout(SPWF_CLOSE_TIMEOUT); + if (!_spwf.close(socket->spwf_id)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + _internal_ids[socket->spwf_id] = SPWFSA_SOCKET_COUNT; + socket->spwf_id = SPWFSA_SOCKET_COUNT; + } + + _spwf.setTimeout(SPWF_CONN_SND_TIMEOUT); + if (!this->_socket_has_connected(socket)) { + nsapi_error_t err = socket_connect(socket, addr); + if (err < 0) { + return err; + } + } + + return socket_send(socket, data, size); +} + +/** + * @brief receive data on a udp socket + * @param handle: Pointer to handle + * addr: address of udp socket + * data: pointer to data + * size: size of data + * @retval number of bytes read + */ +nsapi_size_or_error_t SpwfSAInterface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + spwf_socket_t *socket = (spwf_socket_t*)handle; + nsapi_error_t ret; + SYNC_HANDLER; + + CHECK_NOT_CONNECTED_ERR(); + + ret = _socket_recv(socket, data, size, true); + if (ret >= 0 && addr) { + *addr = socket->addr; + } + + return ret; +} + +/** + * @brief attach function/callback to the socket + * @param handle: Pointer to handle + * callback: callback function pointer + * data: pointer to data + * @retval none + */ +void SpwfSAInterface::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + spwf_socket_t *socket = (spwf_socket_t*)handle; + SYNC_HANDLER; + + if(!_socket_is_open(socket)) return; // might happen e.g. after module hard fault or voluntary disconnection + + _cbs[socket->internal_id].callback = callback; + _cbs[socket->internal_id].data = data; +} + +void SpwfSAInterface::event(void) { + for (int internal_id = 0; internal_id < SPWFSA_SOCKET_COUNT; internal_id++) { + if (_cbs[internal_id].callback && (_ids[internal_id].internal_id != SPWFSA_SOCKET_COUNT)) { + _cbs[internal_id].callback(_cbs[internal_id].data); + } + } +} + +nsapi_error_t SpwfSAInterface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security) +{ + SYNC_HANDLER; + + if(ssid == NULL) { + return NSAPI_ERROR_PARAMETER; + } + + if(pass != NULL) { + if(strlen(pass) < sizeof(ap_pass)) { + if(security == NSAPI_SECURITY_NONE) { + return NSAPI_ERROR_PARAMETER; + } + } else { + return NSAPI_ERROR_PARAMETER; + } + } else if(security != NSAPI_SECURITY_NONE) { + return NSAPI_ERROR_PARAMETER; + } + + reset_credentials(); + + ap_sec = security; + strncpy(ap_ssid, ssid, sizeof(ap_ssid) - 1); + strncpy(ap_pass, pass, sizeof(ap_pass) - 1); + + return NSAPI_ERROR_OK; +} + +nsapi_error_t SpwfSAInterface::set_channel(uint8_t channel) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int8_t SpwfSAInterface::get_rssi(void) +{ + SYNC_HANDLER; + + if(!_connected_to_network) return 0; + + _spwf.setTimeout(SPWF_MISC_TIMEOUT); + return _spwf.getRssi(); +} + +nsapi_size_or_error_t SpwfSAInterface::scan(WiFiAccessPoint *res, unsigned count) +{ + SYNC_HANDLER; + + nsapi_size_or_error_t ret; + + //initialize the device before scanning + if(!_isInitialized) + { + if(init() != NSAPI_ERROR_OK) return NSAPI_ERROR_DEVICE_ERROR; + } + + _spwf.setTimeout(SPWF_SCAN_TIMEOUT); + + /* block asynchronous indications */ + if(!_spwf._winds_off()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + ret = _spwf.scan(res, count); + + /* unblock asynchronous indications */ + _spwf._winds_on(); + + //de-initialize the device after scanning + if(!_isInitialized) + { + nsapi_error_t err = disconnect(); + if(err != NSAPI_ERROR_OK) return err; + } + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/SpwfSAInterface.h Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,450 @@ +/* mbed Microcontroller Library +* Copyright (c) 2015 ARM Limited +* +* 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. +*/ + +/** + ****************************************************************************** + * @file SpwfSAInterface.h + * @author STMicroelectronics + * @brief Header file of the NetworkStack for the SPWF Device + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2> + ****************************************************************************** + */ + +#ifndef SPWFSA_INTERFACE_H +#define SPWFSA_INTERFACE_H + +#include <limits.h> + +#include "mbed.h" + +#define IDW01M1 1 +#define IDW04A1 2 + +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1 +#include "SPWFSA01/SPWFSA01.h" +#elif MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 +#include "SPWFSA04/SPWFSA04.h" +#else +#error No (valid) Wi-Fi exapnsion board defined (MBED_CONF_IDW0XX1_EXPANSION_BOARD: options are IDW01M1 and IDW04A1) +#endif + +// Various timeouts for different SPWF operations +#define SPWF_CONNECT_TIMEOUT 60000 +#define SPWF_DISCONNECT_TIMEOUT 30002 +#define SPWF_HF_TIMEOUT 30001 +#define SPWF_NETLOST_TIMEOUT 30000 +#define SPWF_READ_BIN_TIMEOUT 13000 +#define SPWF_CLOSE_TIMEOUT 10001 +#define SPWF_SEND_TIMEOUT 10000 +#define SPWF_INIT_TIMEOUT 8000 +#define SPWF_OPEN_TIMEOUT 5002 +#define SPWF_CONN_SND_TIMEOUT 5001 +#define SPWF_SCAN_TIMEOUT 5000 +#define SPWF_MISC_TIMEOUT 301 +#define SPWF_RECV_TIMEOUT 300 + +/** SpwfSAInterface class + * Implementation of the NetworkStack for the SPWF Device + */ +// NOTE - betzw - TODO: MUST become singleton! +class SpwfSAInterface : public NetworkStack, public WiFiInterface +{ +public: + /** SpwfSAInterface constructor + * @param tx TX pin + * @param rx RX pin + * @param rts RTS pin + * @param cts CTS pin + * @param debug Enable debugging + * @param wakeup Wakeup pin + * @param reset Reset pin + */ + SpwfSAInterface(PinName tx, PinName rx, + PinName rts = SPWFSAXX_RTS_PIN, PinName cts = SPWFSAXX_CTS_PIN, bool debug = false, + PinName wakeup = SPWFSAXX_WAKEUP_PIN, PinName reset = SPWFSAXX_RESET_PIN); + + /** Start the interface + * + * Attempts to connect to a WiFi network. Requires ssid and passphrase to be set. + * If passphrase is invalid, NSAPI_ERROR_AUTH_ERROR is returned. + * + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t connect(); + + /** Start the interface + * + * Attempts to connect to a WiFi network. + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE) + * @param channel This parameter is not supported, setting it to anything else than 0 will result in NSAPI_ERROR_UNSUPPORTED + * @return 0 on success, or error code on failure + */ + virtual nsapi_error_t connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE, + uint8_t channel = 0); + + /** Set the WiFi network credentials + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * (defaults to NSAPI_SECURITY_NONE) + * @return 0 on success, or error code on failure + */ + virtual nsapi_error_t set_credentials(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE); + + /** Set the WiFi network channel - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param channel Channel on which the connection is to be made, or 0 for any (Default: 0) + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual nsapi_error_t set_channel(uint8_t channel); + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual nsapi_error_t disconnect(); + + /** Get the internally stored IP address + * @return IP address of the interface or null if not yet connected + */ + virtual const char *get_ip_address(); + + /** Get the internally stored MAC address + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Get the local gateway + * + * @return Null-terminated representation of the local gateway + * or null if no network mask has been received + */ + virtual const char *get_gateway(); + + /** Get the local network mask + * + * @return Null-terminated representation of the local network mask + * or null if no network mask has been received + */ + virtual const char *get_netmask(); + + /** Gets the current radio signal strength for active connection + * + * @return Connection strength in dBm (negative value) + */ + virtual int8_t get_rssi(); + + /** Scan for available networks + * + * This function will block. If the @a count is 0, function will only return count of available networks, so that + * user can allocated necessary memory. If the @count is grater than 0 and the @a ap is not NULL it'll be populated + * with discovered networks up to value of @a count. + * + * @param res Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @return Number of entries in @a, or if @a count was 0 number of available networks, + * negative on error see @a nsapi_error + */ + virtual nsapi_size_or_error_t scan(WiFiAccessPoint *res, unsigned count); + + /** Translates a hostname to an IP address with specific version + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param address Destination for the host SocketAddress + * @param host Hostname to resolve + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::gethostbyname; + + /** Add a domain name server to list of servers to query + * + * @param addr Destination for the host address + * @return 0 on success, negative error code on failure + */ + using NetworkInterface::add_dns_server; + +private: + /** Open a socket + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative on failure + */ + virtual nsapi_error_t socket_open(void **handle, nsapi_protocol_t proto); + + /** Close the socket + * @param handle Socket handle + * @return 0 on success, negative on failure + * @note On failure, any memory associated with the socket must still + * be cleaned up + */ + virtual nsapi_error_t socket_close(void *handle); + + /** Bind a server socket to a specific port - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param handle Socket handle + * @param address Local address to listen for incoming connections on + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual nsapi_error_t socket_bind(void *handle, const SocketAddress &address); + + /** Start listening for incoming connections - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual nsapi_error_t socket_listen(void *handle, int backlog); + + /** Connects this TCP socket to the server + * @param handle Socket handle + * @param address SocketAddress to connect to + * @return 0 on success, negative on failure + */ + virtual nsapi_error_t socket_connect(void *handle, const SocketAddress &address); + + /** Accept a new connection - NOT SUPPORTED + * + * This function is not supported and will return NSAPI_ERROR_UNSUPPORTED + * + * @param handle Handle in which to store new socket + * @param server Socket handle to server to accept from + * @return Not supported, returns NSAPI_ERROR_UNSUPPORTED + */ + virtual nsapi_error_t socket_accept(void *handle, void **socket, SocketAddress *address); + + /** Send data to the remote host + * @param handle Socket handle + * @param data The buffer to send to the host + * @param size The length of the buffer to send + * @return Number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_BLOCK + */ + virtual nsapi_size_or_error_t socket_send(void *handle, const void *data, unsigned size); + + /** Receive data from the remote host + * @param handle Socket handle + * @param data The buffer in which to store the data received from the host + * @param size The maximum length of the buffer + * @return Number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_BLOCK + */ + virtual nsapi_size_or_error_t socket_recv(void *handle, void *data, unsigned size); + + /** Send a packet to a remote endpoint + * @param handle Socket handle + * @param address The remote SocketAddress + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return The number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_BLOCK + */ + virtual nsapi_size_or_error_t socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + + /** Receive a packet from a remote endpoint + * @param handle Socket handle + * @param address Destination for the remote SocketAddress or null + * @param buffer The buffer for storing the incoming packet data + * If a packet is too long to fit in the supplied buffer, + * excess bytes are discarded + * @param size The length of the buffer + * @return The number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_BLOCK + */ + virtual nsapi_size_or_error_t socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + * @note Callback may be called in an interrupt context. + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + + /** Provide access to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack() + { + return this; + } + +private: + /** spwf_socket class + * Implementation of SPWF socket structure + */ + typedef struct spwf_socket { + int8_t internal_id; + int spwf_id; + bool server_gone; + bool no_more_data; + nsapi_protocol_t proto; + SocketAddress addr; + } spwf_socket_t; + + bool _socket_is_open(spwf_socket_t *sock) { + if(((unsigned int)sock->internal_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)) { + return (_ids[sock->internal_id].internal_id == sock->internal_id); + } + return false; + } + + bool _socket_has_connected(spwf_socket_t *sock) { + return (_socket_is_open(sock) && (((unsigned int)sock->spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT))); + } + + bool _socket_is_still_connected(spwf_socket_t *sock) { + return (_socket_has_connected(sock) && !sock->server_gone); + } + + bool _socket_might_have_data(spwf_socket_t *sock) { + return (_socket_has_connected(sock) && !sock->no_more_data); + } + + bool _socket_is_open(int internal_id) { + if(((unsigned int)internal_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)) { + return (_ids[internal_id].internal_id == internal_id); + } + return false; + } + + bool _socket_has_connected(int internal_id) { + if(!_socket_is_open(internal_id)) return false; + + spwf_socket_t &sock = _ids[internal_id]; + return (sock.spwf_id != SPWFSA_SOCKET_COUNT); + } + + bool _socket_is_still_connected(int internal_id) { + if(!_socket_has_connected(internal_id)) return false; + + spwf_socket_t &sock = _ids[internal_id]; + return (!sock.server_gone); + } + + bool _socket_might_have_data(int internal_id) { + if(!_socket_is_still_connected(internal_id)) return false; + + spwf_socket_t &sock = _ids[internal_id]; + return (!sock.no_more_data); + } + + void reset_credentials() { + memset(ap_ssid, 0, sizeof(ap_ssid)); + memset(ap_pass, 0, sizeof(ap_pass)); + ap_sec = NSAPI_SECURITY_NONE; + } + +#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1 + SPWFSA01 _spwf; +#elif MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 + SPWFSA04 _spwf; +#endif + + bool _isInitialized; + bool _dbg_on; + bool _connected_to_network; + + spwf_socket_t _ids[SPWFSA_SOCKET_COUNT]; + struct { + void (*callback)(void *); + void *data; + } _cbs[SPWFSA_SOCKET_COUNT]; + int _internal_ids[SPWFSA_SOCKET_COUNT]; + + char ap_ssid[33]; /* 32 is what 802.11 defines as longest possible name; +1 for the \0 */ + nsapi_security_t ap_sec; + char ap_pass[64]; /* The longest allowed passphrase */ + +private: + void event(void); + nsapi_error_t init(void); + nsapi_size_or_error_t _socket_recv(void *handle, void *data, unsigned size, bool datagram); + + + int get_internal_id(int spwf_id) { // checks also if `spwf_id` is (still) "valid" + if(((unsigned int)spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)) { // valid `spwf_id` + int internal_id = _internal_ids[spwf_id]; + if((_socket_is_open(internal_id)) && (_ids[internal_id].spwf_id == spwf_id)) { + return internal_id; + } else { + return SPWFSA_SOCKET_COUNT; + } + } else { // invalid `spwf_id` + return SPWFSA_SOCKET_COUNT; + } + } + + /* Called at initialization or after module hard fault */ + void inner_constructor() { + memset(_ids, 0, sizeof(_ids)); + memset(_cbs, 0, sizeof(_cbs)); + + for (int sock_cnt = 0; sock_cnt < SPWFSA_SOCKET_COUNT; sock_cnt++) { + _ids[sock_cnt].internal_id = SPWFSA_SOCKET_COUNT; + _ids[sock_cnt].spwf_id = SPWFSA_SOCKET_COUNT; + _internal_ids[sock_cnt] = SPWFSA_SOCKET_COUNT; + } + + _spwf.attach(this, &SpwfSAInterface::event); + + _connected_to_network = false; + _isInitialized = false; + } + +private: + friend class SPWFSAxx; + friend class SPWFSA01; + friend class SPWFSA04; +}; + +#define CHECK_NOT_CONNECTED_ERR() { \ + if(!_connected_to_network) return NSAPI_ERROR_NO_CONNECTION; \ +} \ + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/mbed_app_idw01m1.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,9 @@ +{ + "target_overrides": { + "*": { + "idw0xx1.expansion-board": "IDW01M1", + "drivers.uart-serial-txbuf-size": 512, + "drivers.uart-serial-rxbuf-size": 512 + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/mbed_app_idw04a1.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,10 @@ +{ + "target_overrides": { + "*": { + "idw0xx1.expansion-board": "IDW04A1", + "drivers.uart-serial-txbuf-size": 512, + "drivers.uart-serial-rxbuf-size": 512 + } + }, + "macros": ["IDW04A1_WIFI_HW_BUG_WA"] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wifi-x-nucleo-idw01m1/mbed_lib.json Thu Jun 21 17:50:21 2018 +0000 @@ -0,0 +1,9 @@ +{ + "name": "idw0xx1", + "config": { + "expansion-board":{ + "help": "Options are IDW01M1 and IDW04A1", + "value": "IDW01M1" + } + } +}