versione corretta
Dependents: DISCO_L475VG_IOT01-Sensors-BSP
Revision 0:766454e296c3, committed 2018-08-21
- Comitter:
- group-Farnell24-IOT-Team
- Date:
- Tue Aug 21 08:34:28 2018 +0000
- Commit message:
- Initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362/ATParser/ATParser.cpp Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,427 @@ +/* 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. + * + * @section DESCRIPTION + * + * Parser for the AT command syntax + * + */ + +#include "ATParser.h" +#include "mbed_debug.h" + +#ifdef LF +#undef LF +#define LF 10 +#else +#define LF 10 +#endif + +#ifdef CR +#undef CR +#define CR 13 +#else +#define CR 13 +#endif +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define dbg_on 0 +//#define TRACE_AT_DATA 1 + +// getc/putc handling with timeouts +int ATParser::putc(char c) +{ + return _serial_spi->putc(c); +} + +int ATParser::getc() +{ + return _serial_spi->getc(); +} + +void ATParser::flush() +{ + _bufferMutex.lock(); + while (_serial_spi->readable()) { + _serial_spi->getc(); + } + _bufferMutex.unlock(); +} + +// read/write handling with timeouts +int ATParser::write(const char *data, int size_of_data, int size_in_buff) +{ + int i = 0; + _bufferMutex.lock(); + for ( ; i < size_of_data; i++) { + if (putc(data[i]) < 0) { + _bufferMutex.unlock(); + return -1; + } + } + + _serial_spi->buffsend(size_of_data + size_in_buff); + _bufferMutex.unlock(); + return (size_of_data + size_in_buff); +} + +int ATParser::read(char *data) +{ + int readsize; + int i = 0; + + _bufferMutex.lock(); + + //this->flush(); + if(!_serial_spi->readable()) { + readsize = _serial_spi->read(); + } else { + error("Pending data when reading from WIFI\r\n"); + return -1; + } + + debug_if(dbg_on, "Avail in SPI %d\r\n", readsize); + + if ( readsize < 0) { + _bufferMutex.unlock(); + return -1; + } + + for (i = 0 ; i < readsize; i++) { + int c = getc(); + if (c < 0) { + _bufferMutex.unlock(); + return -1; + } + data[i] = c; + } + +#if TRACE_AT_DATA + debug_if(dbg_on, "AT<< %d BYTES\r\n", readsize); + for (i = 0; i < readsize; i++) { + debug_if(dbg_on, "%2X ", data[i]); + } + debug_if(dbg_on, "\r\n"); +#endif + + _bufferMutex.unlock(); + + return (readsize); +} + +// printf/scanf handling +int ATParser::vprintf(const char *format, va_list args) +{ + _bufferMutex.lock(); + if (vsprintf(_buffer, format, args) < 0) { + _bufferMutex.unlock(); + return false; + } + + int i = 0; + for ( ; _buffer[i]; i++) { + if (putc(_buffer[i]) < 0) { + _bufferMutex.unlock(); + return -1; + } + } + _bufferMutex.unlock(); + + return i; +} + +int ATParser::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; + + _bufferMutex.lock(); + + 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) { + _bufferMutex.unlock(); + return false; + } + // Recieve next character + int c = getc(); + if (c < 0) { + _bufferMutex.unlock(); + 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); + _bufferMutex.unlock(); + return j; + } + } +} + + +// Command parsing with line handling +bool ATParser::vsend(const char *command, va_list args) +{ + int i=0, j=0; + _bufferMutex.lock(); + // Create and send command + if (vsprintf(_buffer, command, args) < 0) { + _bufferMutex.unlock(); + return false; + } + /* get buffer length */ + for (i = 0; _buffer[i]; i++) { + } + + for (j=0; _delimiter[j]; j++) { + _buffer[i+j] = _delimiter[j]; + } + _buffer[i+j]=0; // only to get a clean debug log + + bool ret = !(_serial_spi->buffwrite(_buffer, i+j) < 0); + + debug_if(dbg_on, "AT> %s\n", _buffer); + _bufferMutex.unlock(); + return ret; +} + +bool ATParser::vrecv(const char *response, va_list args) +{ + _bufferMutex.lock(); + /* Read from the wifi module, fill _rxbuffer */ + //this->flush(); + if(!_serial_spi->readable()) { + debug_if(dbg_on, "NO DATA, read again\r\n"); + if (_serial_spi->read() < 0) { + return false; + } + } else { + debug_if(dbg_on, "Pending data\r\n"); + } +restart: + _aborted = false; + // 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; + bool whole_line_wanted = false; + + while (response[i]) { + if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') { + _buffer[offset++] = '%'; + _buffer[offset++] = '*'; + i++; + } else { + _buffer[offset++] = response[i++]; + // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification + if (response[i - 1] == '\n' && !(i >= 3 && response[i-3] == '[' && response[i-2] == '^')) { + whole_line_wanted = true; + break; + } + } + } + + // 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; + + debug_if(dbg_on, "AT? ====%s====\n", _buffer); + // 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) { + debug_if(dbg_on, "AT(Timeout)\n"); + _bufferMutex.unlock(); + return false; + } + +#if TRACE_AT_DATA + debug_if(dbg_on, "%2X ", c); +#endif + _buffer[offset + j++] = c; + _buffer[offset + j] = 0; + + // Check for oob data + for (struct oob *oob = _oobs; oob; oob = oob->next) { + if ((unsigned)j == oob->len && memcmp( + oob->prefix, _buffer+offset, oob->len) == 0) { + debug_if(dbg_on, "AT! %s\n", oob->prefix); + oob->cb(); + + if (_aborted) { + debug_if(dbg_on, "AT(Aborted)\n"); + _bufferMutex.unlock(); + return false; + } + // oob may have corrupted non-reentrant buffer, + // so we need to set it up again + goto restart; + } + } + + // Check for match + int count = -1; + if (whole_line_wanted && c != '\n' && c != ' ') { + // Don't attempt scanning until we get delimiter if they included it in format + // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string + // (scanf does not itself match whitespace in its format string, so \n is not significant to it) + } else { + 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====\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 ((c == '\n') ) { + debug_if(dbg_on, "New line AT<<< %s", _buffer+offset); + j = 0; + } + if ((j + 1 >= (_buffer_size - offset))) { + + debug_if(dbg_on, "Out of space AT<<< %s, j=%d", _buffer+offset, j); + j = 0; + } + } + } + + _bufferMutex.unlock(); + + return true; +} + + +// Mapping to vararg functions +int ATParser::printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + int res = vprintf(format, args); + va_end(args); + return res; +} + +int ATParser::scanf(const char *format, ...) +{ + va_list args; + va_start(args, format); + int res = vscanf(format, args); + va_end(args); + return res; +} + +bool ATParser::send(const char *command, ...) +{ + va_list args; + va_start(args, command); + bool res = vsend(command, args); + va_end(args); + return res; +} + +bool ATParser::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::oob(const char *prefix, Callback<void()> cb) +{ + struct oob *oob = new struct oob; + oob->len = strlen(prefix); + oob->prefix = prefix; + oob->cb = cb; + oob->next = _oobs; + _oobs = oob; +} + +void ATParser::abort() +{ + _aborted = true; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362/ATParser/ATParser.h Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,264 @@ +/* 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. + * + * @section DESCRIPTION + * + * Parser for the AT command syntax + * + */ +#ifndef AT_PARSER_H +#define AT_PARSER_H + +#include "mbed.h" +#include <cstdarg> +#include <vector> +#include "BufferedSpi.h" +#include "Callback.h" + + +/** +* Parser class for parsing AT commands +* +* Here are some examples: +* @code +* ATParser at = ATParser(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 +{ +private: + // Serial information + BufferedSpi *_serial_spi; + int _buffer_size; + char *_buffer; + Mutex _bufferMutex; + volatile int _timeout; + + // Parsing information + const char *_delimiter; + int _delim_size; + char _in_prev; + bool dbg_on; + volatile bool _aborted; + + struct oob { + unsigned len; + const char *prefix; + mbed::Callback<void()> cb; + oob *next; + }; + oob *_oobs; + +public: + /** + * Constructor + * + * @param serial spi 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(BufferedSpi &serial_spi, const char *delimiter = "\r\n", int buffer_size = 1440, int timeout = 8000, bool debug = false) : + _serial_spi(&serial_spi), + _buffer_size(buffer_size), _in_prev(0), _oobs(NULL) + { + _buffer = new char[buffer_size]; + setTimeout(timeout); + setDelimiter(delimiter); + debugOn(debug); + } + + /** + * Destructor + */ + ~ATParser() + { + while (_oobs) { + struct oob *oob = _oobs; + _oobs = oob->next; + delete oob; + } + delete[] _buffer; + } + + /** + * Allows timeout to be changed between commands + * + * @param timeout timeout of the connection + */ + void setTimeout(int timeout) + { + _timeout = timeout; + _serial_spi->setTimeout(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 a newline + * @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); + + /** + * Receive an AT response + * + * Receives a formatted response using scanf style formatting + * @see scanf + * + * Responses are parsed line at a time. + * Any received 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 + * assuming the header of the command is already in _txbuffer + * + * @param data the array of bytes to write + * @param size_of_data number of bytes in data array + * @param size_in_buff number of bytes already in the internal buff + * @return number of bytes written or -1 on failure + */ + int write(const char *data, int size_of_data, int size_in_buff); + + /** + * 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); + + /** + * 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); + + /** + * Flushes the underlying stream + */ + void flush(); + + /** + * Abort current recv + * + * Can be called from oob handler to interrupt the current + * recv operation. + */ + void abort(); + + /** + * Process out-of-band data + * + * Process out-of-band data in the receive buffer. This function + * returns immediately if there is no data to process. + * + * @return true if oob data processed, false otherwise + */ + bool process_oob(void); + /** + * Get buffer_size + */ + int get_size(void) { + return _buffer_size; + } + +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362/ATParser/BufferedSpi/Buffer/MyBuffer.cpp Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,83 @@ + +/** + * @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> + uint32_t MyBuffer<T>::getNbAvailable() +{ + if ( _wloc >= _rloc) return (_wloc - _rloc); + else return (_size - _rloc + _wloc); +} + +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/ISM43362/ATParser/BufferedSpi/Buffer/MyBuffer.h Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,165 @@ + +/** + * @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(); + uint32_t getNbAvailable(); + + /** 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; + //return 1; +} + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362/ATParser/BufferedSpi/BufferedPrint.c Tue Aug 21 08:34:28 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 BufferedSpiThunk(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 ) { + BufferedSpiThunk(stream, buffer, r); + } + return r; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362/ATParser/BufferedSpi/BufferedSpi.cpp Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,326 @@ +/** + * @file BufferedSpi.cpp + * @brief Software Buffer - Extends mbed SPI functionallity + * @author Armelle Duboc + * @version 1.0 + * @see + * + * Copyright (c) STMicroelectronics 2017 + * + * 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 "BufferedSpi.h" +#include <stdarg.h> +#include "mbed_debug.h" +#include "mbed_error.h" + +// change to true to add few SPI debug lines +#define local_debug false + +extern "C" int BufferedPrintfC(void *stream, int size, const char* format, va_list arg); + +void BufferedSpi::DatareadyRising(void) +{ + if (_cmddata_rdy_rising_event == 1) { + _cmddata_rdy_rising_event=0; + } +} + +int BufferedSpi::wait_cmddata_rdy_high(void) +{ + Timer timer; + timer.start(); + + /* wait for dataready = 1 */ + while(dataready.read() == 0) { + if (timer.read_ms() > _timeout) { + debug_if(local_debug,"ERROR: SPI write timeout\r\n"); + return -1; + } + } + + _cmddata_rdy_rising_event = 1; + + return 0; +} + +int BufferedSpi::wait_cmddata_rdy_rising_event(void) +{ + Timer timer; + timer.start(); + + while (_cmddata_rdy_rising_event == 1) { + if (timer.read_ms() > _timeout) { + _cmddata_rdy_rising_event = 0; + if (dataready.read() == 1) { + debug_if(local_debug,"ERROR: We missed rising event !! (timemout=%d)\r\n", _timeout); + } + debug_if(local_debug,"ERROR: SPI read timeout\r\n"); + return -1; + } + } + + return 0; +} + +BufferedSpi::BufferedSpi(PinName mosi, PinName miso, PinName sclk, PinName _nss, PinName _datareadypin, + uint32_t buf_size, uint32_t tx_multiple, const char* name) + : SPI(mosi, miso, sclk, NC), nss(_nss), _txbuf((uint32_t)(tx_multiple*buf_size)), _rxbuf(buf_size), dataready(_datareadypin) +{ + this->_buf_size = buf_size; + this->_tx_multiple = tx_multiple; + this->_sigio_event = 0; + + _datareadyInt = new InterruptIn(_datareadypin); + _datareadyInt->rise(callback(this, &BufferedSpi::DatareadyRising)); + + _cmddata_rdy_rising_event = 1; + + return; +} + +BufferedSpi::~BufferedSpi(void) +{ + + return; +} + +void BufferedSpi::frequency(int hz) +{ + SPI::frequency(hz); +} + +void BufferedSpi::format(int bits, int mode) +{ + SPI::format(bits, mode); +} + +void BufferedSpi::disable_nss() +{ + nss = 1; + wait_us(15); +} + +void BufferedSpi::enable_nss() +{ + nss = 0; + wait_us(15); +} + +int BufferedSpi::readable(void) +{ + return _rxbuf.available(); // note: look if things are in the buffer +} + +int BufferedSpi::writeable(void) +{ + return 1; // buffer allows overwriting by design, always true +} + +int BufferedSpi::getc(void) +{ + if (_rxbuf.available()) + return _rxbuf; + else return -1; +} + +int BufferedSpi::putc(int c) +{ + _txbuf = (char)c; + + return c; +} + +void BufferedSpi::flush_txbuf(void) +{ + _txbuf.clear(); +} + +int BufferedSpi::puts(const char *s) +{ + if (s != NULL) { + const char* ptr = s; + + while(*(ptr) != 0) { + _txbuf = *(ptr++); + } + _txbuf = '\n'; // done per puts definition + BufferedSpi::txIrq(); // only write to hardware in one place + return (ptr - s) + 1; + } + return 0; +} + +extern "C" size_t BufferedSpiThunk(void *buf_spi, const void *s, size_t length) +{ + BufferedSpi *buffered_spi = (BufferedSpi *)buf_spi; + return buffered_spi->buffwrite(s, length); +} + +int BufferedSpi::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 BufferedSpi::buffwrite(const void *s, size_t length) +{ + /* flush buffer from previous message */ + this->flush_txbuf(); + + if (wait_cmddata_rdy_high() < 0) { + debug_if(local_debug, "BufferedSpi::buffwrite timeout (%d)\r\n", _timeout); + return -1; + } + + this->enable_nss(); + + if (s != NULL && length > 0) { + /* 1st fill _txbuf */ + const char* ptr = (const char*)s; + const char* end = ptr + length; + + while (ptr != end) { + _txbuf = *(ptr++); + } + if (length&1) { /* padding to send the last char */ + _txbuf = '\n'; + length++; + } + + /* 2nd write in SPI */ + BufferedSpi::txIrq(); // only write to hardware in one place + + this->disable_nss(); + return ptr - (const char*)s; + } + this->disable_nss(); + + return 0; +} + +ssize_t BufferedSpi::buffsend(size_t length) +{ + /* wait for dataready = 1 */ + if (wait_cmddata_rdy_high() < 0) { + debug_if(local_debug, "BufferedSpi::buffsend timeout (%d)\r\n", _timeout); + return -1; + } + + this->enable_nss(); + + /* _txbuffer is already filled with data to send */ + /* check if _txbuffer needs padding to send the last char */ + if (length & 1) { + _txbuf = '\n'; + length++; + } + BufferedSpi::txIrq(); // only write to hardware in one place + + this->disable_nss(); + + return length; +} + +ssize_t BufferedSpi::read() +{ + return this->read(0); +} + +ssize_t BufferedSpi::read(uint32_t max) +{ + uint32_t len = 0; + int tmp; + + disable_nss(); + + /* wait for data ready is up */ + if(wait_cmddata_rdy_rising_event() != 0) { + debug_if(local_debug, "BufferedSpi::read timeout (%d)\r\n", _timeout); + return -1; + } + + enable_nss(); + while (dataready.read() == 1 && (len < (_buf_size - 1))) { + tmp = SPI::write(0xAA); // dummy write to receive 2 bytes + + if (!((len == 0) && (tmp == 0x0A0D))) { + /* do not take into account the 2 firts \r \n char in the buffer */ + if ((max == 0) || (len < max)) { + _rxbuf = (char)(tmp & 0x00FF); + _rxbuf = (char)((tmp >>8)& 0xFF); + len += 2; + } + } + } + disable_nss(); + + if (len >= _buf_size) { + debug_if(local_debug, "firmware ERROR ES_WIFI_ERROR_STUFFING_FOREVER\r\n"); + return -1; + } + + debug_if(local_debug, "SPI READ %d BYTES\r\n", len); + + return len; +} + +void BufferedSpi::txIrq(void) +{ /* write everything available in the _txbuffer */ + int value = 0; + int dbg_cnt = 0; + while (_txbuf.available() && (_txbuf.getNbAvailable()>0)) { + value = _txbuf.get(); + if (_txbuf.available() && ((_txbuf.getNbAvailable()%2)!=0)) { + value |= ((_txbuf.get()<<8)&0XFF00); + SPI::write(value); + dbg_cnt++; + } + } + debug_if(local_debug, "SPI Sent %d BYTES\r\n", 2*dbg_cnt); + // disable the TX interrupt when there is nothing left to send + BufferedSpi::attach(NULL, BufferedSpi::TxIrq); + // trigger callback if necessary + if (_cbs[TxIrq]) { + _cbs[TxIrq](); + } + return; +} + +void BufferedSpi::prime(void) +{ + BufferedSpi::txIrq(); // only write to hardware in one place + return; +} + +void BufferedSpi::attach(Callback<void()> func, IrqType type) +{ + _cbs[type] = func; +} + +void BufferedSpi::sigio(Callback<void()> func) { + core_util_critical_section_enter(); + _sigio_cb = func; + if (_sigio_cb) { + if (_sigio_event == 1) { + _sigio_cb(); + _sigio_event = 0; + } + } + core_util_critical_section_exit(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362/ATParser/BufferedSpi/BufferedSpi.h Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,235 @@ + +/** + * @file BufferedSpi.h + * @brief Software Buffer - Extends mbed SPI functionallity + * @author Armelle Duboc + * @version 1.0 + * @see + * + * Copyright (c) STMicroelectronics 2017 + * + * 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 BUFFEREDSPI_H +#define BUFFEREDSPI_H + +#include "mbed.h" +#include "MyBuffer.h" + +/** A spi port (SPI) for communication with wifi device + * + * 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 BufferedSpi + * @brief Software buffers and interrupt driven tx and rx for Serial + */ +class BufferedSpi : public SPI +{ +private: + DigitalOut nss; + MyBuffer <char> _txbuf; + uint32_t _buf_size; + uint32_t _tx_multiple; + volatile int _timeout; + void txIrq(void); + void prime(void); + + InterruptIn* _datareadyInt; + volatile int _cmddata_rdy_rising_event; + void DatareadyRising(void); + int wait_cmddata_rdy_rising_event(void); + int wait_cmddata_rdy_high(void); + + + Callback<void()> _cbs[2]; + + Callback<void()> _sigio_cb; + uint8_t _sigio_event; + +public: + MyBuffer <char> _rxbuf; + DigitalIn dataready; + enum IrqType { + RxIrq = 0, + TxIrq, + + IrqCnt + }; + + /** Create a BufferedSpi Port, connected to the specified transmit and receive pins + * @param SPI mosi pin + * @param SPI miso pin + * @param SPI sclk pin + * @param SPI nss pin + * @param Dataready 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 + */ + BufferedSpi(PinName mosi, PinName miso, PinName sclk, PinName nss, PinName datareadypin, uint32_t buf_size = 1480, uint32_t tx_multiple = 4,const char* name=NULL); + + /** Destroy a BufferedSpi Port + */ + virtual ~BufferedSpi(void); + + /** call to SPI frequency Function + */ + virtual void frequency(int hz); + + /** clear the transmit buffer + */ + virtual void flush_txbuf(void); + + /** call to SPI format function + */ + virtual void format(int bits, int mode); + + virtual void enable_nss(void); + + virtual void disable_nss(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 BufferedSpi Port. + * Should check readable() before calling this. + * @return A byte that came in on the SPI Port + */ + virtual int getc(void); + + /** Write a single byte to the BufferedSpi Port. + * @param c The byte to write to the SPI Port + * @return The byte that was written to the SPI Port Buffer + */ + virtual int putc(int c); + + /** Write a string to the BufferedSpi Port. Must be NULL terminated + * @param s The string to write to the Spi Port + * @return The number of bytes written to the Spi Port Buffer + */ + virtual int puts(const char *s); + + /** Write a formatted string to the BufferedSpi Port. + * @param format The string + format specifiers to write to the Spi Port + * @return The number of bytes written to the Spi Port Buffer + */ + virtual int printf(const char* format, ...); + + /** Write data to the Buffered Spi 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 Spi Port Buffer + */ + virtual ssize_t buffwrite(const void *s, std::size_t length); + + /** Send datas to the Spi port that are already present + * in the internal _txbuffer + * @param length + * @return the number of bytes written on the SPI port + */ + virtual ssize_t buffsend(size_t length); + + /** Read data from the Spi Port to the _rxbuf + * @param max: optional. = max sieze of the input read + * @return The number of bytes read from the SPI port and written to the _rxbuf + */ + virtual ssize_t read(); + virtual ssize_t read(uint32_t max); + + /** + * Allows timeout to be changed between commands + * + * @param timeout timeout of the connection + */ + void setTimeout(int timeout) + { + /* this is a safe guard timeout in case module is stuck + * so take 5 sec margin compared to module timeout, to + * really only detect case where module is stuck */ + _timeout = timeout + 5000; + } + + /** Register a callback once any data is ready for sockets + * @param func Function to call on state change + */ + virtual void sigio(Callback<void()> func); + + /** 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/ISM43362/ISM43362.cpp Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,647 @@ +/* ISM43362 Example +* +* Copyright (c) STMicroelectronics 2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <string.h> +#include "ISM43362.h" +#include "mbed_debug.h" + +// ao activate / de-activate debug +#define ism_debug false + +ISM43362::ISM43362(PinName mosi, PinName miso, PinName sclk, PinName nss, PinName resetpin, PinName datareadypin, PinName wakeup, bool debug) + : _bufferspi(mosi, miso, sclk, nss, datareadypin), _parser(_bufferspi), _resetpin(resetpin), + _packets(0), _packets_end(&_packets) +{ + DigitalOut wakeup_pin(wakeup); + ISM43362::setTimeout((uint32_t)5000); + _bufferspi.format(16, 0); /* 16bits, ploarity low, phase 1Edge, master mode */ + _bufferspi.frequency(20000000); /* up to 20 MHz */ + _active_id = 0xFF; + + reset(); + + _parser.debugOn(debug); +} + +/** + * @brief Parses and returns number from string. + * @param ptr: pointer to string + * @param cnt: pointer to the number of parsed digit + * @retval integer value. + */ +#define CHARISHEXNUM(x) (((x) >= '0' && (x) <= '9') || \ + ((x) >= 'a' && (x) <= 'f') || \ + ((x) >= 'A' && (x) <= 'F')) +#define CHARISNUM(x) ((x) >= '0' && (x) <= '9') +#define CHAR2NUM(x) ((x) - '0') + + +extern "C" int32_t ParseNumber(char* ptr, uint8_t* cnt) +{ + uint8_t minus = 0, i = 0; + int32_t sum = 0; + + if (*ptr == '-') { /* Check for minus character */ + minus = 1; + ptr++; + i++; + } + while (CHARISNUM(*ptr) || (*ptr=='.')) { /* Parse number */ + if (*ptr == '.') { + ptr++; // next char + } else { + sum = 10 * sum + CHAR2NUM(*ptr); + ptr++; + i++; + } + } + + if (cnt != NULL) { /* Save number of characters used for number */ + *cnt = i; + } + if (minus) { /* Minus detected */ + return 0 - sum; + } + return sum; /* Return number */ +} + +const char *ISM43362::get_firmware_version(void) +{ + char tmp_buffer[250]; + char *ptr, *ptr2; + + if(!(_parser.send("I?") && _parser.recv("%s\r\n", tmp_buffer) && check_response())) { + debug_if(ism_debug, "get_firmware_version is FAIL\r\n"); + return 0; + } + + // Get the first version in the string + ptr = strtok((char *)tmp_buffer, ","); + ptr = strtok(NULL, ","); + ptr2 = strtok(NULL, ","); + if (ptr == NULL) { + debug_if(ism_debug, "get_firmware_version decoding is FAIL\r\n"); + return 0; + } + strncpy(_fw_version, ptr , ptr2-ptr); + + debug_if(ism_debug, "get_firmware_version = [%s]\r\n", _fw_version); + + return _fw_version; +} + +bool ISM43362::reset(void) +{ + debug_if(ism_debug,"Reset Module\r\n"); + _resetpin = 0; + wait_ms(10); + _resetpin = 1; + wait_ms(500); + + /* Wait for prompt line */ + if (!_parser.recv("> \r\n")) { + debug_if(ism_debug,"Reset Module failed\r\n"); + return false; + } + + return true; +} + +void ISM43362::print_rx_buff(void) { + char tmp[150] = {0}; + uint16_t i = 0; + while(i < 150) { + int c = _parser.getc(); + if (c < 0) + break; + tmp[i] = c; + debug_if(ism_debug," 0x%2X",c); + i++; + } + debug_if(ism_debug,"Buffer content =====%s=====\r\n",tmp); +} + +/* checks the standard OK response of the WIFI module, shouldbe: + * \r\nDATA\r\nOK\r\n>sp + * or + * \r\nERROR\r\nUSAGE\r\n>sp + * function returns true if OK, false otherwise. In case of error, + * print error content then flush buffer */ +bool ISM43362::check_response(void) +{ + if(!_parser.recv("OK\r\n")) { + print_rx_buff(); + _parser.flush(); + return false; + } + + /* Then we should get "> ", but sometimes it seems it's missing, + * let's make it optional */ + if(!_parser.recv("> \r\n")) { + debug_if(ism_debug, "Missing prompt in WIFI resp\r\n"); + print_rx_buff(); + _parser.flush(); + return false; + } + + /* Inventek module do stuffing / padding of data with 0x15, + * in case buffer containes such */ + while(1) { + int c = _parser.getc(); + if ( c == 0x15) { + continue; + } else { + /* How to put it back if needed ? */ + break; + } + } + + return true; +} + +bool ISM43362::dhcp(bool enabled) +{ + return (_parser.send("C4=%d", enabled ? 1:0) && check_response()); +} + +bool ISM43362::connect(const char *ap, const char *passPhrase) +{ + if (!(_parser.send("C1=%s", ap) && check_response())) { + return false; + } + + if (!(_parser.send("C2=%s", passPhrase) && check_response())) { + return false; + } + /* TODO security level = 3 , is it hardcoded or not ???? */ + if (!(_parser.send("C3=3") && check_response())) { + return false; + } + /* now connect */ + /* connect response contains more data that we don't need now, + * So we only look for OK, the flush the end of it */ + if (!(_parser.send("C0") && check_response())) { + return false; + } + + return true; +} + +bool ISM43362::disconnect(void) +{ + return (_parser.send("CD") && check_response()); +} + +const char *ISM43362::getIPAddress(void) +{ + char tmp_ip_buffer[250]; + char *ptr, *ptr2; + + if(!(_parser.send("C?") + && _parser.recv("%s\r\n", tmp_ip_buffer) && check_response())) { + debug_if(ism_debug,"getIPAddress LINE KO: %s", tmp_ip_buffer); + return 0; + } + + /* Get the IP address in the result */ + /* TODO : check if the begining of the string is always = "eS-WiFi_AP_C47F51011231," */ + ptr = strtok((char *)tmp_ip_buffer, ","); + ptr = strtok(NULL, ","); + ptr = strtok(NULL, ","); + ptr = strtok(NULL, ","); + ptr = strtok(NULL, ","); + ptr = strtok(NULL, ","); + ptr2 = strtok(NULL, ","); + if (ptr == NULL) return 0; + strncpy(_ip_buffer, ptr , ptr2-ptr); + + tmp_ip_buffer[59] = 0; + debug_if(ism_debug,"receivedIPAddress: %s\n", _ip_buffer); + + return _ip_buffer; +} + +const char *ISM43362::getMACAddress(void) +{ + if(!(_parser.send("Z5") && _parser.recv("%s\r\n", _mac_buffer) && check_response())) { + debug_if(ism_debug,"receivedMacAddress LINE KO: %s", _mac_buffer); + return 0; + } + + debug_if(ism_debug,"receivedMacAddress:%s, size=%d\r\n", _mac_buffer, sizeof(_mac_buffer)); + + return _mac_buffer; +} + +const char *ISM43362::getGateway() +{ + char tmp[250]; + + if(!(_parser.send("C?") && _parser.recv("%s\r\n", tmp) && check_response())) { + debug_if(ism_debug,"getGateway LINE KO: %s\r\n", tmp); + return 0; + } + + /* Extract the Gateway in the received buffer */ + char *ptr; + ptr = strtok(tmp,","); + for (int i = 0; i< 7;i++) { + if (ptr == NULL) break; + ptr = strtok(NULL,","); + } + + strncpy(_gateway_buffer, ptr, sizeof(_gateway_buffer)); + + debug_if(ism_debug,"getGateway: %s\r\n", _gateway_buffer); + + return _gateway_buffer; +} + +const char *ISM43362::getNetmask() +{ + char tmp[250]; + + if(!(_parser.send("C?") && _parser.recv("%s\r\n", tmp) && check_response())) { + debug_if(ism_debug,"getNetmask LINE KO: %s", tmp); + return 0; + } + + /* Extract Netmask in the received buffer */ + char *ptr; + ptr = strtok(tmp,","); + for (int i = 0; i< 6;i++) { + if (ptr == NULL) break; + ptr = strtok(NULL,","); + } + + strncpy(_netmask_buffer, ptr, sizeof(_netmask_buffer)); + + debug_if(ism_debug,"getNetmask: %s\r\n", _netmask_buffer); + + return _netmask_buffer; +} + +int8_t ISM43362::getRSSI() +{ + int8_t rssi; + char tmp[25]; + + if(!(_parser.send("CR") && _parser.recv("%s\r\n", tmp) && check_response())) { + debug_if(ism_debug,"getRSSI LINE KO: %s\r\n", tmp); + return 0; + } + + rssi = ParseNumber(tmp, NULL); + + debug_if(ism_debug,"getRSSI: %d\r\n", rssi); + + return rssi; +} +/** + * @brief Parses Security type. + * @param ptr: pointer to string + * @retval Encryption type. + */ +extern "C" nsapi_security_t ParseSecurity(char* ptr) +{ + if(strstr(ptr,"Open")) return NSAPI_SECURITY_NONE; + else if(strstr(ptr,"WEP")) return NSAPI_SECURITY_WEP; + else if(strstr(ptr,"WPA")) return NSAPI_SECURITY_WPA; + else if(strstr(ptr,"WPA2 AES")) return NSAPI_SECURITY_WPA2; + else if(strstr(ptr,"WPA WPA2")) return NSAPI_SECURITY_WPA_WPA2; + else if(strstr(ptr,"WPA2 TKIP")) return NSAPI_SECURITY_UNKNOWN; // ?? no match in mbed ? + else return NSAPI_SECURITY_UNKNOWN; +} + +/** + * @brief Convert char in Hex format to integer. + * @param a: character to convert + * @retval integer value. + */ +extern "C" uint8_t Hex2Num(char a) +{ + if (a >= '0' && a <= '9') { /* Char is num */ + return a - '0'; + } else if (a >= 'a' && a <= 'f') { /* Char is lowercase character A - Z (hex) */ + return (a - 'a') + 10; + } else if (a >= 'A' && a <= 'F') { /* Char is uppercase character A - Z (hex) */ + return (a - 'A') + 10; + } + + return 0; +} + +/** + * @brief Extract a hex number from a string. + * @param ptr: pointer to string + * @param cnt: pointer to the number of parsed digit + * @retval Hex value. + */ +extern "C" uint32_t ParseHexNumber(char* ptr, uint8_t* cnt) +{ + uint32_t sum = 0; + uint8_t i = 0; + + while (CHARISHEXNUM(*ptr)) { /* Parse number */ + sum <<= 4; + sum += Hex2Num(*ptr); + ptr++; + i++; + } + + if (cnt != NULL) { /* Save number of characters used for number */ + *cnt = i; + } + return sum; /* Return number */ +} + +bool ISM43362::isConnected(void) +{ + return getIPAddress() != 0; +} + +int ISM43362::scan(WiFiAccessPoint *res, unsigned limit) +{ + unsigned cnt = 0, num=0; + char *ptr; + char tmp[256]; + + if(!(_parser.send("F0"))) { + debug_if(ism_debug,"scan error\r\n"); + return 0; + } + + /* Parse the received buffer and fill AP buffer */ + while (_parser.recv("#%s\n", tmp)) { + if (limit != 0 && cnt >= limit) { + /* reached end */ + break; + } + nsapi_wifi_ap_t ap = {0}; + debug_if(ism_debug,"received:%s", tmp); + ptr = strtok(tmp, ","); + num = 0; + while (ptr != NULL) { + switch (num++) { + case 0: /* Ignore index */ + case 4: /* Ignore Max Rate */ + case 5: /* Ignore Network Type */ + case 7: /* Ignore Radio Band */ + break; + case 1: + ptr[strlen(ptr) - 1] = 0; + strncpy((char *)ap.ssid, ptr+ 1, 32); + break; + case 2: + for (int i=0; i<6; i++) { + ap.bssid[i] = ParseHexNumber(ptr + (i*3), NULL); + } + break; + case 3: + ap.rssi = ParseNumber(ptr, NULL); + break; + case 6: + ap.security = ParseSecurity(ptr); + break; + case 8: + ap.channel = ParseNumber(ptr, NULL); + num = 1; + break; + default: + break; + } + ptr = strtok(NULL, ","); + } + res[cnt] = WiFiAccessPoint(ap); + cnt++; + } + + /* We may stop before having read all the APs list, so flush the rest of + * it as well as OK commands */ + _parser.flush(); + + debug_if(ism_debug, "End of Scan: cnt=%d\n", cnt); + + return cnt; + +} + +bool ISM43362::open(const char *type, int id, const char* addr, int port) +{ /* TODO : This is the implementation for the client socket, need to check if need to create openserver too */ + //IDs only 0-3 + if((id < 0) ||(id > 3)) { + debug_if(ism_debug, "open: wrong id\n"); + return false; + } + /* Set communication socket */ + debug_if(ism_debug, "OPEN socket\n"); + _active_id = id; + if (!(_parser.send("P0=%d", id) && check_response())) { + return false; + } + /* Set protocol */ + if (!(_parser.send("P1=%s", type) && check_response())) { + return false; + } + /* Set address */ + if (!(_parser.send("P3=%s", addr) && check_response())) { + return false; + } + if (!(_parser.send("P4=%d", port) && check_response())) { + return false; + } + /* Start client */ + if (!(_parser.send("P6=1") && check_response())) { + return false; + } + + /* request as much data as possible - i.e. module max size */ + if (!(_parser.send("R1=%d", ES_WIFI_MAX_RX_PACKET_SIZE)&& check_response())) { + return -1; + } + + return true; +} + +bool ISM43362::dns_lookup(const char* name, char* ip) +{ + char tmp[30]; + + if (!(_parser.send("D0=%s", name) && _parser.recv("%s\r\n", tmp) + && check_response())) { + debug_if(ism_debug,"dns_lookup LINE KO: %s", tmp); + return 0; + } + + strncpy(ip, tmp, sizeof(tmp)); + + debug_if(ism_debug, "ip of DNSlookup: %s\n", ip); + return 1; +} + +bool ISM43362::send(int id, const void *data, uint32_t amount) +{ + // The Size limit has to be checked on caller side. + if (amount > ES_WIFI_MAX_RX_PACKET_SIZE) { + return false; + } + + /* Activate the socket id in the wifi module */ + if ((id < 0) ||(id > 3)) { + return false; + } + debug_if(ism_debug, "SEND socket amount %d\n", amount); + if (_active_id != id) { + _active_id = id; + if (!(_parser.send("P0=%d",id) && check_response())) { + return false; + } + } + + /* Change the write timeout */ + if (!(_parser.send("S2=%d", _timeout) && check_response())) { + return false; + } + /* set Write Transport Packet Size */ + int i = _parser.printf("S3=%d\r", (int)amount); + if (i < 0) { + return false; + } + i = _parser.write((const char *)data, amount, i); + if (i < 0) { + return false; + } + + if (!check_response()) { + return false; + } + + return true; +} + +int ISM43362::check_recv_status(int id, void *data) +{ + int read_amount; + static int keep_to = 0; + + debug_if(ism_debug, "ISM43362 req check_recv_status\r\n"); + /* Activate the socket id in the wifi module */ + if ((id < 0) ||(id > 3)) { + return -1; + } + + if (_active_id != id) { + _active_id = id; + if (!(_parser.send("P0=%d",id) && check_response())) { + return -1; + } + } + + + /* MBED wifi driver is meant to be non-blocking, but we need anyway to + * wait for some data on the RECV side to avoid overflow on TX side, the + * tiemout is defined in higher layer */ + if (keep_to != _timeout) { + if (!(_parser.send("R2=%d", _timeout) && check_response())) { + return -1; + } + keep_to = _timeout; + } + + if (!_parser.send("R0")) { + return -1; + } + read_amount = _parser.read((char *)data); + + if(read_amount < 0) { + debug_if(ism_debug, "ERROR in data RECV, timeout?\r\n"); + return -1; /* nothing to read */ + } + + /* If there are spurious 0x15 at the end of the data, this is an error + * we hall can get rid off of them :-( + * This should not happen, but let's try to clean-up anyway + */ + char *cleanup = (char *) data; + while ((read_amount > 0) && (cleanup[read_amount-1] == 0x15)) { + debug_if(ism_debug, "ISM4336 spurious 0X15 trashed\r\n"); + /* Remove the trailling char then search again */ + read_amount--; + } + + if ((read_amount >= 6) && (strncmp("OK\r\n> ", (char *)data, 6) == 0)) { + debug_if(ism_debug, "ISM4336 recv 2 nothing to read=%d\r\n", read_amount); + return 0; /* nothing to read */ + } else if ((read_amount >= 8) && (strncmp((char *)((uint32_t) data + read_amount - 8), "\r\nOK\r\n> ", 8)) == 0) { + /* bypass ""\r\nOK\r\n> " if present at the end of the chain */ + read_amount -= 8; + } else { + debug_if(ism_debug, "ERROR in data RECV?, flushing %d bytes\r\n", read_amount); + int i = 0; + for (i = 0; i < read_amount; i++) { + debug_if(ism_debug, "%2X ", cleanup[i]); + } + cleanup[i] = 0; + debug_if(ism_debug, "\r\n%s\r\n", cleanup); + return -1; /* nothing to read */ + } + + debug_if(ism_debug, "ISM43362 read_amount=%d\r\n", read_amount); + return read_amount; +} + +bool ISM43362::close(int id) +{ + if ((id <0) || (id > 3)) { + debug_if(ism_debug,"Wrong socket number\n"); + return false; + } + /* Set connection on this socket */ + debug_if(ism_debug,"CLOSE socket id=%d\n", id); + _active_id = id; + if (!(_parser.send("P0=%d", id) && check_response())) { + return false; + } + /* close this socket */ + if (!(_parser.send("P6=0") && check_response())) { + return false; + } + return true; +} + +void ISM43362::setTimeout(uint32_t timeout_ms) +{ + _timeout = timeout_ms; + _parser.setTimeout(timeout_ms); +} + +bool ISM43362::readable() +{ + /* not applicable with SPI api */ + return true; +} + +bool ISM43362::writeable() +{ + /* not applicable with SPI api */ + return true; +} + +void ISM43362::attach(Callback<void()> func) +{ + /* not applicable with SPI api */ +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362/ISM43362.h Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,246 @@ +/* ISM43362Interface Example + * Copyright (c) STMicroelectronics 2017 + * + * 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 ISM43362_H +#define ISM43362_H +#include "ATParser.h" + +#define ES_WIFI_MAX_SSID_NAME_SIZE 32 +#define ES_WIFI_MAX_PSWD_NAME_SIZE 32 +#define ES_WIFI_PRODUCT_ID_SIZE 32 +#define ES_WIFI_PRODUCT_NAME_SIZE 32 +#define ES_WIFI_FW_REV_SIZE 16 +#define ES_WIFI_API_REV_SIZE 16 +#define ES_WIFI_STACK_REV_SIZE 16 +#define ES_WIFI_RTOS_REV_SIZE 16 + +// The input range for AT Command 'R1' is 0 to 1200 bytes +// R1 Set Read Transport Packet Size (bytes) +#define ES_WIFI_MAX_RX_PACKET_SIZE 1200 +// Module maxume DATA payload for Tx packet is 1460 +#define ES_WIFI_MAX_TX_PACKET_SIZE 1460 + +/** ISM43362Interface class. + This is an interface to a ISM43362 radio. + */ +class ISM43362 +{ +public: + ISM43362(PinName mosi, PinName miso, PinName clk, PinName nss, PinName resetpin, PinName datareadypin, PinName wakeup, bool debug=false); + + /** + * Check firmware version of ISM43362 + * + * @return null-terminated fw version or null if no version is read + */ + const char *get_firmware_version(void); + + /** + * Reset ISM43362 + * + * @return true only if ISM43362 resets successfully + */ + bool reset(void); + + /** + * Enable/Disable DHCP + * + * @param enabled DHCP enabled when true + * @return true only if ISM43362 enables/disables DHCP successfully + */ + bool dhcp(bool enabled); + + /** + * Connect ISM43362 to AP + * + * @param ap the name of the AP + * @param passPhrase the password of AP + * @return true only if ISM43362 is connected successfully + */ + bool connect(const char *ap, const char *passPhrase); + + /** + * Disconnect ISM43362 from AP + * + * @return true only if ISM43362 is disconnected successfully + */ + bool disconnect(void); + + /** + * Get the IP address of ISM43362 + * + * @return null-teriminated IP address or null if no IP address is assigned + */ + const char *getIPAddress(void); + + /** + * Get the MAC address of ISM43362 + * + * @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(); + + /** + * Check if ISM43362 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); + + /**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 + * @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 + * @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); + + /** + * Checks if data is available + */ + bool readable(); + + /** + * Checks if data can be written + */ + bool writeable(); + + /** + * 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); + + /** + * Check is datas are available to read for a socket + * @param id socket id + * @param data placeholder for returned information + * @param amount size to read for the check + * @return amount of read value, or -1 for errors + */ + int check_recv_status(int id, void *data); + + /** + * 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)); + } + +private: + BufferedSpi _bufferspi; + ATParser _parser; + DigitalOut _resetpin; + volatile int _timeout; + volatile int _active_id; + void print_rx_buff(void); + bool check_response(void); + struct packet { + struct packet *next; + int id; + uint32_t len; + // data follows + } *_packets, **_packets_end; + void _packet_handler(); + + char _ip_buffer[16]; + char _gateway_buffer[16]; + char _netmask_buffer[16]; + char _mac_buffer[18]; + char _fw_version[16]; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362Interface.cpp Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,505 @@ +/* ISM43362 implementation of NetworkInterfaceAPI + * Copyright (c) STMicroelectronics 2017 + * + * 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 "ISM43362Interface.h" +#include "mbed_debug.h" + +// ao activate / de-activate debug +#define ism_debug false + +// Various timeouts for different ISM43362 operations +#define ISM43362_CONNECT_TIMEOUT 15000 /* milliseconds */ +#define ISM43362_SEND_TIMEOUT 1000 /* milliseconds */ +#define ISM43362_RECV_TIMEOUT 100 /* milliseconds */ +#define ISM43362_MISC_TIMEOUT 100 /* milliseconds */ + +// Tested firmware versions +// Example of versions string returned by the module: +// "ISM43362-M3G-L44-SPI,C3.5.2.3.BETA9,v3.5.2,v1.4.0.rc1,v8.2.1,120000000,Inventek eS-WiFi" +// "ISM43362-M3G-L44-SPI,C3.5.2.2,v3.5.2,v1.4.0.rc1,v8.2.1,120000000,Inventek eS-WiFi" +// Only the first version is checked ! +const char supported_fw_versions[2][15] = {"C3.5.2.3.BETA9", "C3.5.2.2"}; + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +// ISM43362Interface implementation +ISM43362Interface::ISM43362Interface(PinName mosi, PinName miso, PinName sclk, PinName nss, PinName reset, PinName datareadypin, PinName wakeup, bool debug) + : _ism(mosi, miso, sclk, nss, reset, datareadypin, wakeup, debug) +{ + memset(_ids, 0, sizeof(_ids)); + memset(_socket_obj, 0, sizeof(_socket_obj)); + memset(_cbs, 0, sizeof(_cbs)); + thread_read_socket.start(callback(this, &ISM43362Interface::socket_check_read)); +} + +int ISM43362Interface::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 ISM43362Interface::connect() +{ + _mutex.lock(); + const char* read_version; + + _ism.setTimeout(ISM43362_MISC_TIMEOUT); + + // Check all supported firmware versions + read_version = _ism.get_firmware_version(); + + if (!read_version) { + debug_if(ism_debug, "ISM43362: ERROR cannot read firmware version\r\n"); + return NSAPI_ERROR_DEVICE_ERROR; + } + debug_if(ism_debug, "ISM43362: read_version = [%s]\r\n", read_version); + + if ((strcmp(read_version, supported_fw_versions[0]) == 0) || (strcmp(read_version, supported_fw_versions[1]) == 0)) { + debug_if(ism_debug, "ISM43362: firmware version is OK\r\n"); + } else { + debug_if(ism_debug, "ISM43362: WARNING this firmware version has not been tested !\r\n"); + } + + if (!_ism.dhcp(true)) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + _ism.setTimeout(ISM43362_CONNECT_TIMEOUT); + + if (!_ism.connect(ap_ssid, ap_pass)) { + return NSAPI_ERROR_NO_CONNECTION; + } + + _ism.setTimeout(ISM43362_MISC_TIMEOUT); + if (!_ism.getIPAddress()) { + return NSAPI_ERROR_DHCP_FAILURE; + } + + _ism.setTimeout(ISM43362_MISC_TIMEOUT); + _mutex.unlock(); + + return NSAPI_ERROR_OK; +} + +nsapi_error_t ISM43362Interface::gethostbyname(const char *name, SocketAddress *address, nsapi_version_t version) +{ + _mutex.lock(); + if (address->set_ip_address(name)) { + if (version != NSAPI_UNSPEC && address->get_ip_version() != version) { + return NSAPI_ERROR_DNS_FAILURE; + } + + return NSAPI_ERROR_OK; + } + + char *ipbuff = new char[NSAPI_IP_SIZE]; + int ret = 0; + _ism.setTimeout(ISM43362_CONNECT_TIMEOUT); + if(!_ism.dns_lookup(name, ipbuff)) { + ret = NSAPI_ERROR_DEVICE_ERROR; + } else { + address->set_ip_address(ipbuff); + } + _mutex.unlock(); + + delete[] ipbuff; + + return ret; +} + +int ISM43362Interface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security) +{ + _mutex.lock(); + + 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; + _mutex.unlock(); + + return 0; +} + +int ISM43362Interface::set_channel(uint8_t channel) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ISM43362Interface::disconnect() +{ + _mutex.lock(); + + _ism.setTimeout(ISM43362_MISC_TIMEOUT); + + if (!_ism.disconnect()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + _mutex.unlock(); + + return NSAPI_ERROR_OK; +} + +const char *ISM43362Interface::get_ip_address() +{ + _mutex.lock(); + const char *ret = _ism.getIPAddress(); + _mutex.unlock(); + return ret; +} + +const char *ISM43362Interface::get_mac_address() +{ + _mutex.lock(); + const char *ret = _ism.getMACAddress(); + _mutex.unlock(); + return ret; +} + +const char *ISM43362Interface::get_gateway() +{ + _mutex.lock(); + const char *ret = _ism.getGateway(); + _mutex.unlock(); + return ret; +} + +const char *ISM43362Interface::get_netmask() +{ + _mutex.lock(); + const char *ret = _ism.getNetmask(); + _mutex.unlock(); + return ret; +} + +int8_t ISM43362Interface::get_rssi() +{ + _mutex.lock(); + int8_t ret = _ism.getRSSI(); + _mutex.unlock(); + return ret; +} + +int ISM43362Interface::scan(WiFiAccessPoint *res, unsigned count) +{ + _mutex.lock(); + _ism.setTimeout(ISM43362_CONNECT_TIMEOUT); + int ret = _ism.scan(res, count); + _mutex.unlock(); + return ret; +} + +struct ISM43362_socket { + int id; + nsapi_protocol_t proto; + volatile bool connected; + SocketAddress addr; + char read_data[1400]; + volatile uint32_t read_data_size; +}; + +int ISM43362Interface::socket_open(void **handle, nsapi_protocol_t proto) +{ + // Look for an unused socket + int id = -1; + for (int i = 0; i < ISM43362_SOCKET_COUNT; i++) { + if (!_ids[i]) { + id = i; + _ids[i] = true; + break; + } + } + + if (id == -1) { + return NSAPI_ERROR_NO_SOCKET; + } + _mutex.lock(); + struct ISM43362_socket *socket = new struct ISM43362_socket; + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + socket->id = id; + debug_if(ism_debug, "socket_open, id=%d", socket->id); + memset(socket->read_data, 0, sizeof(socket->read_data)); + socket->addr = 0; + socket->read_data_size = 0; + socket->proto = proto; + socket->connected = false; + *handle = socket; + _mutex.unlock(); + + return 0; +} + +int ISM43362Interface::socket_close(void *handle) +{ + _mutex.lock(); + struct ISM43362_socket *socket = (struct ISM43362_socket *)handle; + debug_if(ism_debug, "socket_close, id=%d", socket->id); + int err = 0; + _ism.setTimeout(ISM43362_MISC_TIMEOUT); + + if (!_ism.close(socket->id)) { + err = NSAPI_ERROR_DEVICE_ERROR; + } + + socket->connected = false; + _ids[socket->id] = false; + _socket_obj[socket->id] = 0; + _mutex.unlock(); + delete socket; + return err; +} + +int ISM43362Interface::socket_bind(void *handle, const SocketAddress &address) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ISM43362Interface::socket_listen(void *handle, int backlog) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ISM43362Interface::socket_connect(void *handle, const SocketAddress &addr) +{ + _mutex.lock(); + int ret = socket_connect_nolock(handle, addr); + _mutex.unlock(); + return ret; +} + +int ISM43362Interface::socket_connect_nolock(void *handle, const SocketAddress &addr) +{ + struct ISM43362_socket *socket = (struct ISM43362_socket *)handle; + _ism.setTimeout(ISM43362_CONNECT_TIMEOUT); + const char *proto = (socket->proto == NSAPI_UDP) ? "1" : "0"; + if (!_ism.open(proto, socket->id, addr.get_ip_address(), addr.get_port())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + _ids[socket->id] = true; + _socket_obj[socket->id] = (uint32_t)socket; + socket->connected = true; + return 0; + +} + + + +void ISM43362Interface::socket_check_read() +{ + while (1) { + for (int i = 0; i < ISM43362_SOCKET_COUNT; i++) { + _mutex.lock(); + if (_socket_obj[i] != 0) { + struct ISM43362_socket *socket = (struct ISM43362_socket *)_socket_obj[i]; + /* Check if there is something to read for this socket. But if it */ + /* has already been read : don't read again */ + if ((socket->connected) && (socket->read_data_size == 0) && _cbs[socket->id].callback) { + _ism.setTimeout(1); + /* if no callback is set, no need to read ?*/ + int read_amount = _ism.check_recv_status(socket->id, socket->read_data); + if (read_amount > 0) { + socket->read_data_size = read_amount; + } else if (read_amount < 0) { + /* Mark donw connection has been lost or closed */ + socket->connected = false; + } + if (read_amount != 0) { + /* There is something to read in this socket*/ + if (_cbs[socket->id].callback) { + _cbs[socket->id].callback(_cbs[socket->id].data); + } + } + } + } + _mutex.unlock(); + } + wait_ms(50); + } +} + +int ISM43362Interface::socket_accept(void *server, void **socket, SocketAddress *addr) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int ISM43362Interface::socket_send(void *handle, const void *data, unsigned size) +{ + _mutex.lock(); + int ret = socket_send_nolock(handle, data, size); + _mutex.unlock(); + return ret; +} + +/* CAREFULL LOCK must be taken before callling this function */ +int ISM43362Interface::socket_send_nolock(void *handle, const void *data, unsigned size) +{ + struct ISM43362_socket *socket = (struct ISM43362_socket *)handle; + _ism.setTimeout(ISM43362_SEND_TIMEOUT); + + if (size > ES_WIFI_MAX_TX_PACKET_SIZE) { + size = ES_WIFI_MAX_TX_PACKET_SIZE; + } + + if (!_ism.send(socket->id, data, size)) { + debug_if(ism_debug, "socket_send ERROR\r\n"); + return NSAPI_ERROR_DEVICE_ERROR; // or WOULD_BLOCK ? + } + + return size; +} + +int ISM43362Interface::socket_recv(void *handle, void *data, unsigned size) +{ + _mutex.lock(); + unsigned recv = 0; + struct ISM43362_socket *socket = (struct ISM43362_socket *)handle; + char *ptr = (char *)data; + + debug_if(ism_debug, "[socket_recv] req=%d\r\n", size); + + if (!socket->connected) { + _mutex.unlock(); + return NSAPI_ERROR_CONNECTION_LOST; + } + + _ism.setTimeout(ISM43362_RECV_TIMEOUT); + + if (socket->read_data_size == 0) { + /* if no callback is set, no need to read ?*/ + int read_amount = _ism.check_recv_status(socket->id, socket->read_data); + if (read_amount > 0) { + socket->read_data_size = read_amount; + } else if (read_amount < 0) { + socket->connected = false; + _mutex.unlock(); + return NSAPI_ERROR_CONNECTION_LOST; + } + } + + if (socket->read_data_size != 0) { + debug_if(ism_debug, "read_data_size=%d\r\n", socket->read_data_size); + uint32_t i=0; + while ((i < socket->read_data_size) && (i < size)) { + *ptr++ = socket->read_data[i]; + i++; + } + + debug_if(ism_debug, "Copied i bytes=%d, vs %d requestd\r\n", i, size); + recv += i; + + if (i >= socket->read_data_size) { + /* All the storeed data has been read, reset buffer */ + memset(socket->read_data, 0, sizeof(socket->read_data)); + socket->read_data_size = 0; + debug_if(ism_debug, "Socket_recv buffer reset\r\n"); + } else { + /* In case there is remaining data in buffer, update socket content + * For now by shift copy of all data (not very efficient to be + * revised */ + while (i < socket->read_data_size) { + socket->read_data[i - size] = socket->read_data[i]; + i++; + } + + socket->read_data_size -= size; + } + } else { + debug_if(ism_debug, "Nothing in buffer\r\n"); + } + + debug_if(ism_debug, "[socket_recv]read_datasize=%d, recv=%d\r\n", socket->read_data_size, recv); + _mutex.unlock(); + + if (recv > 0) { + return recv; + } else { + debug_if(ism_debug, "sock_recv returns WOULD BLOCK\r\n"); + return NSAPI_ERROR_WOULD_BLOCK; + } +} + +int ISM43362Interface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + _mutex.lock(); + struct ISM43362_socket *socket = (struct ISM43362_socket *)handle; + + if (socket->connected && socket->addr != addr) { + _ism.setTimeout(ISM43362_MISC_TIMEOUT); + if (!_ism.close(socket->id)) { + debug_if(ism_debug, "socket_send ERROR\r\n"); + _mutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } + socket->connected = false; + _ids[socket->id] = false; + _socket_obj[socket->id] = 0; + } + + if (!socket->connected) { + int err = socket_connect_nolock(socket, addr); + if (err < 0) { + _mutex.unlock(); + return err; + } + socket->addr = addr; + } + + int ret = socket_send_nolock(socket, data, size); + + _mutex.unlock(); + + return ret; +} + +int ISM43362Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + int ret = socket_recv(handle, data, size); + _mutex.lock(); + if ((ret >= 0) && addr) { + struct ISM43362_socket *socket = (struct ISM43362_socket *)handle; + *addr = socket->addr; + } + _mutex.unlock(); + return ret; +} + +void ISM43362Interface::socket_attach(void *handle, void (*cb)(void *), void *data) +{ + _mutex.lock(); + struct ISM43362_socket *socket = (struct ISM43362_socket *)handle; + _cbs[socket->id].callback = cb; + _cbs[socket->id].data = data; + _mutex.unlock(); +} + +void ISM43362Interface::event() { + for (int i = 0; i < ISM43362_SOCKET_COUNT; i++) { + if (_cbs[i].callback) { + _cbs[i].callback(_cbs[i].data); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISM43362Interface.h Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,300 @@ +/* ISM43362 implementation of NetworkInterfaceAPI + * Copyright (c) STMicroelectronics 2017 + * + * 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 ISM43362_INTERFACE_H +#define ISM43362_INTERFACE_H + +#include "mbed.h" +#include "ISM43362.h" + + +#define ISM43362_SOCKET_COUNT 4 + +/** ISM43362Interface class + * Implementation of the NetworkStack for the ISM43362 + */ +class ISM43362Interface : public NetworkStack, public WiFiInterface +{ +public: + /** ISM43362Interface lifetime + * @param mosi MOSI pin + * @param miso MISO pin + * @param clk CLOCK pin + * @param nss NSS pin + * @param debug Enable debugging + */ + ISM43362Interface(PinName mosi, PinName miso, PinName clk, PinName nss, PinName reset, PinName dataready, PinName wakeup, 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); + + /** 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. + * + * + * @param host Hostname to resolve + * @param address Destination for the host SocketAddress + * @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 + */ + virtual nsapi_error_t gethostbyname(const char *name, SocketAddress *address, nsapi_version_t version = NSAPI_UNSPEC); + + /** 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; + +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: + ISM43362 _ism; + bool _ids[ISM43362_SOCKET_COUNT]; + uint32_t _socket_obj[ISM43362_SOCKET_COUNT]; // store addresses of socket handles + Mutex _mutex; + Thread thread_read_socket; + 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 */ + + void event(); + struct { + void (*callback)(void *); + void *data; + } _cbs[ISM43362_SOCKET_COUNT]; + + /** Function called by the socket read thread to check if data is available on the wifi module + * + */ + virtual void socket_check_read(); + int socket_send_nolock(void *handle, const void *data, unsigned size); + int socket_connect_nolock(void *handle, const SocketAddress &addr); + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Tue Aug 21 08:34:28 2018 +0000 @@ -0,0 +1,50 @@ +# ISM43362 WiFi driver for mbed-os +The mbed OS driver for the ISM43362 WiFi module + +## Currently supported platforms +ISM43362 module is soldered on the following platforms from STMicroelectronics +* DISCO_L475VG_IOT01A +* DISCO_F413ZH + +## Configuration +Add the following lines to the target_overrides section of mbed_app.json of your application +``` +"DISCO_L475VG_IOT1A": { + "wifi-spi_miso": "PC_11", + "wifi-spi_mosi": "PC_12", + "wifi-spi_sclk": "PC_10", + "wifi-spi_nss": "PE_0", + "wifi-reset": "PE_8", + "wifi-dataready": "PE_1", + "wifi-wakeup": "PB_13" +}, +"DISCO_F413ZH": { + "wifi-spi_miso": "PB_4", + "wifi-spi_mosi": "PB_5", + "wifi-spi_sclk": "PB_12", + "wifi-spi_nss": "PG_11", + "wifi-reset": "PH_1", + "wifi-dataready": "PG_12", + "wifi-wakeup": "PB_15" +} +``` + +- MBED_CFG_ISM43362_SSID - SSID of the wifi access point to connect to +- MBED_CFG_ISM43362_PASS - Passphrase of the wifi access point to connect to +- MBED_CONF_APP_WIFI_SPI_MISO - spi-miso pin for the ism43362 connection +- MBED_CONF_APP_WIFI_SPI_MOSI - spi-mosi pin for the ism43362 connection +- MBED_CONF_APP_WIFI_SPI_SCLK - spi-clock pin for the ism43362 connection +- MBED_CONF_APP_WIFI_SPI_NSS - spi-nss pin for the ism43362 connection +- MBED_CONF_APP_WIFI_RESET - Reset pin for the ism43362 wifi module +- MBED_CONF_APP_WIFI_DATAREADY - Data Ready pin for the ism43362 wifi module +- MBED_CONF_APP_WIFI_WAKEUP - Wakeup pin for the ism43362 wifi module + + +## Firmware version +This driver supports ISM43362-M3G-L44-SPI,C3.5.2.3.BETA9 and C3.5.2.2 firmware version + +## wifi module FW update +For more information about the wifi FW version, refer to the detailed procedure in +http://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32cube-embedded-software-expansion/x-cube-azure.html + +```