diff --git a/src/drivers/ninaw10/include/nina.h b/src/drivers/ninaw10/include/nina.h new file mode 100644 index 000000000..df406135e --- /dev/null +++ b/src/drivers/ninaw10/include/nina.h @@ -0,0 +1,92 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * NINA-W10 driver. + */ +#ifndef __NINA_H__ +#define __NINA_H__ +#define NINA_FW_VER_LEN (6) +#define NINA_IPV4_ADDR_LEN (4) +#define NINA_MAC_ADDR_LEN (6) +#define NINA_MAX_SSID_LEN (32) +#define NINA_MAX_WEP_LEN (13) +#define NINA_MAX_WPA_LEN (63) +#define NINA_MAX_NETWORK_LIST (10) +#define NINA_MAX_SOCKET (10) + +typedef enum { + NINA_SEC_INVALID = 0, + NINA_SEC_OPEN, + NINA_SEC_WPA_PSK, + NINA_SEC_WEP +} nina_security_t; + +typedef enum { + NINA_SOCKET_TYPE_TCP = 0, + NINA_SOCKET_TYPE_UDP, + NINA_SOCKET_TYPE_TLS, + NINA_SOCKET_TYPE_UDP_MULTICAST, + NINA_SOCKET_TYPE_TLS_BEARSSL +} nina_socket_type_t; + +typedef enum { + NINA_ERROR_IO = -1, + NINA_ERROR_TIMEOUT = -2, +} nina_error_t; + +typedef struct { + uint8_t ip_addr[NINA_IPV4_ADDR_LEN]; + uint8_t subnet_addr[NINA_IPV4_ADDR_LEN]; + uint8_t gateway_addr[NINA_IPV4_ADDR_LEN]; + uint8_t dns_addr[NINA_IPV4_ADDR_LEN]; +} nina_ifconfig_t; + +typedef struct { + int8_t rssi; + uint8_t security; + uint8_t channel; + uint8_t bssid[NINA_MAC_ADDR_LEN]; + char ssid[NINA_MAX_SSID_LEN]; +} nina_scan_result_t; + +typedef struct { + int8_t rssi; + uint8_t security; + char ssid[NINA_MAX_SSID_LEN]; + uint8_t bssid[NINA_MAC_ADDR_LEN]; +} nina_netinfo_t; + +typedef int (*nina_scan_callback_t) (nina_scan_result_t *, void *); + +int nina_init(); +int nina_deinit(); +int nina_connect(const char *ssid, uint8_t security, const char *key, uint16_t channel); +int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t channel); +int nina_disconnect(); +int nina_isconnected(); +int nina_connected_sta(uint32_t *sta_ip); +int nina_wait_for_sta(uint32_t *sta_ip, uint32_t timeout); +int nina_ifconfig(nina_ifconfig_t *ifconfig, bool set); +int nina_netinfo(nina_netinfo_t *netinfo); +int nina_scan(nina_scan_callback_t scan_callback, void *arg, uint32_t timeout); +int nina_get_rssi(); +int nina_fw_version(uint8_t *fw_ver); +int nina_set_hostname(const char *name); +int nina_gethostbyname(const char *name, uint8_t *out_ip); +int nina_socket_socket(uint8_t type); +int nina_socket_close(int fd); +int nina_socket_bind(int fd, uint8_t *ip, uint16_t port, int type); +int nina_socket_listen(int fd, uint32_t backlog); +int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_t timeout); +int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, uint32_t timeout); +int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, uint32_t timeout); +int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, uint32_t timeout); +int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t port, uint32_t timeout); +int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t *port, uint32_t timeout); +int nina_socket_setsockopt(int fd, uint32_t level, uint32_t opt, const void *optval, uint32_t optlen); +#endif //define __NINA_H__ diff --git a/src/drivers/ninaw10/include/nina_bsp.h b/src/drivers/ninaw10/include/nina_bsp.h new file mode 100644 index 000000000..9f292c957 --- /dev/null +++ b/src/drivers/ninaw10/include/nina_bsp.h @@ -0,0 +1,18 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * NINA-W10 driver BSP. + */ +#ifndef __NINA_BSP_H__ +#define __NINA_BSP_H__ +int nina_bsp_init(); +int nina_bsp_reset(); +int nina_bsp_spi_slave_select(uint32_t timeout); +int nina_bsp_spi_slave_deselect(); +int nina_bsp_spi_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, uint32_t size); +#endif //define __NINA_BSP_H__ diff --git a/src/drivers/ninaw10/src/nina.c b/src/drivers/ninaw10/src/nina.c new file mode 100644 index 000000000..8c1aae8a7 --- /dev/null +++ b/src/drivers/ninaw10/src/nina.c @@ -0,0 +1,806 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * NINA-W10 driver. + */ +#if MICROPY_PY_NINAW10 +#include +#include +#include +#include "py/mphal.h" +#include "nina.h" +#include "nina_bsp.h" + +#define DATA_FLAG (0x40) + +#define SPI_ACK (1) +#define SPI_ERR (0xFF) + +#define NO_SOCKET_AVAIL (255) + +#define CMD_START (0xE0) +#define CMD_END (0xEE) +#define CMD_ERROR (0xEF) +#define CMD_REPLY (1<<7) + +#define ARG_8BITS (1) +#define ARG_16BITS (2) + +#define ARG_STR(x) {strlen(x), (const void *) x} +#define ARG_BYTE(x) {1, (uint8_t [1]){x}} +#define ARG_SHORT(x) {2, (uint16_t [1]){x}} +#define ARG_WORD(x) {4, (uint32_t [1]){x}} + +#define NINA_ARGS(...) (nina_args_t []){__VA_ARGS__} +#define NINA_VALS(...) (nina_vals_t []){__VA_ARGS__} + +#define NINA_SSELECT_TIMEOUT (10000) +#define NINA_RESP_TIMEOUT (1000) +#define NINA_CONNECT_TIMEOUT (1000) + +typedef struct { + uint32_t size; + const void *data; +} nina_args_t; + +typedef struct { + uint32_t *size; + void *data; +} nina_vals_t; + +typedef enum { + SET_NET_CMD = 0x10, + SET_PASSPHRASE_CMD = 0x11, + SET_KEY_CMD = 0x12, + //TEST_CMD = 0x13, + SET_IP_CONFIG_CMD = 0x14, + SET_DNS_CONFIG_CMD = 0x15, + SET_HOSTNAME_CMD = 0x16, + SET_POWER_MODE_CMD = 0x17, + SET_AP_NET_CMD = 0x18, + SET_AP_PASSPHRASE_CMD = 0x19, + SET_DEBUG_CMD = 0x1A, + GET_TEMPERATURE_CMD = 0x1B, + GET_REASON_CODE_CMD = 0x1F, + + GET_CONN_STATUS_CMD = 0x20, + GET_IPADDR_CMD = 0x21, + GET_MACADDR_CMD = 0x22, + GET_CURR_SSID_CMD = 0x23, + GET_CURR_BSSID_CMD = 0x24, + GET_CURR_RSSI_CMD = 0x25, + GET_CURR_ENCT_CMD = 0x26, + SCAN_NETWORKS = 0x27, + START_SERVER_TCP_CMD = 0x28, + GET_STATE_TCP_CMD = 0x29, + DATA_SENT_TCP_CMD = 0x2A, + AVAIL_DATA_TCP_CMD = 0x2B, + GET_DATA_TCP_CMD = 0x2C, + START_CLIENT_TCP_CMD = 0x2D, + STOP_CLIENT_TCP_CMD = 0x2E, + GET_CLIENT_STATE_TCP_CMD= 0x2F, + DISCONNECT_CMD = 0x30, + //GET_IDX_SSID_CMD = 0x31, + GET_IDX_RSSI_CMD = 0x32, + GET_IDX_ENCT_CMD = 0x33, + REQ_HOST_BY_NAME_CMD = 0x34, + GET_HOST_BY_NAME_CMD = 0x35, + START_SCAN_NETWORKS = 0x36, + GET_FW_VERSION_CMD = 0x37, + //GET_TEST_CMD = 0x38, + SEND_DATA_UDP_CMD = 0x39, + GET_REMOTE_DATA_CMD = 0x3A, + GET_TIME_CMD = 0x3B, + GET_IDX_BSSID_CMD = 0x3C, + GET_IDX_CHANNEL_CMD = 0x3D, + PING_CMD = 0x3E, + GET_SOCKET_CMD = 0x3F, + + // All command with DATA_FLAG 0x40 send a 16bit Len + SET_ENT_CMD = 0x40, + SEND_DATA_TCP_CMD = 0x44, + GET_DATABUF_TCP_CMD = 0x45, + INSERT_DATABUF_CMD = 0x46, + + // regular format commands + SET_PIN_MODE = 0x50, + SET_DIGITAL_WRITE = 0x51, + SET_ANALOG_WRITE = 0x52, + GET_DIGITAL_READ = 0x53, + GET_ANALOG_READ = 0x54, + + // regular format commands + CMD_WRITE_FILE = 0x60, + CMD_READ_FILE = 0x61, + CMD_DELETE_FILE = 0x62, + CMD_EXISTS_FILE = 0x63, + CMD_DOWNLOAD_FILE = 0x64, + CMD_APPLY_OTA_COMMAND = 0x65, + CMD_RENAME_FILE = 0x66, + CMD_DOWNLOAD_OTA = 0x67, +} nina_cmd_t; + +typedef enum { + WL_NO_SHIELD = 255, + WL_NO_MODULE = WL_NO_SHIELD, + WL_IDLE_STATUS = 0, + WL_NO_SSID_AVAIL, + WL_SCAN_COMPLETED, + WL_CONNECTED, + WL_CONNECT_FAILED, + WL_CONNECTION_LOST, + WL_DISCONNECTED, + WL_AP_LISTENING, + WL_AP_CONNECTED, + WL_AP_FAILED +} nina_wl_status_t; + +typedef enum { + SOCKET_STATE_CLOSED = 0, + SOCKET_STATE_LISTEN, + SOCKET_STATE_SYN_SENT, + SOCKET_STATE_SYN_RCVD, + SOCKET_STATE_ESTABLISHED, + SOCKET_STATE_FIN_WAIT_1, + SOCKET_STATE_FIN_WAIT_2, + SOCKET_STATE_CLOSE_WAIT, + SOCKET_STATE_CLOSING, + SOCKET_STATE_LAST_ACK, + SOCKET_STATE_TIME_WAIT +} nina_sock_state_t; + +static uint8_t nina_bsp_spi_read_byte() +{ + uint8_t byte = 0; + nina_bsp_spi_transfer(NULL, &byte, 1); + return byte; +} + +static int nina_wait_for_cmd(uint8_t cmd, uint32_t timeout) +{ + uint8_t buf = 0; + for (mp_uint_t start = mp_hal_ticks_ms(); ;) { + buf = nina_bsp_spi_read_byte(); + if (buf == CMD_ERROR || buf == cmd + || ((mp_hal_ticks_ms() - start) >= timeout)) { + break; + } + mp_hal_delay_ms(1); + } + + return ((buf == cmd) ? 0 : -1); +} + +static int nina_send_command(uint32_t cmd, uint32_t nargs, uint32_t width, nina_args_t *args) +{ + int ret = -1; + uint32_t length = 4; // 3 bytes header + 1 end byte + + if (nina_bsp_spi_slave_select(NINA_SSELECT_TIMEOUT) != 0) { + return -1; + } + + // Send command header. + uint8_t cmdbuf_hdr[3] = {CMD_START, cmd, nargs}; + if (nina_bsp_spi_transfer(cmdbuf_hdr, NULL, sizeof(cmdbuf_hdr)) != 0) { + goto error_out; + } + + // Send command arg(s). + for (uint32_t i=0; i rvals) { + nvals = rvals; + } + + // Read return value(s). + for (uint32_t i=0; i= NINA_CONNECT_TIMEOUT) { + break; + } + } + + return (status == WL_CONNECTED) ? 0 : -1; +} + +int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t channel) +{ + return -1; +} + +int nina_disconnect() +{ + if (nina_send_command_read_ack(DISCONNECT_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF))) != SPI_ACK) { + return -1; + } + return 0; +} + +int nina_isconnected() +{ + int status = nina_connection_status(); + if (status == -1) { + return -1; + } + return (status == WL_CONNECTED); +} + +int nina_connected_sta(uint32_t *sta_ip) +{ + return -1; +} + +int nina_wait_for_sta(uint32_t *sta_ip, uint32_t timeout) +{ + return NINA_ERROR_TIMEOUT; +} + +int nina_ifconfig(nina_ifconfig_t *ifconfig, bool set) +{ + uint32_t ip_len = NINA_IPV4_ADDR_LEN; + uint32_t sub_len = NINA_IPV4_ADDR_LEN; + uint32_t gw_len = NINA_IPV4_ADDR_LEN; + uint32_t dns_len = NINA_IPV4_ADDR_LEN; + + if (set) { + if (nina_send_command_read_ack(SET_IP_CONFIG_CMD, + 4, ARG_8BITS, + NINA_ARGS( + ARG_BYTE(3), // Valid number of args. + {ip_len, ifconfig->ip_addr}, + {gw_len, ifconfig->gateway_addr}, + {sub_len, ifconfig->subnet_addr})) != SPI_ACK) { + return -1; + } + + if (nina_send_command_read_ack(SET_DNS_CONFIG_CMD, + 3, ARG_8BITS, + NINA_ARGS( + ARG_BYTE(1), // Valid number of args. + {dns_len, ifconfig->dns_addr}, + {dns_len, ifconfig->dns_addr})) != SPI_ACK) { + return -1; + } + + } else { + if (nina_send_command_read_vals(GET_IPADDR_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)), + 3, ARG_8BITS, + NINA_VALS( + {&ip_len, ifconfig->ip_addr}, + {&gw_len, ifconfig->gateway_addr}, + {&sub_len, ifconfig->subnet_addr})) != 0) { + return -1; + } + // No command to get DNS ? + memcpy(ifconfig->dns_addr, ifconfig->gateway_addr, NINA_IPV4_ADDR_LEN); + } + return 0; +} + +int nina_netinfo(nina_netinfo_t *netinfo) +{ + uint32_t rssi_len = 4; + uint32_t sec_len = 1; + uint32_t ssid_len = NINA_MAX_SSID_LEN; + uint32_t bssid_len = NINA_MAC_ADDR_LEN; + + if (nina_send_command_read_vals(GET_CURR_RSSI_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)), + 1, ARG_8BITS, NINA_VALS({&rssi_len, &netinfo->rssi})) != 0) { + return -1; + } + + if (nina_send_command_read_vals(GET_CURR_ENCT_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)), + 1, ARG_8BITS, NINA_VALS({&sec_len, &netinfo->security})) != 0) { + return -1; + } + + if (nina_send_command_read_vals(GET_CURR_SSID_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)), + 1, ARG_8BITS, NINA_VALS({&ssid_len, &netinfo->ssid})) != 0) { + return -1; + } + + if (nina_send_command_read_vals(GET_CURR_BSSID_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(0xFF)), + 1, ARG_8BITS, NINA_VALS({&bssid_len, &netinfo->bssid})) != 0) { + return -1; + } + + return 0; +} + +int nina_scan(nina_scan_callback_t scan_callback, void *arg, uint32_t timeout) +{ + uint32_t sizes[NINA_MAX_NETWORK_LIST]; + char ssids[NINA_MAX_NETWORK_LIST][NINA_MAX_SSID_LEN]; + nina_vals_t vals[NINA_MAX_NETWORK_LIST]; + + // Initialize the values list. + for (int i=0; i< NINA_MAX_NETWORK_LIST; i++) { + sizes[i] = NINA_MAX_SSID_LEN - 1; + memset(ssids[i], 0, NINA_MAX_SSID_LEN); + vals[i].size = &sizes[i]; + vals[i].data = ssids[i]; + } + + if (nina_send_command_read_ack(START_SCAN_NETWORKS, + 0, ARG_8BITS, NULL) != SPI_ACK) { + return -1; + } + + for (mp_uint_t start = mp_hal_ticks_ms(); ;) { + if (nina_send_command_read_vals(SCAN_NETWORKS, + 0, ARG_8BITS, NULL, + NINA_MAX_NETWORK_LIST, ARG_8BITS, vals) != 0) { + return -1; + } + + if (ssids[0][0] != 0) { + // Found at least 1 network. + break; + } + + if ((mp_hal_ticks_ms() - start) >= timeout) { + // Timeout, no networks. + return NINA_ERROR_TIMEOUT; + } + + mp_hal_delay_ms(100); + } + + for (int i=0; i= timeout) { + return NINA_ERROR_TIMEOUT; + } + } + + return 0; +} + +int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, uint32_t timeout) +{ + uint32_t size = 2; + uint16_t bytes = 0; + + if (nina_send_command_read_vals(SEND_DATA_TCP_CMD, + 2, ARG_16BITS, NINA_ARGS(ARG_SHORT(fd), {len, buf}), + 1, ARG_8BITS, NINA_VALS({&size, &bytes})) != 0 || bytes <= 0) { + return -1; + } + + for (mp_uint_t start = mp_hal_ticks_ms(); ;) { + int resp = nina_send_command_read_ack(DATA_SENT_TCP_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_SHORT(fd))); + + if (resp == -1) { + return -1; + } + + if (resp == SPI_ACK) { + break; + } + + if ((mp_hal_ticks_ms() - start) >= timeout) { + return NINA_ERROR_TIMEOUT; + } + mp_hal_delay_ms(1); + } + + return bytes; +} + +int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, uint32_t timeout) +{ + uint32_t size = 2; + uint16_t bytes = 0; + + if (nina_send_command_read_vals(GET_DATABUF_TCP_CMD, + 2, ARG_16BITS, NINA_ARGS(ARG_SHORT(fd), {len, buf}), + 1, ARG_16BITS, NINA_VALS({&size, &bytes})) != 0 || bytes <= 0) { + return -1; + } + + return bytes; +} + +// Check from the upper layer if the socket is bound, if not then auto-bind it 32768->60999 first. +int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t port, uint32_t timeout) +{ + // TODO do we need to split the packet somewhere? + if (nina_send_command_read_ack(START_CLIENT_TCP_CMD, + 4, ARG_8BITS, + NINA_ARGS( + ARG_WORD((*(uint32_t*)ip)), + ARG_SHORT(port), + ARG_BYTE(fd), + ARG_BYTE(NINA_SOCKET_TYPE_UDP))) != SPI_ACK) { + return -1; + } + + // Buffer length and socket number are passed as 16bits. + if (nina_send_command_read_ack(INSERT_DATABUF_CMD, + 2, ARG_16BITS, NINA_ARGS(ARG_SHORT(fd), {len, buf})) != SPI_ACK) { + return -1; + } + + if (nina_send_command_read_ack(SEND_DATA_UDP_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd))) != SPI_ACK) { + return -1; + } + + return 0; +} + +// Check from the upper layer if the socket is bound, if not then auto-bind it 32768->60999 first. +int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t *port, uint32_t timeout) +{ + uint32_t size = 2; + uint16_t bytes = 0; + + uint32_t ip_len = NINA_IPV4_ADDR_LEN; + uint32_t port_len = 2; + + if (nina_send_command_read_vals(GET_DATABUF_TCP_CMD, + 2, ARG_16BITS, NINA_ARGS(ARG_SHORT(fd), {len, buf}), + 1, ARG_16BITS, NINA_VALS({&size, &bytes})) != 0 || bytes <= 0) { + return -1; + } + + if (nina_send_command_read_vals(GET_REMOTE_DATA_CMD, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd)), + 2, ARG_8BITS, NINA_VALS({&ip_len, ip}, {&port_len, port})) != 0) { + return -1; + } + + return bytes; +} + +int nina_socket_setsockopt(int fd, uint32_t level, uint32_t opt, const void *optval, uint32_t optlen) +{ + return -1; +} +#endif //MICROPY_PY_NINAW10 diff --git a/src/micropython b/src/micropython index 0cfe9fd8d..5ede9f316 160000 --- a/src/micropython +++ b/src/micropython @@ -1 +1 @@ -Subproject commit 0cfe9fd8d402669af8a4345fd6006cbdc7114a69 +Subproject commit 5ede9f316bbcd4f462337633e1720e490acd6c96 diff --git a/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.cmake b/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.cmake index f17c2a91b..43e03073e 100644 --- a/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.cmake +++ b/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.cmake @@ -1,8 +1,7 @@ set(MICROPY_PY_SENSOR 1) set(MICROPY_PY_ULAB 1) +set(MICROPY_PY_NINAW10 1) set(MICROPY_PY_WINC1500 0) -set(MICROPY_PY_LWIP 0) -set(MICROPY_PY_NETWORK_CYW43 0) set(MICROPY_PY_BLUETOOTH 0) set(MICROPY_BLUETOOTH_NIMBLE 0) set(MICROPY_PY_AUDIO 1) diff --git a/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.h b/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.h index 29315a657..308721cdf 100644 --- a/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.h +++ b/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/omv_boardconfig.h @@ -124,24 +124,29 @@ extern unsigned char *OMV_UNIQUE_ID_ADDR; #define I2C1_SDA_PIN (6) #define I2C1_SPEED (CAMBUS_SPEED_FULL) +// LCD config. #define LCD_SPI (spi0) +#define LCD_CS_PIN (5) #define LCD_MOSI_PIN (7) #define LCD_SCLK_PIN (6) - #define LCD_RST_PIN (4) -#define LCD_RST_PIN_WRITE(bit) gpio_put(LCD_RST_PIN, bit); - #define LCD_RS_PIN (0) -#define LCD_RS_PIN_WRITE(bit) gpio_put(LCD_RS_PIN, bit); -#define LCD_CS_PIN (5) -#define LCD_CS_PIN_WRITE(bit) gpio_put(LCD_CS_PIN, bit); +// WiFi/NINA config. +#define WIFI_SPI (spi1) +#define WIFI_CS_PIN (9) +#define WIFI_MOSI_PIN (11) +#define WIFI_MISO_PIN (8) +#define WIFI_SCLK_PIN (14) +#define WIFI_RST_PIN (3) +#define WIFI_GPIO0_PIN (2) +#define WIFI_ACK_PIN (10) // AUDIO config. #define PDM_PIO (pio1) #define PDM_SM (0) #define PDM_DMA (1) -#define PDM_DMA_IRQ (DMA_IRQ_1) +#define PDM_DMA_IRQ (DMA_IRQ_1) #define PDM_DMA_CHANNEL (0) #define PDM_CLK_PIN (23) diff --git a/src/omv/boards/PICO/omv_boardconfig.cmake b/src/omv/boards/PICO/omv_boardconfig.cmake index 2d06228cc..c3bf37420 100644 --- a/src/omv/boards/PICO/omv_boardconfig.cmake +++ b/src/omv/boards/PICO/omv_boardconfig.cmake @@ -1,8 +1,7 @@ set(MICROPY_PY_SENSOR 1) set(MICROPY_PY_ULAB 1) +set(MICROPY_PY_NINAW10 0) set(MICROPY_PY_WINC1500 0) -set(MICROPY_PY_LWIP 0) -set(MICROPY_PY_NETWORK_CYW43 0) set(MICROPY_PY_BLUETOOTH 0) set(MICROPY_BLUETOOTH_NIMBLE 0) set(MICROPY_PY_AUDIO 0) diff --git a/src/omv/boards/PICO/omv_boardconfig.h b/src/omv/boards/PICO/omv_boardconfig.h index 83eafbb32..f489657e5 100644 --- a/src/omv/boards/PICO/omv_boardconfig.h +++ b/src/omv/boards/PICO/omv_boardconfig.h @@ -125,17 +125,21 @@ extern unsigned char *OMV_UNIQUE_ID_ADDR; #define I2C1_SPEED (CAMBUS_SPEED_FULL) #define LCD_SPI (spi0) +#define LCD_CS_PIN (17) #define LCD_MOSI_PIN (19) #define LCD_SCLK_PIN (18) - #define LCD_RST_PIN (20) -#define LCD_RST_PIN_WRITE(bit) gpio_put(LCD_RST_PIN, bit); - #define LCD_RS_PIN (21) -#define LCD_RS_PIN_WRITE(bit) gpio_put(LCD_RS_PIN, bit); -#define LCD_CS_PIN (17) -#define LCD_CS_PIN_WRITE(bit) gpio_put(LCD_CS_PIN, bit); +// WiFi/NINA config. +//#define WIFI_SPI (spi1) +//#define WIFI_CS_PIN (15) +//#define WIFI_MOSI_PIN (16) +//#define WIFI_MISO_PIN (17) +//#define WIFI_SCLK_PIN (18) +//#define WIFI_RST_PIN (19) +//#define WIFI_GPIO0_PIN (20) +//#define WIFI_ACK_PIN (21) // AUDIO config. //#define PDM_PIO (pio1) diff --git a/src/omv/ports/rp2/modules/py_nina.c b/src/omv/ports/rp2/modules/py_nina.c new file mode 100644 index 000000000..d13cf9034 --- /dev/null +++ b/src/omv/ports/rp2/modules/py_nina.c @@ -0,0 +1,537 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * NINA-W10 Python module. + */ +#if MICROPY_PY_NINAW10 +#include +#include +#include +#include + +#include "py/nlr.h" +#include "py/mphal.h" +#include "py/objtuple.h" +#include "py/objlist.h" +#include "py/stream.h" +#include "py/runtime.h" + +#include "mperrno.h" +#include "lib/netutils/netutils.h" +#include "modnetwork.h" + +#include "nina.h" + +typedef struct _nina_obj_t { + mp_obj_base_t base; +} nina_obj_t; + +static const nina_obj_t nina_obj = {{(mp_obj_type_t*) &mod_network_nic_type_nina}}; + +// Initialise the module using the given SPI bus and pins and return a nina object. +static mp_obj_t py_nina_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *all_args) +{ + // Init Nina module + int error = nina_init(); + if (error != 0) { + mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize Nina-W10 module: %d\n"), error); + } + return (mp_obj_t)&nina_obj; +} + +// method connect(ssid, key=None, *, security=WPA2, bssid=None) +static mp_obj_t py_nina_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) +{ + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_key, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NINA_SEC_WPA_PSK} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get ssid + const char *ssid = mp_obj_str_get_str(args[0].u_obj); + + if (strlen(ssid) == 0) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("SSID can't be empty!")); + } + + // get key and sec + const char *key = NULL; + mp_uint_t security = NINA_SEC_OPEN; + + if (args[1].u_obj != mp_const_none) { + key = mp_obj_str_get_str(args[1].u_obj); + security = args[2].u_int; + } + + if (security != NINA_SEC_OPEN && strlen(key) == 0) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Key can't be empty!")); + } + + // connect to AP + if (nina_connect(ssid, security, key, 0) != 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("could not connect to ssid=%s, sec=%d, key=%s\n"), ssid, security, key); + } + + return mp_const_none; +} + +// method start_ap(ssid, key=None, security=OPEN) +static mp_obj_t py_nina_start_ap(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) +{ + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ssid, MP_ARG_REQUIRED| MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NINA_SEC_OPEN } }, + { MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get ssid + const char *ssid = mp_obj_str_get_str(args[0].u_obj); + + // get key and security + const char *key = NULL; + mp_uint_t security = args[2].u_int; + + if (security != NINA_SEC_OPEN && security != NINA_SEC_WEP) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "AP mode supports WEP security only.")); + } + + if (security == NINA_SEC_WEP && args[1].u_obj == MP_OBJ_NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Missing WEP key!")); + } + + if (security != NINA_SEC_OPEN) { + key = mp_obj_str_get_str(args[1].u_obj); + } + + // get channel + mp_uint_t channel = args[3].u_int; + + // Initialize WiFi in AP mode. + if (nina_start_ap(ssid, security, key, channel) != 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "failed to start in AP mode")); + } + + printf("AP mode started. You can connect to %s.\r\n", ssid); + return mp_const_none; +} + +static mp_obj_t py_nina_disconnect(mp_obj_t self_in) +{ + nina_disconnect(); + return mp_const_none; +} + +static mp_obj_t py_nina_isconnected(mp_obj_t self_in) +{ + return mp_obj_new_bool(nina_isconnected()); +} + +static mp_obj_t py_nina_connected_sta(mp_obj_t self_in) +{ + uint32_t sta_ip = 0; + mp_obj_t sta_list = mp_obj_new_list(0, NULL); + + if (nina_connected_sta(&sta_ip) == 0) { + mp_obj_list_append(sta_list, netutils_format_inet_addr( + (uint8_t*)&sta_ip, 0, NETUTILS_BIG)); + } + + return sta_list; +} + +static mp_obj_t py_nina_wait_for_sta(mp_obj_t self_in, mp_obj_t timeout_in) +{ + uint32_t sta_ip; + // get timeout + mp_obj_t sta_list = mp_obj_new_list(0, NULL); + mp_uint_t timeout = mp_obj_get_int(timeout_in); + + if (nina_wait_for_sta(&sta_ip, timeout) == 0) { + mp_obj_list_append(sta_list, netutils_format_inet_addr( + (uint8_t*)&sta_ip, 0, NETUTILS_BIG)); + } + + return sta_list; +} + +static mp_obj_t py_nina_ifconfig(size_t n_args, const mp_obj_t *args) +{ + nina_ifconfig_t ifconfig; + if (n_args == 1) { + // get ifconfig info + nina_ifconfig(&ifconfig, false); + mp_obj_t tuple[4] = { + netutils_format_ipv4_addr(ifconfig.ip_addr, NETUTILS_BIG), + netutils_format_ipv4_addr(ifconfig.subnet_addr, NETUTILS_BIG), + netutils_format_ipv4_addr(ifconfig.gateway_addr, NETUTILS_BIG), + netutils_format_ipv4_addr(ifconfig.dns_addr, NETUTILS_BIG), + }; + return mp_obj_new_tuple(4, tuple); + } else { + // set ifconfig info + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 4, &items); + netutils_parse_ipv4_addr(items[0], ifconfig.ip_addr, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[1], ifconfig.subnet_addr, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[2], ifconfig.gateway_addr, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[3], ifconfig.dns_addr, NETUTILS_BIG); + nina_ifconfig(&ifconfig, true); + return mp_const_none; + } +} + +static mp_obj_t py_nina_netinfo(mp_obj_t self_in) +{ + nina_netinfo_t netinfo; + nina_netinfo(&netinfo); + + // Format MAC address + VSTR_FIXED(bssid_vstr, 18); + vstr_printf(&bssid_vstr, "%02x:%02x:%02x:%02x:%02x:%02x", + netinfo.bssid[0], netinfo.bssid[1], netinfo.bssid[2], + netinfo.bssid[3], netinfo.bssid[4], netinfo.bssid[5]); + + // Add connection info + mp_obj_t tuple[4] = { + mp_obj_new_int(netinfo.rssi), + mp_obj_new_int(netinfo.security), + mp_obj_new_str(netinfo.ssid, strlen(netinfo.ssid)), + mp_obj_new_str(bssid_vstr.buf, bssid_vstr.len), + }; + return mp_obj_new_tuple(4, tuple); +} + +static int nina_scan_callback(nina_scan_result_t *scan_result, void *arg) +{ + mp_obj_t scan_list = (mp_obj_t) arg; + + // Format MAC address + VSTR_FIXED(bssid_vstr, 18); + vstr_printf(&bssid_vstr, "%02X:%02X:%02X:%02X:%02X:%02X", + scan_result->bssid[0], scan_result->bssid[1], scan_result->bssid[2], + scan_result->bssid[3], scan_result->bssid[4], scan_result->bssid[5]); + + mp_obj_t ap[5] = { + mp_obj_new_int(scan_result->channel), + mp_obj_new_int(scan_result->rssi), + mp_obj_new_int(scan_result->security), + mp_obj_new_str(bssid_vstr.buf, bssid_vstr.len), + mp_obj_new_str(scan_result->ssid, strlen(scan_result->ssid)), + }; + + mp_obj_list_append(scan_list, mp_obj_new_tuple(MP_ARRAY_SIZE(ap), ap)); + + return 0; +} + +static mp_obj_t py_nina_scan(mp_obj_t self_in) +{ + mp_obj_t scan_list; + scan_list = mp_obj_new_list(0, NULL); + nina_scan(nina_scan_callback, scan_list, 10000); + return scan_list; +} + +static mp_obj_t py_nina_get_rssi(mp_obj_t self_in) +{ + return mp_obj_new_int(nina_get_rssi()); +} + +static mp_obj_t py_nina_fw_version(mp_obj_t self_in) +{ + uint8_t fwver[NINA_FW_VER_LEN]; + nina_fw_version(fwver); + + // We don't know how the fw version looks like, assume ints. + mp_obj_tuple_t *t_fwver = mp_obj_new_tuple(NINA_FW_VER_LEN, NULL); + for (int i=0; iitems[i] = mp_obj_new_int(fwver[i]); + } + return t_fwver; +} + +static int py_nina_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) +{ + return nina_gethostbyname(name, out_ip); +} + +static int py_nina_socket_socket(mod_network_socket_obj_t *socket, int *_errno) +{ + uint8_t type; + + if (socket->u_param.domain != MOD_NETWORK_AF_INET) { + *_errno = MP_EAFNOSUPPORT; + return -1; + } + + switch (socket->u_param.type) { + case MOD_NETWORK_SOCK_STREAM: + type = NINA_SOCKET_TYPE_TCP; + break; + + case MOD_NETWORK_SOCK_DGRAM: + type = NINA_SOCKET_TYPE_UDP; + break; + + default: + *_errno = MP_EINVAL; + return -1; + } + + // open socket + int fd = nina_socket_socket(type); + if (fd < 0) { + *_errno = fd; + return -1; + } + + // store state of this socket + socket->fd = fd; + socket->timeout = 0; // blocking + return 0; +} + +static void py_nina_socket_close(mod_network_socket_obj_t *socket) +{ + if (socket->fd >= 0) { + nina_socket_close(socket->fd); + socket->fd = -1; // Mark socket FD as invalid + } +} + +static int py_nina_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) +{ + uint8_t type; + switch (socket->u_param.type) { + case MOD_NETWORK_SOCK_STREAM: + type = NINA_SOCKET_TYPE_TCP; + break; + + case MOD_NETWORK_SOCK_DGRAM: + type = NINA_SOCKET_TYPE_UDP; + break; + + default: + *_errno = MP_EINVAL; + return -1; + } + + int ret = nina_socket_bind(socket->fd, ip, port, type); + if (ret < 0) { + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return 0; +} + +static int py_nina_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) +{ + int ret = nina_socket_listen(socket->fd, backlog); + if (ret < 0) { + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return 0; +} + +static int py_nina_socket_accept(mod_network_socket_obj_t *socket, + mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) +{ + int fd = 0; + // Call accept. + int ret = nina_socket_accept(socket->fd, ip, (uint16_t*) port, &fd, socket->timeout); + if (ret < 0) { + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + + // Set default socket timeout. + socket2->fd = fd; + socket2->timeout = 0; + return 0; +} + +static int py_nina_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) +{ + int ret = nina_socket_connect(socket->fd, ip, port, socket->timeout); + if (ret < 0) { + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return 0; +} + +static mp_uint_t py_nina_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) +{ + int ret = nina_socket_send(socket->fd, buf, len, socket->timeout); + if (ret == NINA_ERROR_TIMEOUT) { + // The socket is Not closed on timeout when calling functions that accept a timeout. + *_errno = MP_ETIMEDOUT; + return 0; + } else if (ret < 0) { + // Close the socket on any other errors. + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return ret; +} + +static mp_uint_t py_nina_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) +{ + int ret = nina_socket_recv(socket->fd, buf, len, socket->timeout); + if (ret == NINA_ERROR_TIMEOUT) { + // The socket is Not closed on timeout when calling functions that accept a timeout. + *_errno = MP_ETIMEDOUT; + return 0; + } else if (ret < 0) { + // Close the socket on any other errors. + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return ret; +} + +static mp_uint_t py_nina_socket_sendto(mod_network_socket_obj_t *socket, + const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) +{ + int ret = nina_socket_sendto(socket->fd, buf, len, ip, port, socket->timeout); + if (ret == NINA_ERROR_TIMEOUT) { + // The socket is Not closed on timeout when calling functions that accept a timeout. + *_errno = MP_ETIMEDOUT; + return 0; + } else if (ret < 0) { + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return ret; +} + +static mp_uint_t py_nina_socket_recvfrom(mod_network_socket_obj_t *socket, + byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) +{ + int ret = nina_socket_recvfrom(socket->fd, buf, len, ip, (uint16_t *) port, socket->timeout); + if (ret == NINA_ERROR_TIMEOUT) { + // The socket is Not closed on timeout when calling functions that accept a timeout. + *_errno = MP_ETIMEDOUT; + return 0; + } else if (ret < 0) { + // Close the socket on any other errors. + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return ret; +} + +static int py_nina_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t + level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) +{ + int ret = nina_socket_setsockopt(socket->fd, level, opt, optval, optlen); + if (ret < 0) { + *_errno = ret; + py_nina_socket_close(socket); + return -1; + } + return 0; +} + +static int py_nina_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) +{ + if (timeout_ms == UINT32_MAX) { + // no timeout is given, set the socket to blocking mode. + timeout_ms = 0; + } else if (timeout_ms == 0) { + // non-blocking mode, set the timeout to a small number other than zero. + timeout_ms = 10; + } // otherwise, timeout is provided. + + socket->timeout = timeout_ms; + return 0; +} + +static int py_nina_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) +{ + *_errno = MP_EIO; + return -1; +} + +static MP_DEFINE_CONST_FUN_OBJ_KW(py_nina_connect_obj, 1, py_nina_connect); +static MP_DEFINE_CONST_FUN_OBJ_KW(py_nina_start_ap_obj,1, py_nina_start_ap); +static MP_DEFINE_CONST_FUN_OBJ_1(py_nina_disconnect_obj, py_nina_disconnect); +static MP_DEFINE_CONST_FUN_OBJ_1(py_nina_isconnected_obj, py_nina_isconnected); +static MP_DEFINE_CONST_FUN_OBJ_1(py_nina_connected_sta_obj, py_nina_connected_sta); +static MP_DEFINE_CONST_FUN_OBJ_2(py_nina_wait_for_sta_obj, py_nina_wait_for_sta); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_nina_ifconfig_obj, 1, 2, py_nina_ifconfig); +static MP_DEFINE_CONST_FUN_OBJ_1(py_nina_netinfo_obj, py_nina_netinfo); +static MP_DEFINE_CONST_FUN_OBJ_1(py_nina_scan_obj, py_nina_scan); +static MP_DEFINE_CONST_FUN_OBJ_1(py_nina_get_rssi_obj, py_nina_get_rssi); +static MP_DEFINE_CONST_FUN_OBJ_1(py_nina_fw_version_obj, py_nina_fw_version); + +static const mp_map_elem_t nina_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&py_nina_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_start_ap), (mp_obj_t)&py_nina_start_ap_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&py_nina_disconnect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&py_nina_isconnected_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connected_sta), (mp_obj_t)&py_nina_connected_sta_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_wait_for_sta), (mp_obj_t)&py_nina_wait_for_sta_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&py_nina_ifconfig_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_netinfo), (mp_obj_t)&py_nina_netinfo_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_scan), (mp_obj_t)&py_nina_scan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_rssi), (mp_obj_t)&py_nina_get_rssi_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_fw_version), (mp_obj_t)&py_nina_fw_version_obj }, + // Network is not secured. + { MP_OBJ_NEW_QSTR(MP_QSTR_OPEN), MP_OBJ_NEW_SMALL_INT(NINA_SEC_OPEN) }, + // Security type WEP (40 or 104). + { MP_OBJ_NEW_QSTR(MP_QSTR_WEP), MP_OBJ_NEW_SMALL_INT(NINA_SEC_WEP) }, + // Network secured with WPA/WPA2 personal(PSK). + { MP_OBJ_NEW_QSTR(MP_QSTR_WPA_PSK), MP_OBJ_NEW_SMALL_INT(NINA_SEC_WPA_PSK) }, +}; + +static MP_DEFINE_CONST_DICT(nina_locals_dict, nina_locals_dict_table); + +const mod_network_nic_type_t mod_network_nic_type_nina = { + .base = { + { &mp_type_type }, + .name = MP_QSTR_nina, + .make_new = py_nina_make_new, + .locals_dict = (mp_obj_t)&nina_locals_dict, + }, + .gethostbyname = py_nina_gethostbyname, + .socket = py_nina_socket_socket, + .close = py_nina_socket_close, + .bind = py_nina_socket_bind, + .listen = py_nina_socket_listen, + .accept = py_nina_socket_accept, + .connect = py_nina_socket_connect, + .send = py_nina_socket_send, + .recv = py_nina_socket_recv, + .sendto = py_nina_socket_sendto, + .recvfrom = py_nina_socket_recvfrom, + .setsockopt = py_nina_socket_setsockopt, + .settimeout = py_nina_socket_settimeout, + .ioctl = py_nina_socket_ioctl, +}; +#endif // MICROPY_PY_NINAW10 diff --git a/src/omv/ports/rp2/nina_bsp.c b/src/omv/ports/rp2/nina_bsp.c new file mode 100644 index 000000000..4890fe857 --- /dev/null +++ b/src/omv/ports/rp2/nina_bsp.c @@ -0,0 +1,102 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * NINA-W10 driver BSP implementation. + */ +#if MICROPY_PY_NINAW10 +#include +#include +#include "py/mphal.h" + +#include "pico/stdlib.h" +#include "hardware/spi.h" +#include "omv_boardconfig.h" + +#include "nina.h" +#include "nina_bsp.h" + +int nina_bsp_init() +{ + gpio_init(WIFI_CS_PIN); + gpio_set_dir(WIFI_CS_PIN, GPIO_OUT); + + gpio_init(WIFI_ACK_PIN); + gpio_set_dir(WIFI_ACK_PIN, GPIO_IN); + + gpio_init(WIFI_RST_PIN); + gpio_set_dir(WIFI_RST_PIN, GPIO_OUT); + + gpio_init(WIFI_GPIO0_PIN); + gpio_set_dir(WIFI_GPIO0_PIN, GPIO_OUT); + + gpio_put(WIFI_GPIO0_PIN, 1); + gpio_put(WIFI_CS_PIN, 1); + + gpio_put(WIFI_RST_PIN, 0); + mp_hal_delay_ms(10); + gpio_put(WIFI_RST_PIN, 1); + mp_hal_delay_ms(750); + + gpio_put(WIFI_GPIO0_PIN, 0); + + spi_init(WIFI_SPI, 8 * 1000 * 1000); + + gpio_set_function(WIFI_SCLK_PIN, GPIO_FUNC_SPI); + gpio_set_function(WIFI_MOSI_PIN, GPIO_FUNC_SPI); + + return 0; +} + +int nina_bsp_reset() +{ + return 0; +} + +int nina_bsp_spi_slave_select(uint32_t timeout) +{ + // Wait for ACK to go low. + for (mp_uint_t start = mp_hal_ticks_ms(); gpio_get(WIFI_ACK_PIN) != 0; mp_hal_delay_ms(1)) { + if ((mp_hal_ticks_ms() - start) >= timeout) { + return -1; + } + } + + // Chip select. + gpio_put(WIFI_CS_PIN, 0); + + // Wait for ACK to go high. + for (mp_uint_t start = mp_hal_ticks_ms(); gpio_get(WIFI_ACK_PIN) == 0; mp_hal_delay_ms(1)) { + if ((mp_hal_ticks_ms() - start) >= 100) { + gpio_put(WIFI_CS_PIN, 1); + return -1; + } + } + + return 0; +} + +int nina_bsp_spi_slave_deselect() +{ + gpio_put(WIFI_CS_PIN, 1); + return 0; +} + +int nina_bsp_spi_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, uint32_t size) +{ + gpio_put(WIFI_CS_PIN, 0); + if (tx_buf && rx_buf) { + spi_write_read_blocking(WIFI_SPI, tx_buf, rx_buf, size); + } else if (tx_buf) { + spi_write_blocking(WIFI_SPI, tx_buf, size); + } else if (rx_buf) { + spi_read_blocking(WIFI_SPI, 0x00, rx_buf, size); + } + gpio_put(WIFI_CS_PIN, 1); + return 0; +} +#endif //MICROPY_PY_NINAW10 diff --git a/src/omv/ports/rp2/omv_portconfig.cmake b/src/omv/ports/rp2/omv_portconfig.cmake index 8414ceb68..165e5a289 100644 --- a/src/omv/ports/rp2/omv_portconfig.cmake +++ b/src/omv/ports/rp2/omv_portconfig.cmake @@ -14,6 +14,7 @@ set(CMSIS_DIR hal/cmsis) set(LEPTON_DIR drivers/lepton) set(LSM6DS3_DIR drivers/lsm6ds3) set(WINC1500_DIR drivers/winc1500) +set(NINAW10_DIR drivers/ninaw10) set(MLX90621_DIR drivers/mlx90621) set(MLX90640_DIR drivers/mlx90640) set(MLX90641_DIR drivers/mlx90641) @@ -77,6 +78,7 @@ target_include_directories(${MICROPY_TARGET} PRIVATE ${TOP_DIR}/${LEPTON_DIR}/include/ ${TOP_DIR}/${LSM6DS3_DIR}/include/ ${TOP_DIR}/${WINC1500_DIR}/include/ + ${TOP_DIR}/${NINAW10}/include/ ${TOP_DIR}/${MLX90621_DIR}/include/ ${TOP_DIR}/${MLX90640_DIR}/include/ ${TOP_DIR}/${MLX90641_DIR}/include/ @@ -251,6 +253,26 @@ if(MICROPY_PY_ULAB) ) endif() +if(MICROPY_PY_NINAW10) + target_include_directories(${MICROPY_TARGET} PRIVATE + ${TOP_DIR}/${NINAW10_DIR}/include/ + ) + + set(NINA_SOURCES + ${TOP_DIR}/${NINAW10_DIR}/src/nina.c + ${TOP_DIR}/${OMV_DIR}/ports/${PORT}/nina_bsp.c + ${TOP_DIR}/${OMV_DIR}/ports/${PORT}/modules/py_nina.c + ) + + target_sources(${MICROPY_TARGET} PRIVATE ${NINA_SOURCES}) + + target_compile_definitions(${MICROPY_TARGET} PRIVATE + MICROPY_PY_USOCKET=1 + MICROPY_PY_NETWORK=1 + MICROPY_PY_NINAW10=1 + ) +endif() + add_custom_command(TARGET ${MICROPY_TARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${BIN_DIR}