summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Halbert <halbert@halwitz.org>2019-01-19 19:45:35 -0500
committerDan Halbert <halbert@halwitz.org>2019-01-19 19:45:35 -0500
commit28cfd8a5135a1e420b56d693cef20fc1a4721081 (patch)
treeb6715f13a8312c1ae1e9c0ffd57c116c45dae108
parent7a33e588d40e01862b60d6f0b9d2880ad19be030 (diff)
CharacteristicBuffer: make it be a stream class; add locking
-rwxr-xr-xports/nrf/Makefile1
-rw-r--r--ports/nrf/common-hal/bleio/Characteristic.c2
-rw-r--r--ports/nrf/common-hal/bleio/CharacteristicBuffer.c80
-rw-r--r--ports/nrf/common-hal/bleio/CharacteristicBuffer.h5
-rw-r--r--ports/nrf/common-hal/bleio/Device.c2
-rw-r--r--ports/nrf/common-hal/busio/UART.c39
-rw-r--r--ports/nrf/sd_mutex.c56
-rw-r--r--ports/nrf/sd_mutex.h46
-rw-r--r--py/ringbuf.h32
-rw-r--r--py/stream.c2
-rw-r--r--shared-bindings/bleio/CharacteristicBuffer.c159
-rw-r--r--shared-bindings/bleio/CharacteristicBuffer.h9
-rw-r--r--shared-bindings/busio/UART.c4
13 files changed, 371 insertions, 66 deletions
diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile
index 2bc508475..1b4aef050 100755
--- a/ports/nrf/Makefile
+++ b/ports/nrf/Makefile
@@ -142,6 +142,7 @@ SRC_C += \
peripherals/nrf/$(MCU_CHIP)/pins.c \
peripherals/nrf/$(MCU_CHIP)/power.c \
peripherals/nrf/timers.c \
+ sd_mutex.c \
supervisor/shared/memory.c
diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c
index 0bade8d01..1db564549 100644
--- a/ports/nrf/common-hal/bleio/Characteristic.c
+++ b/ports/nrf/common-hal/bleio/Characteristic.c
@@ -36,9 +36,9 @@
#include "common-hal/bleio/Characteristic.h"
#include "shared-module/bleio/Characteristic.h"
-// TODO - should these be per object?? *****
STATIC volatile bleio_characteristic_obj_t *m_read_characteristic;
STATIC volatile uint8_t m_tx_in_progress;
+// Serialize gattc writes that send a response. This might be done per object?
STATIC nrf_mutex_t *m_write_mutex;
STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) {
diff --git a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c
index 4bd3e147f..42e45abdf 100644
--- a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c
+++ b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2018 Artur Pacholec
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -29,9 +29,13 @@
#include "ble_drv.h"
#include "ble_gatts.h"
-#include "nrf_soc.h"
+#include "sd_mutex.h"
+#include "lib/utils/interrupt_char.h"
#include "py/runtime.h"
+#include "py/stream.h"
+
+#include "tick.h"
#include "common-hal/bleio/__init__.h"
#include "common-hal/bleio/CharacteristicBuffer.h"
@@ -43,10 +47,13 @@ STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) {
ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
// Event handle must match the handle for my characteristic.
if (evt_write->handle == self->characteristic->handle) {
- // Push all the data onto the ring buffer.
+ // Push all the data onto the ring buffer, but wait for any reads to finish.
+ sd_mutex_acquire_wait_no_vm(&self->ringbuf_mutex);
for (size_t i = 0; i < evt_write->len; i++) {
ringbuf_put(&self->ringbuf, evt_write->data[i]);
}
+ // Don't check for errors: we're in an event handler.
+ sd_mutex_release(&self->ringbuf_mutex);
break;
}
}
@@ -54,22 +61,75 @@ STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) {
}
-// Assumes that buffer_size has been validated before call.
-void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size) {
+// Assumes that timeout and buffer_size have been validated before call.
+void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self,
+ bleio_characteristic_obj_t *characteristic,
+ mp_float_t timeout,
+ size_t buffer_size) {
self->characteristic = characteristic;
+ self->timeout_ms = timeout * 1000;
// This is a macro.
- ringbuf_alloc(&self->ringbuf, buffer_size);
+ // true means long-lived, so it won't be moved.
+ ringbuf_alloc(&self->ringbuf, buffer_size, true);
+ sd_mutex_new(&self->ringbuf_mutex);
ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self);
}
-// Returns a uint8_t byte value, or -1 if no data is available.
-int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self) {
- return ringbuf_get(&self->ringbuf);
+int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) {
+ uint64_t start_ticks = ticks_ms;
+
+ // Wait for all bytes received or timeout
+ while ( (ringbuf_count(&self->ringbuf) < len) && (ticks_ms - start_ticks < self->timeout_ms) ) {
+#ifdef MICROPY_VM_HOOK_LOOP
+ MICROPY_VM_HOOK_LOOP ;
+ // Allow user to break out of a timeout with a KeyboardInterrupt.
+ if ( mp_hal_is_interrupted() ) {
+ return 0;
+ }
+#endif
+ }
+
+ // Copy received data. Lock out writes while copying.
+ sd_mutex_acquire_wait(&self->ringbuf_mutex);
+
+ size_t rx_bytes = MIN(ringbuf_count(&self->ringbuf), len);
+ for ( size_t i = 0; i < rx_bytes; i++ ) {
+ data[i] = ringbuf_get(&self->ringbuf);
+ }
+
+ // Writes now OK.
+ sd_mutex_release_check(&self->ringbuf_mutex);
+
+ return rx_bytes;
+}
+
+uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) {
+ return ringbuf_count(&self->ringbuf);
+}
+
+void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) {
+ // prevent conflict with uart irq
+ sd_mutex_acquire_wait(&self->ringbuf_mutex);
+ ringbuf_clear(&self->ringbuf);
+ sd_mutex_release_check(&self->ringbuf_mutex);
+}
+
+bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) {
+ return self->characteristic == NULL;
}
void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) {
- ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, self);
+ if (!common_hal_bleio_characteristic_buffer_deinited(self)) {
+ ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, self);
+ }
+}
+
+bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) {
+ return self->characteristic != NULL &&
+ self->characteristic->service != NULL &&
+ self->characteristic->service->device != NULL &&
+ common_hal_bleio_device_get_conn_handle(self->characteristic->service->device) != BLE_CONN_HANDLE_INVALID;
}
diff --git a/ports/nrf/common-hal/bleio/CharacteristicBuffer.h b/ports/nrf/common-hal/bleio/CharacteristicBuffer.h
index f9ff7dd66..f5c715154 100644
--- a/ports/nrf/common-hal/bleio/CharacteristicBuffer.h
+++ b/ports/nrf/common-hal/bleio/CharacteristicBuffer.h
@@ -27,15 +27,18 @@
#ifndef MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H
#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H
-#include "py/ringbuf.h"
+#include "nrf_soc.h"
+#include "py/ringbuf.h"
#include "shared-bindings/bleio/Characteristic.h"
typedef struct {
mp_obj_base_t base;
bleio_characteristic_obj_t *characteristic;
+ uint32_t timeout_ms;
// Ring buffer storing consecutive incoming values.
ringbuf_t ringbuf;
+ nrf_mutex_t ringbuf_mutex;
} bleio_characteristic_buffer_obj_t;
#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H
diff --git a/ports/nrf/common-hal/bleio/Device.c b/ports/nrf/common-hal/bleio/Device.c
index 50c738abe..5f625829e 100644
--- a/ports/nrf/common-hal/bleio/Device.c
+++ b/ports/nrf/common-hal/bleio/Device.c
@@ -262,11 +262,13 @@ STATIC bool discover_services(bleio_device_obj_t *device, uint16_t start_handle)
mp_raise_OSError_msg(translate("Failed to discover services"));
}
+ // Serialize discovery.
err_code = sd_mutex_acquire(m_discovery_mutex);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to acquire mutex"));
}
+ // Wait for someone else to release m_discovery_mutex.
while (sd_mutex_acquire(m_discovery_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
#ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP
diff --git a/ports/nrf/common-hal/busio/UART.c b/ports/nrf/common-hal/busio/UART.c
index 912956369..43868f506 100644
--- a/ports/nrf/common-hal/busio/UART.c
+++ b/ports/nrf/common-hal/busio/UART.c
@@ -52,33 +52,6 @@
static uint32_t get_nrf_baud (uint32_t baudrate);
-static uint16_t ringbuf_count(ringbuf_t *r)
-{
- volatile int count = r->iput - r->iget;
- if ( count < 0 ) {
- count += r->size;
- }
-
- return (uint16_t) count;
-}
-
-static void ringbuf_clear(ringbuf_t *r)
-{
- r->iput = r->iget = 0;
-}
-
-// will overwrite old data
-static void ringbuf_put_n(ringbuf_t* r, uint8_t* buf, uint8_t bufsize)
-{
- for(uint8_t i=0; i < bufsize; i++) {
- if ( ringbuf_put(r, buf[i]) < 0 ) {
- // if full overwrite old data
- (void) ringbuf_get(r);
- ringbuf_put(r, buf[i]);
- }
- }
-}
-
static void uart_callback_irq (const nrfx_uarte_event_t * event, void * context) {
busio_uart_obj_t* self = (busio_uart_obj_t*) context;
@@ -145,16 +118,20 @@ void common_hal_busio_uart_construct (busio_uart_obj_t *self,
// Init buffer for rx
if ( rx != mp_const_none ) {
- self->rbuf.buf = (uint8_t *) gc_alloc(receiver_buffer_size, false, false);
+ // Initially allocate the UART's buffer in the long-lived part of the
+ // heap. UARTs are generally long-lived objects, but the "make long-
+ // lived" machinery is incapable of moving internal pointers like
+ // self->buffer, so do it manually. (However, as long as internal
+ // pointers like this are NOT moved, allocating the buffer
+ // in the long-lived pool is not strictly necessary)
+ // (This is a macro.)
+ ringbuf_alloc(&self->rbuf, receiver_buffer_size, true);
if ( !self->rbuf.buf ) {
nrfx_uarte_uninit(&self->uarte);
mp_raise_msg(&mp_type_MemoryError, translate("Failed to allocate RX buffer"));
}
- self->rbuf.size = receiver_buffer_size;
- self->rbuf.iget = self->rbuf.iput = 0;
-
self->rx_pin_number = rx->number;
claim_pin(rx);
}
diff --git a/ports/nrf/sd_mutex.c b/ports/nrf/sd_mutex.c
new file mode 100644
index 000000000..7682ffa62
--- /dev/null
+++ b/ports/nrf/sd_mutex.c
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#include "py/runtime.h"
+#include "nrf_soc.h"
+
+void sd_mutex_acquire_check(nrf_mutex_t* p_mutex) {
+ uint32_t err_code = sd_mutex_acquire(p_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to acquire mutex, err 0x%04x"), err_code);
+ }
+}
+
+void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex) {
+ while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
+#ifdef MICROPY_VM_HOOK_LOOP
+ MICROPY_VM_HOOK_LOOP
+#endif
+ }
+}
+
+void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex) {
+ while (sd_mutex_acquire(p_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
+ }
+}
+
+void sd_mutex_release_check(nrf_mutex_t* p_mutex) {
+ uint32_t err_code = sd_mutex_release(p_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to release mutex, err 0x%04x"), err_code);
+ }
+}
diff --git a/ports/nrf/sd_mutex.h b/ports/nrf/sd_mutex.h
new file mode 100644
index 000000000..ca4691720
--- /dev/null
+++ b/ports/nrf/sd_mutex.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_NRF_SD_MUTEX_H
+#define MICROPY_INCLUDED_NRF_SD_MUTEX_H
+
+#include "nrf_soc.h"
+
+// Helpers for common usage of nrf_mutex.
+
+// Try to acquire a mutex right now. Raise exception if we can't get it.
+void sd_mutex_acquire_check(nrf_mutex_t* p_mutex);
+
+// Wait for a mutex to become available. Run VM background tasks while waiting.
+void sd_mutex_acquire_wait(nrf_mutex_t* p_mutex);
+
+// Wait for a mutex to become available.. Block VM while waiting.
+void sd_mutex_acquire_wait_no_vm(nrf_mutex_t* p_mutex);
+
+// Release a mutex, and raise exception on error.
+void sd_mutex_release_check(nrf_mutex_t* p_mutex);
+
+#endif // MICROPY_INCLUDED_NRF_SD_MUTEX_H
diff --git a/py/ringbuf.h b/py/ringbuf.h
index c62e20e1d..5f82cc096 100644
--- a/py/ringbuf.h
+++ b/py/ringbuf.h
@@ -26,6 +26,8 @@
#ifndef MICROPY_INCLUDED_PY_RINGBUF_H
#define MICROPY_INCLUDED_PY_RINGBUF_H
+#include "py/gc.h"
+
#include <stdint.h>
typedef struct _ringbuf_t {
@@ -40,9 +42,9 @@ typedef struct _ringbuf_t {
// ringbuf_t buf = {buf_array, sizeof(buf_array)};
// Dynamic initialization. This creates root pointer!
-#define ringbuf_alloc(r, sz) \
+#define ringbuf_alloc(r, sz, long_lived) \
{ \
- (r)->buf = m_new(uint8_t, sz); \
+ (r)->buf = gc_alloc(sz, false, long_lived); \
(r)->size = sz; \
(r)->iget = (r)->iput = 0; \
}
@@ -71,4 +73,30 @@ static inline int ringbuf_put(ringbuf_t *r, uint8_t v) {
return 0;
}
+static inline uint16_t ringbuf_count(ringbuf_t *r)
+{
+ volatile int count = r->iput - r->iget;
+ if ( count < 0 ) {
+ count += r->size;
+ }
+
+ return (uint16_t) count;
+}
+
+static inline void ringbuf_clear(ringbuf_t *r)
+{
+ r->iput = r->iget = 0;
+}
+
+// will overwrite old data
+static inline void ringbuf_put_n(ringbuf_t* r, uint8_t* buf, uint8_t bufsize)
+{
+ for(uint8_t i=0; i < bufsize; i++) {
+ if ( ringbuf_put(r, buf[i]) < 0 ) {
+ // if full overwrite old data
+ (void) ringbuf_get(r);
+ ringbuf_put(r, buf[i]);
+ }
+ }
+}
#endif // MICROPY_INCLUDED_PY_RINGBUF_H
diff --git a/py/stream.c b/py/stream.c
index aca9b8460..9d8be445c 100644
--- a/py/stream.c
+++ b/py/stream.c
@@ -103,7 +103,7 @@ STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte fl
// CPython does a readall, but here we silently let negatives through,
// and they will cause a MemoryError.
mp_int_t sz;
- if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) {
+ if (n_args == 1 || args[1] == mp_const_none || ((sz = mp_obj_get_int(args[1])) == -1)) {
return stream_readall(args[0]);
}
diff --git a/shared-bindings/bleio/CharacteristicBuffer.c b/shared-bindings/bleio/CharacteristicBuffer.c
index 2bb4448c4..32629ca19 100644
--- a/shared-bindings/bleio/CharacteristicBuffer.c
+++ b/shared-bindings/bleio/CharacteristicBuffer.c
@@ -24,10 +24,21 @@
* THE SOFTWARE.
*/
+#include "py/mperrno.h"
+#include "py/ioctl.h"
#include "py/objproperty.h"
#include "py/runtime.h"
+#include "py/stream.h"
+
#include "shared-bindings/bleio/CharacteristicBuffer.h"
#include "shared-bindings/bleio/UUID.h"
+#include "shared-bindings/util.h"
+
+STATIC void raise_error_if_not_connected(bleio_characteristic_buffer_obj_t *self) {
+ if (!common_hal_bleio_characteristic_buffer_connected(self)) {
+ mp_raise_ValueError(translate("Not connected"));
+ }
+}
//| .. currentmodule:: bleio
//|
@@ -36,27 +47,34 @@
//|
//| Accumulates a Characteristic's incoming values in a FIFO buffer.
//|
-//| .. class:: CharacteristicBuffer(Characteristic, buffer_size=0)
+//| .. class:: CharacteristicBuffer(Characteristic, *, timeout=1, buffer_size=64)
//|
//| Create a new Characteristic object identified by the specified UUID.
//|
//| :param bleio.Characteristic characteristic: The characteristic to monitor
+//| :param int timeout: the timeout in seconds to wait for the first character and between subsequent characters.//|
//| :param int buffer_size: Size of ring buffer that stores incoming data coming from client.
//| Must be >= 1.
//|
STATIC mp_obj_t bleio_characteristic_buffer_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_characteristic, ARG_buffer_size, };
+ enum { ARG_characteristic, ARG_timeout, ARG_buffer_size, };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ },
- { MP_QSTR_buffer_size, MP_ARG_REQUIRED | MP_ARG_INT },
+ { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} },
+ { MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
const mp_obj_t characteristic = args[ARG_characteristic].u_obj;
- const int buffer_size = args[ARG_buffer_size].u_int;
+ mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
+ if (timeout < 0.0f) {
+ mp_raise_ValueError(translate("timeout must be >= 0.0"));
+ }
+
+ const int buffer_size = args[ARG_buffer_size].u_int;
if (buffer_size < 1) {
mp_raise_ValueError(translate("buffer_size must be >= 1"));
}
@@ -69,30 +87,117 @@ STATIC mp_obj_t bleio_characteristic_buffer_make_new(const mp_obj_type_t *type,
self->base.type = &bleio_characteristic_buffer_type;
self->characteristic = MP_OBJ_TO_PTR(characteristic);
- common_hal_bleio_characteristic_buffer_construct(self, self->characteristic, buffer_size);
+ common_hal_bleio_characteristic_buffer_construct(self, self->characteristic, timeout, buffer_size);
return MP_OBJ_FROM_PTR(self);
}
-
-//| .. method:: read()
+// These are standard stream methods. Code is in py/stream.c.
+//
+//| .. method:: read(nbytes=None)
+//|
+//| Read characters. If ``nbytes`` is specified then read at most that many
+//| bytes. Otherwise, read everything that arrives until the connection
+//| times out. Providing the number of bytes expected is highly recommended
+//| because it will be faster.
+//|
+//| :return: Data read
+//| :rtype: bytes or None
+//|
+//| .. method:: readinto(buf)
//|
-//| Read a single byte from the buffer. If no character is available, return None.
-STATIC mp_obj_t bleio_characteristic_buffer_read(mp_obj_t self_in) {
+//| Read bytes into the ``buf``. Read at most ``len(buf)`` bytes.
+//|
+//| :return: number of bytes read and stored into ``buf``
+//| :rtype: int or None (on a non-blocking error)
+//|
+//| .. method:: readline()
+//|
+//| Read a line, ending in a newline character.
+//|
+//| :return: the line read
+//| :rtype: int or None
+//|
+
+// These three methods are used by the shared stream methods.
+STATIC mp_uint_t bleio_characteristic_buffer_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_bleio_characteristic_buffer_deinited(self));
+ raise_error_if_not_connected(self);
+ byte *buf = buf_in;
- int byte = common_hal_bleio_characteristic_buffer_read(self);
- if (byte == -1) {
- return mp_const_none;
+ // make sure we want at least 1 char
+ if (size == 0) {
+ return 0;
}
- return MP_OBJ_NEW_SMALL_INT(byte);
+ return common_hal_bleio_characteristic_buffer_read(self, buf, size, errcode);
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_read_obj, bleio_characteristic_buffer_read);
+
+STATIC mp_uint_t bleio_characteristic_buffer_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
+ mp_raise_NotImplementedError(translate("CharacteristicBuffer writing not provided"));
+ return 0;
+}
+
+STATIC mp_uint_t bleio_characteristic_buffer_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
+ bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_bleio_characteristic_buffer_deinited(self));
+ raise_error_if_not_connected(self);
+ if (!common_hal_bleio_characteristic_buffer_connected(self)) {
+ mp_raise_ValueError(translate("Not connected."));
+ }
+ mp_uint_t ret;
+ if (request == MP_IOCTL_POLL) {
+ mp_uint_t flags = arg;
+ ret = 0;
+ if ((flags & MP_IOCTL_POLL_RD) && common_hal_bleio_characteristic_buffer_rx_characters_available(self) > 0) {
+ ret |= MP_IOCTL_POLL_RD;
+ }
+// No writing provided.
+// if ((flags & MP_IOCTL_POLL_WR) && common_hal_busio_uart_ready_to_tx(self)) {
+// ret |= MP_IOCTL_POLL_WR;
+// }
+ } else {
+ *errcode = MP_EINVAL;
+ ret = MP_STREAM_ERROR;
+ }
+ return ret;
+}
+
+//| .. attribute:: in_waiting
+//|
+//| The number of bytes in the input buffer, available to be read
+//|
+STATIC mp_obj_t bleio_characteristic_buffer_obj_get_in_waiting(mp_obj_t self_in) {
+ bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_bleio_characteristic_buffer_deinited(self));
+ return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_buffer_rx_characters_available(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_get_in_waiting_obj, bleio_characteristic_buffer_obj_get_in_waiting);
+
+const mp_obj_property_t bleio_characteristic_buffer_in_waiting_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&bleio_characteristic_buffer_get_in_waiting_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//| .. method:: reset_input_buffer()
+//|
+//| Discard any unread characters in the input buffer.
+//|
+STATIC mp_obj_t bleio_characteristic_buffer_obj_reset_input_buffer(mp_obj_t self_in) {
+ bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ raise_error_if_deinited(common_hal_bleio_characteristic_buffer_deinited(self));
+ common_hal_bleio_characteristic_buffer_clear_rx_buffer(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_reset_input_buffer_obj, bleio_characteristic_buffer_obj_reset_input_buffer);
//| .. method:: deinit()
//|
//| Disable permanently.
+//|
STATIC mp_obj_t bleio_characteristic_buffer_deinit(mp_obj_t self_in) {
bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_characteristic_buffer_deinit(self);
@@ -101,15 +206,39 @@ STATIC mp_obj_t bleio_characteristic_buffer_deinit(mp_obj_t self_in) {
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_deinit_obj, bleio_characteristic_buffer_deinit);
STATIC const mp_rom_map_elem_t bleio_characteristic_buffer_locals_dict_table[] = {
- { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&bleio_characteristic_buffer_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bleio_characteristic_buffer_deinit_obj) },
+
+ // Standard stream methods.
+ { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)},
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+ // CharacteristicBuffer is currently read-only.
+ // { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&bleio_characteristic_buffer_reset_input_buffer_obj) },
+ // Properties
+ { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&bleio_characteristic_buffer_in_waiting_obj) },
+
};
STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_buffer_locals_dict, bleio_characteristic_buffer_locals_dict_table);
+STATIC const mp_stream_p_t characteristic_buffer_stream_p = {
+ .read = bleio_characteristic_buffer_read,
+ .write = bleio_characteristic_buffer_write,
+ .ioctl = bleio_characteristic_buffer_ioctl,
+ .is_text = false,
+ // Match PySerial when possible, such as disallowing optional length argument for .readinto()
+ .pyserial_compatibility = true,
+};
+
+
const mp_obj_type_t bleio_characteristic_buffer_type = {
{ &mp_type_type },
.name = MP_QSTR_CharacteristicBuffer,
.make_new = bleio_characteristic_buffer_make_new,
+ .getiter = mp_identity_getiter,
+ .iternext = mp_stream_unbuffered_iter,
+ .protocol = &characteristic_buffer_stream_p,
.locals_dict = (mp_obj_dict_t*)&bleio_characteristic_buffer_locals_dict
};
diff --git a/shared-bindings/bleio/CharacteristicBuffer.h b/shared-bindings/bleio/CharacteristicBuffer.h
index c5d7580da..f25017c19 100644
--- a/shared-bindings/bleio/CharacteristicBuffer.h
+++ b/shared-bindings/bleio/CharacteristicBuffer.h
@@ -31,9 +31,12 @@
extern const mp_obj_type_t bleio_characteristic_buffer_type;
-extern void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size);
-// Returns a uint8_t byte value, or -1 if no data is available.
-int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self);
+extern void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_float_t timeout, size_t buffer_size);
+int common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode);
+uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self);
+void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self);
+bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self);
int common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self);
+bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H
diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c
index cd0ca3f36..85bf498a4 100644
--- a/shared-bindings/busio/UART.c
+++ b/shared-bindings/busio/UART.c
@@ -62,7 +62,7 @@
//|
//| *New in CircuitPython 4.0:* ``timeout`` has incompatibly changed units from milliseconds to seconds.
//| The new upper limit on ``timeout`` is meant to catch mistaken use of milliseconds.
-
+//|
typedef struct {
mp_obj_base_t base;
} busio_uart_parity_obj_t;
@@ -172,7 +172,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(busio_uart___exit___obj, 4, 4, busio_
//| Read bytes into the ``buf``. Read at most ``len(buf)`` bytes.
//|
//| :return: number of bytes read and stored into ``buf``
-//| :rtype: bytes or None
+//| :rtype: int or None (on a non-blocking error)
//|
//| *New in CircuitPython 4.0:* No length parameter is permitted.