From 70680d5b22aa6cb797ccf26645d553a4c72b5100 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 2 Aug 2019 16:17:38 -0700 Subject: EPaper displays work mostly. --- shared-module/displayio/ColorConverter.c | 90 ++++++ shared-module/displayio/ColorConverter.h | 3 + shared-module/displayio/Display.c | 30 +- shared-module/displayio/Display.h | 2 +- shared-module/displayio/EPaperDisplay.c | 531 +++++++++++++++++++++++++++++++ shared-module/displayio/EPaperDisplay.h | 92 ++++++ shared-module/displayio/FourWire.c | 27 +- shared-module/displayio/Group.c | 3 + shared-module/displayio/I2CDisplay.c | 23 +- shared-module/displayio/OnDiskBitmap.c | 49 +-- shared-module/displayio/Palette.c | 18 +- shared-module/displayio/Palette.h | 8 +- shared-module/displayio/TileGrid.c | 1 + shared-module/displayio/__init__.c | 91 ++++-- shared-module/displayio/__init__.h | 6 +- 15 files changed, 891 insertions(+), 83 deletions(-) create mode 100644 shared-module/displayio/EPaperDisplay.c create mode 100644 shared-module/displayio/EPaperDisplay.h (limited to 'shared-module') diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index 6b9bd5840..0277c79f0 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -45,14 +45,104 @@ uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888) { return (r8 * 19) / 255 + (g8 * 182) / 255 + (b8 + 54) / 255; } +void compute_bounds(uint8_t r8, uint8_t g8, uint8_t b8, uint8_t* min, uint8_t* max) { + if (r8 > g8) { + if (b8 > r8) { + *max = b8; + } else { + *max = r8; + } + if (b8 < g8) { + *min = b8; + } else { + *min = g8; + } + } else { + if (b8 > g8) { + *max = b8; + } else { + *max = g8; + } + if (b8 < r8) { + *min = b8; + } else { + *min = r8; + } + } +} + +uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888) { + uint32_t r8 = (color_rgb888 >> 16); + uint32_t g8 = (color_rgb888 >> 8) & 0xff; + uint32_t b8 = color_rgb888 & 0xff; + uint8_t max; + uint8_t min; + compute_bounds(r8, g8, b8, &min, &max); + return max - min; +} + +uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888) { + uint32_t r8 = (color_rgb888 >> 16); + uint32_t g8 = (color_rgb888 >> 8) & 0xff; + uint32_t b8 = color_rgb888 & 0xff; + uint8_t max; + uint8_t min; + compute_bounds(r8, g8, b8, &min, &max); + uint8_t c = max - min; + if (c == 0) { + return 0; + } + + int32_t hue = 0; + if (max == r8) { + hue = (((int32_t) (g8 - b8) * 40) / c) % 240; + } else if (max == g8) { + hue = (((int32_t) (b8 - r8) + (2 * c)) * 40) / c; + } else if (max == b8) { + hue = (((int32_t) (r8 - g8) + (4 * c)) * 40) / c; + } + if (hue < 0) { + hue += 240; + } + + return hue; +} + +void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t* colorspace, uint8_t pixel_hue, uint8_t pixel_luma, uint32_t* color) { + + int16_t hue_diff = colorspace->tricolor_hue - pixel_hue; + if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) { + if (colorspace->grayscale) { + *color = 0; + } else { + *color = 1; + } + } else if (!colorspace->grayscale) { + *color = 0; + } +} + bool displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t* colorspace, uint32_t input_color, uint32_t* output_color) { if (colorspace->depth == 16) { *output_color = displayio_colorconverter_compute_rgb565(input_color); return true; + } else if (colorspace->tricolor) { + uint8_t luma = displayio_colorconverter_compute_luma(input_color); + *output_color = luma >> (8 - colorspace->depth); + if (displayio_colorconverter_compute_chroma(input_color) <= 16) { + if (!colorspace->grayscale) { + *output_color = 0; + } + return true; + } + uint8_t pixel_hue = displayio_colorconverter_compute_hue(input_color); + displayio_colorconverter_compute_tricolor(colorspace, pixel_hue, luma, output_color); + return true; } else if (colorspace->grayscale && colorspace->depth <= 8) { uint8_t luma = displayio_colorconverter_compute_luma(input_color); *output_color = luma >> (8 - colorspace->depth); return true; + } else if (!colorspace->grayscale && colorspace->depth == 1) { } return false; } diff --git a/shared-module/displayio/ColorConverter.h b/shared-module/displayio/ColorConverter.h index c48b98e6d..c79e6a2f7 100644 --- a/shared-module/displayio/ColorConverter.h +++ b/shared-module/displayio/ColorConverter.h @@ -42,5 +42,8 @@ void displayio_colorconverter_finish_refresh(displayio_colorconverter_t *self); bool displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t* colorspace, uint32_t input_color, uint32_t* output_color); uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888); +uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888); +uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888); +void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t* colorspace, uint8_t pixel_hue, uint8_t pixel_luma, uint32_t* color); #endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_COLORCONVERTER_H diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index d11813b8c..97a18740a 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -40,8 +40,6 @@ #include "tick.h" -#define DELAY 0x80 - void common_hal_displayio_display_construct(displayio_display_obj_t* self, mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, @@ -97,10 +95,10 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, uint8_t full_command[data_size + 1]; full_command[0] = cmd[0]; memcpy(full_command + 1, data, data_size); - self->send(self->bus, true, full_command, data_size + 1); + self->send(self->bus, true, true, full_command, data_size + 1); } else { - self->send(self->bus, true, cmd, 1); - self->send(self->bus, false, data, data_size); + self->send(self->bus, true, true, cmd, 1); + self->send(self->bus, false, false, data, data_size); } uint16_t delay_length_ms = 10; if (delay) { @@ -232,6 +230,9 @@ const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_ob self->area.next = NULL; return &self->area; } else { + if (self->current_group == NULL || self->current_group->base.type != &displayio_group_type) { + asm("bkpt"); + } return displayio_group_get_refresh_areas(self->current_group, NULL); } } @@ -283,12 +284,12 @@ bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, if (ok) { if (self->data_as_commands) { uint8_t set_brightness[2] = {self->brightness_command, (uint8_t) (0xff * brightness)}; - self->send(self->bus, true, set_brightness, 2); + self->send(self->bus, true, true, set_brightness, 2); } else { uint8_t command = self->brightness_command; uint8_t hex_brightness = 0xff * brightness; - self->send(self->bus, true, &command, 1); - self->send(self->bus, false, &hex_brightness, 1); + self->send(self->bus, true, true, &command, 1); + self->send(self->bus, false, false, &hex_brightness, 1); } self->end_transaction(self->bus); } @@ -331,7 +332,7 @@ void displayio_display_set_region_to_update(displayio_display_obj_t* self, displ data[0] = self->set_column_command; uint8_t data_length = 1; if (!self->data_as_commands) { - self->send(self->bus, true, data, 1); + self->send(self->bus, true, true, data, 1); data_length = 0; } if (self->single_byte_bounds) { @@ -345,13 +346,13 @@ void displayio_display_set_region_to_update(displayio_display_obj_t* self, displ data[data_length++] = x2 >> 8; data[data_length++] = x2 & 0xff; } - self->send(self->bus, self->data_as_commands, data, data_length); + self->send(self->bus, self->data_as_commands, self->data_as_commands, data, data_length); // Set row. data[0] = self->set_row_command; data_length = 1; if (!self->data_as_commands) { - self->send(self->bus, true, data, 1); + self->send(self->bus, true, true, data, 1); data_length = 0; } if (self->single_byte_bounds) { @@ -365,7 +366,7 @@ void displayio_display_set_region_to_update(displayio_display_obj_t* self, displ data[data_length++] = y2 >> 8; data[data_length++] = y2 & 0xff; } - self->send(self->bus, self->data_as_commands, data, data_length); + self->send(self->bus, self->data_as_commands, self->data_as_commands, data, data_length); } void displayio_display_start_refresh(displayio_display_obj_t* self) { @@ -391,9 +392,9 @@ void displayio_display_finish_refresh(displayio_display_obj_t* self) { void displayio_display_send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) { if (!self->data_as_commands) { - self->send(self->bus, true, &self->write_ram_command, 1); + self->send(self->bus, true, true, &self->write_ram_command, 1); } - self->send(self->bus, false, pixels, length); + self->send(self->bus, false, false, pixels, length); } void displayio_display_update_backlight(displayio_display_obj_t* self) { @@ -414,7 +415,6 @@ void release_display(displayio_display_obj_t* self) { if (self->current_group != NULL) { self->current_group->in_group = false; } - if (self->backlight_pwm.base.type == &pulseio_pwmout_type) { common_hal_pulseio_pwmout_reset_ok(&self->backlight_pwm); common_hal_pulseio_pwmout_deinit(&self->backlight_pwm); diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index 74ae175b1..c85d69b0e 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -34,7 +34,7 @@ #include "shared-module/displayio/area.h" typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); -typedef void (*display_bus_send)(mp_obj_t bus, bool command, uint8_t *data, uint32_t data_length); +typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); typedef void (*display_bus_end_transaction)(mp_obj_t bus); typedef struct { diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c new file mode 100644 index 000000000..5e4bdc340 --- /dev/null +++ b/shared-module/displayio/EPaperDisplay.c @@ -0,0 +1,531 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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 "shared-bindings/displayio/EPaperDisplay.h" + +#include "py/runtime.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/FourWire.h" +#include "shared-bindings/displayio/I2CDisplay.h" +#include "shared-bindings/displayio/ParallelBus.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/time/__init__.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/display.h" +#include "supervisor/usb.h" + +#include +#include + +#include "tick.h" + +void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self, + mp_obj_t bus, uint8_t* start_sequence, uint16_t start_sequence_len, uint8_t* stop_sequence, uint16_t stop_sequence_len, + uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, + int16_t colstart, int16_t rowstart, uint16_t rotation, + uint16_t set_column_window_command, uint16_t set_row_window_command, + uint16_t set_current_column_command, uint16_t set_current_row_command, + uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t third_color, uint16_t refresh_display_command, + const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select) { + self->colorspace.depth = 1; + self->colorspace.grayscale = true; + self->colorspace.pixels_in_byte_share_row = true; + self->colorspace.bytes_per_cell = 1; + self->colorspace.reverse_pixels_in_byte = true; + + if (third_color != 0x000000) { + self->colorspace.tricolor = true; + self->colorspace.tricolor_hue = displayio_colorconverter_compute_hue(third_color); + self->colorspace.tricolor_luma = displayio_colorconverter_compute_luma(third_color); + } + + self->set_column_window_command = set_column_window_command; + self->set_row_window_command = set_row_window_command; + self->set_current_column_command = set_current_column_command; + self->set_current_row_command = set_current_row_command; + self->write_black_ram_command = write_black_ram_command; + self->black_bits_inverted = black_bits_inverted; + self->write_color_ram_command = write_color_ram_command; + self->color_bits_inverted = color_bits_inverted; + self->refresh_display_command = refresh_display_command; + self->busy_state = busy_state; + self->refresh = true; + self->current_group = NULL; + self->colstart = colstart; + self->rowstart = rowstart; + self->refreshing = false; + self->milliseconds_per_frame = seconds_per_frame * 1000; + self->always_toggle_chip_select = always_toggle_chip_select; + + self->start_sequence = start_sequence; + self->start_sequence_len = start_sequence_len; + self->stop_sequence = stop_sequence; + self->stop_sequence_len = stop_sequence_len; + + + if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) { + self->bus_reset = common_hal_displayio_parallelbus_reset; + self->bus_free = common_hal_displayio_parallelbus_bus_free; + self->begin_transaction = common_hal_displayio_parallelbus_begin_transaction; + self->send = common_hal_displayio_parallelbus_send; + self->end_transaction = common_hal_displayio_parallelbus_end_transaction; + } else if (MP_OBJ_IS_TYPE(bus, &displayio_fourwire_type)) { + self->bus_reset = common_hal_displayio_fourwire_reset; + self->bus_free = common_hal_displayio_fourwire_bus_free; + self->begin_transaction = common_hal_displayio_fourwire_begin_transaction; + self->send = common_hal_displayio_fourwire_send; + self->end_transaction = common_hal_displayio_fourwire_end_transaction; + } else if (MP_OBJ_IS_TYPE(bus, &displayio_i2cdisplay_type)) { + self->bus_reset = common_hal_displayio_i2cdisplay_reset; + self->bus_free = common_hal_displayio_i2cdisplay_bus_free; + self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction; + self->send = common_hal_displayio_i2cdisplay_send; + self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction; + } else { + mp_raise_ValueError(translate("Unsupported display bus type")); + } + self->bus = bus; + + supervisor_start_terminal(width, height); + + self->width = width; + self->height = height; + rotation = rotation % 360; + self->transform.x = 0; + self->transform.y = 0; + self->transform.scale = 1; + self->transform.mirror_x = false; + self->transform.mirror_y = false; + self->transform.transpose_xy = false; + if (rotation == 0 || rotation == 180) { + if (rotation == 180) { + self->transform.mirror_x = true; + self->transform.mirror_y = true; + } + } else { + self->transform.transpose_xy = true; + if (rotation == 270) { + self->transform.mirror_y = true; + } else { + self->transform.mirror_x = true; + } + } + + self->ram_width = ram_width; + self->ram_height = ram_height; + + self->area.x1 = 0; + self->area.y1 = 0; + self->area.next = NULL; + + self->transform.dx = 1; + self->transform.dy = 1; + if (self->transform.transpose_xy) { + self->area.x2 = height; + self->area.y2 = width; + if (self->transform.mirror_x) { + self->transform.x = height; + self->transform.dx = -1; + } + if (self->transform.mirror_y) { + self->transform.y = width; + self->transform.dy = -1; + } + } else { + self->area.x2 = width; + self->area.y2 = height; + if (self->transform.mirror_x) { + self->transform.x = width; + self->transform.dx = -1; + } + if (self->transform.mirror_y) { + self->transform.y = height; + self->transform.dy = -1; + } + } + + self->busy.base.type = &mp_type_NoneType; + if (busy_pin != NULL) { + self->busy.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&self->busy, busy_pin); + never_reset_pin_number(busy_pin->number); + } + + // Set the group after initialization otherwise we may send pixels while we delay in + // initialization. + common_hal_displayio_epaperdisplay_show(self, &circuitpython_splash); +} + +bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self, displayio_group_t* root_group) { + if (root_group == NULL && !circuitpython_splash.in_group) { + root_group = &circuitpython_splash; + } + if (root_group == self->current_group) { + return true; + } + if (root_group != NULL && root_group->in_group) { + return false; + } + if (self->current_group != NULL) { + self->current_group->in_group = false; + } + if (root_group != NULL) { + displayio_group_update_transform(root_group, &self->transform); + root_group->in_group = true; + self->current_group = root_group; + } + self->full_refresh = true; + common_hal_displayio_epaperdisplay_refresh_soon(self); + return true; +} + +void common_hal_displayio_epaperdisplay_refresh_soon(displayio_epaperdisplay_obj_t* self) { + self->refresh = true; +} + +const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self) { + const displayio_area_t* first_area; + if (self->current_group == NULL || self->current_group->base.type != &displayio_group_type) { + asm("bkpt"); + } + if (self->full_refresh) { + first_area = &self->area; + } else { + first_area = displayio_group_get_refresh_areas(self->current_group, NULL); + } + if (first_area != NULL && self->set_row_window_command == NO_COMMAND) { + self->area.next = NULL; + return &self->area; + } + return first_area; +} + +int32_t common_hal_displayio_epaperdisplay_wait_for_frame(displayio_epaperdisplay_obj_t* self) { + uint64_t last_refresh = self->last_refresh; + // Don't try to refresh if we got an exception. + while (last_refresh == self->last_refresh && MP_STATE_VM(mp_pending_exception) == NULL) { + MICROPY_VM_HOOK_LOOP + } + return 0; +} + +uint16_t common_hal_displayio_epaperdisplay_get_width(displayio_epaperdisplay_obj_t* self){ + return self->width; +} + +uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_obj_t* self){ + return self->height; +} + +bool displayio_epaperdisplay_bus_free(displayio_epaperdisplay_obj_t *self) { + return self->bus_free(self->bus); +} + +bool displayio_epaperdisplay_begin_transaction(displayio_epaperdisplay_obj_t* self) { + return self->begin_transaction(self->bus); +} + +void displayio_epaperdisplay_end_transaction(displayio_epaperdisplay_obj_t* self) { + self->end_transaction(self->bus); +} + +STATIC void wait_for_busy(displayio_epaperdisplay_obj_t* self) { + if (self->busy.base.type == &mp_type_NoneType) { + return; + } + while (common_hal_digitalio_digitalinout_get_value(&self->busy) == self->busy_state) { + #ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP + #endif + } +} + +STATIC void send_command_sequence(displayio_epaperdisplay_obj_t* self, bool should_wait_for_busy, uint8_t* sequence, uint32_t sequence_len) { + uint32_t i = 0; + while (i < sequence_len) { + uint8_t *cmd = sequence + i; + uint8_t data_size = *(cmd + 1); + bool delay = (data_size & DELAY) != 0; + data_size &= ~DELAY; + uint8_t *data = cmd + 2; + self->begin_transaction(self->bus); + self->send(self->bus, true, self->always_toggle_chip_select, cmd, 1); + self->send(self->bus, false, self->always_toggle_chip_select, data, data_size); + self->end_transaction(self->bus); + uint16_t delay_length_ms = 0; + if (delay) { + data_size++; + delay_length_ms = *(cmd + 1 + data_size); + if (delay_length_ms == 255) { + delay_length_ms = 500; + } + } + common_hal_time_delay_ms(delay_length_ms); + if (should_wait_for_busy) { + wait_for_busy(self); + } + i += 2 + data_size; + } +} + +void displayio_epaperdisplay_set_region_to_update(displayio_epaperdisplay_obj_t* self, displayio_area_t* area) { + if (self->set_row_window_command == NO_COMMAND) { + return; + } + uint16_t x1 = area->x1; + uint16_t x2 = area->x2; + uint16_t y1 = area->y1; + uint16_t y2 = area->y2; + // Collapse down the dimension where multiple pixels are in a byte. + uint8_t pixels_per_byte = 8 / self->colorspace.depth; + x1 /= pixels_per_byte * self->colorspace.bytes_per_cell; + x2 /= pixels_per_byte * self->colorspace.bytes_per_cell; + + + // Set column. + uint8_t data[5]; + data[0] = self->set_column_window_command; + self->send(self->bus, true, self->always_toggle_chip_select, data, 1); + uint8_t data_length = 0; + if (self->ram_width / pixels_per_byte < 0x100) { + data[data_length++] = x1 + self->colstart; + data[data_length++] = x2 - 1 + self->colstart; + } else { + x1 += self->colstart; + x2 += self->colstart - 1; + data[data_length++] = x1 >> 8; + data[data_length++] = x1 & 0xff; + data[data_length++] = x2 >> 8; + data[data_length++] = x2 & 0xff; + } + self->send(self->bus, false, self->always_toggle_chip_select, data, data_length); + if (self->set_current_column_command != NO_COMMAND) { + uint8_t command = self->set_current_column_command; + self->send(self->bus, true, self->always_toggle_chip_select, &command, 1); + self->send(self->bus, false, self->always_toggle_chip_select, data, data_length / 2); + } + + // Set row. + data[0] = self->set_row_window_command; + self->send(self->bus, true, self->always_toggle_chip_select, data, 1); + data_length = 0; + if (self->ram_height < 0x100) { + data[data_length++] = y1 + self->rowstart; + data[data_length++] = y2 - 1 + self->rowstart; + } else { + y1 += self->rowstart; + y2 += self->rowstart - 1; + data[data_length++] = y1 & 0xff; + data[data_length++] = y1 >> 8; + data[data_length++] = y2 & 0xff; + data[data_length++] = y2 >> 8; + } + self->send(self->bus, false, self->always_toggle_chip_select, data, data_length); + if (self->set_current_row_command != NO_COMMAND) { + uint8_t command = self->set_current_row_command; + self->send(self->bus, true, self->always_toggle_chip_select, &command, 1); + self->send(self->bus, false, self->always_toggle_chip_select, data, data_length / 2); + } +} + +void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self) { + // run start sequence + self->bus_reset(self->bus); + + send_command_sequence(self, true, self->start_sequence, self->start_sequence_len); + self->last_refresh = ticks_ms; +} + +bool displayio_epaperdisplay_frame_queued(displayio_epaperdisplay_obj_t* self) { + if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) { + if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) { + self->refreshing = false; + // Run stop sequence but don't wait for busy because busy is set when sleeping. + send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); + } else { + return false; + } + } + if (self->current_group == NULL) { + return false; + } + // Refresh at seconds per frame rate. + return (ticks_ms - self->last_refresh) > self->milliseconds_per_frame; +} + +void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) { + // Actually refresh the display now that all pixel RAM has been updated. + displayio_epaperdisplay_begin_transaction(self); + self->send(self->bus, true, self->always_toggle_chip_select, &self->refresh_display_command, 1); + displayio_epaperdisplay_end_transaction(self); + self->refreshing = true; + + if (self->current_group != NULL) { + displayio_group_finish_refresh(self->current_group); + } + self->refresh = false; + self->full_refresh = false; + self->last_refresh = ticks_ms; +} + +void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length) { +} + + +bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, const displayio_area_t* area) { + uint16_t buffer_size = 128; // In uint32_ts + + displayio_area_t clipped; + // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. + if (!displayio_epaperdisplay_clip_area(self, area, &clipped)) { + return true; + } + uint16_t subrectangles = 1; + uint16_t rows_per_buffer = displayio_area_height(&clipped); + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->colorspace.depth; + uint16_t pixels_per_buffer = displayio_area_size(&clipped); + if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) { + rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); + if (rows_per_buffer == 0) { + rows_per_buffer = 1; + } + subrectangles = displayio_area_height(&clipped) / rows_per_buffer; + if (displayio_area_height(&clipped) % rows_per_buffer != 0) { + subrectangles++; + } + pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); + buffer_size = pixels_per_buffer / pixels_per_word; + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + } + + // Allocated and shared as a uint32_t array so the compiler knows the + // alignment everywhere. + uint32_t buffer[buffer_size]; + volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask[mask_length]; + + uint8_t passes = 1; + if (self->colorspace.tricolor) { + passes = 2; + } + for (uint8_t pass = 0; pass < passes; pass++) { + uint16_t remaining_rows = displayio_area_height(&clipped); + + displayio_epaperdisplay_begin_transaction(self); + displayio_epaperdisplay_set_region_to_update(self, &clipped); + displayio_epaperdisplay_end_transaction(self); + + uint8_t write_command = self->write_black_ram_command; + if (pass == 1) { + write_command = self->write_color_ram_command; + } + displayio_epaperdisplay_begin_transaction(self); + self->send(self->bus, true, self->always_toggle_chip_select, &write_command, 1); + displayio_epaperdisplay_end_transaction(self); + + for (uint16_t j = 0; j < subrectangles; j++) { + displayio_area_t subrectangle = { + .x1 = clipped.x1, + .y1 = clipped.y1 + rows_per_buffer * j, + .x2 = clipped.x2, + .y2 = clipped.y1 + rows_per_buffer * (j + 1) + }; + if (remaining_rows < rows_per_buffer) { + subrectangle.y2 = subrectangle.y1 + remaining_rows; + } + remaining_rows -= rows_per_buffer; + + + uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->colorspace.depth); + + for (uint16_t k = 0; k < mask_length; k++) { + mask[k] = 0x00000000; + } + for (uint16_t k = 0; k < buffer_size; k++) { + buffer[k] = 0x00000000; + } + + self->colorspace.grayscale = true; + if (pass == 1) { + self->colorspace.grayscale = false; + } + displayio_group_fill_area(self->current_group, &self->colorspace, &subrectangle, mask, buffer); + + // Invert it all. + if ((pass == 1 && self->color_bits_inverted) || + (pass == 0 && self->black_bits_inverted)) { + for (uint16_t k = 0; k < buffer_size; k++) { + buffer[k] = ~buffer[k]; + } + } + + if (!displayio_epaperdisplay_begin_transaction(self)) { + // Can't acquire display bus; skip the rest of the data. Try next display. + return false; + } + self->send(self->bus, false, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes); + displayio_epaperdisplay_end_transaction(self); + + // TODO(tannewt): Make refresh displays faster so we don't starve other + // background tasks. + usb_background(); + } + } + + return true; +} + +void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) { + if (self->current_group != NULL) { + self->current_group->in_group = false; + } + if (self->busy.base.type == &digitalio_digitalinout_type) { + common_hal_digitalio_digitalinout_deinit(&self->busy); + } +} + +bool displayio_epaperdisplay_fill_area(displayio_epaperdisplay_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer) { + return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer); +} + +bool displayio_epaperdisplay_clip_area(displayio_epaperdisplay_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped) { + bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped); + if (!overlaps) { + return false; + } + // Expand the area if we have multiple pixels per byte and we need to byte + // align the bounds. + uint8_t pixels_per_byte = 8; + if (clipped->x1 % pixels_per_byte != 0) { + clipped->x1 -= clipped->x1 % pixels_per_byte; + } + if (clipped->x2 % pixels_per_byte != 0) { + clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte; + } + return true; +} diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h new file mode 100644 index 000000000..737b5987a --- /dev/null +++ b/shared-module/displayio/EPaperDisplay.h @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H +#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H + +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/pulseio/PWMOut.h" + +#include "shared-module/displayio/area.h" + +typedef void (*display_bus_bus_reset)(mp_obj_t bus); +typedef bool (*display_bus_bus_free)(mp_obj_t bus); +typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); +typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); +typedef void (*display_bus_end_transaction)(mp_obj_t bus); + +typedef struct { + mp_obj_base_t base; + mp_obj_t bus; + displayio_group_t *current_group; + uint64_t last_refresh; + display_bus_bus_reset bus_reset; + display_bus_bus_free bus_free; + display_bus_begin_transaction begin_transaction; + display_bus_send send; + display_bus_end_transaction end_transaction; + digitalio_digitalinout_obj_t busy; + uint32_t milliseconds_per_frame; + uint8_t* start_sequence; + uint32_t start_sequence_len; + uint8_t* stop_sequence; + uint32_t stop_sequence_len; + displayio_buffer_transform_t transform; + displayio_area_t area; + uint16_t width; + uint16_t height; + uint16_t ram_width; + uint16_t ram_height; + _displayio_colorspace_t colorspace; + int16_t colstart; + int16_t rowstart; + uint16_t set_column_window_command; + uint16_t set_row_window_command; + uint16_t set_current_column_command; + uint16_t set_current_row_command; + uint16_t write_black_ram_command; + uint16_t write_color_ram_command; + uint8_t refresh_display_command; + uint8_t hue; + bool busy_state; + bool black_bits_inverted; + bool color_bits_inverted; + bool refresh; + bool refreshing; + bool full_refresh; // New group means we need to refresh the whole display. + bool always_toggle_chip_select; +} displayio_epaperdisplay_obj_t; + +bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* display, const displayio_area_t* area); +void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self); +const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self); +bool displayio_epaperdisplay_fill_area(displayio_epaperdisplay_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer); +bool displayio_epaperdisplay_clip_area(displayio_epaperdisplay_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped); +bool displayio_epaperdisplay_bus_free(displayio_epaperdisplay_obj_t *self); +void release_epaperdisplay(displayio_epaperdisplay_obj_t* self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H diff --git a/shared-module/displayio/FourWire.c b/shared-module/displayio/FourWire.c index 913635db8..64ad80665 100644 --- a/shared-module/displayio/FourWire.c +++ b/shared-module/displayio/FourWire.c @@ -59,11 +59,7 @@ void common_hal_displayio_fourwire_construct(displayio_fourwire_obj_t* self, common_hal_digitalio_digitalinout_construct(&self->reset, reset); common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL); never_reset_pin_number(reset->number); - - common_hal_digitalio_digitalinout_set_value(&self->reset, false); - common_hal_mcu_delay_us(10); - common_hal_digitalio_digitalinout_set_value(&self->reset, true); - common_hal_mcu_delay_us(10); + common_hal_displayio_fourwire_reset(self); } never_reset_pin_number(command->number); @@ -80,6 +76,23 @@ void common_hal_displayio_fourwire_deinit(displayio_fourwire_obj_t* self) { reset_pin_number(self->reset.pin->number); } +void common_hal_displayio_fourwire_reset(mp_obj_t obj) { + displayio_fourwire_obj_t* self = MP_OBJ_TO_PTR(obj); + common_hal_digitalio_digitalinout_set_value(&self->reset, false); + common_hal_time_delay_ms(1); + common_hal_digitalio_digitalinout_set_value(&self->reset, true); + common_hal_time_delay_ms(1); +} + +bool common_hal_displayio_fourwire_bus_free(mp_obj_t obj) { + displayio_fourwire_obj_t* self = MP_OBJ_TO_PTR(obj); + if (!common_hal_busio_spi_try_lock(self->bus)) { + return false; + } + common_hal_busio_spi_unlock(self->bus); + return true; +} + bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t obj) { displayio_fourwire_obj_t* self = MP_OBJ_TO_PTR(obj); if (!common_hal_busio_spi_try_lock(self->bus)) { @@ -91,10 +104,10 @@ bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t obj) { return true; } -void common_hal_displayio_fourwire_send(mp_obj_t obj, bool command, uint8_t *data, uint32_t data_length) { +void common_hal_displayio_fourwire_send(mp_obj_t obj, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length) { displayio_fourwire_obj_t* self = MP_OBJ_TO_PTR(obj); common_hal_digitalio_digitalinout_set_value(&self->command, !command); - if (command) { + if (toggle_every_byte) { // Toggle chip select after each command byte in case the display driver // IC latches commands based on it. for (size_t i = 0; i < data_length; i++) { diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 38418a029..eb6e9a453 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -304,6 +304,9 @@ void displayio_group_finish_refresh(displayio_group_t *self) { } displayio_area_t* displayio_group_get_refresh_areas(displayio_group_t *self, displayio_area_t* tail) { + if (self->base.type != &displayio_group_type) { + asm("bkpt"); + } if (self->item_removed) { self->dirty_area.next = tail; tail = &self->dirty_area; diff --git a/shared-module/displayio/I2CDisplay.c b/shared-module/displayio/I2CDisplay.c index 9d8949528..23ebc0889 100644 --- a/shared-module/displayio/I2CDisplay.c +++ b/shared-module/displayio/I2CDisplay.c @@ -59,10 +59,7 @@ void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t* self, common_hal_digitalio_digitalinout_construct(&self->reset, reset); common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL); never_reset_pin_number(reset->number); - - common_hal_digitalio_digitalinout_set_value(&self->reset, false); - common_hal_mcu_delay_us(1); - common_hal_digitalio_digitalinout_set_value(&self->reset, true); + common_hal_displayio_i2cdisplay_reset(self); } } @@ -74,7 +71,17 @@ void common_hal_displayio_i2cdisplay_deinit(displayio_i2cdisplay_obj_t* self) { reset_pin_number(self->reset.pin->number); } -bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t obj) { + +void common_hal_displayio_i2cdisplay_reset(mp_obj_t obj) { + displayio_i2cdisplay_obj_t* self = MP_OBJ_TO_PTR(obj); + + common_hal_digitalio_digitalinout_set_value(&self->reset, false); + common_hal_mcu_delay_us(1); + common_hal_digitalio_digitalinout_set_value(&self->reset, true); +} + + +bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t obj) { displayio_i2cdisplay_obj_t* self = MP_OBJ_TO_PTR(obj); if (!common_hal_busio_i2c_try_lock(self->bus)) { return false; @@ -82,7 +89,11 @@ bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t obj) { return true; } -void common_hal_displayio_i2cdisplay_send(mp_obj_t obj, bool command, uint8_t *data, uint32_t data_length) { +bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t obj) { + return common_hal_displayio_i2cdisplay_bus_free(obj); +} + +void common_hal_displayio_i2cdisplay_send(mp_obj_t obj, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length) { displayio_i2cdisplay_obj_t* self = MP_OBJ_TO_PTR(obj); if (command) { uint8_t command_bytes[2 * data_length]; diff --git a/shared-module/displayio/OnDiskBitmap.c b/shared-module/displayio/OnDiskBitmap.c index 2b5642437..10fc0c4a2 100644 --- a/shared-module/displayio/OnDiskBitmap.c +++ b/shared-module/displayio/OnDiskBitmap.c @@ -74,7 +74,7 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self, self->g_bitmask = 0x3e0; self->b_bitmask = 0x1f; } - } else if ((indexed) && (self->bits_per_pixel != 1)) { + } else if (indexed && self->bits_per_pixel != 1) { uint16_t palette_size = number_of_colors * sizeof(uint32_t); uint16_t palette_offset = 0xe + header_size; @@ -90,25 +90,24 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self, if (palette_bytes_read != palette_size) { mp_raise_ValueError(translate("Unable to read color palette data")); } - - } else if (!(header_size == 12 || header_size == 40 || header_size == 108 || header_size == 124)) { mp_raise_ValueError_varg(translate("Only Windows format, uncompressed BMP supported: given header size is %d"), header_size); } - if ((bits_per_pixel == 4 ) || (( bits_per_pixel == 8) && (number_of_colors == 0))) { - mp_raise_ValueError_varg(translate("Only monochrome, indexed 8bpp, and 16bpp or greater BMPs supported: %d bpp given"), bits_per_pixel); + if (bits_per_pixel == 8 && number_of_colors == 0) { + mp_raise_ValueError_varg(translate("Only monochrome, indexed 4bpp or 8bpp, and 16bpp or greater BMPs supported: %d bpp given"), bits_per_pixel); } - if (self->bits_per_pixel >=8){ - self->stride = (self->width * (bits_per_pixel / 8)); + uint8_t bytes_per_pixel = (self->bits_per_pixel / 8) ? (self->bits_per_pixel /8) : 1; + uint8_t pixels_per_byte = 8 / self->bits_per_pixel; + if (pixels_per_byte == 0){ + self->stride = (self->width * bytes_per_pixel); // Rows are word aligned. if (self->stride % 4 != 0) { self->stride += 4 - self->stride % 4; } - } else { - uint32_t bit_stride = self->width; + uint32_t bit_stride = self->width * self->bits_per_pixel; if (bit_stride % 32 != 0) { bit_stride += 32 - bit_stride % 32; } @@ -126,10 +125,11 @@ uint32_t common_hal_displayio_ondiskbitmap_get_pixel(displayio_ondiskbitmap_t *s uint32_t location; uint8_t bytes_per_pixel = (self->bits_per_pixel / 8) ? (self->bits_per_pixel /8) : 1; - if (self->bits_per_pixel >= 8){ + uint8_t pixels_per_byte = 8 / self->bits_per_pixel; + if (pixels_per_byte == 0){ location = self->data_offset + (self->height - y - 1) * self->stride + x * bytes_per_pixel; } else { - location = self->data_offset + (self->height - y - 1) * self->stride + x / 8; + location = self->data_offset + (self->height - y - 1) * self->stride + x / pixels_per_byte; } // We don't cache here because the underlying FS caches sectors. f_lseek(&self->file->fp, location); @@ -141,20 +141,21 @@ uint32_t common_hal_displayio_ondiskbitmap_get_pixel(displayio_ondiskbitmap_t *s uint8_t red; uint8_t green; uint8_t blue; - if (self->bits_per_pixel == 1) { - uint8_t bit_offset = x%8; - tmp = ( pixel_data & (0x80 >> (bit_offset))) >> (7 - bit_offset); - if (tmp == 1) { - return 0x00FFFFFF; - } else { - return 0x00000000; + if (bytes_per_pixel == 1) { + uint8_t offset = (x % pixels_per_byte) * self->bits_per_pixel; + uint8_t mask = (1 << self->bits_per_pixel) - 1; + + uint8_t index = (pixel_data >> ((8 - self->bits_per_pixel) - offset)) & mask; + if (self->bits_per_pixel == 1) { + if (index == 1) { + return 0xFFFFFF; + } else if (index == 0) { + return 0x000000; + } else { + asm("bkpt"); + } } - } else if (bytes_per_pixel == 1) { - blue = ((self->palette_data[pixel_data] & 0xFF) >> 0); - red = ((self->palette_data[pixel_data] & 0xFF0000) >> 16); - green = ((self->palette_data[pixel_data] & 0xFF00) >> 8); - tmp = (red << 16 | green << 8 | blue ); - return tmp; + return self->palette_data[index]; } else if (bytes_per_pixel == 2) { if (self->g_bitmask == 0x07e0) { // 565 red =((pixel_data & self->r_bitmask) >>11); diff --git a/shared-module/displayio/Palette.c b/shared-module/displayio/Palette.c index 444151b3c..3bce86f48 100644 --- a/shared-module/displayio/Palette.c +++ b/shared-module/displayio/Palette.c @@ -52,6 +52,10 @@ void common_hal_displayio_palette_set_color(displayio_palette_t* self, uint32_t self->colors[palette_index].rgb888 = color; self->colors[palette_index].luma = displayio_colorconverter_compute_luma(color); self->colors[palette_index].rgb565 = displayio_colorconverter_compute_rgb565(color); + + uint8_t chroma = displayio_colorconverter_compute_chroma(color); + self->colors[palette_index].chroma = chroma; + self->colors[palette_index].hue = displayio_colorconverter_compute_hue(color); self->needs_refresh = true; } @@ -64,7 +68,19 @@ bool displayio_palette_get_color(displayio_palette_t *self, const _displayio_col return false; // returns opaque } - if (colorspace->grayscale) { + if (colorspace->tricolor) { + uint8_t luma = self->colors[palette_index].luma; + *color = luma >> (8 - colorspace->depth); + // Chroma 0 means the color is a gray and has no hue so never color based on it. + if (self->colors[palette_index].chroma <= 16) { + if (!colorspace->grayscale) { + *color = 0; + } + return true; + } + uint8_t pixel_hue = self->colors[palette_index].hue; + displayio_colorconverter_compute_tricolor(colorspace, pixel_hue, luma, color); + } else if (colorspace->grayscale) { *color = self->colors[palette_index].luma >> (8 - colorspace->depth); } else { *color = self->colors[palette_index].rgb565; diff --git a/shared-module/displayio/Palette.h b/shared-module/displayio/Palette.h index a917e2432..1cfdd199a 100644 --- a/shared-module/displayio/Palette.h +++ b/shared-module/displayio/Palette.h @@ -34,17 +34,21 @@ typedef struct { uint8_t depth; + uint8_t bytes_per_cell; + uint8_t tricolor_hue; + uint8_t tricolor_luma; bool grayscale; + bool tricolor; bool pixels_in_byte_share_row; - uint8_t bytes_per_cell; bool reverse_pixels_in_byte; - uint8_t hue; } _displayio_colorspace_t; typedef struct { uint32_t rgb888; uint16_t rgb565; uint8_t luma; + uint8_t hue; + uint8_t chroma; bool transparent; // This may have additional bits added later for blending. } _displayio_color_t; diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index 6144f8f99..6553cf901 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -443,6 +443,7 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, const _displayio_c } uint8_t shift = (offset % pixels_per_byte) * colorspace->depth; if (colorspace->reverse_pixels_in_byte) { + // Reverse the shift by subtracting it from the leftmost shift. shift = (pixels_per_byte - 1) * colorspace->depth - shift; } ((uint8_t*)buffer)[offset / pixels_per_byte] |= pixel << shift; diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 3e9c4aa2a..a8f888654 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -133,26 +133,50 @@ void displayio_refresh_displays(void) { // Skip null display. continue; } - displayio_display_obj_t* display = &displays[i].display; - displayio_display_update_backlight(display); - - // Time to refresh at specified frame rate? - if (!displayio_display_frame_queued(display)) { - // Too soon. Try next display. - continue; - } - if (!displayio_display_begin_transaction(display)) { - // Can't acquire display bus; skip updating this display. Try next display. - continue; - } - displayio_display_end_transaction(display); - displayio_display_start_refresh(display); - const displayio_area_t* current_area = displayio_display_get_refresh_areas(display); - while (current_area != NULL) { - refresh_area(display, current_area); - current_area = current_area->next; + if (displays[i].display.base.type == &displayio_display_type) { + displayio_display_obj_t* display = &displays[i].display; + displayio_display_update_backlight(display); + + // Time to refresh at specified frame rate? + if (!displayio_display_frame_queued(display)) { + // Too soon. Try next display. + continue; + } + if (!displayio_display_begin_transaction(display)) { + // Can't acquire display bus; skip updating this display. Try next display. + continue; + } + displayio_display_end_transaction(display); + displayio_display_start_refresh(display); + const displayio_area_t* current_area = displayio_display_get_refresh_areas(display); + while (current_area != NULL) { + refresh_area(display, current_area); + current_area = current_area->next; + } + displayio_display_finish_refresh(display); + } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) { + displayio_epaperdisplay_obj_t* display = &displays[i].epaper_display; + // Time to refresh at specified frame rate? + if (!displayio_epaperdisplay_frame_queued(display)) { + // Too soon. Try next display. + continue; + } + if (!displayio_epaperdisplay_bus_free(display)) { + // Can't acquire display bus; skip updating this display. Try next display. + continue; + } + const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(display); + if (current_area == NULL) { + continue; + } + displayio_epaperdisplay_start_refresh(display); + while (current_area != NULL) { + displayio_epaperdisplay_refresh_area(display, current_area); + current_area = current_area->next; + } + displayio_epaperdisplay_finish_refresh(display); } - displayio_display_finish_refresh(display); + frame_count++; } @@ -175,7 +199,14 @@ void common_hal_displayio_release_displays(void) { displays[i].fourwire_bus.base.type = &mp_type_NoneType; } for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - release_display(&displays[i].display); + mp_const_obj_t display_type = displays[i].display.base.type; + if (display_type == NULL || display_type == &mp_type_NoneType) { + continue; + } else if (display_type == &displayio_display_type) { + release_display(&displays[i].display); + } else if (display_type == &displayio_epaperdisplay_type) { + release_epaperdisplay(&displays[i].epaper_display); + } displays[i].display.base.type = &mp_type_NoneType; } @@ -232,7 +263,7 @@ void reset_displays(void) { } } } else { - // Not an active display. + // Not an active display bus. continue; } } @@ -240,9 +271,14 @@ void reset_displays(void) { for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { // Reset the displayed group. Only the first will get the terminal but // that's ok. - displayio_display_obj_t* display = &displays[i].display; - display->auto_brightness = true; - common_hal_displayio_display_show(display, NULL); + if (displays[i].display.base.type == &displayio_display_type) { + displayio_display_obj_t* display = &displays[i].display; + display->auto_brightness = true; + common_hal_displayio_display_show(display, NULL); + } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) { + displayio_epaperdisplay_obj_t* display = &displays[i].epaper_display; + common_hal_displayio_epaperdisplay_show(display, NULL); + } } } @@ -254,8 +290,11 @@ void displayio_gc_collect(void) { // Alternatively, we could use gc_collect_root over the whole object, // but this is more precise, and is the only field that needs marking. - gc_collect_ptr(displays[i].display.current_group); - + if (displays[i].display.base.type == &displayio_display_type) { + gc_collect_ptr(displays[i].display.current_group); + } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) { + gc_collect_ptr(displays[i].epaper_display.current_group); + } } } diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index caa5ce36d..95eefbdd9 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -28,6 +28,7 @@ #define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO___INIT___H #include "shared-bindings/displayio/Display.h" +#include "shared-bindings/displayio/EPaperDisplay.h" #include "shared-bindings/displayio/FourWire.h" #include "shared-bindings/displayio/Group.h" #include "shared-bindings/displayio/I2CDisplay.h" @@ -39,7 +40,10 @@ typedef struct { displayio_i2cdisplay_obj_t i2cdisplay_bus; displayio_parallelbus_obj_t parallel_bus; }; - displayio_display_obj_t display; + union { + displayio_display_obj_t display; + displayio_epaperdisplay_obj_t epaper_display; + }; } primary_display_t; extern primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; -- cgit v1.2.3 From c247e7df9c043dc6a452a6cabc7bf232c0e9e085 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 14 Aug 2019 14:17:35 -0700 Subject: Begin refresh rework. --- ports/atmel-samd/background.c | 2 +- ports/nrf/background.c | 2 +- ports/stm32f4/background.c | 2 +- py/circuitpy_mpconfig.h | 2 +- shared-bindings/displayio/Display.c | 37 ++++++-- shared-bindings/displayio/Display.h | 14 +-- shared-bindings/displayio/EPaperDisplay.c | 45 +++++---- shared-bindings/displayio/EPaperDisplay.h | 9 +- shared-module/displayio/Display.c | 124 ++++++++++++++++++++++++- shared-module/displayio/Display.h | 8 +- shared-module/displayio/EPaperDisplay.c | 71 ++++++++++---- shared-module/displayio/EPaperDisplay.h | 8 +- shared-module/displayio/__init__.c | 149 ++---------------------------- shared-module/displayio/__init__.h | 2 +- 14 files changed, 254 insertions(+), 221 deletions(-) (limited to 'shared-module') diff --git a/ports/atmel-samd/background.c b/ports/atmel-samd/background.c index 694238070..386ba0715 100644 --- a/ports/atmel-samd/background.c +++ b/ports/atmel-samd/background.c @@ -60,7 +60,7 @@ void run_background_tasks(void) { audio_dma_background(); #endif #if CIRCUITPY_DISPLAYIO - displayio_refresh_displays(); + displayio_background(); #endif #if CIRCUITPY_NETWORK diff --git a/ports/nrf/background.c b/ports/nrf/background.c index 453bc96df..94411cbce 100644 --- a/ports/nrf/background.c +++ b/ports/nrf/background.c @@ -56,7 +56,7 @@ void run_background_tasks(void) { #endif #if CIRCUITPY_DISPLAYIO - displayio_refresh_displays(); + displayio_background(); #endif running_background_tasks = false; diff --git a/ports/stm32f4/background.c b/ports/stm32f4/background.c index e9872b045..b91c3793e 100644 --- a/ports/stm32f4/background.c +++ b/ports/stm32f4/background.c @@ -49,7 +49,7 @@ void run_background_tasks(void) { //usb_background(); #if CIRCUITPY_DISPLAYIO - displayio_refresh_displays(); + displayio_background(); #endif running_background_tasks = false; diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 393b7600c..c2e2e2d02 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -307,7 +307,7 @@ extern const struct _mp_obj_module_t terminalio_module; #define DISPLAYIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_displayio), (mp_obj_t)&displayio_module }, #define FONTIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_fontio), (mp_obj_t)&fontio_module }, #define TERMINALIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_terminalio), (mp_obj_t)&terminalio_module }, -#define CIRCUITPY_DISPLAY_LIMIT (3) +#define CIRCUITPY_DISPLAY_LIMIT (1) #else #define DISPLAYIO_MODULE #define FONTIO_MODULE diff --git a/shared-bindings/displayio/Display.c b/shared-bindings/displayio/Display.c index 0c48b0cc5..fb9bfff85 100644 --- a/shared-bindings/displayio/Display.c +++ b/shared-bindings/displayio/Display.c @@ -212,27 +212,42 @@ STATIC mp_obj_t displayio_display_obj_show(mp_obj_t self_in, mp_obj_t group_in) } MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_show_obj, displayio_display_obj_show); -//| .. method:: refresh_soon() +//| .. method:: refresh(*, target_frames_per_second=None, minimum_frames_per_second=1) //| -//| Queues up a display refresh that happens in the background. +//| Waits for the target frame rate and then refreshes the display. If the call is too late for the given target frame rate, then the refresh returns immediately without updating the screen to hopefully help getting caught up. If the current frame rate is below the minimum frame rate, then an exception will be raised. //| -STATIC mp_obj_t displayio_display_obj_refresh_soon(mp_obj_t self_in) { +STATIC mp_obj_t displayio_display_obj_refresh(mp_obj_t self_in) { displayio_display_obj_t *self = native_display(self_in); - common_hal_displayio_display_refresh_soon(self); + common_hal_displayio_display_refresh(self); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_refresh_soon_obj, displayio_display_obj_refresh_soon); -//| .. method:: wait_for_frame() +//| .. attribute:: auto_refresh //| -//| Waits until the next frame has been transmitted to the display unless the wait count is -//| behind the rendered frames. In that case, this will return immediately with the wait count. +//| True when the display is refreshed automatically. //| -STATIC mp_obj_t displayio_display_obj_wait_for_frame(mp_obj_t self_in) { +STATIC mp_obj_t displayio_display_obj_get_auto_refresh(mp_obj_t self_in) { displayio_display_obj_t *self = native_display(self_in); - return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_wait_for_frame(self)); + return mp_obj_new_bool(common_hal_displayio_display_get_auto_refresh(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_wait_for_frame_obj, displayio_display_obj_wait_for_frame); +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_auto_refresh_obj, displayio_display_obj_get_auto_refresh); + +STATIC mp_obj_t displayio_display_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) { + displayio_display_obj_t *self = native_display(self_in); + + common_hal_displayio_display_set_auto_refresh(self, mp_obj_is_true(auto_refresh)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_auto_refresh_obj, displayio_display_obj_set_auto_refresh); + +const mp_obj_property_t displayio_display_auto_refresh_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&displayio_display_get_auto_refresh_obj, + (mp_obj_t)&displayio_display_set_auto_refresh_obj, + (mp_obj_t)&mp_const_none_obj}, +}; //| .. attribute:: brightness //| @@ -442,6 +457,8 @@ STATIC const mp_rom_map_elem_t displayio_display_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_wait_for_frame), MP_ROM_PTR(&displayio_display_wait_for_frame_obj) }, { MP_ROM_QSTR(MP_QSTR_fill_row), MP_ROM_PTR(&displayio_display_fill_row_obj) }, + { MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&displayio_display_auto_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&displayio_display_brightness_obj) }, { MP_ROM_QSTR(MP_QSTR_auto_brightness), MP_ROM_PTR(&displayio_display_auto_brightness_obj) }, diff --git a/shared-bindings/displayio/Display.h b/shared-bindings/displayio/Display.h index 8df1adc08..0d416b868 100644 --- a/shared-bindings/displayio/Display.h +++ b/shared-bindings/displayio/Display.h @@ -44,26 +44,16 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, uint8_t set_column_command, uint8_t set_row_command, uint8_t write_ram_command, uint8_t set_vertical_scroll, uint8_t* init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t* backlight_pin, uint16_t brightness_command, - mp_float_t brightness, bool auto_brightness, + mp_float_t brightness, bool auto_brightness, bool auto_refresh, uint8_t frames_per_second, bool single_byte_bounds, bool data_as_commands); -int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* self); - bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group); -void common_hal_displayio_display_refresh_soon(displayio_display_obj_t* self); +void common_hal_displayio_display_refresh(displayio_display_obj_t* self); bool displayio_display_begin_transaction(displayio_display_obj_t* self); void displayio_display_end_transaction(displayio_display_obj_t* self); -// The second point of the region is exclusive. -void displayio_display_set_region_to_update(displayio_display_obj_t* self, displayio_area_t* area); -bool displayio_display_frame_queued(displayio_display_obj_t* self); - -bool displayio_display_refresh_queued(displayio_display_obj_t* self); -void displayio_display_finish_refresh(displayio_display_obj_t* self); -void displayio_display_send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length); - bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self); void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness); diff --git a/shared-bindings/displayio/EPaperDisplay.c b/shared-bindings/displayio/EPaperDisplay.c index d13d07e4a..4b00240d1 100644 --- a/shared-bindings/displayio/EPaperDisplay.c +++ b/shared-bindings/displayio/EPaperDisplay.c @@ -51,7 +51,7 @@ //| Most people should not use this class directly. Use a specific display driver instead that will //| contain the startup and shutdown sequences at minimum. //| -//| .. class:: EPaperDisplay(display_bus, start_sequence, stop_sequence, *, width, height, ram_width, ram_height, colstart=0, rowstart=0, rotation=0, set_column_window_command=None, set_row_window_command=None, single_byte_bounds=False, write_black_ram_command, black_bits_inverted=False, write_color_ram_command=None, color_bits_inverted=False, refresh_display_command, busy_pin=None, busy_state=True, seconds_per_frame=180, always_toggle_chip_select=False) +//| .. class:: EPaperDisplay(display_bus, start_sequence, stop_sequence, *, width, height, ram_width, ram_height, colstart=0, rowstart=0, rotation=0, set_column_window_command=None, set_row_window_command=None, single_byte_bounds=False, write_black_ram_command, black_bits_inverted=False, write_color_ram_command=None, color_bits_inverted=False, highlight_color=0x000000, refresh_display_command, busy_pin=None, busy_state=True, seconds_per_frame=180, always_toggle_chip_select=False) //| //| Create a EPaperDisplay object on the given display bus (`displayio.FourWire` or `displayio.ParallelBus`). //| @@ -81,7 +81,7 @@ //| :param bool black_bits_inverted: True if 0 bits are used to show black pixels. Otherwise, 1 means to show black. //| :param int write_color_ram_command: Command used to write pixels values into the update region //| :param bool color_bits_inverted: True if 0 bits are used to show the color. Otherwise, 1 means to show color. -//| :param int third_color: Color of third ePaper color in RGB888. +//| :param int highlight_color: RGB888 of source color to highlight with third ePaper color. //| :param int refresh_display_command: Command used to start a display refresh //| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy //| :param bool busy_state: State of the busy pin when the display is busy @@ -89,7 +89,7 @@ //| :param bool always_toggle_chip_select: When True, chip select is toggled every byte //| STATIC mp_obj_t displayio_epaperdisplay_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_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_third_color, ARG_refresh_display_command, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select }; + enum { ARG_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, ARG_refresh_display_command, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select }; static const mp_arg_t allowed_args[] = { { MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_start_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -109,7 +109,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size { MP_QSTR_black_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, { MP_QSTR_write_color_ram_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, { MP_QSTR_color_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, - { MP_QSTR_third_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, + { MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, { MP_QSTR_refresh_display_command, MP_ARG_INT | MP_ARG_REQUIRED }, { MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, { MP_QSTR_busy_state, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, @@ -155,7 +155,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size mp_float_t seconds_per_frame = mp_obj_get_float(args[ARG_seconds_per_frame].u_obj); mp_int_t write_color_ram_command = NO_COMMAND; - mp_int_t third_color = args[ARG_third_color].u_int; + mp_int_t highlight_color = args[ARG_highlight_color].u_int; if (args[ARG_write_color_ram_command].u_obj != mp_const_none) { write_color_ram_command = mp_obj_get_int(args[ARG_write_color_ram_command].u_obj); } @@ -168,7 +168,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_ram_width].u_int, args[ARG_ram_height].u_int, args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int, args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int, - args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, args[ARG_color_bits_inverted].u_bool, third_color, args[ARG_refresh_display_command].u_int, + args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, args[ARG_color_bits_inverted].u_bool, highlight_color, args[ARG_refresh_display_command].u_int, busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, args[ARG_always_toggle_chip_select].u_bool ); @@ -203,27 +203,38 @@ STATIC mp_obj_t displayio_epaperdisplay_obj_show(mp_obj_t self_in, mp_obj_t grou } MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_show_obj, displayio_epaperdisplay_obj_show); -//| .. method:: refresh_soon() +//| .. method:: refresh() //| -//| Queues up a display refresh that happens in the background. +//| Refreshes the display immediately or raises an exception if too soon. Use +//| ``time.sleep(display.time_to_refresh)`` to sleep until a refresh can occur. //| STATIC mp_obj_t displayio_epaperdisplay_obj_refresh_soon(mp_obj_t self_in) { displayio_epaperdisplay_obj_t *self = native_display(self_in); - common_hal_displayio_epaperdisplay_refresh_soon(self); + bool ok = common_hal_displayio_epaperdisplay_refresh(self); + if (!ok) { + mp_raise_RuntimeError(translate("Refresh too soon")); + } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_soon_obj, displayio_epaperdisplay_obj_refresh_soon); -//| .. method:: wait_for_frame() +//| .. attribute:: time_to_refresh //| -//| Waits until the next frame has been transmitted to the display unless the wait count is -//| behind the rendered frames. In that case, this will return immediately with the wait count. +//| Time, in fractional seconds, until the ePaper display can be refreshed. //| -STATIC mp_obj_t displayio_epaperdisplay_obj_wait_for_frame(mp_obj_t self_in) { +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_time_to_refresh(mp_obj_t self_in) { displayio_epaperdisplay_obj_t *self = native_display(self_in); - return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_wait_for_frame(self)); + return mp_obj_new_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_wait_for_frame_obj, displayio_epaperdisplay_obj_wait_for_frame); +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_time_to_refresh_obj, displayio_epaperdisplay_obj_get_time_to_refresh); + +const mp_obj_property_t displayio_epaperdisplay_time_to_refresh_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&displayio_epaperdisplay_get_time_to_refresh_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; //| .. attribute:: width //| @@ -282,12 +293,12 @@ const mp_obj_property_t displayio_epaperdisplay_bus_obj = { STATIC const mp_rom_map_elem_t displayio_epaperdisplay_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_epaperdisplay_show_obj) }, - { MP_ROM_QSTR(MP_QSTR_refresh_soon), MP_ROM_PTR(&displayio_epaperdisplay_refresh_soon_obj) }, - { MP_ROM_QSTR(MP_QSTR_wait_for_frame), MP_ROM_PTR(&displayio_epaperdisplay_wait_for_frame_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh_soon), MP_ROM_PTR(&displayio_epaperdisplay_refresh_obj) }, { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_epaperdisplay_width_obj) }, { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_epaperdisplay_height_obj) }, { MP_ROM_QSTR(MP_QSTR_bus), MP_ROM_PTR(&displayio_epaperdisplay_bus_obj) }, + { MP_ROM_QSTR(MP_QSTR_time_to_refresh), MP_ROM_PTR(&displayio_epaperdisplay_time_to_refresh_obj) }, }; STATIC MP_DEFINE_CONST_DICT(displayio_epaperdisplay_locals_dict, displayio_epaperdisplay_locals_dict_table); diff --git a/shared-bindings/displayio/EPaperDisplay.h b/shared-bindings/displayio/EPaperDisplay.h index 3deeef959..87feb4da0 100644 --- a/shared-bindings/displayio/EPaperDisplay.h +++ b/shared-bindings/displayio/EPaperDisplay.h @@ -43,23 +43,24 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t set_column_window_command, uint16_t set_row_window_command, uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t third_color, uint16_t refresh_display_command, + uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select); int32_t common_hal_displayio_epaperdisplay_wait_for_frame(displayio_epaperdisplay_obj_t* self); bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self, displayio_group_t* root_group); -void common_hal_displayio_epaperdisplay_refresh_soon(displayio_epaperdisplay_obj_t* self); - bool displayio_epaperdisplay_begin_transaction(displayio_epaperdisplay_obj_t* self); void displayio_epaperdisplay_end_transaction(displayio_epaperdisplay_obj_t* self); +bool displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* self); + +mp_float_t displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self); + // The second point of the region is exclusive. void displayio_epaperdisplay_set_region_to_update(displayio_epaperdisplay_obj_t* self, displayio_area_t* area); bool displayio_epaperdisplay_frame_queued(displayio_epaperdisplay_obj_t* self); -bool displayio_epaperdisplay_refresh_queued(displayio_epaperdisplay_obj_t* self); void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self); void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length); diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index 97a18740a..fafb35f43 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -221,10 +221,6 @@ bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_ return true; } -void common_hal_displayio_display_refresh_soon(displayio_display_obj_t* self) { - self->refresh = true; -} - const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self) { if (self->full_refresh) { self->area.next = NULL; @@ -246,6 +242,118 @@ int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* sel return 0; } +STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_t* area) { + uint16_t buffer_size = 128; // In uint32_ts + + displayio_area_t clipped; + // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. + if (!displayio_display_clip_area(display, area, &clipped)) { + return true; + } + uint16_t subrectangles = 1; + uint16_t rows_per_buffer = displayio_area_height(&clipped); + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / display->colorspace.depth; + uint16_t pixels_per_buffer = displayio_area_size(&clipped); + if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) { + rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); + if (rows_per_buffer == 0) { + rows_per_buffer = 1; + } + // If pixels are packed by column then ensure rows_per_buffer is on a byte boundary. + if (display->colorspace.depth < 8 && !display->colorspace.pixels_in_byte_share_row) { + uint8_t pixels_per_byte = 8 / display->colorspace.depth; + if (rows_per_buffer % pixels_per_byte != 0) { + rows_per_buffer -= rows_per_buffer % pixels_per_byte; + } + } + subrectangles = displayio_area_height(&clipped) / rows_per_buffer; + if (displayio_area_height(&clipped) % rows_per_buffer != 0) { + subrectangles++; + } + pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); + buffer_size = pixels_per_buffer / pixels_per_word; + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + } + + // Allocated and shared as a uint32_t array so the compiler knows the + // alignment everywhere. + uint32_t buffer[buffer_size]; + volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask[mask_length]; + uint16_t remaining_rows = displayio_area_height(&clipped); + + for (uint16_t j = 0; j < subrectangles; j++) { + displayio_area_t subrectangle = { + .x1 = clipped.x1, + .y1 = clipped.y1 + rows_per_buffer * j, + .x2 = clipped.x2, + .y2 = clipped.y1 + rows_per_buffer * (j + 1) + }; + if (remaining_rows < rows_per_buffer) { + subrectangle.y2 = subrectangle.y1 + remaining_rows; + } + remaining_rows -= rows_per_buffer; + + displayio_display_begin_transaction(display); + displayio_display_set_region_to_update(display, &subrectangle); + displayio_display_end_transaction(display); + + uint16_t subrectangle_size_bytes; + if (display->colorspace.depth >= 8) { + subrectangle_size_bytes = displayio_area_size(&subrectangle) * (display->colorspace.depth / 8); + } else { + subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / display->colorspace.depth); + } + + for (uint16_t k = 0; k < mask_length; k++) { + mask[k] = 0x00000000; + } + for (uint16_t k = 0; k < buffer_size; k++) { + buffer[k] = 0x00000000; + } + + displayio_display_fill_area(display, &subrectangle, mask, buffer); + + if (!displayio_display_begin_transaction(display)) { + // Can't acquire display bus; skip the rest of the data. Try next display. + return false; + } + displayio_display_send_pixels(display, (uint8_t*) buffer, subrectangle_size_bytes); + displayio_display_end_transaction(display); + + // TODO(tannewt): Make refresh displays faster so we don't starve other + // background tasks. + usb_background(); + } + return true; +} + +STATIC void refresh_display(displayio_display_obj_t* self) { + if (!displayio_display_begin_transaction(self)) { + // Can't acquire display bus; skip updating this display. Try next display. + continue; + } + displayio_display_end_transaction(self); + displayio_display_start_refresh(self); + const displayio_area_t* current_area = displayio_display_get_refresh_areas(self); + while (current_area != NULL) { + refresh_area(self, current_area); + current_area = current_area->next; + } + displayio_display_finish_refresh(self); +} + +void common_hal_displayio_display_refresh(displayio_display_obj_t* self) { + // Time to refresh at specified frame rate? + while (!displayio_display_frame_queued(self)) { + // Too soon. Try next display. + continue; + } + refresh_display(self); +} + bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self) { return self->auto_brightness; } @@ -411,6 +519,14 @@ void displayio_display_update_backlight(displayio_display_obj_t* self) { self->last_backlight_refresh = ticks_ms; } +void displayio_display_background(displayio_display_obj_t* self) { + displayio_display_update_backlight(self); + + if (self->auto_refresh && (ticks_ms - self->last_refresh) > 16) { + display_refresh(self); + } +} + void release_display(displayio_display_obj_t* self) { if (self->current_group != NULL) { self->current_group->in_group = false; diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index c85d69b0e..db0a23012 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -63,7 +63,7 @@ typedef struct { uint8_t set_column_command; uint8_t set_row_command; uint8_t write_ram_command; - bool refresh; + bool auto_refresh; bool single_byte_bounds; bool data_as_commands; bool auto_brightness; @@ -71,11 +71,7 @@ typedef struct { bool full_refresh; // New group means we need to refresh the whole display. } displayio_display_obj_t; -void displayio_display_start_refresh(displayio_display_obj_t* self); -const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self); -bool displayio_display_fill_area(displayio_display_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer); -void displayio_display_update_backlight(displayio_display_obj_t* self); -bool displayio_display_clip_area(displayio_display_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped); +void displayio_display_background(displayio_display_obj_t* self); void release_display(displayio_display_obj_t* self); #endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 5e4bdc340..434adbdf4 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -48,7 +48,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t set_column_window_command, uint16_t set_row_window_command, uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t third_color, uint16_t refresh_display_command, + uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select) { self->colorspace.depth = 1; self->colorspace.grayscale = true; @@ -56,10 +56,10 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self->colorspace.bytes_per_cell = 1; self->colorspace.reverse_pixels_in_byte = true; - if (third_color != 0x000000) { + if (highlight_color != 0x000000) { self->colorspace.tricolor = true; - self->colorspace.tricolor_hue = displayio_colorconverter_compute_hue(third_color); - self->colorspace.tricolor_luma = displayio_colorconverter_compute_luma(third_color); + self->colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color); + self->colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color); } self->set_column_window_command = set_column_window_command; @@ -174,6 +174,11 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* never_reset_pin_number(busy_pin->number); } + // Clear the color memory if it isn't in use. + if (highlight_color == 0x00 && write_color_ram_command != NO_COMMAND) { + // TODO: Clear + } + // Set the group after initialization otherwise we may send pixels while we delay in // initialization. common_hal_displayio_epaperdisplay_show(self, &circuitpython_splash); @@ -198,14 +203,9 @@ bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self self->current_group = root_group; } self->full_refresh = true; - common_hal_displayio_epaperdisplay_refresh_soon(self); return true; } -void common_hal_displayio_epaperdisplay_refresh_soon(displayio_epaperdisplay_obj_t* self) { - self->refresh = true; -} - const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self) { const displayio_area_t* first_area; if (self->current_group == NULL || self->current_group->base.type != &displayio_group_type) { @@ -359,21 +359,23 @@ void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self) self->last_refresh = ticks_ms; } -bool displayio_epaperdisplay_frame_queued(displayio_epaperdisplay_obj_t* self) { +void displayio_epaperdisplay_background_task(displayio_epaperdisplay_obj_t* self) { if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) { if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) { self->refreshing = false; // Run stop sequence but don't wait for busy because busy is set when sleeping. send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); - } else { - return false; } } - if (self->current_group == NULL) { - return false; - } +} + +uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self) { // Refresh at seconds per frame rate. - return (ticks_ms - self->last_refresh) > self->milliseconds_per_frame; + uint32_t elapsed_time = ticks_ms - self->last_refresh; + if (elapsed_time > self->milliseconds_per_frame) { + return 0; + } + return self->milliseconds_per_frame - elapsed_time; } void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) { @@ -394,7 +396,6 @@ void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length) { } - bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, const displayio_area_t* area) { uint16_t buffer_size = 128; // In uint32_ts @@ -500,6 +501,42 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c return true; } +bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* self) { + + if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) { + if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) { + self->refreshing = false; + // Run stop sequence but don't wait for busy because busy is set when sleeping. + send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); + } else { + return false; + } + } + if (self->current_group == NULL) { + return false; + } + // Refresh at seconds per frame rate. + if (ticks_ms - self->last_refresh) > self->milliseconds_per_frame; + + if (displayio_epaperdisplay_get_time_to_refresh(display) > 0) { + return false; + } + if (!displayio_epaperdisplay_bus_free(display)) { + // Can't acquire display bus; skip updating this display. Try next display. + continue; + } + const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(display); + if (current_area == NULL) { + continue; + } + displayio_epaperdisplay_start_refresh(display); + while (current_area != NULL) { + displayio_epaperdisplay_refresh_area(display, current_area); + current_area = current_area->next; + } + displayio_epaperdisplay_finish_refresh(display); +} + void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) { if (self->current_group != NULL) { self->current_group->in_group = false; diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index 737b5987a..cc7db6f6d 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -75,18 +75,12 @@ typedef struct { bool busy_state; bool black_bits_inverted; bool color_bits_inverted; - bool refresh; bool refreshing; bool full_refresh; // New group means we need to refresh the whole display. bool always_toggle_chip_select; } displayio_epaperdisplay_obj_t; -bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* display, const displayio_area_t* area); -void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self); -const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self); -bool displayio_epaperdisplay_fill_area(displayio_epaperdisplay_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer); -bool displayio_epaperdisplay_clip_area(displayio_epaperdisplay_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped); -bool displayio_epaperdisplay_bus_free(displayio_epaperdisplay_obj_t *self); +void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self); void release_epaperdisplay(displayio_epaperdisplay_obj_t* self); #endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index a8f888654..0ebed58c2 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -19,100 +19,11 @@ #include "supervisor/usb.h" primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; -uint32_t frame_count = 0; -bool refresh_area(displayio_display_obj_t* display, const displayio_area_t* area) { - uint16_t buffer_size = 128; // In uint32_ts +// Check for recursive calls to displayio_background. +bool displayio_background_in_progress = false; - displayio_area_t clipped; - // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. - if (!displayio_display_clip_area(display, area, &clipped)) { - return true; - } - uint16_t subrectangles = 1; - uint16_t rows_per_buffer = displayio_area_height(&clipped); - uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / display->colorspace.depth; - uint16_t pixels_per_buffer = displayio_area_size(&clipped); - if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) { - rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); - if (rows_per_buffer == 0) { - rows_per_buffer = 1; - } - // If pixels are packed by column then ensure rows_per_buffer is on a byte boundary. - if (display->colorspace.depth < 8 && !display->colorspace.pixels_in_byte_share_row) { - uint8_t pixels_per_byte = 8 / display->colorspace.depth; - if (rows_per_buffer % pixels_per_byte != 0) { - rows_per_buffer -= rows_per_buffer % pixels_per_byte; - } - } - subrectangles = displayio_area_height(&clipped) / rows_per_buffer; - if (displayio_area_height(&clipped) % rows_per_buffer != 0) { - subrectangles++; - } - pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); - buffer_size = pixels_per_buffer / pixels_per_word; - if (pixels_per_buffer % pixels_per_word) { - buffer_size += 1; - } - } - - // Allocated and shared as a uint32_t array so the compiler knows the - // alignment everywhere. - uint32_t buffer[buffer_size]; - volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1; - uint32_t mask[mask_length]; - uint16_t remaining_rows = displayio_area_height(&clipped); - - for (uint16_t j = 0; j < subrectangles; j++) { - displayio_area_t subrectangle = { - .x1 = clipped.x1, - .y1 = clipped.y1 + rows_per_buffer * j, - .x2 = clipped.x2, - .y2 = clipped.y1 + rows_per_buffer * (j + 1) - }; - if (remaining_rows < rows_per_buffer) { - subrectangle.y2 = subrectangle.y1 + remaining_rows; - } - remaining_rows -= rows_per_buffer; - - displayio_display_begin_transaction(display); - displayio_display_set_region_to_update(display, &subrectangle); - displayio_display_end_transaction(display); - - uint16_t subrectangle_size_bytes; - if (display->colorspace.depth >= 8) { - subrectangle_size_bytes = displayio_area_size(&subrectangle) * (display->colorspace.depth / 8); - } else { - subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / display->colorspace.depth); - } - - for (uint16_t k = 0; k < mask_length; k++) { - mask[k] = 0x00000000; - } - for (uint16_t k = 0; k < buffer_size; k++) { - buffer[k] = 0x00000000; - } - - displayio_display_fill_area(display, &subrectangle, mask, buffer); - - if (!displayio_display_begin_transaction(display)) { - // Can't acquire display bus; skip the rest of the data. Try next display. - return false; - } - displayio_display_send_pixels(display, (uint8_t*) buffer, subrectangle_size_bytes); - displayio_display_end_transaction(display); - - // TODO(tannewt): Make refresh displays faster so we don't starve other - // background tasks. - usb_background(); - } - return true; -} - -// Check for recursive calls to displayio_refresh_displays. -bool refresh_displays_in_progress = false; - -void displayio_refresh_displays(void) { +void displayio_background(void) { if (mp_hal_is_interrupted()) { return; } @@ -121,12 +32,12 @@ void displayio_refresh_displays(void) { return; } - if (refresh_displays_in_progress) { + if (displayio_background_in_progress) { // Don't allow recursive calls to this routine. return; } - refresh_displays_in_progress = true; + displayio_background_in_progress = true; for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { if (displays[i].display.base.type == NULL || displays[i].display.base.type == &mp_type_NoneType) { @@ -134,54 +45,14 @@ void displayio_refresh_displays(void) { continue; } if (displays[i].display.base.type == &displayio_display_type) { - displayio_display_obj_t* display = &displays[i].display; - displayio_display_update_backlight(display); - - // Time to refresh at specified frame rate? - if (!displayio_display_frame_queued(display)) { - // Too soon. Try next display. - continue; - } - if (!displayio_display_begin_transaction(display)) { - // Can't acquire display bus; skip updating this display. Try next display. - continue; - } - displayio_display_end_transaction(display); - displayio_display_start_refresh(display); - const displayio_area_t* current_area = displayio_display_get_refresh_areas(display); - while (current_area != NULL) { - refresh_area(display, current_area); - current_area = current_area->next; - } - displayio_display_finish_refresh(display); - } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) { - displayio_epaperdisplay_obj_t* display = &displays[i].epaper_display; - // Time to refresh at specified frame rate? - if (!displayio_epaperdisplay_frame_queued(display)) { - // Too soon. Try next display. - continue; - } - if (!displayio_epaperdisplay_bus_free(display)) { - // Can't acquire display bus; skip updating this display. Try next display. - continue; - } - const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(display); - if (current_area == NULL) { - continue; - } - displayio_epaperdisplay_start_refresh(display); - while (current_area != NULL) { - displayio_epaperdisplay_refresh_area(display, current_area); - current_area = current_area->next; - } - displayio_epaperdisplay_finish_refresh(display); + displayio_display_background(&displays[i].display); + } else if (displays[i].epaperdisplay.base.type == &displayio_epaperdisplay_type) { + displayio_epaperdisplay_background(&displays[i].epaperdisplay); } - - frame_count++; } // All done. - refresh_displays_in_progress = false; + displayio_background_in_progress = false; } void common_hal_displayio_release_displays(void) { @@ -245,7 +116,7 @@ void reset_displays(void) { ((uint32_t) i2c->bus) > ((uint32_t) &displays + CIRCUITPY_DISPLAY_LIMIT)) { busio_i2c_obj_t* original_i2c = i2c->bus; #if BOARD_I2C - // We don't need to move original_i2c if it is the board.SPI object because it is + // We don't need to move original_i2c if it is the board.I2C object because it is // statically allocated already. (Doing so would also make it impossible to reference in // a subsequent VM run.) if (original_i2c == common_hal_board_get_i2c()) { diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 95eefbdd9..e78bc61ce 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -50,7 +50,7 @@ extern primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; extern displayio_group_t circuitpython_splash; -void displayio_refresh_displays(void); +void displayio_background(void); void reset_displays(void); void displayio_gc_collect(void); -- cgit v1.2.3 From 36a23e0fe3914b43645e51bf23a4d8bfec2db2c9 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 16 Aug 2019 18:34:00 -0700 Subject: Rework refresh API and factor common display stuff out NOT TESTED! Just compiles Fixes #1691 --- ports/atmel-samd/Makefile | 3 +- .../atmel-samd/boards/hallowing_m0_express/board.c | 4 +- ports/atmel-samd/boards/pybadge/board.c | 4 +- ports/atmel-samd/boards/pybadge_airlift/board.c | 4 +- ports/atmel-samd/boards/pygamer/board.c | 4 +- ports/atmel-samd/boards/pygamer_advance/board.c | 4 +- ports/atmel-samd/boards/pyportal/board.c | 4 +- ports/atmel-samd/boards/pyportal_titano/board.c | 4 +- ports/nrf/Makefile | 3 +- ports/stm32f4/Makefile | 3 +- py/circuitpy_defns.mk | 6 + shared-bindings/_stage/__init__.c | 10 +- shared-bindings/displayio/Display.c | 54 ++- shared-bindings/displayio/Display.h | 23 +- shared-bindings/displayio/EPaperDisplay.c | 10 +- shared-bindings/displayio/EPaperDisplay.h | 19 +- shared-module/_stage/__init__.c | 4 +- shared-module/displayio/Display.c | 479 +++++++-------------- shared-module/displayio/Display.h | 27 +- shared-module/displayio/EPaperDisplay.c | 350 +++------------ shared-module/displayio/EPaperDisplay.h | 28 +- shared-module/displayio/__init__.c | 10 +- shared-module/displayio/display_core.c | 309 +++++++++++++ shared-module/displayio/display_core.h | 90 ++++ 24 files changed, 726 insertions(+), 730 deletions(-) create mode 100644 shared-module/displayio/display_core.c create mode 100644 shared-module/displayio/display_core.h (limited to 'shared-module') diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 8e3fe9460..11e6874ed 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -289,7 +289,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix common-hal/, $(SRC_COMMON_HAL)) SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ - $(addprefix shared-module/, $(SRC_SHARED_MODULE)) + $(addprefix shared-module/, $(SRC_SHARED_MODULE)) \ + $(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL)) SRC_S = supervisor/$(CHIP_FAMILY)_cpu.s diff --git a/ports/atmel-samd/boards/hallowing_m0_express/board.c b/ports/atmel-samd/boards/hallowing_m0_express/board.c index c12431f8b..b4c54062f 100644 --- a/ports/atmel-samd/boards/hallowing_m0_express/board.c +++ b/ports/atmel-samd/boards/hallowing_m0_express/board.c @@ -106,7 +106,9 @@ void board_init(void) { 1.0f, // brightness (ignored) true, // auto_brightness false, // single_byte_bounds - false); // data_as_commands + false, // data_as_commands + true, // auto_refresh + 60); // native_frames_per_second } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/pybadge/board.c b/ports/atmel-samd/boards/pybadge/board.c index e30b901b0..37f7991d1 100644 --- a/ports/atmel-samd/boards/pybadge/board.c +++ b/ports/atmel-samd/boards/pybadge/board.c @@ -108,7 +108,9 @@ void board_init(void) { 1.0f, // brightness (ignored) true, // auto_brightness false, // single_byte_bounds - false); // data_as_commands + false, // data_as_commands + true, // auto_refresh + 60); // native_frames_per_second } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/pybadge_airlift/board.c b/ports/atmel-samd/boards/pybadge_airlift/board.c index 38fb33886..7622de6f0 100644 --- a/ports/atmel-samd/boards/pybadge_airlift/board.c +++ b/ports/atmel-samd/boards/pybadge_airlift/board.c @@ -86,7 +86,9 @@ void board_init(void) { 1.0f, // brightness (ignored) true, // auto_brightness false, // single_byte_bounds - false); // data_as_commands + false, // data_as_commands + true, // auto_refresh + 60); // native_frames_per_second } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/pygamer/board.c b/ports/atmel-samd/boards/pygamer/board.c index 5ccf3fc50..b33ae4fb0 100644 --- a/ports/atmel-samd/boards/pygamer/board.c +++ b/ports/atmel-samd/boards/pygamer/board.c @@ -108,7 +108,9 @@ void board_init(void) { 1.0f, // brightness (ignored) true, // auto_brightness false, // single_byte_bounds - false); // data_as_commands + false, // data_as_commands + true, // auto_refresh + 60); // native_frames_per_second } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/pygamer_advance/board.c b/ports/atmel-samd/boards/pygamer_advance/board.c index 72d4dda8d..8eb501243 100644 --- a/ports/atmel-samd/boards/pygamer_advance/board.c +++ b/ports/atmel-samd/boards/pygamer_advance/board.c @@ -86,7 +86,9 @@ void board_init(void) { 1.0f, // brightness (ignored) true, // auto_brightness false, // single_byte_bounds - false); // data_as_commands + false, // data_as_commands + true, // auto_refresh + 60); // native_frames_per_second } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/pyportal/board.c b/ports/atmel-samd/boards/pyportal/board.c index a5de1d145..2a7289778 100644 --- a/ports/atmel-samd/boards/pyportal/board.c +++ b/ports/atmel-samd/boards/pyportal/board.c @@ -98,7 +98,9 @@ void board_init(void) { 1.0f, // brightness (ignored) true, // auto_brightness false, // single_byte_bounds - false); // data_as_commands + false, // data_as_commands + true, // auto_refresh + 60); // native_frames_per_second } bool board_requests_safe_mode(void) { diff --git a/ports/atmel-samd/boards/pyportal_titano/board.c b/ports/atmel-samd/boards/pyportal_titano/board.c index 0ed1010a1..a18bb380b 100644 --- a/ports/atmel-samd/boards/pyportal_titano/board.c +++ b/ports/atmel-samd/boards/pyportal_titano/board.c @@ -118,7 +118,9 @@ void board_init(void) { 1.0f, // brightness (ignored) true, // auto_brightness false, // single_byte_bounds - false); // data_as_commands + false, // data_as_commands + true, // auto_refresh + 60); // native_frames_per_second } bool board_requests_safe_mode(void) { diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 9a461f9d8..6ec160088 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -192,7 +192,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix common-hal/, $(SRC_COMMON_HAL)) SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ - $(addprefix shared-module/, $(SRC_SHARED_MODULE)) + $(addprefix shared-module/, $(SRC_SHARED_MODULE)) \ + $(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL)) SRC_S = supervisor/cpu.s diff --git a/ports/stm32f4/Makefile b/ports/stm32f4/Makefile index 7f0744443..c75a6c7ee 100755 --- a/ports/stm32f4/Makefile +++ b/ports/stm32f4/Makefile @@ -216,7 +216,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix common-hal/, $(SRC_COMMON_HAL)) SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ - $(addprefix shared-module/, $(SRC_SHARED_MODULE)) + $(addprefix shared-module/, $(SRC_SHARED_MODULE)) \ + $(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL)) diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index d0f0cf7dc..e7b917512 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -364,6 +364,12 @@ SRC_SHARED_MODULE_ALL += \ touchio/__init__.c endif +# All possible sources are listed here, and are filtered by SRC_PATTERNS. +SRC_SHARED_MODULE_INTERNAL = \ +$(filter $(SRC_PATTERNS), \ + displayio/display_core.c \ +) + ifeq ($(INTERNAL_LIBM),1) SRC_LIBM = \ $(addprefix lib/,\ diff --git a/shared-bindings/_stage/__init__.c b/shared-bindings/_stage/__init__.c index 6096f6b6d..1d970cce4 100644 --- a/shared-bindings/_stage/__init__.c +++ b/shared-bindings/_stage/__init__.c @@ -30,6 +30,7 @@ #include "shared-bindings/busio/SPI.h" #include "shared-bindings/displayio/Display.h" #include "shared-module/_stage/__init__.h" +#include "shared-module/displayio/display_core.h" #include "Layer.h" #include "Text.h" @@ -95,7 +96,8 @@ STATIC mp_obj_t stage_render(size_t n_args, const mp_obj_t *args) { scale = mp_obj_get_int(args[7]); } - while (!displayio_display_begin_transaction(display)) { + // TODO: Everything below should be in shared-module because it's not argument parsing. + while (!displayio_display_core_begin_transaction(&display->core)) { RUN_BACKGROUND_TASKS; } displayio_area_t area; @@ -103,12 +105,12 @@ STATIC mp_obj_t stage_render(size_t n_args, const mp_obj_t *args) { area.y1 = y0; area.x2 = x1; area.y2 = y1; - displayio_display_set_region_to_update(display, &area); + displayio_display_core_set_region_to_update(&display->core, display->set_column_command, display->set_row_command, NO_COMMAND, NO_COMMAND, display->data_as_commands, false, &area); - display->send(display->bus, true, &display->write_ram_command, 1); + display->core.send(display->core.bus, true, true, &display->write_ram_command, 1); render_stage(x0, y0, x1, y1, layers, layers_size, buffer, buffer_size, display, scale); - displayio_display_end_transaction(display); + displayio_display_core_end_transaction(&display->core); return mp_const_none; } diff --git a/shared-bindings/displayio/Display.c b/shared-bindings/displayio/Display.c index fb9bfff85..ded4119ca 100644 --- a/shared-bindings/displayio/Display.c +++ b/shared-bindings/displayio/Display.c @@ -51,7 +51,7 @@ //| Most people should not use this class directly. Use a specific display driver instead that will //| contain the initialization sequence at minimum. //| -//| .. class:: Display(display_bus, init_sequence, *, width, height, colstart=0, rowstart=0, rotation=0, color_depth=16, grayscale=False, pixels_in_byte_share_row=True, bytes_per_cell=1, reverse_pixels_in_byte=False, set_column_command=0x2a, set_row_command=0x2b, write_ram_command=0x2c, set_vertical_scroll=0, backlight_pin=None, brightness_command=None, brightness=1.0, auto_brightness=False, single_byte_bounds=False, data_as_commands=False) +//| .. class:: Display(display_bus, init_sequence, *, width, height, colstart=0, rowstart=0, rotation=0, color_depth=16, grayscale=False, pixels_in_byte_share_row=True, bytes_per_cell=1, reverse_pixels_in_byte=False, set_column_command=0x2a, set_row_command=0x2b, write_ram_command=0x2c, set_vertical_scroll=0, backlight_pin=None, brightness_command=None, brightness=1.0, auto_brightness=False, single_byte_bounds=False, data_as_commands=False, auto_refresh=True, native_frames_per_second=60) //| //| Create a Display object on the given display bus (`displayio.FourWire` or `displayio.ParallelBus`). //| @@ -102,9 +102,11 @@ //| :param bool auto_brightness: If True, brightness is controlled via an ambient light sensor or other mechanism. //| :param bool single_byte_bounds: Display column and row commands use single bytes //| :param bool data_as_commands: Treat all init and boundary data as SPI commands. Certain displays require this. +//| :param bool auto_refresh: Automatically refresh the screen +//| :param int native_frames_per_second: Number of display refreshes per second that occur with the given init_sequence. //| STATIC mp_obj_t displayio_display_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_display_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, ARG_brightness, ARG_auto_brightness, ARG_single_byte_bounds, ARG_data_as_commands }; + enum { ARG_display_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, ARG_brightness, ARG_auto_brightness, ARG_single_byte_bounds, ARG_data_as_commands, ARG_auto_refresh, ARG_native_frames_per_second }; static const mp_arg_t allowed_args[] = { { MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_init_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -128,6 +130,8 @@ STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_a { MP_QSTR_auto_brightness, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, { MP_QSTR_single_byte_bounds, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, { MP_QSTR_data_as_commands, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_auto_refresh, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_native_frames_per_second, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60} }, }; 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); @@ -178,7 +182,9 @@ STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_a brightness, args[ARG_auto_brightness].u_bool, args[ARG_single_byte_bounds].u_bool, - args[ARG_data_as_commands].u_bool + args[ARG_data_as_commands].u_bool, + args[ARG_auto_refresh].u_bool, + args[ARG_native_frames_per_second].u_int ); return self; @@ -214,14 +220,28 @@ MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_show_obj, displayio_display_obj_show //| .. method:: refresh(*, target_frames_per_second=None, minimum_frames_per_second=1) //| -//| Waits for the target frame rate and then refreshes the display. If the call is too late for the given target frame rate, then the refresh returns immediately without updating the screen to hopefully help getting caught up. If the current frame rate is below the minimum frame rate, then an exception will be raised. +//| When auto refresh is off, waits for the target frame rate and then refreshes the display. If +//| the call is too late for the given target frame rate, then the refresh returns immediately +//| without updating the screen to hopefully help getting caught up. If the current frame rate +//| is below the minimum frame rate, then an exception will be raised. //| -STATIC mp_obj_t displayio_display_obj_refresh(mp_obj_t self_in) { - displayio_display_obj_t *self = native_display(self_in); - common_hal_displayio_display_refresh(self); +//| When auto refresh is on, updates the display immediately. (The display will also update +//| without calls to this.) +//| +STATIC mp_obj_t displayio_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_target_frames_per_second, ARG_minimum_frames_per_second }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_target_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 60} }, + { MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + }; + 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); + + displayio_display_obj_t *self = native_display(pos_args[0]); + common_hal_displayio_display_refresh(self, 1000 / args[ARG_target_frames_per_second].u_int, 1000 / args[ARG_minimum_frames_per_second].u_int); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_refresh_soon_obj, displayio_display_obj_refresh_soon); +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_refresh_obj, 1, displayio_display_obj_refresh); //| .. attribute:: auto_refresh //| @@ -376,7 +396,7 @@ const mp_obj_property_t displayio_display_rotation_obj = { //| STATIC mp_obj_t displayio_display_obj_get_bus(mp_obj_t self_in) { displayio_display_obj_t *self = native_display(self_in); - return self->bus; + return common_hal_displayio_display_get_bus(self); } MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_bus_obj, displayio_display_obj_get_bus); @@ -412,18 +432,18 @@ STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *po if (bufinfo.typecode != BYTEARRAY_TYPECODE) { mp_raise_ValueError(translate("Buffer is not a bytearray.")); } - if (self->colorspace.depth != 16) { + if (self->core.colorspace.depth != 16) { mp_raise_ValueError(translate("Display must have a 16 bit colorspace.")); } displayio_area_t area = { .x1 = 0, .y1 = y, - .x2 = self->width, + .x2 = self->core.width, .y2 = y + 1 }; - uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->colorspace.depth; - uint16_t buffer_size = self->width / pixels_per_word; + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; + uint16_t buffer_size = self->core.width / pixels_per_word; uint16_t pixels_per_buffer = displayio_area_size(&area); if (pixels_per_buffer % pixels_per_word) { buffer_size += 1; @@ -440,7 +460,7 @@ STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *po mask[k] = 0x00000000; } - displayio_display_fill_area(self, &area, mask, result_buffer); + displayio_display_core_fill_area(&self->core, &area, mask, result_buffer); return result; } else { mp_raise_ValueError(translate("Buffer is too small")); @@ -448,13 +468,9 @@ STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *po } MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_fill_row_obj, 1, displayio_display_obj_fill_row); - - - STATIC const mp_rom_map_elem_t displayio_display_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_display_show_obj) }, - { MP_ROM_QSTR(MP_QSTR_refresh_soon), MP_ROM_PTR(&displayio_display_refresh_soon_obj) }, - { MP_ROM_QSTR(MP_QSTR_wait_for_frame), MP_ROM_PTR(&displayio_display_wait_for_frame_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&displayio_display_refresh_obj) }, { MP_ROM_QSTR(MP_QSTR_fill_row), MP_ROM_PTR(&displayio_display_fill_row_obj) }, { MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&displayio_display_auto_refresh_obj) }, diff --git a/shared-bindings/displayio/Display.h b/shared-bindings/displayio/Display.h index 0d416b868..06c848d0d 100644 --- a/shared-bindings/displayio/Display.h +++ b/shared-bindings/displayio/Display.h @@ -44,24 +44,29 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, uint8_t set_column_command, uint8_t set_row_command, uint8_t write_ram_command, uint8_t set_vertical_scroll, uint8_t* init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t* backlight_pin, uint16_t brightness_command, - mp_float_t brightness, bool auto_brightness, bool auto_refresh, uint8_t frames_per_second, - bool single_byte_bounds, bool data_as_commands); + mp_float_t brightness, bool auto_brightness, + bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second); -bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group); +bool common_hal_displayio_display_show(displayio_display_obj_t* self, + displayio_group_t* root_group); -void common_hal_displayio_display_refresh(displayio_display_obj_t* self); +// Times in ms. +void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_frame_time, uint32_t maximum_frame_time); -bool displayio_display_begin_transaction(displayio_display_obj_t* self); -void displayio_display_end_transaction(displayio_display_obj_t* self); - -bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self); -void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness); +bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self); +void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t* self, bool auto_refresh); uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t* self); uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t* self); uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t* self); +bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self); +void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness); + mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t* self); bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, mp_float_t brightness); +mp_obj_t common_hal_displayio_display_get_bus(displayio_display_obj_t* self); + + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_DISPLAY_H diff --git a/shared-bindings/displayio/EPaperDisplay.c b/shared-bindings/displayio/EPaperDisplay.c index 4b00240d1..8f3c16efd 100644 --- a/shared-bindings/displayio/EPaperDisplay.c +++ b/shared-bindings/displayio/EPaperDisplay.c @@ -208,7 +208,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_show_obj, displayio_epaperdisp //| Refreshes the display immediately or raises an exception if too soon. Use //| ``time.sleep(display.time_to_refresh)`` to sleep until a refresh can occur. //| -STATIC mp_obj_t displayio_epaperdisplay_obj_refresh_soon(mp_obj_t self_in) { +STATIC mp_obj_t displayio_epaperdisplay_obj_refresh(mp_obj_t self_in) { displayio_epaperdisplay_obj_t *self = native_display(self_in); bool ok = common_hal_displayio_epaperdisplay_refresh(self); if (!ok) { @@ -216,7 +216,7 @@ STATIC mp_obj_t displayio_epaperdisplay_obj_refresh_soon(mp_obj_t self_in) { } return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_soon_obj, displayio_epaperdisplay_obj_refresh_soon); +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_obj, displayio_epaperdisplay_obj_refresh); //| .. attribute:: time_to_refresh //| @@ -225,7 +225,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_soon_obj, displayio_ep //| STATIC mp_obj_t displayio_epaperdisplay_obj_get_time_to_refresh(mp_obj_t self_in) { displayio_epaperdisplay_obj_t *self = native_display(self_in); - return mp_obj_new_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self)); + return mp_obj_new_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self) / 1000.0); } MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_time_to_refresh_obj, displayio_epaperdisplay_obj_get_time_to_refresh); @@ -279,7 +279,7 @@ const mp_obj_property_t displayio_epaperdisplay_height_obj = { //| STATIC mp_obj_t displayio_epaperdisplay_obj_get_bus(mp_obj_t self_in) { displayio_epaperdisplay_obj_t *self = native_display(self_in); - return self->bus; + return common_hal_displayio_epaperdisplay_get_bus(self); } MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_bus_obj, displayio_epaperdisplay_obj_get_bus); @@ -293,7 +293,7 @@ const mp_obj_property_t displayio_epaperdisplay_bus_obj = { STATIC const mp_rom_map_elem_t displayio_epaperdisplay_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_epaperdisplay_show_obj) }, - { MP_ROM_QSTR(MP_QSTR_refresh_soon), MP_ROM_PTR(&displayio_epaperdisplay_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&displayio_epaperdisplay_refresh_obj) }, { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_epaperdisplay_width_obj) }, { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_epaperdisplay_height_obj) }, diff --git a/shared-bindings/displayio/EPaperDisplay.h b/shared-bindings/displayio/EPaperDisplay.h index 87feb4da0..2a3cb921f 100644 --- a/shared-bindings/displayio/EPaperDisplay.h +++ b/shared-bindings/displayio/EPaperDisplay.h @@ -46,23 +46,12 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select); -int32_t common_hal_displayio_epaperdisplay_wait_for_frame(displayio_epaperdisplay_obj_t* self); +bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* self); bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self, displayio_group_t* root_group); -bool displayio_epaperdisplay_begin_transaction(displayio_epaperdisplay_obj_t* self); -void displayio_epaperdisplay_end_transaction(displayio_epaperdisplay_obj_t* self); - -bool displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* self); - -mp_float_t displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self); - -// The second point of the region is exclusive. -void displayio_epaperdisplay_set_region_to_update(displayio_epaperdisplay_obj_t* self, displayio_area_t* area); -bool displayio_epaperdisplay_frame_queued(displayio_epaperdisplay_obj_t* self); - -void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self); -void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length); +// Returns time in milliseconds. +uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self); bool common_hal_displayio_epaperdisplay_get_auto_brightness(displayio_epaperdisplay_obj_t* self); void common_hal_displayio_epaperdisplay_set_auto_brightness(displayio_epaperdisplay_obj_t* self, bool auto_brightness); @@ -73,4 +62,6 @@ uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_o mp_float_t common_hal_displayio_epaperdisplay_get_brightness(displayio_epaperdisplay_obj_t* self); bool common_hal_displayio_epaperdisplay_set_brightness(displayio_epaperdisplay_obj_t* self, mp_float_t brightness); +mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t* self); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H diff --git a/shared-module/_stage/__init__.c b/shared-module/_stage/__init__.c index ca71b758e..bfef165e7 100644 --- a/shared-module/_stage/__init__.c +++ b/shared-module/_stage/__init__.c @@ -57,7 +57,7 @@ void render_stage(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, index += 1; // The buffer is full, send it. if (index >= buffer_size) { - display->send(display->bus, false, ((uint8_t*)buffer), + display->core.send(display->core.bus, false, false, ((uint8_t*)buffer), buffer_size * 2); index = 0; } @@ -67,6 +67,6 @@ void render_stage(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, } // Send the remaining data. if (index) { - display->send(display->bus, false, ((uint8_t*)buffer), index * 2); + display->core.send(display->core.bus, false, false, ((uint8_t*)buffer), index * 2); } } diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index fafb35f43..86800328f 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -33,7 +33,9 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/display_core.h" #include "supervisor/shared/display.h" +#include "supervisor/usb.h" #include #include @@ -41,48 +43,34 @@ #include "tick.h" void common_hal_displayio_display_construct(displayio_display_obj_t* self, - mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart, uint16_t rotation, - uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, - uint8_t set_column_command, uint8_t set_row_command, - uint8_t write_ram_command, uint8_t set_vertical_scroll, uint8_t* init_sequence, uint16_t init_sequence_len, - const mcu_pin_obj_t* backlight_pin, uint16_t brightness_command, mp_float_t brightness, bool auto_brightness, - bool single_byte_bounds, bool data_as_commands) { - self->colorspace.depth = color_depth; - self->colorspace.grayscale = grayscale; - self->colorspace.pixels_in_byte_share_row = pixels_in_byte_share_row; - self->colorspace.bytes_per_cell = bytes_per_cell; - self->colorspace.reverse_pixels_in_byte = reverse_pixels_in_byte; + mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart, + uint16_t rotation, uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, + uint8_t bytes_per_cell, bool reverse_pixels_in_byte, uint8_t set_column_command, + uint8_t set_row_command, uint8_t write_ram_command, uint8_t set_vertical_scroll, + uint8_t* init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t* backlight_pin, + uint16_t brightness_command, mp_float_t brightness, bool auto_brightness, + bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second) { + uint16_t ram_width = 0x100; + uint16_t ram_height = 0x100; + if (single_byte_bounds) { + ram_width = 0xff; + ram_height = 0xff; + } + displayio_display_core_construct(&self->core, bus, width, height, ram_width, ram_height, colstart, rowstart, rotation, + color_depth, grayscale, pixels_in_byte_share_row, bytes_per_cell, reverse_pixels_in_byte); + self->set_column_command = set_column_command; self->set_row_command = set_row_command; self->write_ram_command = write_ram_command; - self->refresh = false; - self->current_group = NULL; - self->colstart = colstart; - self->rowstart = rowstart; self->brightness_command = brightness_command; self->auto_brightness = auto_brightness; self->data_as_commands = data_as_commands; - self->single_byte_bounds = single_byte_bounds; - - if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) { - self->begin_transaction = common_hal_displayio_parallelbus_begin_transaction; - self->send = common_hal_displayio_parallelbus_send; - self->end_transaction = common_hal_displayio_parallelbus_end_transaction; - } else if (MP_OBJ_IS_TYPE(bus, &displayio_fourwire_type)) { - self->begin_transaction = common_hal_displayio_fourwire_begin_transaction; - self->send = common_hal_displayio_fourwire_send; - self->end_transaction = common_hal_displayio_fourwire_end_transaction; - } else if (MP_OBJ_IS_TYPE(bus, &displayio_i2cdisplay_type)) { - self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction; - self->send = common_hal_displayio_i2cdisplay_send; - self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction; - } else { - mp_raise_ValueError(translate("Unsupported display bus type")); - } - self->bus = bus; + + self->native_frames_per_second = native_frames_per_second; + self->native_frame_time = 1000 / native_frames_per_second; uint32_t i = 0; - while (!self->begin_transaction(self->bus)) { + while (!displayio_display_core_begin_transaction(&self->core)) { RUN_BACKGROUND_TASKS; } while (i < init_sequence_len) { @@ -95,10 +83,10 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, uint8_t full_command[data_size + 1]; full_command[0] = cmd[0]; memcpy(full_command + 1, data, data_size); - self->send(self->bus, true, true, full_command, data_size + 1); + self->core.send(self->core.bus, true, true, full_command, data_size + 1); } else { - self->send(self->bus, true, true, cmd, 1); - self->send(self->bus, false, false, data, data_size); + self->core.send(self->core.bus, true, true, cmd, 1); + self->core.send(self->core.bus, false, false, data, data_size); } uint16_t delay_length_ms = 10; if (delay) { @@ -111,33 +99,10 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, common_hal_time_delay_ms(delay_length_ms); i += 2 + data_size; } - self->end_transaction(self->bus); + self->core.end_transaction(self->core.bus); supervisor_start_terminal(width, height); - self->width = width; - self->height = height; - self->rotation = rotation % 360; - self->transform.x = 0; - self->transform.y = 0; - self->transform.scale = 1; - self->transform.mirror_x = false; - self->transform.mirror_y = false; - self->transform.transpose_xy = false; - if (rotation == 0 || rotation == 180) { - if (rotation == 180) { - self->transform.mirror_x = true; - self->transform.mirror_y = true; - } - } else { - self->transform.transpose_xy = true; - if (rotation == 270) { - self->transform.mirror_y = true; - } else { - self->transform.mirror_x = true; - } - } - // Always set the backlight type in case we're reusing memory. self->backlight_inout.base.type = &mp_type_NoneType; if (backlight_pin != NULL && common_hal_mcu_pin_is_free(backlight_pin)) { @@ -158,101 +123,98 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, self->current_brightness = -1.0; } - self->area.x1 = 0; - self->area.y1 = 0; - self->area.next = NULL; - - self->transform.dx = 1; - self->transform.dy = 1; - if (self->transform.transpose_xy) { - self->area.x2 = height; - self->area.y2 = width; - if (self->transform.mirror_x) { - self->transform.x = height; - self->transform.dx = -1; - } - if (self->transform.mirror_y) { - self->transform.y = width; - self->transform.dy = -1; - } - } else { - self->area.x2 = width; - self->area.y2 = height; - if (self->transform.mirror_x) { - self->transform.x = width; - self->transform.dx = -1; - } - if (self->transform.mirror_y) { - self->transform.y = height; - self->transform.dy = -1; - } - } - // Set the group after initialization otherwise we may send pixels while we delay in // initialization. common_hal_displayio_display_show(self, &circuitpython_splash); } bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group) { - if (root_group == NULL) { - if (!circuitpython_splash.in_group) { - root_group = &circuitpython_splash; - } else if (self->current_group == &circuitpython_splash) { - return false; + return displayio_display_core_show(&self->core, root_group); +} + +uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t* self){ + return displayio_display_core_get_width(&self->core); +} + +uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t* self){ + return displayio_display_core_get_height(&self->core); +} + +bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self) { + return self->auto_brightness; +} + +void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness) { + self->auto_brightness = auto_brightness; +} + +mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t* self) { + return self->current_brightness; +} + +bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, mp_float_t brightness) { + self->updating_backlight = true; + bool ok = false; + if (self->backlight_pwm.base.type == &pulseio_pwmout_type) { + common_hal_pulseio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t) (0xffff * brightness)); + ok = true; + } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { + common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99); + ok = true; + } else if (self->brightness_command != NO_BRIGHTNESS_COMMAND) { + ok = displayio_display_core_begin_transaction(&self->core); + if (ok) { + if (self->data_as_commands) { + uint8_t set_brightness[2] = {self->brightness_command, (uint8_t) (0xff * brightness)}; + self->core.send(self->core.bus, true, true, set_brightness, 2); + } else { + uint8_t command = self->brightness_command; + uint8_t hex_brightness = 0xff * brightness; + self->core.send(self->core.bus, true, true, &command, 1); + self->core.send(self->core.bus, false, false, &hex_brightness, 1); + } + displayio_display_core_end_transaction(&self->core); } + } - if (root_group == self->current_group) { - return true; - } - if (root_group != NULL && root_group->in_group) { - return false; - } - if (self->current_group != NULL) { - self->current_group->in_group = false; + self->updating_backlight = false; + if (ok) { + self->current_brightness = brightness; } + return ok; +} - if (root_group != NULL) { - displayio_group_update_transform(root_group, &self->transform); - root_group->in_group = true; - } - self->current_group = root_group; - self->full_refresh = true; - common_hal_displayio_display_refresh_soon(self); - return true; +mp_obj_t common_hal_displayio_display_get_bus(displayio_display_obj_t* self) { + return self->core.bus; } -const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self) { - if (self->full_refresh) { - self->area.next = NULL; - return &self->area; +STATIC const displayio_area_t* _get_refresh_areas(displayio_display_obj_t *self) { + if (self->core.full_refresh) { + self->core.area.next = NULL; + return &self->core.area; } else { - if (self->current_group == NULL || self->current_group->base.type != &displayio_group_type) { - asm("bkpt"); - } - return displayio_group_get_refresh_areas(self->current_group, NULL); + return displayio_group_get_refresh_areas(self->core.current_group, NULL); } } -int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* self) { - uint64_t last_refresh = self->last_refresh; - // Don't try to refresh if we got an exception. - while (last_refresh == self->last_refresh && MP_STATE_VM(mp_pending_exception) == NULL) { - RUN_BACKGROUND_TASKS; +STATIC void _send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) { + if (!self->data_as_commands) { + self->core.send(self->core.bus, true, true, &self->write_ram_command, 1); } - return 0; + self->core.send(self->core.bus, false, false, pixels, length); } -STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_t* area) { +STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* area) { uint16_t buffer_size = 128; // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. - if (!displayio_display_clip_area(display, area, &clipped)) { + if (!displayio_display_core_clip_area(&self->core, area, &clipped)) { return true; } uint16_t subrectangles = 1; uint16_t rows_per_buffer = displayio_area_height(&clipped); - uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / display->colorspace.depth; + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; uint16_t pixels_per_buffer = displayio_area_size(&clipped); if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) { rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); @@ -260,8 +222,8 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_ rows_per_buffer = 1; } // If pixels are packed by column then ensure rows_per_buffer is on a byte boundary. - if (display->colorspace.depth < 8 && !display->colorspace.pixels_in_byte_share_row) { - uint8_t pixels_per_byte = 8 / display->colorspace.depth; + if (self->core.colorspace.depth < 8 && !self->core.colorspace.pixels_in_byte_share_row) { + uint8_t pixels_per_byte = 8 / self->core.colorspace.depth; if (rows_per_buffer % pixels_per_byte != 0) { rows_per_buffer -= rows_per_buffer % pixels_per_byte; } @@ -296,15 +258,13 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_ } remaining_rows -= rows_per_buffer; - displayio_display_begin_transaction(display); - displayio_display_set_region_to_update(display, &subrectangle); - displayio_display_end_transaction(display); + displayio_display_core_set_region_to_update(&self->core, self->set_column_command, self->set_row_command, NO_COMMAND, NO_COMMAND, self->data_as_commands, false, &subrectangle); uint16_t subrectangle_size_bytes; - if (display->colorspace.depth >= 8) { - subrectangle_size_bytes = displayio_area_size(&subrectangle) * (display->colorspace.depth / 8); + if (self->core.colorspace.depth >= 8) { + subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); } else { - subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / display->colorspace.depth); + subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); } for (uint16_t k = 0; k < mask_length; k++) { @@ -314,14 +274,16 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_ buffer[k] = 0x00000000; } - displayio_display_fill_area(display, &subrectangle, mask, buffer); + displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - if (!displayio_display_begin_transaction(display)) { - // Can't acquire display bus; skip the rest of the data. Try next display. + // Can't acquire display bus; skip the rest of the data. + if (!displayio_display_core_bus_free(&self->core)) { return false; } - displayio_display_send_pixels(display, (uint8_t*) buffer, subrectangle_size_bytes); - displayio_display_end_transaction(display); + + displayio_display_core_begin_transaction(&self->core); + _send_pixels(self, (uint8_t*) buffer, subrectangle_size_bytes); + displayio_display_core_end_transaction(&self->core); // TODO(tannewt): Make refresh displays faster so we don't starve other // background tasks. @@ -330,182 +292,59 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_ return true; } -STATIC void refresh_display(displayio_display_obj_t* self) { - if (!displayio_display_begin_transaction(self)) { +STATIC void _refresh_display(displayio_display_obj_t* self) { + if (!displayio_display_core_bus_free(&self->core)) { // Can't acquire display bus; skip updating this display. Try next display. - continue; + return; } - displayio_display_end_transaction(self); - displayio_display_start_refresh(self); - const displayio_area_t* current_area = displayio_display_get_refresh_areas(self); + displayio_display_core_start_refresh(&self->core); + const displayio_area_t* current_area = _get_refresh_areas(self); while (current_area != NULL) { - refresh_area(self, current_area); + _refresh_area(self, current_area); current_area = current_area->next; } - displayio_display_finish_refresh(self); -} - -void common_hal_displayio_display_refresh(displayio_display_obj_t* self) { - // Time to refresh at specified frame rate? - while (!displayio_display_frame_queued(self)) { - // Too soon. Try next display. - continue; - } - refresh_display(self); -} - -bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self) { - return self->auto_brightness; -} - -uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t* self){ - return self->width; -} - -uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t* self){ - return self->height; + displayio_display_core_finish_refresh(&self->core); } uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t* self){ - return self->rotation; -} - -void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness) { - self->auto_brightness = auto_brightness; -} - -mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t* self) { - return self->current_brightness; + return self->core.rotation; } -bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, mp_float_t brightness) { - self->updating_backlight = true; - bool ok = false; - if (self->backlight_pwm.base.type == &pulseio_pwmout_type) { - common_hal_pulseio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t) (0xffff * brightness)); - ok = true; - } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { - common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99); - ok = true; - } else if (self->brightness_command != NO_BRIGHTNESS_COMMAND) { - ok = self->begin_transaction(self->bus); - if (ok) { - if (self->data_as_commands) { - uint8_t set_brightness[2] = {self->brightness_command, (uint8_t) (0xff * brightness)}; - self->send(self->bus, true, true, set_brightness, 2); - } else { - uint8_t command = self->brightness_command; - uint8_t hex_brightness = 0xff * brightness; - self->send(self->bus, true, true, &command, 1); - self->send(self->bus, false, false, &hex_brightness, 1); - } - self->end_transaction(self->bus); +void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_frame_time, uint32_t maximum_frame_time) { + if (!self->auto_refresh) { + uint64_t current_time = ticks_ms; + uint32_t current_frame_time = current_time - self->core.last_refresh; + // Test to see if the real frame time is below our minimum. + if (current_frame_time > maximum_frame_time) { + mp_raise_RuntimeError(translate("Below minimum frame rate")); } - - } - self->updating_backlight = false; - if (ok) { - self->current_brightness = brightness; - } - return ok; -} - -bool displayio_display_begin_transaction(displayio_display_obj_t* self) { - return self->begin_transaction(self->bus); -} - -void displayio_display_end_transaction(displayio_display_obj_t* self) { - self->end_transaction(self->bus); -} - -void displayio_display_set_region_to_update(displayio_display_obj_t* self, displayio_area_t* area) { - uint16_t x1 = area->x1; - uint16_t x2 = area->x2; - uint16_t y1 = area->y1; - uint16_t y2 = area->y2; - // Collapse down the dimension where multiple pixels are in a byte. - if (self->colorspace.depth < 8) { - uint8_t pixels_per_byte = 8 / self->colorspace.depth; - if (self->colorspace.pixels_in_byte_share_row) { - x1 /= pixels_per_byte * self->colorspace.bytes_per_cell; - x2 /= pixels_per_byte * self->colorspace.bytes_per_cell; - } else { - y1 /= pixels_per_byte * self->colorspace.bytes_per_cell; - y2 /= pixels_per_byte * self->colorspace.bytes_per_cell; + uint32_t current_call_time = current_time - self->last_refresh_call; + self->last_refresh_call = current_time; + // Skip the actual refresh to help catch up. + if (current_call_time > target_frame_time) { + return; + } + uint32_t remaining_time = target_frame_time - (current_frame_time % target_frame_time); + // We're ahead of the game so wait until we align with the frame rate. + while (ticks_ms - self->last_refresh_call < remaining_time) { + #ifdef MICROPY_VM_HOOK_LOOP + MICROPY_VM_HOOK_LOOP ; + #endif } } - - // Set column. - uint8_t data[5]; - data[0] = self->set_column_command; - uint8_t data_length = 1; - if (!self->data_as_commands) { - self->send(self->bus, true, true, data, 1); - data_length = 0; - } - if (self->single_byte_bounds) { - data[data_length++] = x1 + self->colstart; - data[data_length++] = x2 - 1 + self->colstart; - } else { - x1 += self->colstart; - x2 += self->colstart - 1; - data[data_length++] = x1 >> 8; - data[data_length++] = x1 & 0xff; - data[data_length++] = x2 >> 8; - data[data_length++] = x2 & 0xff; - } - self->send(self->bus, self->data_as_commands, self->data_as_commands, data, data_length); - - // Set row. - data[0] = self->set_row_command; - data_length = 1; - if (!self->data_as_commands) { - self->send(self->bus, true, true, data, 1); - data_length = 0; - } - if (self->single_byte_bounds) { - data[data_length++] = y1 + self->rowstart; - data[data_length++] = y2 - 1 + self->rowstart; - } else { - y1 += self->rowstart; - y2 += self->rowstart - 1; - data[data_length++] = y1 >> 8; - data[data_length++] = y1 & 0xff; - data[data_length++] = y2 >> 8; - data[data_length++] = y2 & 0xff; - } - self->send(self->bus, self->data_as_commands, self->data_as_commands, data, data_length); -} - -void displayio_display_start_refresh(displayio_display_obj_t* self) { - self->last_refresh = ticks_ms; -} - -bool displayio_display_frame_queued(displayio_display_obj_t* self) { - if (self->current_group == NULL) { - return false; - } - // Refresh at ~60 fps. - return (ticks_ms - self->last_refresh) > 16; + _refresh_display(self); } -void displayio_display_finish_refresh(displayio_display_obj_t* self) { - if (self->current_group != NULL) { - displayio_group_finish_refresh(self->current_group); - } - self->refresh = false; - self->full_refresh = false; - self->last_refresh = ticks_ms; +bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self) { + return self->auto_refresh; } -void displayio_display_send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) { - if (!self->data_as_commands) { - self->send(self->bus, true, true, &self->write_ram_command, 1); - } - self->send(self->bus, false, false, pixels, length); +void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t* self, + bool auto_refresh) { + self->auto_refresh = auto_refresh; } -void displayio_display_update_backlight(displayio_display_obj_t* self) { +STATIC void _update_backlight(displayio_display_obj_t* self) { if (!self->auto_brightness || self->updating_backlight) { return; } @@ -520,17 +359,15 @@ void displayio_display_update_backlight(displayio_display_obj_t* self) { } void displayio_display_background(displayio_display_obj_t* self) { - displayio_display_update_backlight(self); + _update_backlight(self); - if (self->auto_refresh && (ticks_ms - self->last_refresh) > 16) { - display_refresh(self); + if (self->auto_refresh && (ticks_ms - self->core.last_refresh) > self->native_frame_time) { + _refresh_display(self); } } void release_display(displayio_display_obj_t* self) { - if (self->current_group != NULL) { - self->current_group->in_group = false; - } + release_display_core(&self->core); if (self->backlight_pwm.base.type == &pulseio_pwmout_type) { common_hal_pulseio_pwmout_reset_ok(&self->backlight_pwm); common_hal_pulseio_pwmout_deinit(&self->backlight_pwm); @@ -539,34 +376,6 @@ void release_display(displayio_display_obj_t* self) { } } -bool displayio_display_fill_area(displayio_display_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer) { - return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer); -} - -bool displayio_display_clip_area(displayio_display_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped) { - bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped); - if (!overlaps) { - return false; - } - // Expand the area if we have multiple pixels per byte and we need to byte - // align the bounds. - if (self->colorspace.depth < 8) { - uint8_t pixels_per_byte = 8 / self->colorspace.depth * self->colorspace.bytes_per_cell; - if (self->colorspace.pixels_in_byte_share_row) { - if (clipped->x1 % pixels_per_byte != 0) { - clipped->x1 -= clipped->x1 % pixels_per_byte; - } - if (clipped->x2 % pixels_per_byte != 0) { - clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte; - } - } else { - if (clipped->y1 % pixels_per_byte != 0) { - clipped->y1 -= clipped->y1 % pixels_per_byte; - } - if (clipped->y2 % pixels_per_byte != 0) { - clipped->y2 += pixels_per_byte - clipped->y2 % pixels_per_byte; - } - } - } - return true; +void displayio_display_collect_ptrs(displayio_display_obj_t* self) { + displayio_display_core_collect_ptrs(&self->core); } diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index db0a23012..cff566a61 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -32,46 +32,33 @@ #include "shared-bindings/pulseio/PWMOut.h" #include "shared-module/displayio/area.h" - -typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); -typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); -typedef void (*display_bus_end_transaction)(mp_obj_t bus); +#include "shared-module/displayio/display_core.h" typedef struct { mp_obj_base_t base; - mp_obj_t bus; - displayio_group_t *current_group; - uint64_t last_refresh; - display_bus_begin_transaction begin_transaction; - display_bus_send send; - display_bus_end_transaction end_transaction; + displayio_display_core_t core; union { digitalio_digitalinout_obj_t backlight_inout; pulseio_pwmout_obj_t backlight_pwm; }; uint64_t last_backlight_refresh; - displayio_buffer_transform_t transform; - displayio_area_t area; + uint64_t last_refresh_call; mp_float_t current_brightness; - uint16_t width; - uint16_t height; - uint16_t rotation; - _displayio_colorspace_t colorspace; - int16_t colstart; - int16_t rowstart; uint16_t brightness_command; + uint16_t native_frames_per_second; + uint16_t native_frame_time; uint8_t set_column_command; uint8_t set_row_command; uint8_t write_ram_command; bool auto_refresh; - bool single_byte_bounds; bool data_as_commands; bool auto_brightness; bool updating_backlight; - bool full_refresh; // New group means we need to refresh the whole display. } displayio_display_obj_t; void displayio_display_background(displayio_display_obj_t* self); void release_display(displayio_display_obj_t* self); +void displayio_display_collect_ptrs(displayio_display_obj_t* self); + #endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 434adbdf4..6ff060a6a 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -50,18 +50,14 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* uint16_t set_current_column_command, uint16_t set_current_row_command, uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select) { - self->colorspace.depth = 1; - self->colorspace.grayscale = true; - self->colorspace.pixels_in_byte_share_row = true; - self->colorspace.bytes_per_cell = 1; - self->colorspace.reverse_pixels_in_byte = true; - if (highlight_color != 0x000000) { - self->colorspace.tricolor = true; - self->colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color); - self->colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color); + self->core.colorspace.tricolor = true; + self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color); + self->core.colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color); } + displayio_display_core_construct(&self->core, bus, width, height, ram_width, ram_height, colstart, rowstart, rotation, 1, true, true, 1, true); + self->set_column_window_command = set_column_window_command; self->set_row_window_command = set_row_window_command; self->set_current_column_command = set_current_column_command; @@ -72,10 +68,6 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self->color_bits_inverted = color_bits_inverted; self->refresh_display_command = refresh_display_command; self->busy_state = busy_state; - self->refresh = true; - self->current_group = NULL; - self->colstart = colstart; - self->rowstart = rowstart; self->refreshing = false; self->milliseconds_per_frame = seconds_per_frame * 1000; self->always_toggle_chip_select = always_toggle_chip_select; @@ -85,88 +77,6 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self->stop_sequence = stop_sequence; self->stop_sequence_len = stop_sequence_len; - - if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) { - self->bus_reset = common_hal_displayio_parallelbus_reset; - self->bus_free = common_hal_displayio_parallelbus_bus_free; - self->begin_transaction = common_hal_displayio_parallelbus_begin_transaction; - self->send = common_hal_displayio_parallelbus_send; - self->end_transaction = common_hal_displayio_parallelbus_end_transaction; - } else if (MP_OBJ_IS_TYPE(bus, &displayio_fourwire_type)) { - self->bus_reset = common_hal_displayio_fourwire_reset; - self->bus_free = common_hal_displayio_fourwire_bus_free; - self->begin_transaction = common_hal_displayio_fourwire_begin_transaction; - self->send = common_hal_displayio_fourwire_send; - self->end_transaction = common_hal_displayio_fourwire_end_transaction; - } else if (MP_OBJ_IS_TYPE(bus, &displayio_i2cdisplay_type)) { - self->bus_reset = common_hal_displayio_i2cdisplay_reset; - self->bus_free = common_hal_displayio_i2cdisplay_bus_free; - self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction; - self->send = common_hal_displayio_i2cdisplay_send; - self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction; - } else { - mp_raise_ValueError(translate("Unsupported display bus type")); - } - self->bus = bus; - - supervisor_start_terminal(width, height); - - self->width = width; - self->height = height; - rotation = rotation % 360; - self->transform.x = 0; - self->transform.y = 0; - self->transform.scale = 1; - self->transform.mirror_x = false; - self->transform.mirror_y = false; - self->transform.transpose_xy = false; - if (rotation == 0 || rotation == 180) { - if (rotation == 180) { - self->transform.mirror_x = true; - self->transform.mirror_y = true; - } - } else { - self->transform.transpose_xy = true; - if (rotation == 270) { - self->transform.mirror_y = true; - } else { - self->transform.mirror_x = true; - } - } - - self->ram_width = ram_width; - self->ram_height = ram_height; - - self->area.x1 = 0; - self->area.y1 = 0; - self->area.next = NULL; - - self->transform.dx = 1; - self->transform.dy = 1; - if (self->transform.transpose_xy) { - self->area.x2 = height; - self->area.y2 = width; - if (self->transform.mirror_x) { - self->transform.x = height; - self->transform.dx = -1; - } - if (self->transform.mirror_y) { - self->transform.y = width; - self->transform.dy = -1; - } - } else { - self->area.x2 = width; - self->area.y2 = height; - if (self->transform.mirror_x) { - self->transform.x = width; - self->transform.dx = -1; - } - if (self->transform.mirror_y) { - self->transform.y = height; - self->transform.dy = -1; - } - } - self->busy.base.type = &mp_type_NoneType; if (busy_pin != NULL) { self->busy.base.type = &digitalio_digitalinout_type; @@ -179,77 +89,37 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* // TODO: Clear } + supervisor_start_terminal(width, height); + // Set the group after initialization otherwise we may send pixels while we delay in // initialization. common_hal_displayio_epaperdisplay_show(self, &circuitpython_splash); } bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self, displayio_group_t* root_group) { - if (root_group == NULL && !circuitpython_splash.in_group) { - root_group = &circuitpython_splash; - } - if (root_group == self->current_group) { - return true; - } - if (root_group != NULL && root_group->in_group) { - return false; - } - if (self->current_group != NULL) { - self->current_group->in_group = false; - } - if (root_group != NULL) { - displayio_group_update_transform(root_group, &self->transform); - root_group->in_group = true; - self->current_group = root_group; - } - self->full_refresh = true; - return true; + return displayio_display_core_show(&self->core, root_group); } const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self) { const displayio_area_t* first_area; - if (self->current_group == NULL || self->current_group->base.type != &displayio_group_type) { - asm("bkpt"); - } - if (self->full_refresh) { - first_area = &self->area; + if (self->core.full_refresh) { + first_area = &self->core.area; } else { - first_area = displayio_group_get_refresh_areas(self->current_group, NULL); + first_area = displayio_group_get_refresh_areas(self->core.current_group, NULL); } if (first_area != NULL && self->set_row_window_command == NO_COMMAND) { - self->area.next = NULL; - return &self->area; + self->core.area.next = NULL; + return &self->core.area; } return first_area; } -int32_t common_hal_displayio_epaperdisplay_wait_for_frame(displayio_epaperdisplay_obj_t* self) { - uint64_t last_refresh = self->last_refresh; - // Don't try to refresh if we got an exception. - while (last_refresh == self->last_refresh && MP_STATE_VM(mp_pending_exception) == NULL) { - MICROPY_VM_HOOK_LOOP - } - return 0; -} - uint16_t common_hal_displayio_epaperdisplay_get_width(displayio_epaperdisplay_obj_t* self){ - return self->width; + return displayio_display_core_get_width(&self->core); } uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_obj_t* self){ - return self->height; -} - -bool displayio_epaperdisplay_bus_free(displayio_epaperdisplay_obj_t *self) { - return self->bus_free(self->bus); -} - -bool displayio_epaperdisplay_begin_transaction(displayio_epaperdisplay_obj_t* self) { - return self->begin_transaction(self->bus); -} - -void displayio_epaperdisplay_end_transaction(displayio_epaperdisplay_obj_t* self) { - self->end_transaction(self->bus); + return displayio_display_core_get_height(&self->core); } STATIC void wait_for_busy(displayio_epaperdisplay_obj_t* self) { @@ -271,10 +141,10 @@ STATIC void send_command_sequence(displayio_epaperdisplay_obj_t* self, bool shou bool delay = (data_size & DELAY) != 0; data_size &= ~DELAY; uint8_t *data = cmd + 2; - self->begin_transaction(self->bus); - self->send(self->bus, true, self->always_toggle_chip_select, cmd, 1); - self->send(self->bus, false, self->always_toggle_chip_select, data, data_size); - self->end_transaction(self->bus); + displayio_display_core_begin_transaction(&self->core); + self->core.send(self->core.bus, true, self->always_toggle_chip_select, cmd, 1); + self->core.send(self->core.bus, false, self->always_toggle_chip_select, data, data_size); + displayio_display_core_end_transaction(&self->core); uint16_t delay_length_ms = 0; if (delay) { data_size++; @@ -291,87 +161,17 @@ STATIC void send_command_sequence(displayio_epaperdisplay_obj_t* self, bool shou } } -void displayio_epaperdisplay_set_region_to_update(displayio_epaperdisplay_obj_t* self, displayio_area_t* area) { - if (self->set_row_window_command == NO_COMMAND) { - return; - } - uint16_t x1 = area->x1; - uint16_t x2 = area->x2; - uint16_t y1 = area->y1; - uint16_t y2 = area->y2; - // Collapse down the dimension where multiple pixels are in a byte. - uint8_t pixels_per_byte = 8 / self->colorspace.depth; - x1 /= pixels_per_byte * self->colorspace.bytes_per_cell; - x2 /= pixels_per_byte * self->colorspace.bytes_per_cell; - - - // Set column. - uint8_t data[5]; - data[0] = self->set_column_window_command; - self->send(self->bus, true, self->always_toggle_chip_select, data, 1); - uint8_t data_length = 0; - if (self->ram_width / pixels_per_byte < 0x100) { - data[data_length++] = x1 + self->colstart; - data[data_length++] = x2 - 1 + self->colstart; - } else { - x1 += self->colstart; - x2 += self->colstart - 1; - data[data_length++] = x1 >> 8; - data[data_length++] = x1 & 0xff; - data[data_length++] = x2 >> 8; - data[data_length++] = x2 & 0xff; - } - self->send(self->bus, false, self->always_toggle_chip_select, data, data_length); - if (self->set_current_column_command != NO_COMMAND) { - uint8_t command = self->set_current_column_command; - self->send(self->bus, true, self->always_toggle_chip_select, &command, 1); - self->send(self->bus, false, self->always_toggle_chip_select, data, data_length / 2); - } - - // Set row. - data[0] = self->set_row_window_command; - self->send(self->bus, true, self->always_toggle_chip_select, data, 1); - data_length = 0; - if (self->ram_height < 0x100) { - data[data_length++] = y1 + self->rowstart; - data[data_length++] = y2 - 1 + self->rowstart; - } else { - y1 += self->rowstart; - y2 += self->rowstart - 1; - data[data_length++] = y1 & 0xff; - data[data_length++] = y1 >> 8; - data[data_length++] = y2 & 0xff; - data[data_length++] = y2 >> 8; - } - self->send(self->bus, false, self->always_toggle_chip_select, data, data_length); - if (self->set_current_row_command != NO_COMMAND) { - uint8_t command = self->set_current_row_command; - self->send(self->bus, true, self->always_toggle_chip_select, &command, 1); - self->send(self->bus, false, self->always_toggle_chip_select, data, data_length / 2); - } -} - void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self) { // run start sequence - self->bus_reset(self->bus); + self->core.bus_reset(self->core.bus); send_command_sequence(self, true, self->start_sequence, self->start_sequence_len); - self->last_refresh = ticks_ms; -} - -void displayio_epaperdisplay_background_task(displayio_epaperdisplay_obj_t* self) { - if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) { - if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) { - self->refreshing = false; - // Run stop sequence but don't wait for busy because busy is set when sleeping. - send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); - } - } + displayio_display_core_start_refresh(&self->core); } uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self) { // Refresh at seconds per frame rate. - uint32_t elapsed_time = ticks_ms - self->last_refresh; + uint32_t elapsed_time = ticks_ms - self->core.last_refresh; if (elapsed_time > self->milliseconds_per_frame) { return 0; } @@ -380,20 +180,16 @@ uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaper void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) { // Actually refresh the display now that all pixel RAM has been updated. - displayio_epaperdisplay_begin_transaction(self); - self->send(self->bus, true, self->always_toggle_chip_select, &self->refresh_display_command, 1); - displayio_epaperdisplay_end_transaction(self); + displayio_display_core_begin_transaction(&self->core); + self->core.send(self->core.bus, true, self->always_toggle_chip_select, &self->refresh_display_command, 1); + displayio_display_core_end_transaction(&self->core); self->refreshing = true; - if (self->current_group != NULL) { - displayio_group_finish_refresh(self->current_group); - } - self->refresh = false; - self->full_refresh = false; - self->last_refresh = ticks_ms; + displayio_display_core_finish_refresh(&self->core); } -void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length) { +mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t* self) { + return self->core.bus; } bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, const displayio_area_t* area) { @@ -401,12 +197,12 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. - if (!displayio_epaperdisplay_clip_area(self, area, &clipped)) { + if (!displayio_display_core_clip_area(&self->core, area, &clipped)) { return true; } uint16_t subrectangles = 1; uint16_t rows_per_buffer = displayio_area_height(&clipped); - uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->colorspace.depth; + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; uint16_t pixels_per_buffer = displayio_area_size(&clipped); if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) { rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); @@ -431,23 +227,23 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c uint32_t mask[mask_length]; uint8_t passes = 1; - if (self->colorspace.tricolor) { + if (self->core.colorspace.tricolor) { passes = 2; } for (uint8_t pass = 0; pass < passes; pass++) { uint16_t remaining_rows = displayio_area_height(&clipped); - displayio_epaperdisplay_begin_transaction(self); - displayio_epaperdisplay_set_region_to_update(self, &clipped); - displayio_epaperdisplay_end_transaction(self); + if (self->set_row_window_command != NO_COMMAND) { + displayio_display_core_set_region_to_update(&self->core, self->set_column_window_command, self->set_row_window_command, self->set_current_column_command, self->set_current_row_command, false, self->always_toggle_chip_select, &clipped); + } uint8_t write_command = self->write_black_ram_command; if (pass == 1) { write_command = self->write_color_ram_command; } - displayio_epaperdisplay_begin_transaction(self); - self->send(self->bus, true, self->always_toggle_chip_select, &write_command, 1); - displayio_epaperdisplay_end_transaction(self); + displayio_display_core_begin_transaction(&self->core); + self->core.send(self->core.bus, true, self->always_toggle_chip_select, &write_command, 1); + displayio_display_core_end_transaction(&self->core); for (uint16_t j = 0; j < subrectangles; j++) { displayio_area_t subrectangle = { @@ -462,7 +258,7 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c remaining_rows -= rows_per_buffer; - uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->colorspace.depth); + uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); for (uint16_t k = 0; k < mask_length; k++) { mask[k] = 0x00000000; @@ -471,11 +267,11 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c buffer[k] = 0x00000000; } - self->colorspace.grayscale = true; + self->core.colorspace.grayscale = true; if (pass == 1) { - self->colorspace.grayscale = false; + self->core.colorspace.grayscale = false; } - displayio_group_fill_area(self->current_group, &self->colorspace, &subrectangle, mask, buffer); + displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); // Invert it all. if ((pass == 1 && self->color_bits_inverted) || @@ -485,12 +281,12 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c } } - if (!displayio_epaperdisplay_begin_transaction(self)) { + if (!displayio_display_core_begin_transaction(&self->core)) { // Can't acquire display bus; skip the rest of the data. Try next display. return false; } - self->send(self->bus, false, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes); - displayio_epaperdisplay_end_transaction(self); + self->core.send(self->core.bus, false, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes); + displayio_display_core_end_transaction(&self->core); // TODO(tannewt): Make refresh displays faster so we don't starve other // background tasks. @@ -512,57 +308,47 @@ bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* s return false; } } - if (self->current_group == NULL) { - return false; + if (self->core.current_group == NULL) { + return true; } // Refresh at seconds per frame rate. - if (ticks_ms - self->last_refresh) > self->milliseconds_per_frame; - - if (displayio_epaperdisplay_get_time_to_refresh(display) > 0) { + if (common_hal_displayio_epaperdisplay_get_time_to_refresh(self) > 0) { return false; } - if (!displayio_epaperdisplay_bus_free(display)) { + if (!displayio_display_core_bus_free(&self->core)) { // Can't acquire display bus; skip updating this display. Try next display. - continue; + return false; } - const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(display); + const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(self); if (current_area == NULL) { - continue; + return true; } - displayio_epaperdisplay_start_refresh(display); + displayio_epaperdisplay_start_refresh(self); while (current_area != NULL) { - displayio_epaperdisplay_refresh_area(display, current_area); + displayio_epaperdisplay_refresh_area(self, current_area); current_area = current_area->next; } - displayio_epaperdisplay_finish_refresh(display); + displayio_epaperdisplay_finish_refresh(self); + return true; } -void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) { - if (self->current_group != NULL) { - self->current_group->in_group = false; +void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self) { + if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) { + if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) { + self->refreshing = false; + // Run stop sequence but don't wait for busy because busy is set when sleeping. + send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); + } } +} + +void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) { + release_display_core(&self->core); if (self->busy.base.type == &digitalio_digitalinout_type) { common_hal_digitalio_digitalinout_deinit(&self->busy); } } -bool displayio_epaperdisplay_fill_area(displayio_epaperdisplay_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer) { - return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer); -} - -bool displayio_epaperdisplay_clip_area(displayio_epaperdisplay_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped) { - bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped); - if (!overlaps) { - return false; - } - // Expand the area if we have multiple pixels per byte and we need to byte - // align the bounds. - uint8_t pixels_per_byte = 8; - if (clipped->x1 % pixels_per_byte != 0) { - clipped->x1 -= clipped->x1 % pixels_per_byte; - } - if (clipped->x2 % pixels_per_byte != 0) { - clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte; - } - return true; +void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t* self) { + displayio_display_core_collect_ptrs(&self->core); } diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index cc7db6f6d..88b5ebfaf 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -32,38 +32,17 @@ #include "shared-bindings/pulseio/PWMOut.h" #include "shared-module/displayio/area.h" - -typedef void (*display_bus_bus_reset)(mp_obj_t bus); -typedef bool (*display_bus_bus_free)(mp_obj_t bus); -typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); -typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); -typedef void (*display_bus_end_transaction)(mp_obj_t bus); +#include "shared-module/displayio/display_core.h" typedef struct { mp_obj_base_t base; - mp_obj_t bus; - displayio_group_t *current_group; - uint64_t last_refresh; - display_bus_bus_reset bus_reset; - display_bus_bus_free bus_free; - display_bus_begin_transaction begin_transaction; - display_bus_send send; - display_bus_end_transaction end_transaction; + displayio_display_core_t core; digitalio_digitalinout_obj_t busy; uint32_t milliseconds_per_frame; uint8_t* start_sequence; uint32_t start_sequence_len; uint8_t* stop_sequence; uint32_t stop_sequence_len; - displayio_buffer_transform_t transform; - displayio_area_t area; - uint16_t width; - uint16_t height; - uint16_t ram_width; - uint16_t ram_height; - _displayio_colorspace_t colorspace; - int16_t colstart; - int16_t rowstart; uint16_t set_column_window_command; uint16_t set_row_window_command; uint16_t set_current_column_command; @@ -76,11 +55,12 @@ typedef struct { bool black_bits_inverted; bool color_bits_inverted; bool refreshing; - bool full_refresh; // New group means we need to refresh the whole display. bool always_toggle_chip_select; } displayio_epaperdisplay_obj_t; void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self); void release_epaperdisplay(displayio_epaperdisplay_obj_t* self); +void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t* self); + #endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 0ebed58c2..0a3b1fd10 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -4,7 +4,6 @@ #include "shared-module/displayio/__init__.h" #include "lib/utils/interrupt_char.h" -#include "py/gc.h" #include "py/reload.h" #include "py/runtime.h" #include "shared-bindings/board/__init__.h" @@ -16,7 +15,6 @@ #include "supervisor/shared/autoreload.h" #include "supervisor/shared/display.h" #include "supervisor/memory.h" -#include "supervisor/usb.h" primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; @@ -46,8 +44,8 @@ void displayio_background(void) { } if (displays[i].display.base.type == &displayio_display_type) { displayio_display_background(&displays[i].display); - } else if (displays[i].epaperdisplay.base.type == &displayio_epaperdisplay_type) { - displayio_epaperdisplay_background(&displays[i].epaperdisplay); + } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) { + displayio_epaperdisplay_background(&displays[i].epaper_display); } } @@ -162,9 +160,9 @@ void displayio_gc_collect(void) { // Alternatively, we could use gc_collect_root over the whole object, // but this is more precise, and is the only field that needs marking. if (displays[i].display.base.type == &displayio_display_type) { - gc_collect_ptr(displays[i].display.current_group); + displayio_display_collect_ptrs(&displays[i].display); } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) { - gc_collect_ptr(displays[i].epaper_display.current_group); + displayio_epaperdisplay_collect_ptrs(&displays[i].epaper_display); } } } diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c new file mode 100644 index 000000000..f8a8d8fea --- /dev/null +++ b/shared-module/displayio/display_core.c @@ -0,0 +1,309 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 "shared-bindings/displayio/Display.h" + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/FourWire.h" +#include "shared-bindings/displayio/I2CDisplay.h" +#include "shared-bindings/displayio/ParallelBus.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/time/__init__.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/display.h" + +#include +#include + +#include "tick.h" + +void displayio_display_core_construct(displayio_display_core_t* self, + mp_obj_t bus, uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation, + uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte) { + self->colorspace.depth = color_depth; + self->colorspace.grayscale = grayscale; + self->colorspace.pixels_in_byte_share_row = pixels_in_byte_share_row; + self->colorspace.bytes_per_cell = bytes_per_cell; + self->colorspace.reverse_pixels_in_byte = reverse_pixels_in_byte; + self->current_group = NULL; + self->colstart = colstart; + self->rowstart = rowstart; + + if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) { + self->bus_reset = common_hal_displayio_parallelbus_reset; + self->bus_free = common_hal_displayio_parallelbus_bus_free; + self->begin_transaction = common_hal_displayio_parallelbus_begin_transaction; + self->send = common_hal_displayio_parallelbus_send; + self->end_transaction = common_hal_displayio_parallelbus_end_transaction; + } else if (MP_OBJ_IS_TYPE(bus, &displayio_fourwire_type)) { + self->bus_reset = common_hal_displayio_fourwire_reset; + self->bus_free = common_hal_displayio_fourwire_bus_free; + self->begin_transaction = common_hal_displayio_fourwire_begin_transaction; + self->send = common_hal_displayio_fourwire_send; + self->end_transaction = common_hal_displayio_fourwire_end_transaction; + } else if (MP_OBJ_IS_TYPE(bus, &displayio_i2cdisplay_type)) { + self->bus_reset = common_hal_displayio_i2cdisplay_reset; + self->bus_free = common_hal_displayio_i2cdisplay_bus_free; + self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction; + self->send = common_hal_displayio_i2cdisplay_send; + self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction; + } else { + mp_raise_ValueError(translate("Unsupported display bus type")); + } + self->bus = bus; + + + supervisor_start_terminal(width, height); + + self->width = width; + self->height = height; + self->ram_width = width; + self->ram_height = height; + rotation = rotation % 360; + self->transform.x = 0; + self->transform.y = 0; + self->transform.scale = 1; + self->transform.mirror_x = false; + self->transform.mirror_y = false; + self->transform.transpose_xy = false; + if (rotation == 0 || rotation == 180) { + if (rotation == 180) { + self->transform.mirror_x = true; + self->transform.mirror_y = true; + } + } else { + self->transform.transpose_xy = true; + if (rotation == 270) { + self->transform.mirror_y = true; + } else { + self->transform.mirror_x = true; + } + } + + self->area.x1 = 0; + self->area.y1 = 0; + self->area.next = NULL; + + self->transform.dx = 1; + self->transform.dy = 1; + if (self->transform.transpose_xy) { + self->area.x2 = height; + self->area.y2 = width; + if (self->transform.mirror_x) { + self->transform.x = height; + self->transform.dx = -1; + } + if (self->transform.mirror_y) { + self->transform.y = width; + self->transform.dy = -1; + } + } else { + self->area.x2 = width; + self->area.y2 = height; + if (self->transform.mirror_x) { + self->transform.x = width; + self->transform.dx = -1; + } + if (self->transform.mirror_y) { + self->transform.y = height; + self->transform.dy = -1; + } + } +} + +bool displayio_display_core_show(displayio_display_core_t* self, displayio_group_t* root_group) { + if (root_group == NULL) { + if (!circuitpython_splash.in_group) { + root_group = &circuitpython_splash; + } else if (self->current_group == &circuitpython_splash) { + return false; + } + } + if (root_group == self->current_group) { + return true; + } + if (root_group != NULL && root_group->in_group) { + return false; + } + if (self->current_group != NULL) { + self->current_group->in_group = false; + } + + if (root_group != NULL) { + displayio_group_update_transform(root_group, &self->transform); + root_group->in_group = true; + } + self->current_group = root_group; + self->full_refresh = true; + return true; +} + +uint16_t displayio_display_core_get_width(displayio_display_core_t* self){ + return self->width; +} + +uint16_t displayio_display_core_get_height(displayio_display_core_t* self){ + return self->height; +} + +bool displayio_display_core_bus_free(displayio_display_core_t *self) { + return self->bus_free(self->bus); +} + +bool displayio_display_core_begin_transaction(displayio_display_core_t* self) { + return self->begin_transaction(self->bus); +} + +void displayio_display_core_end_transaction(displayio_display_core_t* self) { + self->end_transaction(self->bus); +} + +void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, bool data_as_commands, bool always_toggle_chip_select, displayio_area_t* area) { + displayio_display_core_begin_transaction(self); + uint16_t x1 = area->x1; + uint16_t x2 = area->x2; + uint16_t y1 = area->y1; + uint16_t y2 = area->y2; + // Collapse down the dimension where multiple pixels are in a byte. + if (self->colorspace.depth < 8) { + uint8_t pixels_per_byte = 8 / self->colorspace.depth; + if (self->colorspace.pixels_in_byte_share_row) { + x1 /= pixels_per_byte * self->colorspace.bytes_per_cell; + x2 /= pixels_per_byte * self->colorspace.bytes_per_cell; + } else { + y1 /= pixels_per_byte * self->colorspace.bytes_per_cell; + y2 /= pixels_per_byte * self->colorspace.bytes_per_cell; + } + } + + // Set column. + uint8_t data[5]; + data[0] = column_command; + uint8_t data_length = 1; + if (!data_as_commands) { + self->send(self->bus, true, true, data, 1); + data_length = 0; + } + if (self->ram_width < 0x100) { + data[data_length++] = x1 + self->colstart; + data[data_length++] = x2 - 1 + self->colstart; + } else { + x1 += self->colstart; + x2 += self->colstart - 1; + data[data_length++] = x1 >> 8; + data[data_length++] = x1 & 0xff; + data[data_length++] = x2 >> 8; + data[data_length++] = x2 & 0xff; + } + self->send(self->bus, data_as_commands, data_as_commands, data, data_length); + if (set_current_column_command != NO_COMMAND) { + uint8_t command = set_current_column_command; + self->send(self->bus, true, always_toggle_chip_select, &command, 1); + self->send(self->bus, false, always_toggle_chip_select, data, data_length / 2); + } + + // Set row. + data[0] = row_command; + data_length = 1; + if (!data_as_commands) { + self->send(self->bus, true, true, data, 1); + data_length = 0; + } + if (self->ram_height < 0x100) { + data[data_length++] = y1 + self->rowstart; + data[data_length++] = y2 - 1 + self->rowstart; + } else { + y1 += self->rowstart; + y2 += self->rowstart - 1; + data[data_length++] = y1 >> 8; + data[data_length++] = y1 & 0xff; + data[data_length++] = y2 >> 8; + data[data_length++] = y2 & 0xff; + } + self->send(self->bus, data_as_commands, data_as_commands, data, data_length); + if (set_current_row_command != NO_COMMAND) { + uint8_t command = set_current_row_command; + self->send(self->bus, true, always_toggle_chip_select, &command, 1); + self->send(self->bus, false, always_toggle_chip_select, data, data_length / 2); + } + + displayio_display_core_end_transaction(self); +} + +void displayio_display_core_start_refresh(displayio_display_core_t* self) { + self->last_refresh = ticks_ms; +} + +void displayio_display_core_finish_refresh(displayio_display_core_t* self) { + if (self->current_group != NULL) { + displayio_group_finish_refresh(self->current_group); + } + self->full_refresh = false; + self->last_refresh = ticks_ms; +} + +void release_display_core(displayio_display_core_t* self) { + if (self->current_group != NULL) { + self->current_group->in_group = false; + } +} + +void displayio_display_core_collect_ptrs(displayio_display_core_t* self) { + gc_collect_ptr(self->current_group); +} + +bool displayio_display_core_fill_area(displayio_display_core_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer) { + return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer); +} + +bool displayio_display_core_clip_area(displayio_display_core_t *self, const displayio_area_t* area, displayio_area_t* clipped) { + bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped); + if (!overlaps) { + return false; + } + // Expand the area if we have multiple pixels per byte and we need to byte + // align the bounds. + if (self->colorspace.depth < 8) { + uint8_t pixels_per_byte = 8 / self->colorspace.depth * self->colorspace.bytes_per_cell; + if (self->colorspace.pixels_in_byte_share_row) { + if (clipped->x1 % pixels_per_byte != 0) { + clipped->x1 -= clipped->x1 % pixels_per_byte; + } + if (clipped->x2 % pixels_per_byte != 0) { + clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte; + } + } else { + if (clipped->y1 % pixels_per_byte != 0) { + clipped->y1 -= clipped->y1 % pixels_per_byte; + } + if (clipped->y2 % pixels_per_byte != 0) { + clipped->y2 += pixels_per_byte - clipped->y2 % pixels_per_byte; + } + } + } + return true; +} diff --git a/shared-module/displayio/display_core.h b/shared-module/displayio/display_core.h new file mode 100644 index 000000000..c837ce913 --- /dev/null +++ b/shared-module/displayio/display_core.h @@ -0,0 +1,90 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H +#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H + +#include "shared-bindings/displayio/Group.h" + +#include "shared-module/displayio/area.h" + +#define NO_COMMAND 0x100 + +typedef void (*display_bus_bus_reset)(mp_obj_t bus); +typedef bool (*display_bus_bus_free)(mp_obj_t bus); +typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); +typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); +typedef void (*display_bus_end_transaction)(mp_obj_t bus); + +typedef struct { + mp_obj_t bus; + displayio_group_t *current_group; + uint64_t last_refresh; + display_bus_bus_reset bus_reset; + display_bus_bus_free bus_free; + display_bus_begin_transaction begin_transaction; + display_bus_send send; + display_bus_end_transaction end_transaction; + displayio_buffer_transform_t transform; + displayio_area_t area; + uint16_t width; + uint16_t height; + uint16_t rotation; + uint16_t ram_width; + uint16_t ram_height; + _displayio_colorspace_t colorspace; + int16_t colstart; + int16_t rowstart; + bool full_refresh; // New group means we need to refresh the whole display. +} displayio_display_core_t; + +void displayio_display_core_construct(displayio_display_core_t* self, + mp_obj_t bus, uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation, + uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte); + +bool displayio_display_core_show(displayio_display_core_t* self, displayio_group_t* root_group); + +uint16_t displayio_display_core_get_width(displayio_display_core_t* self); +uint16_t displayio_display_core_get_height(displayio_display_core_t* self); + +bool displayio_display_core_bus_free(displayio_display_core_t *self); +bool displayio_display_core_begin_transaction(displayio_display_core_t* self); +void displayio_display_core_end_transaction(displayio_display_core_t* self); + +void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, bool data_as_commands, bool always_toggle_chip_select, displayio_area_t* area); + +void release_display_core(displayio_display_core_t* self); + +void displayio_display_core_start_refresh(displayio_display_core_t* self); +void displayio_display_core_finish_refresh(displayio_display_core_t* self); + +void displayio_display_core_collect_ptrs(displayio_display_core_t* self); + +bool displayio_display_core_fill_area(displayio_display_core_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer); + +bool displayio_display_core_clip_area(displayio_display_core_t *self, const displayio_area_t* area, displayio_area_t* clipped); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H -- cgit v1.2.3 From 24b30965c41ae32b82c56b166f3287cf2c14f419 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 20 Aug 2019 21:35:42 -0700 Subject: Refresh ePaper displays once code.py is done running --- main.c | 8 ++++++++ shared-bindings/displayio/__init__.c | 2 +- shared-module/displayio/EPaperDisplay.c | 27 +++++++++++++++++++++++++++ shared-module/displayio/EPaperDisplay.h | 1 + shared-module/displayio/__init__.c | 24 +++++++++++++----------- shared-module/displayio/display_core.c | 3 ++- 6 files changed, 52 insertions(+), 13 deletions(-) (limited to 'shared-module') diff --git a/main.c b/main.c index 6ab50fc3c..076674c44 100755 --- a/main.c +++ b/main.c @@ -253,6 +253,7 @@ bool run_code_py(safe_mode_t safe_mode) { } bool serial_connected_before_animation = false; + bool refreshed_epaper_display = false; rgb_status_animation_t animation; prep_rgb_status_animation(&result, found_main, safe_mode, &animation); while (true) { @@ -290,6 +291,13 @@ bool run_code_py(safe_mode_t safe_mode) { } serial_connected_before_animation = serial_connected(); + // Refresh the ePaper display if we have one. That way it'll show an error message. + #if CIRCUITPY_DISPLAYIO + if (!refreshed_epaper_display) { + refreshed_epaper_display = maybe_refresh_epaperdisplay(); + } + #endif + tick_rgb_status_animation(&animation); } } diff --git a/shared-bindings/displayio/__init__.c b/shared-bindings/displayio/__init__.c index 8c5d1f228..f78325593 100644 --- a/shared-bindings/displayio/__init__.c +++ b/shared-bindings/displayio/__init__.c @@ -77,7 +77,7 @@ //| //| Releases any actively used displays so their busses and pins can be used again. This will also //| release the builtin display on boards that have one. You will need to reinitialize it yourself -//| afterwards. +//| afterwards. This may take seconds to complete if an active EPaperDisplay is refreshing. //| //| Use this once in your code.py if you initialize a display. Place it right before the //| initialization so the display is active as long as possible. diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 6ff060a6a..7e65502d4 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -170,6 +170,9 @@ void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self) } uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self) { + if (self->core.last_refresh == 0) { + return 0; + } // Refresh at seconds per frame rate. uint32_t elapsed_time = ticks_ms - self->core.last_refresh; if (elapsed_time > self->milliseconds_per_frame) { @@ -343,6 +346,13 @@ void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self) { } void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) { + if (self->refreshing) { + wait_for_busy(self); + self->refreshing = false; + // Run stop sequence but don't wait for busy because busy is set when sleeping. + send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); + } + release_display_core(&self->core); if (self->busy.base.type == &digitalio_digitalinout_type) { common_hal_digitalio_digitalinout_deinit(&self->busy); @@ -352,3 +362,20 @@ void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) { void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t* self) { displayio_display_core_collect_ptrs(&self->core); } + +bool maybe_refresh_epaperdisplay(void) { + for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { + if (displays[i].epaper_display.base.type != &displayio_epaperdisplay_type || + displays[i].epaper_display.core.current_group != &circuitpython_splash) { + // Skip regular displays and those not showing the splash. + continue; + } + displayio_epaperdisplay_obj_t* display = &displays[i].epaper_display; + if (common_hal_displayio_epaperdisplay_get_time_to_refresh(display) != 0) { + return false; + } + return common_hal_displayio_epaperdisplay_refresh(display); + } + // Return true if no ePaper displays are available to pretend it was updated. + return true; +} diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index 88b5ebfaf..2be2add4b 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -60,6 +60,7 @@ typedef struct { void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self); void release_epaperdisplay(displayio_epaperdisplay_obj_t* self); +bool maybe_refresh_epaperdisplay(void); void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t* self); diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 0a3b1fd10..a41884c78 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -54,6 +54,19 @@ void displayio_background(void) { } void common_hal_displayio_release_displays(void) { + // Release displays before busses so that they can send any final commands to turn the display + // off properly. + for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { + mp_const_obj_t display_type = displays[i].display.base.type; + if (display_type == NULL || display_type == &mp_type_NoneType) { + continue; + } else if (display_type == &displayio_display_type) { + release_display(&displays[i].display); + } else if (display_type == &displayio_epaperdisplay_type) { + release_epaperdisplay(&displays[i].epaper_display); + } + displays[i].display.base.type = &mp_type_NoneType; + } for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { mp_const_obj_t bus_type = displays[i].fourwire_bus.base.type; if (bus_type == NULL || bus_type == &mp_type_NoneType) { @@ -67,17 +80,6 @@ void common_hal_displayio_release_displays(void) { } displays[i].fourwire_bus.base.type = &mp_type_NoneType; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_type = displays[i].display.base.type; - if (display_type == NULL || display_type == &mp_type_NoneType) { - continue; - } else if (display_type == &displayio_display_type) { - release_display(&displays[i].display); - } else if (display_type == &displayio_epaperdisplay_type) { - release_epaperdisplay(&displays[i].epaper_display); - } - displays[i].display.base.type = &mp_type_NoneType; - } supervisor_stop_terminal(); } diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c index f8a8d8fea..24d8ab51f 100644 --- a/shared-module/displayio/display_core.c +++ b/shared-module/displayio/display_core.c @@ -52,6 +52,7 @@ void displayio_display_core_construct(displayio_display_core_t* self, self->current_group = NULL; self->colstart = colstart; self->rowstart = rowstart; + self->last_refresh = 0; if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) { self->bus_reset = common_hal_displayio_parallelbus_reset; @@ -140,7 +141,7 @@ bool displayio_display_core_show(displayio_display_core_t* self, displayio_group if (!circuitpython_splash.in_group) { root_group = &circuitpython_splash; } else if (self->current_group == &circuitpython_splash) { - return false; + return true; } } if (root_group == self->current_group) { -- cgit v1.2.3 From 3a98de12368a63de1538f61da7690e9fa230b540 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 21 Aug 2019 11:49:49 -0700 Subject: Add reset() to display busses to detect whether it works --- .../boards/itsybitsy_m0_express/mpconfigboard.mk | 3 +++ ports/atmel-samd/common-hal/displayio/ParallelBus.c | 7 ++++++- shared-bindings/displayio/FourWire.c | 16 ++++++++++++++++ shared-bindings/displayio/FourWire.h | 2 +- shared-bindings/displayio/I2CDisplay.c | 16 ++++++++++++++++ shared-bindings/displayio/I2CDisplay.h | 2 +- shared-bindings/displayio/ParallelBus.c | 16 ++++++++++++++++ shared-bindings/displayio/ParallelBus.h | 2 +- shared-module/displayio/FourWire.c | 8 +++++++- shared-module/displayio/I2CDisplay.c | 10 +++++++--- shared-module/displayio/display_core.h | 2 +- 11 files changed, 75 insertions(+), 9 deletions(-) (limited to 'shared-module') diff --git a/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk index fd66f425d..410a0e087 100644 --- a/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/itsybitsy_m0_express/mpconfigboard.mk @@ -12,6 +12,9 @@ EXTERNAL_FLASH_DEVICE_COUNT = 2 EXTERNAL_FLASH_DEVICES = "W25Q16FW, GD25Q16C" LONGINT_IMPL = MPZ +CIRCUITPY_I2CSLAVE = 0 +CIRCUITPY_RTC = 0 + CFLAGS_INLINE_LIMIT = 60 SUPEROPT_GC = 0 diff --git a/ports/atmel-samd/common-hal/displayio/ParallelBus.c b/ports/atmel-samd/common-hal/displayio/ParallelBus.c index a43d20511..e2ce9952b 100644 --- a/ports/atmel-samd/common-hal/displayio/ParallelBus.c +++ b/ports/atmel-samd/common-hal/displayio/ParallelBus.c @@ -79,6 +79,7 @@ void common_hal_displayio_parallelbus_construct(displayio_parallelbus_obj_t* sel self->write_group = &PORT->Group[write->number / 32]; self->write_mask = 1 << (write->number % 32); + self->reset.base.type = &mp_type_NoneType; if (reset != NULL) { self->reset.base.type = &digitalio_digitalinout_type; common_hal_digitalio_digitalinout_construct(&self->reset, reset); @@ -108,12 +109,16 @@ void common_hal_displayio_parallelbus_deinit(displayio_parallelbus_obj_t* self) reset_pin_number(self->reset.pin->number); } -void common_hal_displayio_parallelbus_reset(mp_obj_t obj) { +bool common_hal_displayio_parallelbus_reset(mp_obj_t obj) { displayio_parallelbus_obj_t* self = MP_OBJ_TO_PTR(obj); + if (self->reset.base.type == &mp_type_NoneType) { + return false; + } common_hal_digitalio_digitalinout_set_value(&self->reset, false); common_hal_mcu_delay_us(4); common_hal_digitalio_digitalinout_set_value(&self->reset, true); + return true; } bool common_hal_displayio_parallelbus_bus_free(mp_obj_t obj) { diff --git a/shared-bindings/displayio/FourWire.c b/shared-bindings/displayio/FourWire.c index efd553e2e..c69623344 100644 --- a/shared-bindings/displayio/FourWire.c +++ b/shared-bindings/displayio/FourWire.c @@ -103,6 +103,21 @@ STATIC mp_obj_t displayio_fourwire_make_new(const mp_obj_type_t *type, size_t n_ return self; } +//| .. method:: reset() +//| +//| Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available. +//| +STATIC mp_obj_t displayio_fourwire_obj_reset(mp_obj_t self_in) { + displayio_fourwire_obj_t *self = self_in; + + if (!common_hal_displayio_fourwire_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_fourwire_reset_obj, displayio_fourwire_obj_reset); + //| .. method:: send(command, data, *, toggle_every_byte=False) //| //| Sends the given command value followed by the full set of data. Display state, such as @@ -140,6 +155,7 @@ STATIC mp_obj_t displayio_fourwire_obj_send(size_t n_args, const mp_obj_t *pos_a MP_DEFINE_CONST_FUN_OBJ_KW(displayio_fourwire_send_obj, 3, displayio_fourwire_obj_send); STATIC const mp_rom_map_elem_t displayio_fourwire_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&displayio_fourwire_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_fourwire_send_obj) }, }; STATIC MP_DEFINE_CONST_DICT(displayio_fourwire_locals_dict, displayio_fourwire_locals_dict_table); diff --git a/shared-bindings/displayio/FourWire.h b/shared-bindings/displayio/FourWire.h index ee69a63ac..9c71d63cc 100644 --- a/shared-bindings/displayio/FourWire.h +++ b/shared-bindings/displayio/FourWire.h @@ -40,7 +40,7 @@ void common_hal_displayio_fourwire_construct(displayio_fourwire_obj_t* self, void common_hal_displayio_fourwire_deinit(displayio_fourwire_obj_t* self); -void common_hal_displayio_fourwire_reset(mp_obj_t self); +bool common_hal_displayio_fourwire_reset(mp_obj_t self); bool common_hal_displayio_fourwire_bus_free(mp_obj_t self); bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t self); diff --git a/shared-bindings/displayio/I2CDisplay.c b/shared-bindings/displayio/I2CDisplay.c index 4428e8dbb..4de5e83ef 100644 --- a/shared-bindings/displayio/I2CDisplay.c +++ b/shared-bindings/displayio/I2CDisplay.c @@ -95,6 +95,21 @@ STATIC mp_obj_t displayio_i2cdisplay_make_new(const mp_obj_type_t *type, size_t return self; } +//| .. method:: reset() +//| +//| Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available. +//| +STATIC mp_obj_t displayio_i2cdisplay_obj_reset(mp_obj_t self_in) { + displayio_i2cdisplay_obj_t *self = self_in; + + if (!common_hal_displayio_i2cdisplay_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_i2cdisplay_reset_obj, displayio_i2cdisplay_obj_reset); + //| .. method:: send(command, data) //| //| Sends the given command value followed by the full set of data. Display state, such as @@ -124,6 +139,7 @@ STATIC mp_obj_t displayio_i2cdisplay_obj_send(mp_obj_t self, mp_obj_t command_ob MP_DEFINE_CONST_FUN_OBJ_3(displayio_i2cdisplay_send_obj, displayio_i2cdisplay_obj_send); STATIC const mp_rom_map_elem_t displayio_i2cdisplay_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&displayio_i2cdisplay_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_i2cdisplay_send_obj) }, }; STATIC MP_DEFINE_CONST_DICT(displayio_i2cdisplay_locals_dict, displayio_i2cdisplay_locals_dict_table); diff --git a/shared-bindings/displayio/I2CDisplay.h b/shared-bindings/displayio/I2CDisplay.h index 6a75fc488..683cff383 100644 --- a/shared-bindings/displayio/I2CDisplay.h +++ b/shared-bindings/displayio/I2CDisplay.h @@ -37,7 +37,7 @@ void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t* self, void common_hal_displayio_i2cdisplay_deinit(displayio_i2cdisplay_obj_t* self); -void common_hal_displayio_i2cdisplay_reset(mp_obj_t self); +bool common_hal_displayio_i2cdisplay_reset(mp_obj_t self); bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t self); bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t self); diff --git a/shared-bindings/displayio/ParallelBus.c b/shared-bindings/displayio/ParallelBus.c index e260f9b7e..a08c37679 100644 --- a/shared-bindings/displayio/ParallelBus.c +++ b/shared-bindings/displayio/ParallelBus.c @@ -106,6 +106,21 @@ STATIC mp_obj_t displayio_parallelbus_make_new(const mp_obj_type_t *type, size_t return self; } +//| .. method:: reset() +//| +//| Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available. +//| +STATIC mp_obj_t displayio_parallelbus_obj_reset(mp_obj_t self_in) { + displayio_parallelbus_obj_t *self = self_in; + + if (!common_hal_displayio_parallelbus_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_parallelbus_reset_obj, displayio_parallelbus_obj_reset); + //| .. method:: send(command, data) //| //| Sends the given command value followed by the full set of data. Display state, such as @@ -133,6 +148,7 @@ STATIC mp_obj_t displayio_parallelbus_obj_send(mp_obj_t self, mp_obj_t command_o MP_DEFINE_CONST_FUN_OBJ_3(displayio_parallelbus_send_obj, displayio_parallelbus_obj_send); STATIC const mp_rom_map_elem_t displayio_parallelbus_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&displayio_parallelbus_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_parallelbus_send_obj) }, }; STATIC MP_DEFINE_CONST_DICT(displayio_parallelbus_locals_dict, displayio_parallelbus_locals_dict_table); diff --git a/shared-bindings/displayio/ParallelBus.h b/shared-bindings/displayio/ParallelBus.h index b8da91041..a0f967332 100644 --- a/shared-bindings/displayio/ParallelBus.h +++ b/shared-bindings/displayio/ParallelBus.h @@ -40,7 +40,7 @@ void common_hal_displayio_parallelbus_construct(displayio_parallelbus_obj_t* sel void common_hal_displayio_parallelbus_deinit(displayio_parallelbus_obj_t* self); -void common_hal_displayio_parallelbus_reset(mp_obj_t self); +bool common_hal_displayio_parallelbus_reset(mp_obj_t self); bool common_hal_displayio_parallelbus_bus_free(mp_obj_t self); bool common_hal_displayio_parallelbus_begin_transaction(mp_obj_t self); diff --git a/shared-module/displayio/FourWire.c b/shared-module/displayio/FourWire.c index 64ad80665..7294e9772 100644 --- a/shared-module/displayio/FourWire.c +++ b/shared-module/displayio/FourWire.c @@ -55,7 +55,9 @@ void common_hal_displayio_fourwire_construct(displayio_fourwire_obj_t* self, common_hal_digitalio_digitalinout_construct(&self->chip_select, chip_select); common_hal_digitalio_digitalinout_switch_to_output(&self->chip_select, true, DRIVE_MODE_PUSH_PULL); + self->reset.base.type = &mp_type_NoneType; if (reset != NULL) { + self->reset.base.type = &digitalio_digitalinout_type; common_hal_digitalio_digitalinout_construct(&self->reset, reset); common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL); never_reset_pin_number(reset->number); @@ -76,12 +78,16 @@ void common_hal_displayio_fourwire_deinit(displayio_fourwire_obj_t* self) { reset_pin_number(self->reset.pin->number); } -void common_hal_displayio_fourwire_reset(mp_obj_t obj) { +bool common_hal_displayio_fourwire_reset(mp_obj_t obj) { displayio_fourwire_obj_t* self = MP_OBJ_TO_PTR(obj); + if (self->reset.base.type == &mp_type_NoneType) { + return false; + } common_hal_digitalio_digitalinout_set_value(&self->reset, false); common_hal_time_delay_ms(1); common_hal_digitalio_digitalinout_set_value(&self->reset, true); common_hal_time_delay_ms(1); + return true; } bool common_hal_displayio_fourwire_bus_free(mp_obj_t obj) { diff --git a/shared-module/displayio/I2CDisplay.c b/shared-module/displayio/I2CDisplay.c index 23ebc0889..95830ba0e 100644 --- a/shared-module/displayio/I2CDisplay.c +++ b/shared-module/displayio/I2CDisplay.c @@ -55,7 +55,9 @@ void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t* self, self->address = device_address; + self->reset.base.type = &mp_type_NoneType; if (reset != NULL) { + self->reset.base.type = &digitalio_digitalinout_type; common_hal_digitalio_digitalinout_construct(&self->reset, reset); common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL); never_reset_pin_number(reset->number); @@ -71,16 +73,18 @@ void common_hal_displayio_i2cdisplay_deinit(displayio_i2cdisplay_obj_t* self) { reset_pin_number(self->reset.pin->number); } - -void common_hal_displayio_i2cdisplay_reset(mp_obj_t obj) { +bool common_hal_displayio_i2cdisplay_reset(mp_obj_t obj) { displayio_i2cdisplay_obj_t* self = MP_OBJ_TO_PTR(obj); + if (self->reset.base.type == &mp_type_NoneType) { + return false; + } common_hal_digitalio_digitalinout_set_value(&self->reset, false); common_hal_mcu_delay_us(1); common_hal_digitalio_digitalinout_set_value(&self->reset, true); + return true; } - bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t obj) { displayio_i2cdisplay_obj_t* self = MP_OBJ_TO_PTR(obj); if (!common_hal_busio_i2c_try_lock(self->bus)) { diff --git a/shared-module/displayio/display_core.h b/shared-module/displayio/display_core.h index c837ce913..b1a412883 100644 --- a/shared-module/displayio/display_core.h +++ b/shared-module/displayio/display_core.h @@ -33,7 +33,7 @@ #define NO_COMMAND 0x100 -typedef void (*display_bus_bus_reset)(mp_obj_t bus); +typedef bool (*display_bus_bus_reset)(mp_obj_t bus); typedef bool (*display_bus_bus_free)(mp_obj_t bus); typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); -- cgit v1.2.3 From f2a1972ba8a3eeeed33f9ac693ef4176c883aec4 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 21 Aug 2019 13:15:35 -0700 Subject: Add refresh_time to use if busy_pin is not given --- shared-bindings/displayio/EPaperDisplay.c | 11 +++++++---- shared-bindings/displayio/EPaperDisplay.h | 2 +- shared-module/displayio/EPaperDisplay.c | 14 +++++++++++--- shared-module/displayio/EPaperDisplay.h | 1 + 4 files changed, 20 insertions(+), 8 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/displayio/EPaperDisplay.c b/shared-bindings/displayio/EPaperDisplay.c index 8f3c16efd..dd773e410 100644 --- a/shared-bindings/displayio/EPaperDisplay.c +++ b/shared-bindings/displayio/EPaperDisplay.c @@ -51,7 +51,7 @@ //| Most people should not use this class directly. Use a specific display driver instead that will //| contain the startup and shutdown sequences at minimum. //| -//| .. class:: EPaperDisplay(display_bus, start_sequence, stop_sequence, *, width, height, ram_width, ram_height, colstart=0, rowstart=0, rotation=0, set_column_window_command=None, set_row_window_command=None, single_byte_bounds=False, write_black_ram_command, black_bits_inverted=False, write_color_ram_command=None, color_bits_inverted=False, highlight_color=0x000000, refresh_display_command, busy_pin=None, busy_state=True, seconds_per_frame=180, always_toggle_chip_select=False) +//| .. class:: EPaperDisplay(display_bus, start_sequence, stop_sequence, *, width, height, ram_width, ram_height, colstart=0, rowstart=0, rotation=0, set_column_window_command=None, set_row_window_command=None, single_byte_bounds=False, write_black_ram_command, black_bits_inverted=False, write_color_ram_command=None, color_bits_inverted=False, highlight_color=0x000000, refresh_display_command, refresh_time=40, busy_pin=None, busy_state=True, seconds_per_frame=180, always_toggle_chip_select=False) //| //| Create a EPaperDisplay object on the given display bus (`displayio.FourWire` or `displayio.ParallelBus`). //| @@ -83,13 +83,14 @@ //| :param bool color_bits_inverted: True if 0 bits are used to show the color. Otherwise, 1 means to show color. //| :param int highlight_color: RGB888 of source color to highlight with third ePaper color. //| :param int refresh_display_command: Command used to start a display refresh +//| :param float refresh_time: Time it takes to refresh the display before the stop_sequence should be sent. Ignored when busy_pin is provided. //| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy //| :param bool busy_state: State of the busy pin when the display is busy -//| :param int seconds_per_frame: Minimum number of seconds between screen refreshes +//| :param float seconds_per_frame: Minimum number of seconds between screen refreshes //| :param bool always_toggle_chip_select: When True, chip select is toggled every byte //| STATIC mp_obj_t displayio_epaperdisplay_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_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, ARG_refresh_display_command, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select }; + enum { ARG_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, ARG_refresh_display_command, ARG_refresh_time, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select }; static const mp_arg_t allowed_args[] = { { MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_start_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -111,6 +112,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size { MP_QSTR_color_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, { MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, { MP_QSTR_refresh_display_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_refresh_time, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(40)} }, { MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, { MP_QSTR_busy_state, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, { MP_QSTR_seconds_per_frame, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(180)} }, @@ -152,6 +154,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size mp_raise_RuntimeError(translate("Too many displays")); } + mp_float_t refresh_time = mp_obj_get_float(args[ARG_refresh_time].u_obj); mp_float_t seconds_per_frame = mp_obj_get_float(args[ARG_seconds_per_frame].u_obj); mp_int_t write_color_ram_command = NO_COMMAND; @@ -168,7 +171,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_ram_width].u_int, args[ARG_ram_height].u_int, args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int, args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int, - args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, args[ARG_color_bits_inverted].u_bool, highlight_color, args[ARG_refresh_display_command].u_int, + args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, args[ARG_color_bits_inverted].u_bool, highlight_color, args[ARG_refresh_display_command].u_int, refresh_time, busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, args[ARG_always_toggle_chip_select].u_bool ); diff --git a/shared-bindings/displayio/EPaperDisplay.h b/shared-bindings/displayio/EPaperDisplay.h index 2a3cb921f..786fc3628 100644 --- a/shared-bindings/displayio/EPaperDisplay.h +++ b/shared-bindings/displayio/EPaperDisplay.h @@ -43,7 +43,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t set_column_window_command, uint16_t set_row_window_command, uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, + uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, mp_float_t refresh_time, const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select); bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* self); diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 7e65502d4..bf0e6676f 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -48,7 +48,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t set_column_window_command, uint16_t set_row_window_command, uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, + uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, mp_float_t refresh_time, const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select) { if (highlight_color != 0x000000) { self->core.colorspace.tricolor = true; @@ -67,6 +67,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self->write_color_ram_command = write_color_ram_command; self->color_bits_inverted = color_bits_inverted; self->refresh_display_command = refresh_display_command; + self->refresh_time = refresh_time * 1000; self->busy_state = busy_state; self->refreshing = false; self->milliseconds_per_frame = seconds_per_frame * 1000; @@ -336,8 +337,15 @@ bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* s } void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self) { - if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) { - if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) { + if (self->refreshing) { + bool refresh_done = false; + if (self->busy.base.type == &digitalio_digitalinout_type) { + bool busy = common_hal_digitalio_digitalinout_get_value(&self->busy); + refresh_done = busy != self->busy_state; + } else { + refresh_done = ticks_ms - self->core.last_refresh > self->refresh_time; + } + if (refresh_done) { self->refreshing = false; // Run stop sequence but don't wait for busy because busy is set when sleeping. send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index 2be2add4b..a5d4ec93f 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -43,6 +43,7 @@ typedef struct { uint32_t start_sequence_len; uint8_t* stop_sequence; uint32_t stop_sequence_len; + uint16_t refresh_time; uint16_t set_column_window_command; uint16_t set_row_window_command; uint16_t set_current_column_command; -- cgit v1.2.3 From 8d836fa248d10b8943945423a069a3fba387f8a2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 22 Aug 2019 00:33:27 -0700 Subject: Regular display fixes including refresh tweaks --- shared-module/displayio/Display.c | 25 ++++++++++++++++++------- shared-module/displayio/Display.h | 2 ++ shared-module/displayio/EPaperDisplay.c | 8 +++++--- shared-module/displayio/TileGrid.c | 2 +- shared-module/displayio/TileGrid.h | 2 +- shared-module/displayio/__init__.c | 4 +--- shared-module/displayio/display_core.c | 11 +++++++---- 7 files changed, 35 insertions(+), 19 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index 86800328f..109b91507 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -64,21 +64,23 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, self->write_ram_command = write_ram_command; self->brightness_command = brightness_command; self->auto_brightness = auto_brightness; + self->auto_refresh = auto_refresh; + self->first_manual_refresh = !auto_refresh; self->data_as_commands = data_as_commands; self->native_frames_per_second = native_frames_per_second; self->native_frame_time = 1000 / native_frames_per_second; uint32_t i = 0; - while (!displayio_display_core_begin_transaction(&self->core)) { - RUN_BACKGROUND_TASKS; - } while (i < init_sequence_len) { uint8_t *cmd = init_sequence + i; uint8_t data_size = *(cmd + 1); bool delay = (data_size & DELAY) != 0; data_size &= ~DELAY; uint8_t *data = cmd + 2; + while (!displayio_display_core_begin_transaction(&self->core)) { + RUN_BACKGROUND_TASKS; + } if (self->data_as_commands) { uint8_t full_command[data_size + 1]; full_command[0] = cmd[0]; @@ -88,6 +90,7 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, self->core.send(self->core.bus, true, true, cmd, 1); self->core.send(self->core.bus, false, false, data, data_size); } + self->core.end_transaction(self->core.bus); uint16_t delay_length_ms = 10; if (delay) { data_size++; @@ -99,7 +102,6 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, common_hal_time_delay_ms(delay_length_ms); i += 2 + data_size; } - self->core.end_transaction(self->core.bus); supervisor_start_terminal(width, height); @@ -192,9 +194,10 @@ STATIC const displayio_area_t* _get_refresh_areas(displayio_display_obj_t *self) if (self->core.full_refresh) { self->core.area.next = NULL; return &self->core.area; - } else { + } else if (self->core.current_group != NULL) { return displayio_group_get_refresh_areas(self->core.current_group, NULL); } + return NULL; } STATIC void _send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) { @@ -242,7 +245,7 @@ STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* // Allocated and shared as a uint32_t array so the compiler knows the // alignment everywhere. uint32_t buffer[buffer_size]; - volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask_length = (pixels_per_buffer / 32) + 1; uint32_t mask[mask_length]; uint16_t remaining_rows = displayio_area_height(&clipped); @@ -311,7 +314,7 @@ uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t* self } void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_frame_time, uint32_t maximum_frame_time) { - if (!self->auto_refresh) { + if (!self->auto_refresh && !self->first_manual_refresh) { uint64_t current_time = ticks_ms; uint32_t current_frame_time = current_time - self->core.last_refresh; // Test to see if the real frame time is below our minimum. @@ -332,6 +335,7 @@ void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_ #endif } } + self->first_manual_refresh = false; _refresh_display(self); } @@ -341,6 +345,7 @@ bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t* self, bool auto_refresh) { + self->first_manual_refresh = !auto_refresh; self->auto_refresh = auto_refresh; } @@ -376,6 +381,12 @@ void release_display(displayio_display_obj_t* self) { } } +void reset_display(displayio_display_obj_t* self) { + self->auto_refresh = true; + self->auto_brightness = true; + common_hal_displayio_display_show(self, NULL); +} + void displayio_display_collect_ptrs(displayio_display_obj_t* self) { displayio_display_core_collect_ptrs(&self->core); } diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index cff566a61..bf2889c6d 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -51,6 +51,7 @@ typedef struct { uint8_t set_row_command; uint8_t write_ram_command; bool auto_refresh; + bool first_manual_refresh; bool data_as_commands; bool auto_brightness; bool updating_backlight; @@ -58,6 +59,7 @@ typedef struct { void displayio_display_background(displayio_display_obj_t* self); void release_display(displayio_display_obj_t* self); +void reset_display(displayio_display_obj_t* self); void displayio_display_collect_ptrs(displayio_display_obj_t* self); diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index bf0e6676f..48143f14f 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -102,10 +102,12 @@ bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self } const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self) { - const displayio_area_t* first_area; if (self->core.full_refresh) { - first_area = &self->core.area; - } else { + self->core.area.next = NULL; + return &self->core.area; + } + const displayio_area_t* first_area = NULL; + if (self->core.current_group != NULL) { first_area = displayio_group_get_refresh_areas(self->core.current_group, NULL); } if (first_area != NULL && self->set_row_window_command == NO_COMMAND) { diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index 6553cf901..f34d2fc52 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -207,7 +207,7 @@ uint8_t common_hal_displayio_tilegrid_get_tile(displayio_tilegrid_t *self, uint1 void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y, uint8_t tile_index) { if (tile_index >= self->tiles_in_bitmap) { - mp_raise_ValueError(translate("Tile value out of bounds")); + mp_raise_ValueError(translate("Tile index out of bounds")); } uint8_t* tiles = self->tiles; if (self->inline_tiles) { diff --git a/shared-module/displayio/TileGrid.h b/shared-module/displayio/TileGrid.h index 222aaed19..0a414b062 100644 --- a/shared-module/displayio/TileGrid.h +++ b/shared-module/displayio/TileGrid.h @@ -43,7 +43,7 @@ typedef struct { uint16_t pixel_width; uint16_t pixel_height; uint16_t bitmap_width_in_tiles;; - uint8_t tiles_in_bitmap; + uint16_t tiles_in_bitmap; uint16_t width_in_tiles; uint16_t height_in_tiles; uint16_t tile_width; diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index a41884c78..af22cb0df 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -143,9 +143,7 @@ void reset_displays(void) { // Reset the displayed group. Only the first will get the terminal but // that's ok. if (displays[i].display.base.type == &displayio_display_type) { - displayio_display_obj_t* display = &displays[i].display; - display->auto_brightness = true; - common_hal_displayio_display_show(display, NULL); + reset_display(&displays[i].display); } else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) { displayio_epaperdisplay_obj_t* display = &displays[i].epaper_display; common_hal_displayio_epaperdisplay_show(display, NULL); diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c index 24d8ab51f..88878866a 100644 --- a/shared-module/displayio/display_core.c +++ b/shared-module/displayio/display_core.c @@ -82,8 +82,8 @@ void displayio_display_core_construct(displayio_display_core_t* self, self->width = width; self->height = height; - self->ram_width = width; - self->ram_height = height; + self->ram_width = ram_width; + self->ram_height = ram_height; rotation = rotation % 360; self->transform.x = 0; self->transform.y = 0; @@ -206,7 +206,7 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, data[0] = column_command; uint8_t data_length = 1; if (!data_as_commands) { - self->send(self->bus, true, true, data, 1); + self->send(self->bus, true, false, data, 1); data_length = 0; } if (self->ram_width < 0x100) { @@ -227,11 +227,14 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, self->send(self->bus, false, always_toggle_chip_select, data, data_length / 2); } + displayio_display_core_end_transaction(self); + displayio_display_core_begin_transaction(self); + // Set row. data[0] = row_command; data_length = 1; if (!data_as_commands) { - self->send(self->bus, true, true, data, 1); + self->send(self->bus, true, false, data, 1); data_length = 0; } if (self->ram_height < 0x100) { -- cgit v1.2.3 From 72e7ffa324f8184cb2d9a5ca9fa620a2a5a56b69 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 22 Aug 2019 16:16:09 -0700 Subject: More cleanup --- ports/atmel-samd/boards/snekboard/mpconfigboard.mk | 3 +++ shared-bindings/displayio/EPaperDisplay.c | 2 +- shared-bindings/displayio/EPaperDisplay.h | 6 ------ shared-module/displayio/ColorConverter.c | 1 - shared-module/displayio/Group.c | 3 --- shared-module/displayio/OnDiskBitmap.c | 4 +--- 6 files changed, 5 insertions(+), 14 deletions(-) (limited to 'shared-module') diff --git a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk index 9892ade8f..6ad78b0da 100644 --- a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk +++ b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk @@ -12,5 +12,8 @@ EXTERNAL_FLASH_DEVICE_COUNT = 1 EXTERNAL_FLASH_DEVICES = "W25Q16JV_IQ" LONGINT_IMPL = MPZ +CIRCUITPY_GAMEPAD = 0 +CIRCUITPY_I2CSLAVE = 0 + CFLAGS_INLINE_LIMIT = 60 SUPEROPT_GC = 0 diff --git a/shared-bindings/displayio/EPaperDisplay.c b/shared-bindings/displayio/EPaperDisplay.c index dd773e410..5d0d8ac94 100644 --- a/shared-bindings/displayio/EPaperDisplay.c +++ b/shared-bindings/displayio/EPaperDisplay.c @@ -42,7 +42,7 @@ //| .. currentmodule:: displayio //| //| :class:`EPaperDisplay` -- Manage updating an epaper display over a display bus -//| ========================================================================== +//| ============================================================================== //| //| This initializes an epaper display and connects it into CircuitPython. Unlike other //| objects in CircuitPython, EPaperDisplay objects live until `displayio.release_displays()` diff --git a/shared-bindings/displayio/EPaperDisplay.h b/shared-bindings/displayio/EPaperDisplay.h index 786fc3628..e4b81c883 100644 --- a/shared-bindings/displayio/EPaperDisplay.h +++ b/shared-bindings/displayio/EPaperDisplay.h @@ -53,15 +53,9 @@ bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self // Returns time in milliseconds. uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self); -bool common_hal_displayio_epaperdisplay_get_auto_brightness(displayio_epaperdisplay_obj_t* self); -void common_hal_displayio_epaperdisplay_set_auto_brightness(displayio_epaperdisplay_obj_t* self, bool auto_brightness); - uint16_t common_hal_displayio_epaperdisplay_get_width(displayio_epaperdisplay_obj_t* self); uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_obj_t* self); -mp_float_t common_hal_displayio_epaperdisplay_get_brightness(displayio_epaperdisplay_obj_t* self); -bool common_hal_displayio_epaperdisplay_set_brightness(displayio_epaperdisplay_obj_t* self, mp_float_t brightness); - mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t* self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index 0277c79f0..e0c4ca286 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -142,7 +142,6 @@ bool displayio_colorconverter_convert(displayio_colorconverter_t *self, const _d uint8_t luma = displayio_colorconverter_compute_luma(input_color); *output_color = luma >> (8 - colorspace->depth); return true; - } else if (!colorspace->grayscale && colorspace->depth == 1) { } return false; } diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index eb6e9a453..38418a029 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -304,9 +304,6 @@ void displayio_group_finish_refresh(displayio_group_t *self) { } displayio_area_t* displayio_group_get_refresh_areas(displayio_group_t *self, displayio_area_t* tail) { - if (self->base.type != &displayio_group_type) { - asm("bkpt"); - } if (self->item_removed) { self->dirty_area.next = tail; tail = &self->dirty_area; diff --git a/shared-module/displayio/OnDiskBitmap.c b/shared-module/displayio/OnDiskBitmap.c index 10fc0c4a2..993fc03de 100644 --- a/shared-module/displayio/OnDiskBitmap.c +++ b/shared-module/displayio/OnDiskBitmap.c @@ -149,10 +149,8 @@ uint32_t common_hal_displayio_ondiskbitmap_get_pixel(displayio_ondiskbitmap_t *s if (self->bits_per_pixel == 1) { if (index == 1) { return 0xFFFFFF; - } else if (index == 0) { - return 0x000000; } else { - asm("bkpt"); + return 0x000000; } } return self->palette_data[index]; -- cgit v1.2.3 From 7324b70a7c231a20be971ed81050c8359b87aaab Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 23 Aug 2019 15:27:21 -0700 Subject: Rework based on Dan's review --- .../atmel-samd/boards/monster_m4sk/mpconfigboard.h | 2 - .../atmel-samd/common-hal/displayio/ParallelBus.c | 4 +- ports/nrf/common-hal/displayio/ParallelBus.c | 5 ++- py/circuitpy_mpconfig.h | 4 -- shared-bindings/_stage/__init__.c | 2 +- shared-bindings/displayio/Display.c | 24 +++++++---- shared-bindings/displayio/Display.h | 2 +- shared-bindings/displayio/EPaperDisplay.c | 2 +- shared-bindings/displayio/FourWire.c | 8 +++- shared-bindings/displayio/FourWire.h | 4 +- shared-bindings/displayio/I2CDisplay.c | 2 +- shared-bindings/displayio/I2CDisplay.h | 4 +- shared-bindings/displayio/ParallelBus.c | 4 +- shared-bindings/displayio/ParallelBus.h | 5 ++- shared-bindings/displayio/__init__.h | 19 +++++++++ shared-module/_stage/__init__.c | 4 +- shared-module/displayio/ColorConverter.c | 38 +++-------------- shared-module/displayio/Display.c | 47 ++++++++++------------ shared-module/displayio/Display.h | 2 +- shared-module/displayio/EPaperDisplay.c | 24 +++++------ shared-module/displayio/EPaperDisplay.h | 2 +- shared-module/displayio/FourWire.c | 7 ++-- shared-module/displayio/I2CDisplay.c | 7 ++-- shared-module/displayio/display_core.c | 38 +++++++++++------ shared-module/displayio/display_core.h | 7 +--- supervisor/shared/filesystem.c | 2 +- 26 files changed, 137 insertions(+), 132 deletions(-) (limited to 'shared-module') diff --git a/ports/atmel-samd/boards/monster_m4sk/mpconfigboard.h b/ports/atmel-samd/boards/monster_m4sk/mpconfigboard.h index 484f3d7b2..fc77520f5 100644 --- a/ports/atmel-samd/boards/monster_m4sk/mpconfigboard.h +++ b/ports/atmel-samd/boards/monster_m4sk/mpconfigboard.h @@ -27,5 +27,3 @@ // USB is always used internally so skip the pin objects for it. #define IGNORE_PIN_PA24 1 #define IGNORE_PIN_PA25 1 - -#define CIRCUITPY_FS_NAME "MONSTERM4SK" diff --git a/ports/atmel-samd/common-hal/displayio/ParallelBus.c b/ports/atmel-samd/common-hal/displayio/ParallelBus.c index e2ce9952b..2479e3b40 100644 --- a/ports/atmel-samd/common-hal/displayio/ParallelBus.c +++ b/ports/atmel-samd/common-hal/displayio/ParallelBus.c @@ -131,9 +131,9 @@ bool common_hal_displayio_parallelbus_begin_transaction(mp_obj_t obj) { return true; } -void common_hal_displayio_parallelbus_send(mp_obj_t obj, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length) { +void common_hal_displayio_parallelbus_send(mp_obj_t obj, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length) { displayio_parallelbus_obj_t* self = MP_OBJ_TO_PTR(obj); - common_hal_digitalio_digitalinout_set_value(&self->command, !command); + common_hal_digitalio_digitalinout_set_value(&self->command, byte_type == DISPLAY_DATA); uint32_t* clear_write = (uint32_t*) &self->write_group->OUTCLR.reg; uint32_t* set_write = (uint32_t*) &self->write_group->OUTSET.reg; uint32_t mask = self->write_mask; diff --git a/ports/nrf/common-hal/displayio/ParallelBus.c b/ports/nrf/common-hal/displayio/ParallelBus.c index a8c2b9f73..be4b28a6e 100644 --- a/ports/nrf/common-hal/displayio/ParallelBus.c +++ b/ports/nrf/common-hal/displayio/ParallelBus.c @@ -142,9 +142,10 @@ bool common_hal_displayio_parallelbus_begin_transaction(mp_obj_t obj) { return true; } -void common_hal_displayio_parallelbus_send(mp_obj_t obj, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length) { +// This ignores chip_select behaviour because data is clocked in by the write line toggling. +void common_hal_displayio_parallelbus_send(mp_obj_t obj, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length) { displayio_parallelbus_obj_t* self = MP_OBJ_TO_PTR(obj); - common_hal_digitalio_digitalinout_set_value(&self->command, !command); + common_hal_digitalio_digitalinout_set_value(&self->command, byte_type == DISPLAY_DATA); uint32_t* clear_write = (uint32_t*) &self->write_group->OUTCLR; uint32_t* set_write = (uint32_t*) &self->write_group->OUTSET; uint32_t mask = self->write_mask; diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 923c02b9e..c2e2e2d02 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -657,8 +657,4 @@ void run_background_tasks(void); #define CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS 1000 #define CIRCUITPY_BOOT_OUTPUT_FILE "/boot_out.txt" -#ifndef CIRCUITPY_FS_NAME -#define CIRCUITPY_FS_NAME "CIRCUITPY" -#endif - #endif // __INCLUDED_MPCONFIG_CIRCUITPY_H diff --git a/shared-bindings/_stage/__init__.c b/shared-bindings/_stage/__init__.c index 1d970cce4..d18d306f4 100644 --- a/shared-bindings/_stage/__init__.c +++ b/shared-bindings/_stage/__init__.c @@ -107,7 +107,7 @@ STATIC mp_obj_t stage_render(size_t n_args, const mp_obj_t *args) { area.y2 = y1; displayio_display_core_set_region_to_update(&display->core, display->set_column_command, display->set_row_command, NO_COMMAND, NO_COMMAND, display->data_as_commands, false, &area); - display->core.send(display->core.bus, true, true, &display->write_ram_command, 1); + display->core.send(display->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &display->write_ram_command, 1); render_stage(x0, y0, x1, y1, layers, layers_size, buffer, buffer_size, display, scale); displayio_display_core_end_transaction(&display->core); diff --git a/shared-bindings/displayio/Display.c b/shared-bindings/displayio/Display.c index ded4119ca..1eb1943b8 100644 --- a/shared-bindings/displayio/Display.c +++ b/shared-bindings/displayio/Display.c @@ -218,16 +218,22 @@ STATIC mp_obj_t displayio_display_obj_show(mp_obj_t self_in, mp_obj_t group_in) } MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_show_obj, displayio_display_obj_show); -//| .. method:: refresh(*, target_frames_per_second=None, minimum_frames_per_second=1) +//| .. method:: refresh(*, target_frames_per_second=60, minimum_frames_per_second=1) //| -//| When auto refresh is off, waits for the target frame rate and then refreshes the display. If -//| the call is too late for the given target frame rate, then the refresh returns immediately -//| without updating the screen to hopefully help getting caught up. If the current frame rate -//| is below the minimum frame rate, then an exception will be raised. +//| When auto refresh is off, waits for the target frame rate and then refreshes the display, +//| returning True. If the call has taken too long since the last refresh call for the given +//| target frame rate, then the refresh returns False immediately without updating the screen to +//| hopefully help getting caught up. +//| +//| If the time since the last successful refresh is below the minimum frame rate, then an +//| exception will be raised. Set minimum_frames_per_second to 0 to disable. //| //| When auto refresh is on, updates the display immediately. (The display will also update //| without calls to this.) //| +//| :param int target_frames_per_second: How many times a second `refresh` should be called and the screen updated. +//| :param int minimum_frames_per_second: The minimum number of times the screen should be updated per second. +//| STATIC mp_obj_t displayio_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_target_frames_per_second, ARG_minimum_frames_per_second }; static const mp_arg_t allowed_args[] = { @@ -238,8 +244,12 @@ STATIC mp_obj_t displayio_display_obj_refresh(size_t n_args, const mp_obj_t *pos mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); displayio_display_obj_t *self = native_display(pos_args[0]); - common_hal_displayio_display_refresh(self, 1000 / args[ARG_target_frames_per_second].u_int, 1000 / args[ARG_minimum_frames_per_second].u_int); - return mp_const_none; + uint32_t maximum_ms_per_real_frame = 0xffffffff; + mp_int_t minimum_frames_per_second = args[ARG_minimum_frames_per_second].u_int; + if (minimum_frames_per_second > 0) { + maximum_ms_per_real_frame = 1000 / minimum_frames_per_second; + } + return mp_obj_new_bool(common_hal_displayio_display_refresh(self, 1000 / args[ARG_target_frames_per_second].u_int, maximum_ms_per_real_frame)); } MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_refresh_obj, 1, displayio_display_obj_refresh); diff --git a/shared-bindings/displayio/Display.h b/shared-bindings/displayio/Display.h index 06c848d0d..2d7b68c4e 100644 --- a/shared-bindings/displayio/Display.h +++ b/shared-bindings/displayio/Display.h @@ -51,7 +51,7 @@ bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group); // Times in ms. -void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_frame_time, uint32_t maximum_frame_time); +bool common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame); bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self); void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t* self, bool auto_refresh); diff --git a/shared-bindings/displayio/EPaperDisplay.c b/shared-bindings/displayio/EPaperDisplay.c index 5d0d8ac94..81e06f82f 100644 --- a/shared-bindings/displayio/EPaperDisplay.c +++ b/shared-bindings/displayio/EPaperDisplay.c @@ -55,7 +55,7 @@ //| //| Create a EPaperDisplay object on the given display bus (`displayio.FourWire` or `displayio.ParallelBus`). //| -//| The ``start_sequence`` and ``stop_sequence`` bitpacked to minimize the ram impact. Every +//| The ``start_sequence`` and ``stop_sequence`` are bitpacked to minimize the ram impact. Every //| command begins with a command byte followed by a byte to determine the parameter count and if //| a delay is need after. When the top bit of the second byte is 1, the next byte will be the //| delay time in milliseconds. The remaining 7 bits are the parameter count excluding any delay diff --git a/shared-bindings/displayio/FourWire.c b/shared-bindings/displayio/FourWire.c index c69623344..51203d460 100644 --- a/shared-bindings/displayio/FourWire.c +++ b/shared-bindings/displayio/FourWire.c @@ -146,8 +146,12 @@ STATIC mp_obj_t displayio_fourwire_obj_send(size_t n_args, const mp_obj_t *pos_a while (!common_hal_displayio_fourwire_begin_transaction(self)) { RUN_BACKGROUND_TASKS; } - common_hal_displayio_fourwire_send(self, true, args[ARG_toggle_every_byte].u_bool, &command, 1); - common_hal_displayio_fourwire_send(self, false, args[ARG_toggle_every_byte].u_bool, ((uint8_t*) bufinfo.buf), bufinfo.len); + display_chip_select_behavior_t chip_select = CHIP_SELECT_UNTOUCHED; + if (args[ARG_toggle_every_byte].u_bool) { + chip_select = CHIP_SELECT_TOGGLE_EVERY_BYTE; + } + common_hal_displayio_fourwire_send(self, DISPLAY_COMMAND, chip_select, &command, 1); + common_hal_displayio_fourwire_send(self, DISPLAY_DATA, chip_select, ((uint8_t*) bufinfo.buf), bufinfo.len); common_hal_displayio_fourwire_end_transaction(self); return mp_const_none; diff --git a/shared-bindings/displayio/FourWire.h b/shared-bindings/displayio/FourWire.h index 9c71d63cc..d0935f063 100644 --- a/shared-bindings/displayio/FourWire.h +++ b/shared-bindings/displayio/FourWire.h @@ -28,6 +28,8 @@ #define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_FOURWIRE_H #include "shared-module/displayio/FourWire.h" + +#include "shared-bindings/displayio/__init__.h" #include "common-hal/microcontroller/Pin.h" #include "shared-module/displayio/Group.h" @@ -45,7 +47,7 @@ bool common_hal_displayio_fourwire_bus_free(mp_obj_t self); bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t self); -void common_hal_displayio_fourwire_send(mp_obj_t self, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); +void common_hal_displayio_fourwire_send(mp_obj_t self, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length); void common_hal_displayio_fourwire_end_transaction(mp_obj_t self); diff --git a/shared-bindings/displayio/I2CDisplay.c b/shared-bindings/displayio/I2CDisplay.c index 4de5e83ef..4339d182f 100644 --- a/shared-bindings/displayio/I2CDisplay.c +++ b/shared-bindings/displayio/I2CDisplay.c @@ -131,7 +131,7 @@ STATIC mp_obj_t displayio_i2cdisplay_obj_send(mp_obj_t self, mp_obj_t command_ob uint8_t full_command[bufinfo.len + 1]; full_command[0] = command; memcpy(full_command + 1, ((uint8_t*) bufinfo.buf), bufinfo.len); - common_hal_displayio_i2cdisplay_send(self, true, false, full_command, bufinfo.len + 1); + common_hal_displayio_i2cdisplay_send(self, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, full_command, bufinfo.len + 1); common_hal_displayio_i2cdisplay_end_transaction(self); return mp_const_none; diff --git a/shared-bindings/displayio/I2CDisplay.h b/shared-bindings/displayio/I2CDisplay.h index 683cff383..bae53c491 100644 --- a/shared-bindings/displayio/I2CDisplay.h +++ b/shared-bindings/displayio/I2CDisplay.h @@ -28,6 +28,8 @@ #define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_I2CDISPLAY_H #include "shared-module/displayio/I2CDisplay.h" + +#include "shared-bindings/displayio/__init__.h" #include "common-hal/microcontroller/Pin.h" extern const mp_obj_type_t displayio_i2cdisplay_type; @@ -42,7 +44,7 @@ bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t self); bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t self); -void common_hal_displayio_i2cdisplay_send(mp_obj_t self, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); +void common_hal_displayio_i2cdisplay_send(mp_obj_t self, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length); void common_hal_displayio_i2cdisplay_end_transaction(mp_obj_t self); diff --git a/shared-bindings/displayio/ParallelBus.c b/shared-bindings/displayio/ParallelBus.c index a08c37679..f7195b9cc 100644 --- a/shared-bindings/displayio/ParallelBus.c +++ b/shared-bindings/displayio/ParallelBus.c @@ -139,8 +139,8 @@ STATIC mp_obj_t displayio_parallelbus_obj_send(mp_obj_t self, mp_obj_t command_o while (!common_hal_displayio_parallelbus_begin_transaction(self)) { RUN_BACKGROUND_TASKS; } - common_hal_displayio_parallelbus_send(self, true, false, &command, 1); - common_hal_displayio_parallelbus_send(self, false, false, ((uint8_t*) bufinfo.buf), bufinfo.len); + common_hal_displayio_parallelbus_send(self, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, &command, 1); + common_hal_displayio_parallelbus_send(self, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ((uint8_t*) bufinfo.buf), bufinfo.len); common_hal_displayio_parallelbus_end_transaction(self); return mp_const_none; diff --git a/shared-bindings/displayio/ParallelBus.h b/shared-bindings/displayio/ParallelBus.h index a0f967332..be2aef7d4 100644 --- a/shared-bindings/displayio/ParallelBus.h +++ b/shared-bindings/displayio/ParallelBus.h @@ -28,8 +28,9 @@ #define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_PARALLELBUS_H #include "common-hal/displayio/ParallelBus.h" -#include "common-hal/microcontroller/Pin.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/displayio/__init__.h" #include "shared-module/displayio/Group.h" extern const mp_obj_type_t displayio_parallelbus_type; @@ -45,7 +46,7 @@ bool common_hal_displayio_parallelbus_bus_free(mp_obj_t self); bool common_hal_displayio_parallelbus_begin_transaction(mp_obj_t self); -void common_hal_displayio_parallelbus_send(mp_obj_t self, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); +void common_hal_displayio_parallelbus_send(mp_obj_t self, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length); void common_hal_displayio_parallelbus_end_transaction(mp_obj_t self); diff --git a/shared-bindings/displayio/__init__.h b/shared-bindings/displayio/__init__.h index 427ddb47d..122b1fbcb 100644 --- a/shared-bindings/displayio/__init__.h +++ b/shared-bindings/displayio/__init__.h @@ -27,6 +27,25 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H +#include "py/obj.h" + +typedef enum { + DISPLAY_COMMAND, + DISPLAY_DATA +} display_byte_type_t; + + +typedef enum { + CHIP_SELECT_UNTOUCHED, + CHIP_SELECT_TOGGLE_EVERY_BYTE +} display_chip_select_behavior_t; + +typedef bool (*display_bus_bus_reset)(mp_obj_t bus); +typedef bool (*display_bus_bus_free)(mp_obj_t bus); +typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); +typedef void (*display_bus_send)(mp_obj_t bus, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length); +typedef void (*display_bus_end_transaction)(mp_obj_t bus); + void common_hal_displayio_release_displays(void); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H diff --git a/shared-module/_stage/__init__.c b/shared-module/_stage/__init__.c index bfef165e7..2c525be87 100644 --- a/shared-module/_stage/__init__.c +++ b/shared-module/_stage/__init__.c @@ -57,7 +57,7 @@ void render_stage(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, index += 1; // The buffer is full, send it. if (index >= buffer_size) { - display->core.send(display->core.bus, false, false, ((uint8_t*)buffer), + display->core.send(display->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ((uint8_t*)buffer), buffer_size * 2); index = 0; } @@ -67,6 +67,6 @@ void render_stage(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, } // Send the remaining data. if (index) { - display->core.send(display->core.bus, false, false, ((uint8_t*)buffer), index * 2); + display->core.send(display->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ((uint8_t*)buffer), index * 2); } } diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index e0c4ca286..940aaa1c6 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -26,6 +26,8 @@ #include "shared-bindings/displayio/ColorConverter.h" +#include "py/misc.h" + void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t* self) { } @@ -45,39 +47,12 @@ uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888) { return (r8 * 19) / 255 + (g8 * 182) / 255 + (b8 + 54) / 255; } -void compute_bounds(uint8_t r8, uint8_t g8, uint8_t b8, uint8_t* min, uint8_t* max) { - if (r8 > g8) { - if (b8 > r8) { - *max = b8; - } else { - *max = r8; - } - if (b8 < g8) { - *min = b8; - } else { - *min = g8; - } - } else { - if (b8 > g8) { - *max = b8; - } else { - *max = g8; - } - if (b8 < r8) { - *min = b8; - } else { - *min = r8; - } - } -} - uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888) { uint32_t r8 = (color_rgb888 >> 16); uint32_t g8 = (color_rgb888 >> 8) & 0xff; uint32_t b8 = color_rgb888 & 0xff; - uint8_t max; - uint8_t min; - compute_bounds(r8, g8, b8, &min, &max); + uint8_t max = MAX(r8, MAX(g8, b8)); + uint8_t min = MIN(r8, MIN(g8, b8)); return max - min; } @@ -85,9 +60,8 @@ uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888) { uint32_t r8 = (color_rgb888 >> 16); uint32_t g8 = (color_rgb888 >> 8) & 0xff; uint32_t b8 = color_rgb888 & 0xff; - uint8_t max; - uint8_t min; - compute_bounds(r8, g8, b8, &min, &max); + uint8_t max = MAX(r8, MAX(g8, b8)); + uint8_t min = MIN(r8, MIN(g8, b8)); uint8_t c = max - min; if (c == 0) { return 0; diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index 109b91507..cb5051bb7 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -69,7 +69,7 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, self->data_as_commands = data_as_commands; self->native_frames_per_second = native_frames_per_second; - self->native_frame_time = 1000 / native_frames_per_second; + self->native_ms_per_frame = 1000 / native_frames_per_second; uint32_t i = 0; while (i < init_sequence_len) { @@ -85,10 +85,10 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, uint8_t full_command[data_size + 1]; full_command[0] = cmd[0]; memcpy(full_command + 1, data, data_size); - self->core.send(self->core.bus, true, true, full_command, data_size + 1); + self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, full_command, data_size + 1); } else { - self->core.send(self->core.bus, true, true, cmd, 1); - self->core.send(self->core.bus, false, false, data, data_size); + self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, cmd, 1); + self->core.send(self->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, data_size); } self->core.end_transaction(self->core.bus); uint16_t delay_length_ms = 10; @@ -168,12 +168,12 @@ bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, if (ok) { if (self->data_as_commands) { uint8_t set_brightness[2] = {self->brightness_command, (uint8_t) (0xff * brightness)}; - self->core.send(self->core.bus, true, true, set_brightness, 2); + self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, set_brightness, 2); } else { uint8_t command = self->brightness_command; uint8_t hex_brightness = 0xff * brightness; - self->core.send(self->core.bus, true, true, &command, 1); - self->core.send(self->core.bus, false, false, &hex_brightness, 1); + self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &command, 1); + self->core.send(self->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, &hex_brightness, 1); } displayio_display_core_end_transaction(&self->core); } @@ -202,9 +202,9 @@ STATIC const displayio_area_t* _get_refresh_areas(displayio_display_obj_t *self) STATIC void _send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) { if (!self->data_as_commands) { - self->core.send(self->core.bus, true, true, &self->write_ram_command, 1); + self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &self->write_ram_command, 1); } - self->core.send(self->core.bus, false, false, pixels, length); + self->core.send(self->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, pixels, length); } STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* area) { @@ -270,12 +270,8 @@ STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); } - for (uint16_t k = 0; k < mask_length; k++) { - mask[k] = 0x00000000; - } - for (uint16_t k = 0; k < buffer_size; k++) { - buffer[k] = 0x00000000; - } + memset(mask, 0, mask_length * sizeof(uint32_t)); + memset(buffer, 0, buffer_size * sizeof(uint32_t)); displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); @@ -313,30 +309,29 @@ uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t* self return self->core.rotation; } -void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_frame_time, uint32_t maximum_frame_time) { +bool common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame) { if (!self->auto_refresh && !self->first_manual_refresh) { uint64_t current_time = ticks_ms; - uint32_t current_frame_time = current_time - self->core.last_refresh; + uint32_t current_ms_since_real_refresh = current_time - self->core.last_refresh; // Test to see if the real frame time is below our minimum. - if (current_frame_time > maximum_frame_time) { + if (current_ms_since_real_refresh > maximum_ms_per_real_frame) { mp_raise_RuntimeError(translate("Below minimum frame rate")); } - uint32_t current_call_time = current_time - self->last_refresh_call; + uint32_t current_ms_since_last_call = current_time - self->last_refresh_call; self->last_refresh_call = current_time; // Skip the actual refresh to help catch up. - if (current_call_time > target_frame_time) { - return; + if (current_ms_since_last_call > target_ms_per_frame) { + return false; } - uint32_t remaining_time = target_frame_time - (current_frame_time % target_frame_time); + uint32_t remaining_time = target_ms_per_frame - (current_ms_since_real_refresh % target_ms_per_frame); // We're ahead of the game so wait until we align with the frame rate. while (ticks_ms - self->last_refresh_call < remaining_time) { - #ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP ; - #endif + RUN_BACKGROUND_TASKS; } } self->first_manual_refresh = false; _refresh_display(self); + return true; } bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self) { @@ -366,7 +361,7 @@ STATIC void _update_backlight(displayio_display_obj_t* self) { void displayio_display_background(displayio_display_obj_t* self) { _update_backlight(self); - if (self->auto_refresh && (ticks_ms - self->core.last_refresh) > self->native_frame_time) { + if (self->auto_refresh && (ticks_ms - self->core.last_refresh) > self->native_ms_per_frame) { _refresh_display(self); } } diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index bf2889c6d..e5fe0a708 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -46,7 +46,7 @@ typedef struct { mp_float_t current_brightness; uint16_t brightness_command; uint16_t native_frames_per_second; - uint16_t native_frame_time; + uint16_t native_ms_per_frame; uint8_t set_column_command; uint8_t set_row_command; uint8_t write_ram_command; diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 48143f14f..38b63df0f 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -71,7 +71,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self->busy_state = busy_state; self->refreshing = false; self->milliseconds_per_frame = seconds_per_frame * 1000; - self->always_toggle_chip_select = always_toggle_chip_select; + self->always_toggle_chip_select = always_toggle_chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED; self->start_sequence = start_sequence; self->start_sequence_len = start_sequence_len; @@ -130,9 +130,7 @@ STATIC void wait_for_busy(displayio_epaperdisplay_obj_t* self) { return; } while (common_hal_digitalio_digitalinout_get_value(&self->busy) == self->busy_state) { - #ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP - #endif + RUN_BACKGROUND_TASKS; } } @@ -145,8 +143,8 @@ STATIC void send_command_sequence(displayio_epaperdisplay_obj_t* self, bool shou data_size &= ~DELAY; uint8_t *data = cmd + 2; displayio_display_core_begin_transaction(&self->core); - self->core.send(self->core.bus, true, self->always_toggle_chip_select, cmd, 1); - self->core.send(self->core.bus, false, self->always_toggle_chip_select, data, data_size); + self->core.send(self->core.bus, DISPLAY_COMMAND, self->always_toggle_chip_select, cmd, 1); + self->core.send(self->core.bus, DISPLAY_DATA, self->always_toggle_chip_select, data, data_size); displayio_display_core_end_transaction(&self->core); uint16_t delay_length_ms = 0; if (delay) { @@ -187,7 +185,7 @@ uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaper void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) { // Actually refresh the display now that all pixel RAM has been updated. displayio_display_core_begin_transaction(&self->core); - self->core.send(self->core.bus, true, self->always_toggle_chip_select, &self->refresh_display_command, 1); + self->core.send(self->core.bus, DISPLAY_COMMAND, self->always_toggle_chip_select, &self->refresh_display_command, 1); displayio_display_core_end_transaction(&self->core); self->refreshing = true; @@ -248,7 +246,7 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c write_command = self->write_color_ram_command; } displayio_display_core_begin_transaction(&self->core); - self->core.send(self->core.bus, true, self->always_toggle_chip_select, &write_command, 1); + self->core.send(self->core.bus, DISPLAY_COMMAND, self->always_toggle_chip_select, &write_command, 1); displayio_display_core_end_transaction(&self->core); for (uint16_t j = 0; j < subrectangles; j++) { @@ -266,12 +264,8 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); - for (uint16_t k = 0; k < mask_length; k++) { - mask[k] = 0x00000000; - } - for (uint16_t k = 0; k < buffer_size; k++) { - buffer[k] = 0x00000000; - } + memset(mask, 0, mask_length * sizeof(uint32_t)); + memset(buffer, 0, buffer_size * sizeof(uint32_t)); self->core.colorspace.grayscale = true; if (pass == 1) { @@ -291,7 +285,7 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c // Can't acquire display bus; skip the rest of the data. Try next display. return false; } - self->core.send(self->core.bus, false, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes); + self->core.send(self->core.bus, DISPLAY_DATA, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes); displayio_display_core_end_transaction(&self->core); // TODO(tannewt): Make refresh displays faster so we don't starve other diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index a5d4ec93f..580171a5f 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -56,7 +56,7 @@ typedef struct { bool black_bits_inverted; bool color_bits_inverted; bool refreshing; - bool always_toggle_chip_select; + display_chip_select_behavior_t always_toggle_chip_select; } displayio_epaperdisplay_obj_t; void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self); diff --git a/shared-module/displayio/FourWire.c b/shared-module/displayio/FourWire.c index 7294e9772..fcc4c1a20 100644 --- a/shared-module/displayio/FourWire.c +++ b/shared-module/displayio/FourWire.c @@ -33,6 +33,7 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/time/__init__.h" +#include "shared-module/displayio/display_core.h" #include "tick.h" @@ -110,10 +111,10 @@ bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t obj) { return true; } -void common_hal_displayio_fourwire_send(mp_obj_t obj, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length) { +void common_hal_displayio_fourwire_send(mp_obj_t obj, display_byte_type_t data_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length) { displayio_fourwire_obj_t* self = MP_OBJ_TO_PTR(obj); - common_hal_digitalio_digitalinout_set_value(&self->command, !command); - if (toggle_every_byte) { + common_hal_digitalio_digitalinout_set_value(&self->command, data_type == DISPLAY_DATA); + if (chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE) { // Toggle chip select after each command byte in case the display driver // IC latches commands based on it. for (size_t i = 0; i < data_length; i++) { diff --git a/shared-module/displayio/I2CDisplay.c b/shared-module/displayio/I2CDisplay.c index 95830ba0e..87261dca0 100644 --- a/shared-module/displayio/I2CDisplay.c +++ b/shared-module/displayio/I2CDisplay.c @@ -35,6 +35,7 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/time/__init__.h" +#include "shared-module/displayio/display_core.h" #include "tick.h" @@ -80,7 +81,7 @@ bool common_hal_displayio_i2cdisplay_reset(mp_obj_t obj) { } common_hal_digitalio_digitalinout_set_value(&self->reset, false); - common_hal_mcu_delay_us(1); + common_hal_mcu_delay_us(4); common_hal_digitalio_digitalinout_set_value(&self->reset, true); return true; } @@ -97,9 +98,9 @@ bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t obj) { return common_hal_displayio_i2cdisplay_bus_free(obj); } -void common_hal_displayio_i2cdisplay_send(mp_obj_t obj, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length) { +void common_hal_displayio_i2cdisplay_send(mp_obj_t obj, display_byte_type_t data_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length) { displayio_i2cdisplay_obj_t* self = MP_OBJ_TO_PTR(obj); - if (command) { + if (data_type == DISPLAY_COMMAND) { uint8_t command_bytes[2 * data_length]; for (uint32_t i = 0; i < data_length; i++) { command_bytes[2 * i] = 0x80; diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c index 88878866a..0f449ea34 100644 --- a/shared-module/displayio/display_core.c +++ b/shared-module/displayio/display_core.c @@ -184,7 +184,6 @@ void displayio_display_core_end_transaction(displayio_display_core_t* self) { } void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, bool data_as_commands, bool always_toggle_chip_select, displayio_area_t* area) { - displayio_display_core_begin_transaction(self); uint16_t x1 = area->x1; uint16_t x2 = area->x2; uint16_t y1 = area->y1; @@ -201,13 +200,22 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, } } + display_chip_select_behavior_t chip_select = CHIP_SELECT_UNTOUCHED; + if (always_toggle_chip_select || data_as_commands) { + chip_select = CHIP_SELECT_TOGGLE_EVERY_BYTE; + } + // Set column. + displayio_display_core_begin_transaction(self); uint8_t data[5]; data[0] = column_command; uint8_t data_length = 1; + display_byte_type_t data_type = DISPLAY_DATA; if (!data_as_commands) { - self->send(self->bus, true, false, data, 1); + self->send(self->bus, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data, 1); data_length = 0; + } else { + data_type = DISPLAY_COMMAND; } if (self->ram_width < 0x100) { data[data_length++] = x1 + self->colstart; @@ -220,21 +228,23 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, data[data_length++] = x2 >> 8; data[data_length++] = x2 & 0xff; } - self->send(self->bus, data_as_commands, data_as_commands, data, data_length); + self->send(self->bus, data_type, chip_select, data, data_length); + displayio_display_core_end_transaction(self); if (set_current_column_command != NO_COMMAND) { uint8_t command = set_current_column_command; - self->send(self->bus, true, always_toggle_chip_select, &command, 1); - self->send(self->bus, false, always_toggle_chip_select, data, data_length / 2); + displayio_display_core_begin_transaction(self); + self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); + self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); + displayio_display_core_end_transaction(self); } - displayio_display_core_end_transaction(self); - displayio_display_core_begin_transaction(self); // Set row. + displayio_display_core_begin_transaction(self); data[0] = row_command; data_length = 1; if (!data_as_commands) { - self->send(self->bus, true, false, data, 1); + self->send(self->bus, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data, 1); data_length = 0; } if (self->ram_height < 0x100) { @@ -248,14 +258,16 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, data[data_length++] = y2 >> 8; data[data_length++] = y2 & 0xff; } - self->send(self->bus, data_as_commands, data_as_commands, data, data_length); + self->send(self->bus, data_type, chip_select, data, data_length); + displayio_display_core_end_transaction(self); + if (set_current_row_command != NO_COMMAND) { uint8_t command = set_current_row_command; - self->send(self->bus, true, always_toggle_chip_select, &command, 1); - self->send(self->bus, false, always_toggle_chip_select, data, data_length / 2); + displayio_display_core_begin_transaction(self); + self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); + self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); + displayio_display_core_end_transaction(self); } - - displayio_display_core_end_transaction(self); } void displayio_display_core_start_refresh(displayio_display_core_t* self) { diff --git a/shared-module/displayio/display_core.h b/shared-module/displayio/display_core.h index b1a412883..e92fdbc9f 100644 --- a/shared-module/displayio/display_core.h +++ b/shared-module/displayio/display_core.h @@ -27,18 +27,13 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H #define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H +#include "shared-bindings/displayio/__init__.h" #include "shared-bindings/displayio/Group.h" #include "shared-module/displayio/area.h" #define NO_COMMAND 0x100 -typedef bool (*display_bus_bus_reset)(mp_obj_t bus); -typedef bool (*display_bus_bus_free)(mp_obj_t bus); -typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); -typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length); -typedef void (*display_bus_end_transaction)(mp_obj_t bus); - typedef struct { mp_obj_t bus; displayio_group_t *current_group; diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index 36b30f462..c50d7f4ee 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -91,7 +91,7 @@ void filesystem_init(bool create_allowed, bool force_create) { } // set label - f_setlabel(&vfs_fat->fatfs, CIRCUITPY_FS_NAME); + f_setlabel(&vfs_fat->fatfs, "CIRCUITPY"); // inhibit file indexing on MacOS f_mkdir(&vfs_fat->fatfs, "/.fseventsd"); -- cgit v1.2.3 From bea77c651afd919e6cb87927341714adbddb939c Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 26 Aug 2019 16:37:59 -0700 Subject: Minor renames --- shared-bindings/displayio/Display.h | 1 - shared-module/displayio/Display.c | 4 ++-- shared-module/displayio/EPaperDisplay.c | 20 ++++++++++---------- shared-module/displayio/EPaperDisplay.h | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/displayio/Display.h b/shared-bindings/displayio/Display.h index 2d7b68c4e..a3c77e4e8 100644 --- a/shared-bindings/displayio/Display.h +++ b/shared-bindings/displayio/Display.h @@ -50,7 +50,6 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group); -// Times in ms. bool common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame); bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self); diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index cb5051bb7..f210a80c3 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -270,8 +270,8 @@ STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); } - memset(mask, 0, mask_length * sizeof(uint32_t)); - memset(buffer, 0, buffer_size * sizeof(uint32_t)); + memset(mask, 0, mask_length * sizeof(mask[0])); + memset(buffer, 0, buffer_size * sizeof(buffer[0])); displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 38b63df0f..efbd9f3dd 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -49,7 +49,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* uint16_t set_column_window_command, uint16_t set_row_window_command, uint16_t set_current_column_command, uint16_t set_current_row_command, uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, mp_float_t refresh_time, - const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select) { + const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool chip_select) { if (highlight_color != 0x000000) { self->core.colorspace.tricolor = true; self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color); @@ -71,7 +71,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self->busy_state = busy_state; self->refreshing = false; self->milliseconds_per_frame = seconds_per_frame * 1000; - self->always_toggle_chip_select = always_toggle_chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED; + self->chip_select = chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED; self->start_sequence = start_sequence; self->start_sequence_len = start_sequence_len; @@ -143,8 +143,8 @@ STATIC void send_command_sequence(displayio_epaperdisplay_obj_t* self, bool shou data_size &= ~DELAY; uint8_t *data = cmd + 2; displayio_display_core_begin_transaction(&self->core); - self->core.send(self->core.bus, DISPLAY_COMMAND, self->always_toggle_chip_select, cmd, 1); - self->core.send(self->core.bus, DISPLAY_DATA, self->always_toggle_chip_select, data, data_size); + self->core.send(self->core.bus, DISPLAY_COMMAND, self->chip_select, cmd, 1); + self->core.send(self->core.bus, DISPLAY_DATA, self->chip_select, data, data_size); displayio_display_core_end_transaction(&self->core); uint16_t delay_length_ms = 0; if (delay) { @@ -185,7 +185,7 @@ uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaper void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) { // Actually refresh the display now that all pixel RAM has been updated. displayio_display_core_begin_transaction(&self->core); - self->core.send(self->core.bus, DISPLAY_COMMAND, self->always_toggle_chip_select, &self->refresh_display_command, 1); + self->core.send(self->core.bus, DISPLAY_COMMAND, self->chip_select, &self->refresh_display_command, 1); displayio_display_core_end_transaction(&self->core); self->refreshing = true; @@ -238,7 +238,7 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c uint16_t remaining_rows = displayio_area_height(&clipped); if (self->set_row_window_command != NO_COMMAND) { - displayio_display_core_set_region_to_update(&self->core, self->set_column_window_command, self->set_row_window_command, self->set_current_column_command, self->set_current_row_command, false, self->always_toggle_chip_select, &clipped); + displayio_display_core_set_region_to_update(&self->core, self->set_column_window_command, self->set_row_window_command, self->set_current_column_command, self->set_current_row_command, false, self->chip_select, &clipped); } uint8_t write_command = self->write_black_ram_command; @@ -246,7 +246,7 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c write_command = self->write_color_ram_command; } displayio_display_core_begin_transaction(&self->core); - self->core.send(self->core.bus, DISPLAY_COMMAND, self->always_toggle_chip_select, &write_command, 1); + self->core.send(self->core.bus, DISPLAY_COMMAND, self->chip_select, &write_command, 1); displayio_display_core_end_transaction(&self->core); for (uint16_t j = 0; j < subrectangles; j++) { @@ -264,8 +264,8 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); - memset(mask, 0, mask_length * sizeof(uint32_t)); - memset(buffer, 0, buffer_size * sizeof(uint32_t)); + memset(mask, 0, mask_length * sizeof(mask[0])); + memset(buffer, 0, buffer_size * sizeof(buffer[0])); self->core.colorspace.grayscale = true; if (pass == 1) { @@ -285,7 +285,7 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c // Can't acquire display bus; skip the rest of the data. Try next display. return false; } - self->core.send(self->core.bus, DISPLAY_DATA, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes); + self->core.send(self->core.bus, DISPLAY_DATA, self->chip_select, (uint8_t*) buffer, subrectangle_size_bytes); displayio_display_core_end_transaction(&self->core); // TODO(tannewt): Make refresh displays faster so we don't starve other diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index 580171a5f..d08bed546 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -56,7 +56,7 @@ typedef struct { bool black_bits_inverted; bool color_bits_inverted; bool refreshing; - display_chip_select_behavior_t always_toggle_chip_select; + display_chip_select_behavior_t chip_select; } displayio_epaperdisplay_obj_t; void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self); -- cgit v1.2.3 From 7a64af92807f012828636c101f4350272f6e929a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 29 Aug 2019 18:44:27 -0400 Subject: rename bleio module to _bleio --- ports/nrf/bluetooth/ble_uart.c | 10 +- ports/nrf/common-hal/_bleio/Adapter.c | 204 +++++++++ ports/nrf/common-hal/_bleio/Adapter.h | 38 ++ ports/nrf/common-hal/_bleio/Attribute.c | 60 +++ ports/nrf/common-hal/_bleio/Attribute.h | 34 ++ ports/nrf/common-hal/_bleio/Central.c | 145 ++++++ ports/nrf/common-hal/_bleio/Central.h | 44 ++ ports/nrf/common-hal/_bleio/Characteristic.c | 299 ++++++++++++ ports/nrf/common-hal/_bleio/Characteristic.h | 54 +++ ports/nrf/common-hal/_bleio/CharacteristicBuffer.c | 155 +++++++ ports/nrf/common-hal/_bleio/CharacteristicBuffer.h | 43 ++ ports/nrf/common-hal/_bleio/Descriptor.c | 143 ++++++ ports/nrf/common-hal/_bleio/Descriptor.h | 50 ++ ports/nrf/common-hal/_bleio/Peripheral.c | 361 +++++++++++++++ ports/nrf/common-hal/_bleio/Peripheral.h | 67 +++ ports/nrf/common-hal/_bleio/Scanner.c | 105 +++++ ports/nrf/common-hal/_bleio/Scanner.h | 40 ++ ports/nrf/common-hal/_bleio/Service.c | 122 +++++ ports/nrf/common-hal/_bleio/Service.h | 50 ++ ports/nrf/common-hal/_bleio/UUID.c | 99 ++++ ports/nrf/common-hal/_bleio/UUID.h | 49 ++ ports/nrf/common-hal/_bleio/__init__.c | 502 +++++++++++++++++++++ ports/nrf/common-hal/_bleio/__init__.h | 42 ++ ports/nrf/common-hal/bleio/Adapter.c | 204 --------- ports/nrf/common-hal/bleio/Adapter.h | 38 -- ports/nrf/common-hal/bleio/Attribute.c | 60 --- ports/nrf/common-hal/bleio/Attribute.h | 34 -- ports/nrf/common-hal/bleio/Central.c | 145 ------ ports/nrf/common-hal/bleio/Central.h | 44 -- ports/nrf/common-hal/bleio/Characteristic.c | 299 ------------ ports/nrf/common-hal/bleio/Characteristic.h | 54 --- ports/nrf/common-hal/bleio/CharacteristicBuffer.c | 155 ------- ports/nrf/common-hal/bleio/CharacteristicBuffer.h | 43 -- ports/nrf/common-hal/bleio/Descriptor.c | 143 ------ ports/nrf/common-hal/bleio/Descriptor.h | 50 -- ports/nrf/common-hal/bleio/Peripheral.c | 361 --------------- ports/nrf/common-hal/bleio/Peripheral.h | 67 --- ports/nrf/common-hal/bleio/Scanner.c | 105 ----- ports/nrf/common-hal/bleio/Scanner.h | 40 -- ports/nrf/common-hal/bleio/Service.c | 122 ----- ports/nrf/common-hal/bleio/Service.h | 50 -- ports/nrf/common-hal/bleio/UUID.c | 99 ---- ports/nrf/common-hal/bleio/UUID.h | 49 -- ports/nrf/common-hal/bleio/__init__.c | 502 --------------------- ports/nrf/common-hal/bleio/__init__.h | 42 -- ports/nrf/supervisor/port.c | 2 +- py/circuitpy_defns.mk | 36 +- py/circuitpy_mpconfig.h | 2 +- py/circuitpy_mpconfig.mk | 2 +- shared-bindings/_bleio/Adapter.c | 125 +++++ shared-bindings/_bleio/Adapter.h | 40 ++ shared-bindings/_bleio/Address.c | 210 +++++++++ shared-bindings/_bleio/Address.h | 48 ++ shared-bindings/_bleio/Attribute.c | 94 ++++ shared-bindings/_bleio/Attribute.h | 39 ++ shared-bindings/_bleio/Central.c | 207 +++++++++ shared-bindings/_bleio/Central.h | 43 ++ shared-bindings/_bleio/Characteristic.c | 341 ++++++++++++++ shared-bindings/_bleio/Characteristic.h | 49 ++ shared-bindings/_bleio/CharacteristicBuffer.c | 252 +++++++++++ shared-bindings/_bleio/CharacteristicBuffer.h | 42 ++ shared-bindings/_bleio/Descriptor.c | 231 ++++++++++ shared-bindings/_bleio/Descriptor.h | 44 ++ shared-bindings/_bleio/Peripheral.c | 325 +++++++++++++ shared-bindings/_bleio/Peripheral.h | 48 ++ shared-bindings/_bleio/ScanEntry.c | 111 +++++ shared-bindings/_bleio/ScanEntry.h | 41 ++ shared-bindings/_bleio/Scanner.c | 129 ++++++ shared-bindings/_bleio/Scanner.h | 40 ++ shared-bindings/_bleio/Service.c | 201 +++++++++ shared-bindings/_bleio/Service.h | 43 ++ shared-bindings/_bleio/UUID.c | 280 ++++++++++++ shared-bindings/_bleio/UUID.h | 43 ++ shared-bindings/_bleio/__init__.c | 107 +++++ shared-bindings/_bleio/__init__.h | 52 +++ shared-bindings/bleio/Adapter.c | 125 ----- shared-bindings/bleio/Adapter.h | 40 -- shared-bindings/bleio/Address.c | 210 --------- shared-bindings/bleio/Address.h | 48 -- shared-bindings/bleio/Attribute.c | 94 ---- shared-bindings/bleio/Attribute.h | 39 -- shared-bindings/bleio/Central.c | 207 --------- shared-bindings/bleio/Central.h | 43 -- shared-bindings/bleio/Characteristic.c | 341 -------------- shared-bindings/bleio/Characteristic.h | 49 -- shared-bindings/bleio/CharacteristicBuffer.c | 252 ----------- shared-bindings/bleio/CharacteristicBuffer.h | 42 -- shared-bindings/bleio/Descriptor.c | 231 ---------- shared-bindings/bleio/Descriptor.h | 44 -- shared-bindings/bleio/Peripheral.c | 325 ------------- shared-bindings/bleio/Peripheral.h | 48 -- shared-bindings/bleio/ScanEntry.c | 111 ----- shared-bindings/bleio/ScanEntry.h | 41 -- shared-bindings/bleio/Scanner.c | 129 ------ shared-bindings/bleio/Scanner.h | 40 -- shared-bindings/bleio/Service.c | 201 --------- shared-bindings/bleio/Service.h | 43 -- shared-bindings/bleio/UUID.c | 280 ------------ shared-bindings/bleio/UUID.h | 43 -- shared-bindings/bleio/__init__.c | 104 ----- shared-bindings/bleio/__init__.h | 52 --- shared-module/_bleio/Address.c | 45 ++ shared-module/_bleio/Address.h | 41 ++ shared-module/_bleio/Attribute.c | 46 ++ shared-module/_bleio/Attribute.h | 41 ++ shared-module/_bleio/Characteristic.h | 43 ++ shared-module/_bleio/ScanEntry.c | 45 ++ shared-module/_bleio/ScanEntry.h | 42 ++ shared-module/bleio/Address.c | 45 -- shared-module/bleio/Address.h | 41 -- shared-module/bleio/Attribute.c | 46 -- shared-module/bleio/Attribute.h | 41 -- shared-module/bleio/Characteristic.h | 43 -- shared-module/bleio/ScanEntry.c | 45 -- shared-module/bleio/ScanEntry.h | 42 -- 115 files changed, 6220 insertions(+), 6217 deletions(-) create mode 100644 ports/nrf/common-hal/_bleio/Adapter.c create mode 100644 ports/nrf/common-hal/_bleio/Adapter.h create mode 100644 ports/nrf/common-hal/_bleio/Attribute.c create mode 100644 ports/nrf/common-hal/_bleio/Attribute.h create mode 100644 ports/nrf/common-hal/_bleio/Central.c create mode 100644 ports/nrf/common-hal/_bleio/Central.h create mode 100644 ports/nrf/common-hal/_bleio/Characteristic.c create mode 100644 ports/nrf/common-hal/_bleio/Characteristic.h create mode 100644 ports/nrf/common-hal/_bleio/CharacteristicBuffer.c create mode 100644 ports/nrf/common-hal/_bleio/CharacteristicBuffer.h create mode 100644 ports/nrf/common-hal/_bleio/Descriptor.c create mode 100644 ports/nrf/common-hal/_bleio/Descriptor.h create mode 100644 ports/nrf/common-hal/_bleio/Peripheral.c create mode 100644 ports/nrf/common-hal/_bleio/Peripheral.h create mode 100644 ports/nrf/common-hal/_bleio/Scanner.c create mode 100644 ports/nrf/common-hal/_bleio/Scanner.h create mode 100644 ports/nrf/common-hal/_bleio/Service.c create mode 100644 ports/nrf/common-hal/_bleio/Service.h create mode 100644 ports/nrf/common-hal/_bleio/UUID.c create mode 100644 ports/nrf/common-hal/_bleio/UUID.h create mode 100644 ports/nrf/common-hal/_bleio/__init__.c create mode 100644 ports/nrf/common-hal/_bleio/__init__.h delete mode 100644 ports/nrf/common-hal/bleio/Adapter.c delete mode 100644 ports/nrf/common-hal/bleio/Adapter.h delete mode 100644 ports/nrf/common-hal/bleio/Attribute.c delete mode 100644 ports/nrf/common-hal/bleio/Attribute.h delete mode 100644 ports/nrf/common-hal/bleio/Central.c delete mode 100644 ports/nrf/common-hal/bleio/Central.h delete mode 100644 ports/nrf/common-hal/bleio/Characteristic.c delete mode 100644 ports/nrf/common-hal/bleio/Characteristic.h delete mode 100644 ports/nrf/common-hal/bleio/CharacteristicBuffer.c delete mode 100644 ports/nrf/common-hal/bleio/CharacteristicBuffer.h delete mode 100644 ports/nrf/common-hal/bleio/Descriptor.c delete mode 100644 ports/nrf/common-hal/bleio/Descriptor.h delete mode 100644 ports/nrf/common-hal/bleio/Peripheral.c delete mode 100644 ports/nrf/common-hal/bleio/Peripheral.h delete mode 100644 ports/nrf/common-hal/bleio/Scanner.c delete mode 100644 ports/nrf/common-hal/bleio/Scanner.h delete mode 100644 ports/nrf/common-hal/bleio/Service.c delete mode 100644 ports/nrf/common-hal/bleio/Service.h delete mode 100644 ports/nrf/common-hal/bleio/UUID.c delete mode 100644 ports/nrf/common-hal/bleio/UUID.h delete mode 100644 ports/nrf/common-hal/bleio/__init__.c delete mode 100644 ports/nrf/common-hal/bleio/__init__.h create mode 100644 shared-bindings/_bleio/Adapter.c create mode 100644 shared-bindings/_bleio/Adapter.h create mode 100644 shared-bindings/_bleio/Address.c create mode 100644 shared-bindings/_bleio/Address.h create mode 100644 shared-bindings/_bleio/Attribute.c create mode 100644 shared-bindings/_bleio/Attribute.h create mode 100644 shared-bindings/_bleio/Central.c create mode 100644 shared-bindings/_bleio/Central.h create mode 100644 shared-bindings/_bleio/Characteristic.c create mode 100644 shared-bindings/_bleio/Characteristic.h create mode 100644 shared-bindings/_bleio/CharacteristicBuffer.c create mode 100644 shared-bindings/_bleio/CharacteristicBuffer.h create mode 100644 shared-bindings/_bleio/Descriptor.c create mode 100644 shared-bindings/_bleio/Descriptor.h create mode 100644 shared-bindings/_bleio/Peripheral.c create mode 100644 shared-bindings/_bleio/Peripheral.h create mode 100644 shared-bindings/_bleio/ScanEntry.c create mode 100644 shared-bindings/_bleio/ScanEntry.h create mode 100644 shared-bindings/_bleio/Scanner.c create mode 100644 shared-bindings/_bleio/Scanner.h create mode 100644 shared-bindings/_bleio/Service.c create mode 100644 shared-bindings/_bleio/Service.h create mode 100644 shared-bindings/_bleio/UUID.c create mode 100644 shared-bindings/_bleio/UUID.h create mode 100644 shared-bindings/_bleio/__init__.c create mode 100644 shared-bindings/_bleio/__init__.h delete mode 100644 shared-bindings/bleio/Adapter.c delete mode 100644 shared-bindings/bleio/Adapter.h delete mode 100644 shared-bindings/bleio/Address.c delete mode 100644 shared-bindings/bleio/Address.h delete mode 100644 shared-bindings/bleio/Attribute.c delete mode 100644 shared-bindings/bleio/Attribute.h delete mode 100644 shared-bindings/bleio/Central.c delete mode 100644 shared-bindings/bleio/Central.h delete mode 100644 shared-bindings/bleio/Characteristic.c delete mode 100644 shared-bindings/bleio/Characteristic.h delete mode 100644 shared-bindings/bleio/CharacteristicBuffer.c delete mode 100644 shared-bindings/bleio/CharacteristicBuffer.h delete mode 100644 shared-bindings/bleio/Descriptor.c delete mode 100644 shared-bindings/bleio/Descriptor.h delete mode 100644 shared-bindings/bleio/Peripheral.c delete mode 100644 shared-bindings/bleio/Peripheral.h delete mode 100644 shared-bindings/bleio/ScanEntry.c delete mode 100644 shared-bindings/bleio/ScanEntry.h delete mode 100644 shared-bindings/bleio/Scanner.c delete mode 100644 shared-bindings/bleio/Scanner.h delete mode 100644 shared-bindings/bleio/Service.c delete mode 100644 shared-bindings/bleio/Service.h delete mode 100644 shared-bindings/bleio/UUID.c delete mode 100644 shared-bindings/bleio/UUID.h delete mode 100644 shared-bindings/bleio/__init__.c delete mode 100644 shared-bindings/bleio/__init__.h create mode 100644 shared-module/_bleio/Address.c create mode 100644 shared-module/_bleio/Address.h create mode 100644 shared-module/_bleio/Attribute.c create mode 100644 shared-module/_bleio/Attribute.h create mode 100644 shared-module/_bleio/Characteristic.h create mode 100644 shared-module/_bleio/ScanEntry.c create mode 100644 shared-module/_bleio/ScanEntry.h delete mode 100644 shared-module/bleio/Address.c delete mode 100644 shared-module/bleio/Address.h delete mode 100644 shared-module/bleio/Attribute.c delete mode 100644 shared-module/bleio/Attribute.h delete mode 100644 shared-module/bleio/Characteristic.h delete mode 100644 shared-module/bleio/ScanEntry.c delete mode 100644 shared-module/bleio/ScanEntry.h (limited to 'shared-module') diff --git a/ports/nrf/bluetooth/ble_uart.c b/ports/nrf/bluetooth/ble_uart.c index d83cf3980..787a2a117 100644 --- a/ports/nrf/bluetooth/ble_uart.c +++ b/ports/nrf/bluetooth/ble_uart.c @@ -34,11 +34,11 @@ #include "py/mphal.h" #include "py/runtime.h" #include "lib/utils/interrupt_char.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Device.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Device.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" #if (MICROPY_PY_BLE_NUS == 1) diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c new file mode 100644 index 000000000..a8e1f1905 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -0,0 +1,204 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2016 Glenn Ruben Bakke + * Copyright (c) 2018 Artur Pacholec + * + * 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 +#include + +#include "ble.h" +#include "ble_drv.h" +#include "nrfx_power.h" +#include "nrf_nvic.h" +#include "nrf_sdm.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "supervisor/usb.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Address.h" + +#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) +#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) +#define BLE_SLAVE_LATENCY 0 +#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) + +STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { + mp_raise_msg_varg(&mp_type_AssertionError, + translate("Soft device assert, id: 0x%08lX, pc: 0x%08lX"), id, pc); +} + +STATIC uint32_t ble_stack_enable(void) { + nrf_clock_lf_cfg_t clock_config = { +#if BOARD_HAS_32KHZ_XTAL + .source = NRF_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .accuracy = NRF_CLOCK_LF_ACCURACY_20_PPM, +#else + .source = NRF_CLOCK_LF_SRC_RC, + .rc_ctiv = 16, + .rc_temp_ctiv = 2, + .accuracy = NRF_CLOCK_LF_ACCURACY_250_PPM, +#endif + }; + + uint32_t err_code = sd_softdevice_enable(&clock_config, softdevice_assert_handler); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + err_code = sd_nvic_EnableIRQ(SD_EVT_IRQn); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + // Start with no event handlers, etc. + ble_drv_reset(); + + uint32_t app_ram_start; + app_ram_start = 0x20004000; + + ble_cfg_t ble_conf; + ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM; + ble_conf.conn_cfg.params.gap_conn_cfg.conn_count = BLE_GAP_CONN_COUNT_DEFAULT; + ble_conf.conn_cfg.params.gap_conn_cfg.event_length = BLE_GAP_EVENT_LENGTH_DEFAULT; + err_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_conf, app_ram_start); + if (err_code != NRF_SUCCESS) + return err_code; + + memset(&ble_conf, 0, sizeof(ble_conf)); + ble_conf.gap_cfg.role_count_cfg.periph_role_count = 1; + ble_conf.gap_cfg.role_count_cfg.central_role_count = 1; + err_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_conf, app_ram_start); + if (err_code != NRF_SUCCESS) + return err_code; + + memset(&ble_conf, 0, sizeof(ble_conf)); + ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM; + ble_conf.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = MAX_TX_IN_PROGRESS; + err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &ble_conf, app_ram_start); + if (err_code != NRF_SUCCESS) + return err_code; + + err_code = sd_ble_enable(&app_ram_start); + if (err_code != NRF_SUCCESS) + return err_code; + + ble_gap_conn_params_t gap_conn_params = { + .min_conn_interval = BLE_MIN_CONN_INTERVAL, + .max_conn_interval = BLE_MAX_CONN_INTERVAL, + .slave_latency = BLE_SLAVE_LATENCY, + .conn_sup_timeout = BLE_CONN_SUP_TIMEOUT, + }; + err_code = sd_ble_gap_ppcp_set(&gap_conn_params); + if (err_code != NRF_SUCCESS) { + return err_code; + } + + err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_UNKNOWN); + return err_code; +} + +void common_hal_bleio_adapter_set_enabled(bool enabled) { + const bool is_enabled = common_hal_bleio_adapter_get_enabled(); + + // Don't enable or disable twice + if ((is_enabled && enabled) || (!is_enabled && !enabled)) { + return; + } + + uint32_t err_code; + if (enabled) { + // The SD takes over the POWER module and will fail if the module is already in use. + // Occurs when USB is initialized previously + nrfx_power_uninit(); + + err_code = ble_stack_enable(); + + // Re-init USB hardware + init_usb_hardware(); + } else { + err_code = sd_softdevice_disable(); + + // Re-init USB hardware + init_usb_hardware(); + } + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg(translate("Failed to change softdevice state")); + } +} + +bool common_hal_bleio_adapter_get_enabled(void) { + uint8_t is_enabled; + + const uint32_t err_code = sd_softdevice_is_enabled(&is_enabled); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg(translate("Failed to get softdevice state")); + } + + return is_enabled; +} + +void get_address(ble_gap_addr_t *address) { + uint32_t err_code; + + common_hal_bleio_adapter_set_enabled(true); + err_code = sd_ble_gap_addr_get(address); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg(translate("Failed to get local address")); + } +} + +bleio_address_obj_t *common_hal_bleio_adapter_get_address(void) { + common_hal_bleio_adapter_set_enabled(true); + + ble_gap_addr_t local_address; + get_address(&local_address); + + bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t); + address->base.type = &bleio_address_type; + + common_hal_bleio_address_construct(address, local_address.addr, local_address.addr_type); + return address; +} + +mp_obj_t common_hal_bleio_adapter_get_default_name(void) { + common_hal_bleio_adapter_set_enabled(true); + + ble_gap_addr_t local_address; + get_address(&local_address); + + char name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0 }; + + name[sizeof(name) - 4] = nibble_to_hex_lower[local_address.addr[1] >> 4 & 0xf]; + name[sizeof(name) - 3] = nibble_to_hex_lower[local_address.addr[1] & 0xf]; + name[sizeof(name) - 2] = nibble_to_hex_lower[local_address.addr[0] >> 4 & 0xf]; + name[sizeof(name) - 1] = nibble_to_hex_lower[local_address.addr[0] & 0xf]; + + return mp_obj_new_str(name, sizeof(name)); +} diff --git a/ports/nrf/common-hal/_bleio/Adapter.h b/ports/nrf/common-hal/_bleio/Adapter.h new file mode 100644 index 000000000..5dcc62540 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Adapter.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_COMMON_HAL_BLEIO_ADAPTER_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ADAPTER_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} super_adapter_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ADAPTER_H diff --git a/ports/nrf/common-hal/_bleio/Attribute.c b/ports/nrf/common-hal/_bleio/Attribute.c new file mode 100644 index 000000000..c55914b10 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Attribute.c @@ -0,0 +1,60 @@ +/* + * 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 "shared-bindings/_bleio/Attribute.h" + +// Convert a _bleio security mode to a ble_gap_conn_sec_mode_t setting. +void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode) { + switch (security_mode) { + case SECURITY_MODE_NO_ACCESS: + BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(perm); + break; + + case SECURITY_MODE_OPEN: + BLE_GAP_CONN_SEC_MODE_SET_OPEN(perm); + break; + + case SECURITY_MODE_ENC_NO_MITM: + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(perm); + break; + + case SECURITY_MODE_ENC_WITH_MITM: + BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(perm); + break; + + case SECURITY_MODE_LESC_ENC_WITH_MITM: + BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(perm); + break; + + case SECURITY_MODE_SIGNED_NO_MITM: + BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(perm); + break; + + case SECURITY_MODE_SIGNED_WITH_MITM: + BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(perm); + break; + } +} diff --git a/ports/nrf/common-hal/_bleio/Attribute.h b/ports/nrf/common-hal/_bleio/Attribute.h new file mode 100644 index 000000000..8cc361046 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Attribute.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_COMMON_HAL_BLEIO_ATTRIBUTE_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H + +#include "shared-module/_bleio/Attribute.h" + +extern void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode); + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H diff --git a/ports/nrf/common-hal/_bleio/Central.c b/ports/nrf/common-hal/_bleio/Central.c new file mode 100644 index 000000000..db67b763c --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Central.c @@ -0,0 +1,145 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 +#include + +#include "ble.h" +#include "ble_drv.h" +#include "ble_hci.h" +#include "nrf_soc.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Central.h" + +STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) { + bleio_central_obj_t *central = (bleio_central_obj_t*)central_in; + + switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_CONNECTED: + central->conn_handle = ble_evt->evt.gap_evt.conn_handle; + central->waiting_to_connect = false; + break; + + case BLE_GAP_EVT_TIMEOUT: + // Handle will be invalid. + central->waiting_to_connect = false; + break; + + case BLE_GAP_EVT_DISCONNECTED: + central->conn_handle = BLE_CONN_HANDLE_INVALID; + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + sd_ble_gap_sec_params_reply(central->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { + ble_gap_evt_conn_param_update_request_t *request = + &ble_evt->evt.gap_evt.params.conn_param_update_request; + sd_ble_gap_conn_param_update(central->conn_handle, &request->conn_params); + break; + } + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + +void common_hal_bleio_central_construct(bleio_central_obj_t *self) { + common_hal_bleio_adapter_set_enabled(true); + + self->remote_service_list = mp_obj_new_list(0, NULL); + self->conn_handle = BLE_CONN_HANDLE_INVALID; +} + +void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { + common_hal_bleio_adapter_set_enabled(true); + ble_drv_add_event_handler(central_on_ble_evt, self); + + ble_gap_addr_t addr; + + addr.addr_type = address->type; + mp_buffer_info_t address_buf_info; + mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ); + memcpy(addr.addr, (uint8_t *) address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); + + ble_gap_scan_params_t scan_params = { + .interval = MSEC_TO_UNITS(100, UNIT_0_625_MS), + .window = MSEC_TO_UNITS(100, UNIT_0_625_MS), + .scan_phys = BLE_GAP_PHY_1MBPS, + // timeout of 0 means no timeout + .timeout = SEC_TO_UNITS(timeout, UNIT_10_MS), + }; + + ble_gap_conn_params_t conn_params = { + .conn_sup_timeout = MSEC_TO_UNITS(4000, UNIT_10_MS), + .min_conn_interval = MSEC_TO_UNITS(15, UNIT_1_25_MS), + .max_conn_interval = MSEC_TO_UNITS(300, UNIT_1_25_MS), + .slave_latency = 0, // number of conn events + }; + + self->waiting_to_connect = true; + + uint32_t err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_CUSTOM); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start connecting, error 0x%04x"), err_code); + } + + while (self->waiting_to_connect) { + RUN_BACKGROUND_TASKS; + } + + if (self->conn_handle == BLE_CONN_HANDLE_INVALID) { + mp_raise_OSError_msg(translate("Failed to connect: timeout")); + } +} + +void common_hal_bleio_central_disconnect(bleio_central_obj_t *self) { + sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); +} + +bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self) { + return self->conn_handle != BLE_CONN_HANDLE_INVALID; +} + +mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist) { + common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist); + // Convert to a tuple and then clear the list so the callee will take ownership. + mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_service_list->len, + self->remote_service_list->items); + mp_obj_list_clear(self->remote_service_list); + return services_tuple; +} + +mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self) { + return self->remote_service_list; +} diff --git a/ports/nrf/common-hal/_bleio/Central.h b/ports/nrf/common-hal/_bleio/Central.h new file mode 100644 index 000000000..01f7faca7 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Central.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_MODULE_BLEIO_CENTRAL_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H + +#include + +#include "py/objlist.h" +#include "shared-module/_bleio/Address.h" + +typedef struct { + mp_obj_base_t base; + volatile bool waiting_to_connect; + volatile uint16_t conn_handle; + // Services discovered after connecting to a remote peripheral. + mp_obj_list_t *remote_service_list; +} bleio_central_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H diff --git a/ports/nrf/common-hal/_bleio/Characteristic.c b/ports/nrf/common-hal/_bleio/Characteristic.c new file mode 100644 index 000000000..e8454d528 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Characteristic.c @@ -0,0 +1,299 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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/runtime.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" + +static volatile bleio_characteristic_obj_t *m_read_characteristic; + +STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) { + uint16_t cccd; + ble_gatts_value_t value = { + .p_value = (uint8_t*) &cccd, + .len = 2, + }; + + const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, cccd_handle, &value); + + if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) { + // CCCD is not set, so say that neither Notify nor Indicate is enabled. + cccd = 0; + } else if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read CCCD value, err 0x%04x"), err_code); + } + + return cccd; +} + +STATIC void characteristic_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { + switch (ble_evt->header.evt_id) { + + // More events may be handled later, so keep this as a switch. + + case BLE_GATTC_EVT_READ_RSP: { + ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; + if (m_read_characteristic) { + m_read_characteristic->value = mp_obj_new_bytearray(response->len, response->data); + } + // Indicate to busy-wait loop that we've read the attribute value. + m_read_characteristic = NULL; + break; + } + + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + +STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) { + uint16_t hvx_len = bufinfo->len; + + ble_gatts_hvx_params_t hvx_params = { + .handle = handle, + .type = hvx_type, + .offset = 0, + .p_len = &hvx_len, + .p_data = bufinfo->buf, + }; + + while (1) { + const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params); + if (err_code == NRF_SUCCESS) { + break; + } + // TX buffer is full + // We could wait for an event indicating the write is complete, but just retrying is easier. + if (err_code == NRF_ERROR_RESOURCES) { + RUN_BACKGROUND_TASKS; + continue; + } + + // Some real error has occurred. + mp_raise_OSError_msg_varg(translate("Failed to notify or indicate attribute value, err 0x%04x"), err_code); + } +} + +STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic) { + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); + common_hal_bleio_check_connected(conn_handle); + + // Set to NULL in event loop after event. + m_read_characteristic = characteristic; + + ble_drv_add_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic); + + const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code); + } + + while (m_read_characteristic != NULL) { + RUN_BACKGROUND_TASKS; + } + + ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic); +} + +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { + self->service = service; + self->uuid = uuid; + self->handle = BLE_GATT_HANDLE_INVALID; + self->props = props; + self->read_perm = read_perm; + self->write_perm = write_perm; + self->descriptor_list = mp_obj_new_list(0, NULL); + + const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; + if (max_length < 0 || max_length > max_length_max) { + mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), + max_length_max, fixed_length ? "True" : "False"); + } + self->max_length = max_length; + self->fixed_length = fixed_length; + + common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo); +} + +mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) { + return self->descriptor_list; +} + +bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) { + return self->service; +} + +mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) { + // Do GATT operations only if this characteristic has been added to a registered service. + if (self->handle != BLE_GATT_HANDLE_INVALID) { + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); + if (common_hal_bleio_service_get_is_remote(self->service)) { + // self->value is set by evt handler. + characteristic_gattc_read(self); + } else { + self->value = common_hal_bleio_gatts_read(self->handle, conn_handle); + } + } + + return self->value; +} + +void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { + if (self->fixed_length && bufinfo->len != self->max_length) { + mp_raise_ValueError(translate("Value length != required fixed length")); + } + if (bufinfo->len > self->max_length) { + mp_raise_ValueError(translate("Value length > max_length")); + } + + self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len); + + // Do GATT operations only if this characteristic has been added to a registered service. + if (self->handle != BLE_GATT_HANDLE_INVALID) { + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); + + if (common_hal_bleio_service_get_is_remote(self->service)) { + // Last argument is true if write-no-reponse desired. + common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, + (self->props & CHAR_PROP_WRITE_NO_RESPONSE)); + } else { + + bool sent = false; + uint16_t cccd = 0; + + const bool notify = self->props & CHAR_PROP_NOTIFY; + const bool indicate = self->props & CHAR_PROP_INDICATE; + if (notify | indicate) { + cccd = characteristic_get_cccd(self->cccd_handle, conn_handle); + } + + // It's possible that both notify and indicate are set. + if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) { + characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION); + sent = true; + } + if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) { + characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION); + sent = true; + } + + if (!sent) { + common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); + } + } + } +} + +bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) { + return self->uuid; +} + +bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self) { + return self->props; +} + +void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { + ble_uuid_t desc_uuid; + bleio_uuid_convert_to_nrf_ble_uuid(descriptor->uuid, &desc_uuid); + + ble_gatts_attr_md_t desc_attr_md = { + // Data passed is not in a permanent location and should be copied. + .vloc = BLE_GATTS_VLOC_STACK, + .vlen = !descriptor->fixed_length, + }; + + bleio_attribute_gatts_set_security_mode(&desc_attr_md.read_perm, descriptor->read_perm); + bleio_attribute_gatts_set_security_mode(&desc_attr_md.write_perm, descriptor->write_perm); + + mp_buffer_info_t desc_value_bufinfo; + mp_get_buffer_raise(descriptor->value, &desc_value_bufinfo, MP_BUFFER_READ); + + ble_gatts_attr_t desc_attr = { + .p_uuid = &desc_uuid, + .p_attr_md = &desc_attr_md, + .init_len = desc_value_bufinfo.len, + .p_value = desc_value_bufinfo.buf, + .init_offs = 0, + .max_len = descriptor->max_length, + }; + + uint32_t err_code = sd_ble_gatts_descriptor_add(self->handle, &desc_attr, &descriptor->handle); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to add descriptor, err 0x%04x"), err_code); + } + + mp_obj_list_append(self->descriptor_list, MP_OBJ_FROM_PTR(descriptor)); +} + +void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) { + if (self->cccd_handle == BLE_GATT_HANDLE_INVALID) { + mp_raise_ValueError(translate("No CCCD for this Characteristic")); + } + + if (!common_hal_bleio_service_get_is_remote(self->service)) { + mp_raise_ValueError(translate("Can't set CCCD on local Characteristic")); + } + + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); + common_hal_bleio_check_connected(conn_handle); + + uint16_t cccd_value = + (notify ? BLE_GATT_HVX_NOTIFICATION : 0) | + (indicate ? BLE_GATT_HVX_INDICATION : 0); + + ble_gattc_write_params_t write_params = { + .write_op = BLE_GATT_OP_WRITE_REQ, + .handle = self->cccd_handle, + .p_value = (uint8_t *) &cccd_value, + .len = 2, + }; + + while (1) { + uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); + if (err_code == NRF_SUCCESS) { + break; + } + + // Write with response will return NRF_ERROR_BUSY if the response has not been received. + // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending. + if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) { + // We could wait for an event indicating the write is complete, but just retrying is easier. + RUN_BACKGROUND_TASKS; + continue; + } + + // Some real error occurred. + mp_raise_OSError_msg_varg(translate("Failed to write CCCD, err 0x%04x"), err_code); + } + +} diff --git a/ports/nrf/common-hal/_bleio/Characteristic.h b/ports/nrf/common-hal/_bleio/Characteristic.h new file mode 100644 index 000000000..3a7691d07 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Characteristic.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_COMMON_HAL_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H + +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-module/_bleio/Characteristic.h" +#include "common-hal/_bleio/Service.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct { + mp_obj_base_t base; + // Will be MP_OBJ_NULL before being assigned to a Service. + bleio_service_obj_t *service; + bleio_uuid_obj_t *uuid; + mp_obj_t value; + uint16_t max_length; + bool fixed_length; + uint16_t handle; + bleio_characteristic_properties_t props; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; + mp_obj_list_t *descriptor_list; + uint16_t user_desc_handle; + uint16_t cccd_handle; + uint16_t sccd_handle; +} bleio_characteristic_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H diff --git a/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c new file mode 100644 index 000000000..95794feec --- /dev/null +++ b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c @@ -0,0 +1,155 @@ +/* + * 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 +#include + +#include "ble_drv.h" +#include "ble_gatts.h" +#include "nrf_nvic.h" + +#include "lib/utils/interrupt_char.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "tick.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "common-hal/_bleio/CharacteristicBuffer.h" + +STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) { + // Push all the data onto the ring buffer. + uint8_t is_nested_critical_region; + sd_nvic_critical_region_enter(&is_nested_critical_region); + for (size_t i = 0; i < len; i++) { + ringbuf_put(&self->ringbuf, data[i]); + } + sd_nvic_critical_region_exit(is_nested_critical_region); +} + +STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) { + bleio_characteristic_buffer_obj_t *self = (bleio_characteristic_buffer_obj_t *) param; + switch (ble_evt->header.evt_id) { + case BLE_GATTS_EVT_WRITE: { + // A client wrote to this server characteristic. + + 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) { + write_to_ringbuf(self, evt_write->data, evt_write->len); + } + break; + } + + case BLE_GATTC_EVT_HVX: { + // A remote service wrote to this characteristic. + + ble_gattc_evt_hvx_t* evt_hvx = &ble_evt->evt.gattc_evt.params.hvx; + // Must be a notification, and event handle must match the handle for my characteristic. + if (evt_hvx->type == BLE_GATT_HVX_NOTIFICATION && + evt_hvx->handle == self->characteristic->handle) { + write_to_ringbuf(self, evt_hvx->data, evt_hvx->len); + } + break; + } + } +} + +// 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. + // true means long-lived, so it won't be moved. + ringbuf_alloc(&self->ringbuf, buffer_size, true); + + ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self); + +} + +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) ) { + RUN_BACKGROUND_TASKS; + // Allow user to break out of a timeout with a KeyboardInterrupt. + if ( mp_hal_is_interrupted() ) { + return 0; + } + } + + // Copy received data. Lock out write interrupt handler while copying. + uint8_t is_nested_critical_region; + sd_nvic_critical_region_enter(&is_nested_critical_region); + + 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_nvic_critical_region_exit(is_nested_critical_region); + + return rx_bytes; +} + +uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) { + uint8_t is_nested_critical_region; + sd_nvic_critical_region_enter(&is_nested_critical_region); + uint16_t count = ringbuf_count(&self->ringbuf); + sd_nvic_critical_region_exit(is_nested_critical_region); + return count; +} + +void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) { + // prevent conflict with uart irq + uint8_t is_nested_critical_region; + sd_nvic_critical_region_enter(&is_nested_critical_region); + ringbuf_clear(&self->ringbuf); + sd_nvic_critical_region_exit(is_nested_critical_region); +} + +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) { + 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 new file mode 100644 index 000000000..f0949251c --- /dev/null +++ b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.h @@ -0,0 +1,43 @@ +/* + * 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_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_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; +} bleio_characteristic_buffer_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H diff --git a/ports/nrf/common-hal/_bleio/Descriptor.c b/ports/nrf/common-hal/_bleio/Descriptor.c new file mode 100644 index 000000000..137f004ba --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Descriptor.c @@ -0,0 +1,143 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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/runtime.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +static volatile bleio_descriptor_obj_t *m_read_descriptor; + +void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { + self->characteristic = characteristic; + self->uuid = uuid; + self->handle = BLE_GATT_HANDLE_INVALID; + self->read_perm = read_perm; + self->write_perm = write_perm; + + const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; + if (max_length < 0 || max_length > max_length_max) { + mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), + max_length_max, fixed_length ? "True" : "False"); + } + self->max_length = max_length; + self->fixed_length = fixed_length; + + common_hal_bleio_descriptor_set_value(self, initial_value_bufinfo); +} + +bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) { + return self->uuid; +} + +bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) { + return self->characteristic; +} + +STATIC void descriptor_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { + switch (ble_evt->header.evt_id) { + + // More events may be handled later, so keep this as a switch. + + case BLE_GATTC_EVT_READ_RSP: { + ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; + if (m_read_descriptor) { + m_read_descriptor->value = mp_obj_new_bytearray(response->len, response->data); + } + // Indicate to busy-wait loop that we've read the attribute value. + m_read_descriptor = NULL; + break; + } + + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled descriptor event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + +STATIC void descriptor_gattc_read(bleio_descriptor_obj_t *descriptor) { + const uint16_t conn_handle = + common_hal_bleio_device_get_conn_handle(descriptor->characteristic->service->device); + common_hal_bleio_check_connected(conn_handle); + + // Set to NULL in event loop after event. + m_read_descriptor = descriptor; + + ble_drv_add_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor); + + const uint32_t err_code = sd_ble_gattc_read(conn_handle, descriptor->handle, 0); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code); + } + + while (m_read_descriptor != NULL) { + MICROPY_VM_HOOK_LOOP; + } + + ble_drv_remove_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor); +} + +mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self) { + // Do GATT operations only if this descriptor has been registered + if (self->handle != BLE_GATT_HANDLE_INVALID) { + if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { + descriptor_gattc_read(self); + } else { + self->value = common_hal_bleio_gatts_read( + self->handle, common_hal_bleio_device_get_conn_handle(self->characteristic->service->device)); + } + } + + return self->value; +} + +void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) { + if (self->fixed_length && bufinfo->len != self->max_length) { + mp_raise_ValueError(translate("Value length != required fixed length")); + } + if (bufinfo->len > self->max_length) { + mp_raise_ValueError(translate("Value length > max_length")); + } + + self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len); + + // Do GATT operations only if this descriptor has been registered. + if (self->handle != BLE_GATT_HANDLE_INVALID) { + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->characteristic->service->device); + if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { + // false means WRITE_REQ, not write-no-response + common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false); + } else { + common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); + } + } + +} diff --git a/ports/nrf/common-hal/_bleio/Descriptor.h b/ports/nrf/common-hal/_bleio/Descriptor.h new file mode 100644 index 000000000..3adb0df18 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Descriptor.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_COMMON_HAL_BLEIO_DESCRIPTOR_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H + +#include "py/obj.h" + +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct { + mp_obj_base_t base; + // Will be MP_OBJ_NULL before being assigned to a Characteristic. + bleio_characteristic_obj_t *characteristic; + bleio_uuid_obj_t *uuid; + mp_obj_t value; + uint16_t max_length; + bool fixed_length; + uint16_t handle; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; +} bleio_descriptor_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H diff --git a/ports/nrf/common-hal/_bleio/Peripheral.c b/ports/nrf/common-hal/_bleio/Peripheral.c new file mode 100644 index 000000000..9b0f96d48 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Peripheral.c @@ -0,0 +1,361 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 +#include + +#include "ble.h" +#include "ble_drv.h" +#include "ble_hci.h" +#include "nrf_soc.h" +#include "py/gc.h" +#include "py/objlist.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Peripheral.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +#define BLE_ADV_LENGTH_FIELD_SIZE 1 +#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 +#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 + +static const ble_gap_sec_params_t pairing_sec_params = { + .bond = 1, + .mitm = 0, + .lesc = 0, + .keypress = 0, + .oob = 0, + .io_caps = BLE_GAP_IO_CAPS_NONE, + .min_key_size = 7, + .max_key_size = 16, + .kdist_own = { .enc = 1, .id = 1}, + .kdist_peer = { .enc = 1, .id = 1}, +}; + +STATIC void check_data_fit(size_t data_len) { + if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) { + mp_raise_ValueError(translate("Data too large for advertisement packet")); + } +} + +STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { + bleio_peripheral_obj_t *self = (bleio_peripheral_obj_t*)self_in; + + // For debugging. + // mp_printf(&mp_plat_print, "Peripheral event: 0x%04x\n", ble_evt->header.evt_id); + + switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_CONNECTED: { + // Central has connected. + ble_gap_evt_connected_t* connected = &ble_evt->evt.gap_evt.params.connected; + + self->conn_handle = ble_evt->evt.gap_evt.conn_handle; + + // See if connection interval set by Central is out of range. + // If so, negotiate our preferred range. + ble_gap_conn_params_t conn_params; + sd_ble_gap_ppcp_get(&conn_params); + if (conn_params.min_conn_interval < connected->conn_params.min_conn_interval || + conn_params.min_conn_interval > connected->conn_params.max_conn_interval) { + sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params); + } + break; + } + + case BLE_GAP_EVT_DISCONNECTED: + // Central has disconnected. + self->conn_handle = BLE_CONN_HANDLE_INVALID; + break; + + case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { + ble_gap_phys_t const phys = { + .rx_phys = BLE_GAP_PHY_AUTO, + .tx_phys = BLE_GAP_PHY_AUTO, + }; + sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys); + break; + } + + case BLE_GAP_EVT_ADV_SET_TERMINATED: + // TODO: Someday may handle timeouts or limit reached. + break; + + case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST: + // SoftDevice will respond to a length update request. + sd_ble_gap_data_length_update(self->conn_handle, NULL, NULL); + break; + + case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: { + // We only handle MTU of size BLE_GATT_ATT_MTU_DEFAULT. + sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT); + break; + } + + case BLE_GATTS_EVT_SYS_ATTR_MISSING: + sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { + ble_gap_sec_keyset_t keyset = { + .keys_own = { + .p_enc_key = &self->bonding_keys.own_enc, + .p_id_key = NULL, + .p_sign_key = NULL, + .p_pk = NULL + }, + + .keys_peer = { + .p_enc_key = &self->bonding_keys.peer_enc, + .p_id_key = &self->bonding_keys.peer_id, + .p_sign_key = NULL, + .p_pk = NULL + } + }; + + sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, + &pairing_sec_params, &keyset); + break; + } + + case BLE_GAP_EVT_LESC_DHKEY_REQUEST: + // TODO for LESC pairing: + // sd_ble_gap_lesc_dhkey_reply(...); + break; + + case BLE_GAP_EVT_AUTH_STATUS: { + // Pairing process completed + ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status; + if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) { + // TODO _ediv = bonding_keys->own_enc.master_id.ediv; + self->pair_status = PAIR_PAIRED; + } else { + self->pair_status = PAIR_NOT_PAIRED; + } + break; + } + + case BLE_GAP_EVT_SEC_INFO_REQUEST: { + // Peer asks for the stored keys. + // - load key and return if bonded previously. + // - Else return NULL --> Initiate key exchange + ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request; + (void) sec_info_request; + //if ( bond_load_keys(_role, sec_req->master_id.ediv, &bkeys) ) { + //sd_ble_gap_sec_info_reply(_conn_hdl, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL); + // + //_ediv = bkeys.own_enc.master_id.ediv; + // } else { + sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL); + // } + break; + } + + case BLE_GAP_EVT_CONN_SEC_UPDATE: { + ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec; + if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) { + // Security setup did not succeed: + // mode 0, level 0 means no access + // mode 1, level 1 means open link + // mode >=1 and/or level >=1 means encryption is set up + self->pair_status = PAIR_NOT_PAIRED; + } else { + //if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) { + if (true) { // TODO: no bonding yet + // Initialize system attributes fresh. + sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); + } + // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS. + self->ediv = self->bonding_keys.own_enc.master_id.ediv; + } + break; + } + + + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + +void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_t name) { + common_hal_bleio_adapter_set_enabled(true); + + self->service_list = mp_obj_new_list(0, NULL); + // Used only for discovery when acting as a client. + self->remote_service_list = mp_obj_new_list(0, NULL); + self->name = name; + + self->conn_handle = BLE_CONN_HANDLE_INVALID; + self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; + self->pair_status = PAIR_NOT_PAIRED; + + memset(&self->bonding_keys, 0, sizeof(self->bonding_keys)); +} + +void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio_service_obj_t *service) { + ble_uuid_t uuid; + bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); + + uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; + if (common_hal_bleio_service_get_is_secondary(service)) { + service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; + } + + const uint32_t err_code = sd_ble_gatts_service_add(service_type, &uuid, &service->handle); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to add service, err 0x%04x"), err_code); + } + + mp_obj_list_append(self->service_list, MP_OBJ_FROM_PTR(service)); +} + +mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self) { + return self->service_list; +} + +bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) { + return self->conn_handle != BLE_CONN_HANDLE_INVALID; +} + +mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self) { + return self->name; +} + +void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *self, bool connectable, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo) { + + // interval value has already been validated. + + if (connectable) { + ble_drv_add_event_handler(peripheral_on_ble_evt, self); + } + + common_hal_bleio_adapter_set_enabled(true); + + uint32_t err_code; + + GET_STR_DATA_LEN(self->name, name_data, name_len); + if (name_len > 0) { + // Set device name, and make it available to anyone. + ble_gap_conn_sec_mode_t sec_mode; + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); + err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to set device name, err 0x%04x"), err_code); + + } + } + + check_data_fit(advertising_data_bufinfo->len); + // The advertising data buffers must not move, because the SoftDevice depends on them. + // So make them long-lived. + self->advertising_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true); + memcpy(self->advertising_data, advertising_data_bufinfo->buf, advertising_data_bufinfo->len); + + check_data_fit(scan_response_data_bufinfo->len); + self->scan_response_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true); + memcpy(self->scan_response_data, scan_response_data_bufinfo->buf, scan_response_data_bufinfo->len); + + + ble_gap_adv_params_t adv_params = { + .interval = SEC_TO_UNITS(interval, UNIT_0_625_MS), + .properties.type = connectable ? BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED + : BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED, + .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED, + .filter_policy = BLE_GAP_ADV_FP_ANY, + .primary_phy = BLE_GAP_PHY_1MBPS, + }; + + common_hal_bleio_peripheral_stop_advertising(self); + + const ble_gap_adv_data_t ble_gap_adv_data = { + .adv_data.p_data = self->advertising_data, + .adv_data.len = advertising_data_bufinfo->len, + .scan_rsp_data.p_data = scan_response_data_bufinfo-> len > 0 ? self->scan_response_data : NULL, + .scan_rsp_data.len = scan_response_data_bufinfo->len, + }; + + err_code = sd_ble_gap_adv_set_configure(&self->adv_handle, &ble_gap_adv_data, &adv_params); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to configure advertising, err 0x%04x"), err_code); + } + + err_code = sd_ble_gap_adv_start(self->adv_handle, BLE_CONN_CFG_TAG_CUSTOM); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *self) { + if (self->adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) + return; + + const uint32_t err_code = sd_ble_gap_adv_stop(self->adv_handle); + + if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { + mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *self) { + sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); +} + +mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist) { + common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist); + // Convert to a tuple and then clear the list so the callee will take ownership. + mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_service_list->len, + self->remote_service_list->items); + mp_obj_list_clear(self->remote_service_list); + return services_tuple; +} + +void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *self) { + self->pair_status = PAIR_WAITING; + + uint32_t err_code = sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start pairing, error 0x%04x"), err_code); + } + + while (self->pair_status == PAIR_WAITING) { + RUN_BACKGROUND_TASKS; + } + + if (self->pair_status == PAIR_NOT_PAIRED) { + mp_raise_OSError_msg(translate("Failed to pair")); + } + + +} diff --git a/ports/nrf/common-hal/_bleio/Peripheral.h b/ports/nrf/common-hal/_bleio/Peripheral.h new file mode 100644 index 000000000..a4f62d288 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Peripheral.h @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_COMMON_HAL_BLEIO_PERIPHERAL_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H + +#include + +#include "ble.h" + +#include "py/obj.h" +#include "py/objlist.h" + +#include "common-hal/_bleio/__init__.h" +#include "shared-module/_bleio/Address.h" + +typedef enum { + PAIR_NOT_PAIRED, + PAIR_WAITING, + PAIR_PAIRED, +} pair_status_t; + +typedef struct { + mp_obj_base_t base; + mp_obj_t name; + volatile uint16_t conn_handle; + // Services provided by this peripheral. + mp_obj_list_t *service_list; + // Remote services discovered when this peripheral is acting as a client. + mp_obj_list_t *remote_service_list; + // The advertising data and scan response buffers are held by us, not by the SD, so we must + // maintain them and not change it. If we need to change the contents during advertising, + // there are tricks to get the SD to notice (see DevZone - TBS). + // EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing. + bonding_keys_t bonding_keys; + uint16_t ediv; + uint8_t* advertising_data; + uint8_t* scan_response_data; + uint8_t adv_handle; + pair_status_t pair_status; +} bleio_peripheral_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H diff --git a/ports/nrf/common-hal/_bleio/Scanner.c b/ports/nrf/common-hal/_bleio/Scanner.c new file mode 100644 index 000000000..ec73f7eb8 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Scanner.c @@ -0,0 +1,105 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 + +#include "ble_drv.h" +#include "ble_gap.h" +#include "py/mphal.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/Scanner.h" +#include "shared-module/_bleio/ScanEntry.h" + +static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN]; + +static ble_data_t m_scan_buffer = { + m_scan_buffer_data, + BLE_GAP_SCAN_BUFFER_MIN +}; + +STATIC void scanner_on_ble_evt(ble_evt_t *ble_evt, void *scanner_in) { + bleio_scanner_obj_t *scanner = (bleio_scanner_obj_t*)scanner_in; + ble_gap_evt_adv_report_t *report = &ble_evt->evt.gap_evt.params.adv_report; + + if (ble_evt->header.evt_id != BLE_GAP_EVT_ADV_REPORT) { + return; + } + + bleio_scanentry_obj_t *entry = m_new_obj(bleio_scanentry_obj_t); + entry->base.type = &bleio_scanentry_type; + entry->rssi = report->rssi; + + bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t); + address->base.type = &bleio_address_type; + common_hal_bleio_address_construct(MP_OBJ_TO_PTR(address), + report->peer_addr.addr, report->peer_addr.addr_type); + entry->address = address; + + entry->data = mp_obj_new_bytes(report->data.p_data, report->data.len); + + mp_obj_list_append(scanner->scan_entries, MP_OBJ_FROM_PTR(entry)); + + const uint32_t err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to continue scanning, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_scanner_construct(bleio_scanner_obj_t *self) { +} + +mp_obj_t common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_float_t timeout, mp_float_t interval, mp_float_t window) { + common_hal_bleio_adapter_set_enabled(true); + ble_drv_add_event_handler(scanner_on_ble_evt, self); + + ble_gap_scan_params_t scan_params = { + .interval = SEC_TO_UNITS(interval, UNIT_0_625_MS), + .window = SEC_TO_UNITS(window, UNIT_0_625_MS), + .scan_phys = BLE_GAP_PHY_1MBPS, + }; + + self->scan_entries = mp_obj_new_list(0, NULL); + + uint32_t err_code; + err_code = sd_ble_gap_scan_start(&scan_params, &m_scan_buffer); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start scanning, err 0x%04x"), err_code); + } + + mp_hal_delay_ms(timeout * 1000); + sd_ble_gap_scan_stop(); + + // Return list, and don't hang on to it, so it can be GC'd. + mp_obj_t entries = self->scan_entries; + self->scan_entries = MP_OBJ_NULL; + return entries; +} diff --git a/ports/nrf/common-hal/_bleio/Scanner.h b/ports/nrf/common-hal/_bleio/Scanner.h new file mode 100644 index 000000000..3768a52cb --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Scanner.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_COMMON_HAL_BLEIO_SCANNER_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SCANNER_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t scan_entries; + uint16_t interval; + uint16_t window; +} bleio_scanner_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SCANNER_H diff --git a/ports/nrf/common-hal/_bleio/Service.c b/ports/nrf/common-hal/_bleio/Service.c new file mode 100644 index 000000000..650cdc4ec --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Service.c @@ -0,0 +1,122 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 "ble_drv.h" +#include "ble.h" +#include "py/runtime.h" +#include "common-hal/_bleio/__init__.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Peripheral.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/Adapter.h" + +void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary) { + self->device = MP_OBJ_FROM_PTR(peripheral); + self->handle = 0xFFFF; + self->uuid = uuid; + self->characteristic_list = mp_obj_new_list(0, NULL); + self->is_remote = false; + self->is_secondary = is_secondary; +} + +bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { + return self->uuid; +} + +mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self) { + return self->characteristic_list; +} + +bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self) { + return self->is_remote; +} + +bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { + return self->is_secondary; +} + +void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic) { + ble_gatts_char_md_t char_md = { + .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0, + .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0, + .char_props.write_wo_resp = (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) ? 1 : 0, + .char_props.write = (characteristic->props & CHAR_PROP_WRITE) ? 1 : 0, + .char_props.notify = (characteristic->props & CHAR_PROP_NOTIFY) ? 1 : 0, + .char_props.indicate = (characteristic->props & CHAR_PROP_INDICATE) ? 1 : 0, + }; + + ble_gatts_attr_md_t cccd_md = { + .vloc = BLE_GATTS_VLOC_STACK, + }; + + ble_uuid_t char_uuid; + bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &char_uuid); + + ble_gatts_attr_md_t char_attr_md = { + .vloc = BLE_GATTS_VLOC_STACK, + .vlen = !characteristic->fixed_length, + }; + + if (char_md.char_props.notify || char_md.char_props.indicate) { + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); + // Make CCCD write permission match characteristic read permission. + bleio_attribute_gatts_set_security_mode(&cccd_md.write_perm, characteristic->read_perm); + + char_md.p_cccd_md = &cccd_md; + } + + bleio_attribute_gatts_set_security_mode(&char_attr_md.read_perm, characteristic->read_perm); + bleio_attribute_gatts_set_security_mode(&char_attr_md.write_perm, characteristic->write_perm); + + mp_buffer_info_t char_value_bufinfo; + mp_get_buffer_raise(characteristic->value, &char_value_bufinfo, MP_BUFFER_READ); + + ble_gatts_attr_t char_attr = { + .p_uuid = &char_uuid, + .p_attr_md = &char_attr_md, + .init_len = char_value_bufinfo.len, + .p_value = char_value_bufinfo.buf, + .init_offs = 0, + .max_len = characteristic->max_length, + }; + + ble_gatts_char_handles_t char_handles; + + uint32_t err_code; + err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &char_attr, &char_handles); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code); + } + + characteristic->user_desc_handle = char_handles.user_desc_handle; + characteristic->cccd_handle = char_handles.cccd_handle; + characteristic->sccd_handle = char_handles.sccd_handle; + characteristic->handle = char_handles.value_handle; + + mp_obj_list_append(self->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); +} diff --git a/ports/nrf/common-hal/_bleio/Service.h b/ports/nrf/common-hal/_bleio/Service.h new file mode 100644 index 000000000..a4614b9f5 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/Service.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_COMMON_HAL_BLEIO_SERVICE_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SERVICE_H + +#include "py/objlist.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct { + mp_obj_base_t base; + // Handle for this service. + uint16_t handle; + // True if created during discovery. + bool is_remote; + bool is_secondary; + bleio_uuid_obj_t *uuid; + // May be a Peripheral, Central, etc. + mp_obj_t device; + mp_obj_list_t *characteristic_list; + // Range of attribute handles of this service. + uint16_t start_handle; + uint16_t end_handle; +} bleio_service_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SERVICE_H diff --git a/ports/nrf/common-hal/_bleio/UUID.c b/ports/nrf/common-hal/_bleio/UUID.c new file mode 100644 index 000000000..0e65b7d58 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/UUID.c @@ -0,0 +1,99 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 + +#include "py/runtime.h" +#include "common-hal/_bleio/UUID.h" +#include "shared-bindings/_bleio/Adapter.h" + +#include "ble.h" +#include "ble_drv.h" +#include "nrf_error.h" + +// If uuid128 is NULL, this is a Bluetooth SIG 16-bit UUID. +// If uuid128 is not NULL, it's a 128-bit (16-byte) UUID, with bytes 12 and 13 zero'd out, where +// the 16-bit part goes. Those 16 bits are passed in uuid16. +void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, uint32_t uuid16, uint8_t uuid128[]) { + common_hal_bleio_adapter_set_enabled(true); + + self->nrf_ble_uuid.uuid = uuid16; + if (uuid128 == NULL) { + self->nrf_ble_uuid.type = BLE_UUID_TYPE_BLE; + } else { + ble_uuid128_t vs_uuid; + memcpy(vs_uuid.uuid128, uuid128, sizeof(vs_uuid.uuid128)); + + // Register this vendor-specific UUID. Bytes 12 and 13 will be zero. + const uint32_t err_code = sd_ble_uuid_vs_add(&vs_uuid, &self->nrf_ble_uuid.type); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to register Vendor-Specific UUID, err 0x%04x"), err_code); + } + } +} + +uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) { + return self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE ? 16 : 128; +} + +uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) { + return self->nrf_ble_uuid.uuid; +} + +// True if uuid128 has been successfully filled in. +bool common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) { + uint8_t length; + const uint32_t err_code = sd_ble_uuid_encode(&self->nrf_ble_uuid, &length, uuid128); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Could not decode ble_uuid, err 0x%04x"), err_code); + } + // If not 16 bytes, this is not a 128-bit UUID, so return. + return length == 16; +} + +// Returns 0 if this is a 16-bit UUID, otherwise returns a non-zero index +// into the 128-bit uuid registration table. +uint32_t common_hal_bleio_uuid_get_uuid128_reference(bleio_uuid_obj_t *self) { + return self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE ? 0 : self->nrf_ble_uuid.type; +} + + +void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_ble_uuid) { + if (nrf_ble_uuid->type == BLE_UUID_TYPE_UNKNOWN) { + mp_raise_RuntimeError(translate("Unexpected nrfx uuid type")); + } + self->nrf_ble_uuid.uuid = nrf_ble_uuid->uuid; + self->nrf_ble_uuid.type = nrf_ble_uuid->type; +} + +// Fill in a ble_uuid_t from my values. +void bleio_uuid_convert_to_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_ble_uuid) { + nrf_ble_uuid->uuid = self->nrf_ble_uuid.uuid; + nrf_ble_uuid->type = self->nrf_ble_uuid.type; +} diff --git a/ports/nrf/common-hal/_bleio/UUID.h b/ports/nrf/common-hal/_bleio/UUID.h new file mode 100644 index 000000000..7d3a6204e --- /dev/null +++ b/ports/nrf/common-hal/_bleio/UUID.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_COMMON_HAL_BLEIO_UUID_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_UUID_H + +#include "py/obj.h" + +#include "ble.h" + +typedef struct { + mp_obj_base_t base; + // Use the native way of storing UUID's: + // - ble_uuid_t.uuid is a 16-bit uuid. + // - ble_uuid_t.type is BLE_UUID_TYPE_BLE if it's a 16-bit Bluetooth SIG UUID. + // or is BLE_UUID_TYPE_VENDOR_BEGIN and higher, which indexes into a table of registered + // 128-bit UUIDs. + ble_uuid_t nrf_ble_uuid; +} bleio_uuid_obj_t; + +void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid); +void bleio_uuid_convert_to_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid); + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_UUID_H diff --git a/ports/nrf/common-hal/_bleio/__init__.c b/ports/nrf/common-hal/_bleio/__init__.c new file mode 100644 index 000000000..553044190 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/__init__.c @@ -0,0 +1,502 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Central.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Peripheral.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +#include "common-hal/_bleio/__init__.h" + +static volatile bool m_discovery_in_process; +static volatile bool m_discovery_successful; + +static bleio_service_obj_t *m_char_discovery_service; +static bleio_characteristic_obj_t *m_desc_discovery_characteristic; + +// Turn off BLE on a reset or reload. +void bleio_reset() { + if (common_hal_bleio_adapter_get_enabled()) { + common_hal_bleio_adapter_set_enabled(false); + } +} + +// The singleton _bleio.Adapter object, bound to _bleio.adapter +// It currently only has properties and no state +const super_adapter_obj_t common_hal_bleio_adapter_obj = { + .base = { + .type = &bleio_adapter_type, + }, +}; + +void common_hal_bleio_check_connected(uint16_t conn_handle) { + if (conn_handle == BLE_CONN_HANDLE_INVALID) { + mp_raise_OSError_msg(translate("Not connected")); + } +} + +uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { + if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { + return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; + } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { + return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; + } else { + return BLE_CONN_HANDLE_INVALID; + } +} + +mp_obj_list_t *common_hal_bleio_device_get_remote_service_list(mp_obj_t device) { + if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { + return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->remote_service_list; + } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { + return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->remote_service_list; + } else { + return NULL; + } +} + +// service_uuid may be NULL, to discover all services. +STATIC bool discover_next_services(mp_obj_t device, uint16_t start_handle, ble_uuid_t *service_uuid) { + m_discovery_successful = false; + m_discovery_in_process = true; + + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); + uint32_t err_code = sd_ble_gattc_primary_services_discover(conn_handle, start_handle, service_uuid); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg(translate("Failed to discover services")); + } + + // Wait for a discovery event. + while (m_discovery_in_process) { + MICROPY_VM_HOOK_LOOP; + } + return m_discovery_successful; +} + +STATIC bool discover_next_characteristics(mp_obj_t device, bleio_service_obj_t *service, uint16_t start_handle) { + m_char_discovery_service = service; + + ble_gattc_handle_range_t handle_range; + handle_range.start_handle = start_handle; + handle_range.end_handle = service->end_handle; + + m_discovery_successful = false; + m_discovery_in_process = true; + + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); + uint32_t err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range); + if (err_code != NRF_SUCCESS) { + return false; + } + + // Wait for a discovery event. + while (m_discovery_in_process) { + MICROPY_VM_HOOK_LOOP; + } + return m_discovery_successful; +} + +STATIC bool discover_next_descriptors(mp_obj_t device, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) { + m_desc_discovery_characteristic = characteristic; + + ble_gattc_handle_range_t handle_range; + handle_range.start_handle = start_handle; + handle_range.end_handle = end_handle; + + m_discovery_successful = false; + m_discovery_in_process = true; + + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); + uint32_t err_code = sd_ble_gattc_descriptors_discover(conn_handle, &handle_range); + if (err_code != NRF_SUCCESS) { + return false; + } + + // Wait for a discovery event. + while (m_discovery_in_process) { + MICROPY_VM_HOOK_LOOP; + } + return m_discovery_successful; +} + +STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, mp_obj_t device) { + for (size_t i = 0; i < response->count; ++i) { + ble_gattc_service_t *gattc_service = &response->services[i]; + + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + + // Initialize several fields at once. + common_hal_bleio_service_construct(service, device, NULL, false); + + service->is_remote = true; + service->start_handle = gattc_service->handle_range.start_handle; + service->end_handle = gattc_service->handle_range.end_handle; + service->handle = gattc_service->handle_range.start_handle; + + if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) { + // Known service UUID. + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid); + service->uuid = uuid; + } else { + // The discovery response contained a 128-bit UUID that has not yet been registered with the + // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. + // For now, just set the UUID to NULL. + service->uuid = NULL; + } + + mp_obj_list_append(common_hal_bleio_device_get_remote_service_list(device), service); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + m_discovery_in_process = false; +} + +STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, mp_obj_t device) { + for (size_t i = 0; i < response->count; ++i) { + ble_gattc_char_t *gattc_char = &response->chars[i]; + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + characteristic->base.type = &bleio_characteristic_type; + + bleio_uuid_obj_t *uuid = NULL; + + if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) { + // Known characteristic UUID. + uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid); + } else { + // The discovery response contained a 128-bit UUID that has not yet been registered with the + // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. + // For now, just leave the UUID as NULL. + } + + bleio_characteristic_properties_t props = + (gattc_char->char_props.broadcast ? CHAR_PROP_BROADCAST : 0) | + (gattc_char->char_props.indicate ? CHAR_PROP_INDICATE : 0) | + (gattc_char->char_props.notify ? CHAR_PROP_NOTIFY : 0) | + (gattc_char->char_props.read ? CHAR_PROP_READ : 0) | + (gattc_char->char_props.write ? CHAR_PROP_WRITE : 0) | + (gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0); + + // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler. + common_hal_bleio_characteristic_construct( + characteristic, m_char_discovery_service, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc + mp_obj_new_list(0, NULL)); + characteristic->handle = gattc_char->handle_value; + + mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + m_discovery_in_process = false; +} + +STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, mp_obj_t device) { + for (size_t i = 0; i < response->count; ++i) { + ble_gattc_desc_t *gattc_desc = &response->descs[i]; + + // Remember handles for certain well-known descriptors. + switch (gattc_desc->uuid.uuid) { + case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG: + m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle; + break; + + case BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG: + m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle; + break; + + case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC: + m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle; + break; + + default: + // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors, + // so ignore those. + // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors + break; + } + + bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); + descriptor->base.type = &bleio_descriptor_type; + + bleio_uuid_obj_t *uuid = NULL; + + if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) { + // Known descriptor UUID. + uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid); + } else { + // The discovery response contained a 128-bit UUID that has not yet been registered with the + // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. + // For now, just leave the UUID as NULL. + } + + common_hal_bleio_descriptor_construct( + descriptor, m_desc_discovery_characteristic, uuid, + SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + GATT_MAX_DATA_LENGTH, false, mp_const_empty_bytes); + descriptor->handle = gattc_desc->handle; + + mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor)); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + m_discovery_in_process = false; +} + +STATIC void discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t device) { + switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_DISCONNECTED: + m_discovery_successful = false; + m_discovery_in_process = false; + break; + + case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: + on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, device); + break; + + case BLE_GATTC_EVT_CHAR_DISC_RSP: + on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, device); + break; + + case BLE_GATTC_EVT_DESC_DISC_RSP: + on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, device); + break; + + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + + +void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) { + mp_obj_list_t *remote_services_list = common_hal_bleio_device_get_remote_service_list(device); + + ble_drv_add_event_handler(discovery_on_ble_evt, device); + + // Start over with an empty list. + mp_obj_list_clear(MP_OBJ_FROM_PTR(common_hal_bleio_device_get_remote_service_list(device))); + + if (service_uuids_whitelist == mp_const_none) { + // List of service UUID's not given, so discover all available services. + + uint16_t next_service_start_handle = BLE_GATT_HANDLE_START; + + while (discover_next_services(device, next_service_start_handle, MP_OBJ_NULL)) { + // discover_next_services() appends to remote_services_list. + + // Get the most recently discovered service, and then ask for services + // whose handles start after the last attribute handle inside that service. + const bleio_service_obj_t *service = + MP_OBJ_TO_PTR(remote_services_list->items[remote_services_list->len - 1]); + next_service_start_handle = service->end_handle + 1; + } + } else { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf); + mp_obj_t uuid_obj; + while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("non-UUID found in service_uuids_whitelist")); + } + bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); + + ble_uuid_t nrf_uuid; + bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid); + + // Service might or might not be discovered; that's ok. Caller has to check + // Central.remote_services to find out. + // We only need to call this once for each service to discover. + discover_next_services(device, BLE_GATT_HANDLE_START, &nrf_uuid); + } + } + + + for (size_t service_idx = 0; service_idx < remote_services_list->len; ++service_idx) { + bleio_service_obj_t *service = MP_OBJ_TO_PTR(remote_services_list->items[service_idx]); + + // Skip the service if it had an unknown (unregistered) UUID. + if (service->uuid == NULL) { + continue; + } + + uint16_t next_char_start_handle = service->start_handle; + + // Stop when we go past the end of the range of handles for this service or + // discovery call returns nothing. + // discover_next_characteristics() appends to the characteristic_list. + while (next_char_start_handle <= service->end_handle && + discover_next_characteristics(device, service, next_char_start_handle)) { + + + // Get the most recently discovered characteristic, and then ask for characteristics + // whose handles start after the last attribute handle inside that characteristic. + const bleio_characteristic_obj_t *characteristic = + MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]); + next_char_start_handle = characteristic->handle + 1; + } + + // Got characteristics for this service. Now discover descriptors for each characteristic. + size_t char_list_len = service->characteristic_list->len; + for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) { + bleio_characteristic_obj_t *characteristic = + MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]); + const bool last_characteristic = char_idx == char_list_len - 1; + bleio_characteristic_obj_t *next_characteristic = last_characteristic + ? NULL + : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]); + + // Skip the characteristic if it had an unknown (unregistered) UUID. + if (characteristic->uuid == NULL) { + continue; + } + + uint16_t next_desc_start_handle = characteristic->handle + 1; + + // Don't run past the end of this service or the beginning of the next characteristic. + uint16_t next_desc_end_handle = next_characteristic == NULL + ? service->end_handle + : next_characteristic->handle - 1; + + // Stop when we go past the end of the range of handles for this service or + // discovery call returns nothing. + // discover_next_descriptors() appends to the descriptor_list. + while (next_desc_start_handle <= service->end_handle && + next_desc_start_handle < next_desc_end_handle && + discover_next_descriptors(device, characteristic, + next_desc_start_handle, next_desc_end_handle)) { + + // Get the most recently discovered descriptor, and then ask for descriptors + // whose handles start after that descriptor's handle. + const bleio_descriptor_obj_t *descriptor = + MP_OBJ_TO_PTR(characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1]); + next_desc_start_handle = descriptor->handle + 1; + } + } + } + + // This event handler is no longer needed. + ble_drv_remove_event_handler(discovery_on_ble_evt, device); + +} + +// GATTS read of a Characteristic or Descriptor. +mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle) { + // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because + // we can still read and write the local value. + + mp_buffer_info_t bufinfo; + ble_gatts_value_t gatts_value = { + .p_value = NULL, + .len = 0, + }; + + // Read once to find out what size buffer we need, then read again to fill buffer. + + mp_obj_t value = mp_const_none; + uint32_t err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value); + if (err_code == NRF_SUCCESS) { + value = mp_obj_new_bytearray_of_zeros(gatts_value.len); + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_WRITE); + gatts_value.p_value = bufinfo.buf; + + // Read again, with the correct size of buffer. + err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value); + } + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code); + } + + return value; +} + +void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { + // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because + // we can still read and write the local value. + + ble_gatts_value_t gatts_value = { + .p_value = bufinfo->buf, + .len = bufinfo->len, + }; + + const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { + common_hal_bleio_check_connected(conn_handle); + + ble_gattc_write_params_t write_params = { + .write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ, + .handle = handle, + .p_value = bufinfo->buf, + .len = bufinfo->len, + }; + + while (1) { + uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); + if (err_code == NRF_SUCCESS) { + break; + } + + // Write with response will return NRF_ERROR_BUSY if the response has not been received. + // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending. + if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) { + // We could wait for an event indicating the write is complete, but just retrying is easier. + MICROPY_VM_HOOK_LOOP; + continue; + } + + // Some real error occurred. + mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code); + } + +} diff --git a/ports/nrf/common-hal/_bleio/__init__.h b/ports/nrf/common-hal/_bleio/__init__.h new file mode 100644 index 000000000..cf1a06945 --- /dev/null +++ b/ports/nrf/common-hal/_bleio/__init__.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_COMMON_HAL_BLEIO_INIT_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H + +void bleio_reset(void); + +typedef struct { + ble_gap_enc_key_t own_enc; + ble_gap_enc_key_t peer_enc; + ble_gap_id_key_t peer_id; +} bonding_keys_t; + +// We assume variable length data. +// 20 bytes max (23 - 3). +#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3) + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H diff --git a/ports/nrf/common-hal/bleio/Adapter.c b/ports/nrf/common-hal/bleio/Adapter.c deleted file mode 100644 index 74bee9e56..000000000 --- a/ports/nrf/common-hal/bleio/Adapter.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2016 Glenn Ruben Bakke - * Copyright (c) 2018 Artur Pacholec - * - * 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 -#include - -#include "ble.h" -#include "ble_drv.h" -#include "nrfx_power.h" -#include "nrf_nvic.h" -#include "nrf_sdm.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "supervisor/usb.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Address.h" - -#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) -#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) -#define BLE_SLAVE_LATENCY 0 -#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) - -STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { - mp_raise_msg_varg(&mp_type_AssertionError, - translate("Soft device assert, id: 0x%08lX, pc: 0x%08lX"), id, pc); -} - -STATIC uint32_t ble_stack_enable(void) { - nrf_clock_lf_cfg_t clock_config = { -#if BOARD_HAS_32KHZ_XTAL - .source = NRF_CLOCK_LF_SRC_XTAL, - .rc_ctiv = 0, - .rc_temp_ctiv = 0, - .accuracy = NRF_CLOCK_LF_ACCURACY_20_PPM, -#else - .source = NRF_CLOCK_LF_SRC_RC, - .rc_ctiv = 16, - .rc_temp_ctiv = 2, - .accuracy = NRF_CLOCK_LF_ACCURACY_250_PPM, -#endif - }; - - uint32_t err_code = sd_softdevice_enable(&clock_config, softdevice_assert_handler); - if (err_code != NRF_SUCCESS) { - return err_code; - } - - err_code = sd_nvic_EnableIRQ(SD_EVT_IRQn); - if (err_code != NRF_SUCCESS) { - return err_code; - } - - // Start with no event handlers, etc. - ble_drv_reset(); - - uint32_t app_ram_start; - app_ram_start = 0x20004000; - - ble_cfg_t ble_conf; - ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM; - ble_conf.conn_cfg.params.gap_conn_cfg.conn_count = BLE_GAP_CONN_COUNT_DEFAULT; - ble_conf.conn_cfg.params.gap_conn_cfg.event_length = BLE_GAP_EVENT_LENGTH_DEFAULT; - err_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_conf, app_ram_start); - if (err_code != NRF_SUCCESS) - return err_code; - - memset(&ble_conf, 0, sizeof(ble_conf)); - ble_conf.gap_cfg.role_count_cfg.periph_role_count = 1; - ble_conf.gap_cfg.role_count_cfg.central_role_count = 1; - err_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_conf, app_ram_start); - if (err_code != NRF_SUCCESS) - return err_code; - - memset(&ble_conf, 0, sizeof(ble_conf)); - ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM; - ble_conf.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = MAX_TX_IN_PROGRESS; - err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &ble_conf, app_ram_start); - if (err_code != NRF_SUCCESS) - return err_code; - - err_code = sd_ble_enable(&app_ram_start); - if (err_code != NRF_SUCCESS) - return err_code; - - ble_gap_conn_params_t gap_conn_params = { - .min_conn_interval = BLE_MIN_CONN_INTERVAL, - .max_conn_interval = BLE_MAX_CONN_INTERVAL, - .slave_latency = BLE_SLAVE_LATENCY, - .conn_sup_timeout = BLE_CONN_SUP_TIMEOUT, - }; - err_code = sd_ble_gap_ppcp_set(&gap_conn_params); - if (err_code != NRF_SUCCESS) { - return err_code; - } - - err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_UNKNOWN); - return err_code; -} - -void common_hal_bleio_adapter_set_enabled(bool enabled) { - const bool is_enabled = common_hal_bleio_adapter_get_enabled(); - - // Don't enable or disable twice - if ((is_enabled && enabled) || (!is_enabled && !enabled)) { - return; - } - - uint32_t err_code; - if (enabled) { - // The SD takes over the POWER module and will fail if the module is already in use. - // Occurs when USB is initialized previously - nrfx_power_uninit(); - - err_code = ble_stack_enable(); - - // Re-init USB hardware - init_usb_hardware(); - } else { - err_code = sd_softdevice_disable(); - - // Re-init USB hardware - init_usb_hardware(); - } - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to change softdevice state")); - } -} - -bool common_hal_bleio_adapter_get_enabled(void) { - uint8_t is_enabled; - - const uint32_t err_code = sd_softdevice_is_enabled(&is_enabled); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to get softdevice state")); - } - - return is_enabled; -} - -void get_address(ble_gap_addr_t *address) { - uint32_t err_code; - - common_hal_bleio_adapter_set_enabled(true); - err_code = sd_ble_gap_addr_get(address); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to get local address")); - } -} - -bleio_address_obj_t *common_hal_bleio_adapter_get_address(void) { - common_hal_bleio_adapter_set_enabled(true); - - ble_gap_addr_t local_address; - get_address(&local_address); - - bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t); - address->base.type = &bleio_address_type; - - common_hal_bleio_address_construct(address, local_address.addr, local_address.addr_type); - return address; -} - -mp_obj_t common_hal_bleio_adapter_get_default_name(void) { - common_hal_bleio_adapter_set_enabled(true); - - ble_gap_addr_t local_address; - get_address(&local_address); - - char name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0 }; - - name[sizeof(name) - 4] = nibble_to_hex_lower[local_address.addr[1] >> 4 & 0xf]; - name[sizeof(name) - 3] = nibble_to_hex_lower[local_address.addr[1] & 0xf]; - name[sizeof(name) - 2] = nibble_to_hex_lower[local_address.addr[0] >> 4 & 0xf]; - name[sizeof(name) - 1] = nibble_to_hex_lower[local_address.addr[0] & 0xf]; - - return mp_obj_new_str(name, sizeof(name)); -} diff --git a/ports/nrf/common-hal/bleio/Adapter.h b/ports/nrf/common-hal/bleio/Adapter.h deleted file mode 100644 index 5dcc62540..000000000 --- a/ports/nrf/common-hal/bleio/Adapter.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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_COMMON_HAL_BLEIO_ADAPTER_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ADAPTER_H - -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; -} super_adapter_obj_t; - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ADAPTER_H diff --git a/ports/nrf/common-hal/bleio/Attribute.c b/ports/nrf/common-hal/bleio/Attribute.c deleted file mode 100644 index fd1e7538d..000000000 --- a/ports/nrf/common-hal/bleio/Attribute.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 "shared-bindings/bleio/Attribute.h" - -// Convert a bleio security mode to a ble_gap_conn_sec_mode_t setting. -void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode) { - switch (security_mode) { - case SECURITY_MODE_NO_ACCESS: - BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(perm); - break; - - case SECURITY_MODE_OPEN: - BLE_GAP_CONN_SEC_MODE_SET_OPEN(perm); - break; - - case SECURITY_MODE_ENC_NO_MITM: - BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(perm); - break; - - case SECURITY_MODE_ENC_WITH_MITM: - BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(perm); - break; - - case SECURITY_MODE_LESC_ENC_WITH_MITM: - BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(perm); - break; - - case SECURITY_MODE_SIGNED_NO_MITM: - BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(perm); - break; - - case SECURITY_MODE_SIGNED_WITH_MITM: - BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(perm); - break; - } -} diff --git a/ports/nrf/common-hal/bleio/Attribute.h b/ports/nrf/common-hal/bleio/Attribute.h deleted file mode 100644 index cf84f01dc..000000000 --- a/ports/nrf/common-hal/bleio/Attribute.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 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_COMMON_HAL_BLEIO_ATTRIBUTE_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H - -#include "shared-module/bleio/Attribute.h" - -extern void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode); - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H diff --git a/ports/nrf/common-hal/bleio/Central.c b/ports/nrf/common-hal/bleio/Central.c deleted file mode 100644 index 0f8432b73..000000000 --- a/ports/nrf/common-hal/bleio/Central.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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 -#include - -#include "ble.h" -#include "ble_drv.h" -#include "ble_hci.h" -#include "nrf_soc.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/__init__.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Central.h" - -STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) { - bleio_central_obj_t *central = (bleio_central_obj_t*)central_in; - - switch (ble_evt->header.evt_id) { - case BLE_GAP_EVT_CONNECTED: - central->conn_handle = ble_evt->evt.gap_evt.conn_handle; - central->waiting_to_connect = false; - break; - - case BLE_GAP_EVT_TIMEOUT: - // Handle will be invalid. - central->waiting_to_connect = false; - break; - - case BLE_GAP_EVT_DISCONNECTED: - central->conn_handle = BLE_CONN_HANDLE_INVALID; - break; - - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: - sd_ble_gap_sec_params_reply(central->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); - break; - - case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { - ble_gap_evt_conn_param_update_request_t *request = - &ble_evt->evt.gap_evt.params.conn_param_update_request; - sd_ble_gap_conn_param_update(central->conn_handle, &request->conn_params); - break; - } - default: - // For debugging. - // mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id); - break; - } -} - -void common_hal_bleio_central_construct(bleio_central_obj_t *self) { - common_hal_bleio_adapter_set_enabled(true); - - self->remote_service_list = mp_obj_new_list(0, NULL); - self->conn_handle = BLE_CONN_HANDLE_INVALID; -} - -void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { - common_hal_bleio_adapter_set_enabled(true); - ble_drv_add_event_handler(central_on_ble_evt, self); - - ble_gap_addr_t addr; - - addr.addr_type = address->type; - mp_buffer_info_t address_buf_info; - mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ); - memcpy(addr.addr, (uint8_t *) address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); - - ble_gap_scan_params_t scan_params = { - .interval = MSEC_TO_UNITS(100, UNIT_0_625_MS), - .window = MSEC_TO_UNITS(100, UNIT_0_625_MS), - .scan_phys = BLE_GAP_PHY_1MBPS, - // timeout of 0 means no timeout - .timeout = SEC_TO_UNITS(timeout, UNIT_10_MS), - }; - - ble_gap_conn_params_t conn_params = { - .conn_sup_timeout = MSEC_TO_UNITS(4000, UNIT_10_MS), - .min_conn_interval = MSEC_TO_UNITS(15, UNIT_1_25_MS), - .max_conn_interval = MSEC_TO_UNITS(300, UNIT_1_25_MS), - .slave_latency = 0, // number of conn events - }; - - self->waiting_to_connect = true; - - uint32_t err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_CUSTOM); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to start connecting, error 0x%04x"), err_code); - } - - while (self->waiting_to_connect) { - RUN_BACKGROUND_TASKS; - } - - if (self->conn_handle == BLE_CONN_HANDLE_INVALID) { - mp_raise_OSError_msg(translate("Failed to connect: timeout")); - } -} - -void common_hal_bleio_central_disconnect(bleio_central_obj_t *self) { - sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); -} - -bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self) { - return self->conn_handle != BLE_CONN_HANDLE_INVALID; -} - -mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist) { - common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist); - // Convert to a tuple and then clear the list so the callee will take ownership. - mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_service_list->len, - self->remote_service_list->items); - mp_obj_list_clear(self->remote_service_list); - return services_tuple; -} - -mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self) { - return self->remote_service_list; -} diff --git a/ports/nrf/common-hal/bleio/Central.h b/ports/nrf/common-hal/bleio/Central.h deleted file mode 100644 index 2e00aa35e..000000000 --- a/ports/nrf/common-hal/bleio/Central.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_MODULE_BLEIO_CENTRAL_H -#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H - -#include - -#include "py/objlist.h" -#include "shared-module/bleio/Address.h" - -typedef struct { - mp_obj_base_t base; - volatile bool waiting_to_connect; - volatile uint16_t conn_handle; - // Services discovered after connecting to a remote peripheral. - mp_obj_list_t *remote_service_list; -} bleio_central_obj_t; - -#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c deleted file mode 100644 index 2596f17d8..000000000 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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/runtime.h" - -#include "shared-bindings/bleio/__init__.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/Service.h" - -static volatile bleio_characteristic_obj_t *m_read_characteristic; - -STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) { - uint16_t cccd; - ble_gatts_value_t value = { - .p_value = (uint8_t*) &cccd, - .len = 2, - }; - - const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, cccd_handle, &value); - - if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) { - // CCCD is not set, so say that neither Notify nor Indicate is enabled. - cccd = 0; - } else if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to read CCCD value, err 0x%04x"), err_code); - } - - return cccd; -} - -STATIC void characteristic_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { - switch (ble_evt->header.evt_id) { - - // More events may be handled later, so keep this as a switch. - - case BLE_GATTC_EVT_READ_RSP: { - ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; - if (m_read_characteristic) { - m_read_characteristic->value = mp_obj_new_bytearray(response->len, response->data); - } - // Indicate to busy-wait loop that we've read the attribute value. - m_read_characteristic = NULL; - break; - } - - default: - // For debugging. - // mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id); - break; - } -} - -STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) { - uint16_t hvx_len = bufinfo->len; - - ble_gatts_hvx_params_t hvx_params = { - .handle = handle, - .type = hvx_type, - .offset = 0, - .p_len = &hvx_len, - .p_data = bufinfo->buf, - }; - - while (1) { - const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params); - if (err_code == NRF_SUCCESS) { - break; - } - // TX buffer is full - // We could wait for an event indicating the write is complete, but just retrying is easier. - if (err_code == NRF_ERROR_RESOURCES) { - RUN_BACKGROUND_TASKS; - continue; - } - - // Some real error has occurred. - mp_raise_OSError_msg_varg(translate("Failed to notify or indicate attribute value, err 0x%04x"), err_code); - } -} - -STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic) { - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); - common_hal_bleio_check_connected(conn_handle); - - // Set to NULL in event loop after event. - m_read_characteristic = characteristic; - - ble_drv_add_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic); - - const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code); - } - - while (m_read_characteristic != NULL) { - RUN_BACKGROUND_TASKS; - } - - ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic); -} - -void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { - self->service = service; - self->uuid = uuid; - self->handle = BLE_GATT_HANDLE_INVALID; - self->props = props; - self->read_perm = read_perm; - self->write_perm = write_perm; - self->descriptor_list = mp_obj_new_list(0, NULL); - - const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; - if (max_length < 0 || max_length > max_length_max) { - mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), - max_length_max, fixed_length ? "True" : "False"); - } - self->max_length = max_length; - self->fixed_length = fixed_length; - - common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo); -} - -mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) { - return self->descriptor_list; -} - -bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) { - return self->service; -} - -mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) { - // Do GATT operations only if this characteristic has been added to a registered service. - if (self->handle != BLE_GATT_HANDLE_INVALID) { - uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); - if (common_hal_bleio_service_get_is_remote(self->service)) { - // self->value is set by evt handler. - characteristic_gattc_read(self); - } else { - self->value = common_hal_bleio_gatts_read(self->handle, conn_handle); - } - } - - return self->value; -} - -void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - if (self->fixed_length && bufinfo->len != self->max_length) { - mp_raise_ValueError(translate("Value length != required fixed length")); - } - if (bufinfo->len > self->max_length) { - mp_raise_ValueError(translate("Value length > max_length")); - } - - self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len); - - // Do GATT operations only if this characteristic has been added to a registered service. - if (self->handle != BLE_GATT_HANDLE_INVALID) { - uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); - - if (common_hal_bleio_service_get_is_remote(self->service)) { - // Last argument is true if write-no-reponse desired. - common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, - (self->props & CHAR_PROP_WRITE_NO_RESPONSE)); - } else { - - bool sent = false; - uint16_t cccd = 0; - - const bool notify = self->props & CHAR_PROP_NOTIFY; - const bool indicate = self->props & CHAR_PROP_INDICATE; - if (notify | indicate) { - cccd = characteristic_get_cccd(self->cccd_handle, conn_handle); - } - - // It's possible that both notify and indicate are set. - if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) { - characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION); - sent = true; - } - if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) { - characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION); - sent = true; - } - - if (!sent) { - common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); - } - } - } -} - -bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) { - return self->uuid; -} - -bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self) { - return self->props; -} - -void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { - ble_uuid_t desc_uuid; - bleio_uuid_convert_to_nrf_ble_uuid(descriptor->uuid, &desc_uuid); - - ble_gatts_attr_md_t desc_attr_md = { - // Data passed is not in a permanent location and should be copied. - .vloc = BLE_GATTS_VLOC_STACK, - .vlen = !descriptor->fixed_length, - }; - - bleio_attribute_gatts_set_security_mode(&desc_attr_md.read_perm, descriptor->read_perm); - bleio_attribute_gatts_set_security_mode(&desc_attr_md.write_perm, descriptor->write_perm); - - mp_buffer_info_t desc_value_bufinfo; - mp_get_buffer_raise(descriptor->value, &desc_value_bufinfo, MP_BUFFER_READ); - - ble_gatts_attr_t desc_attr = { - .p_uuid = &desc_uuid, - .p_attr_md = &desc_attr_md, - .init_len = desc_value_bufinfo.len, - .p_value = desc_value_bufinfo.buf, - .init_offs = 0, - .max_len = descriptor->max_length, - }; - - uint32_t err_code = sd_ble_gatts_descriptor_add(self->handle, &desc_attr, &descriptor->handle); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to add descriptor, err 0x%04x"), err_code); - } - - mp_obj_list_append(self->descriptor_list, MP_OBJ_FROM_PTR(descriptor)); -} - -void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) { - if (self->cccd_handle == BLE_GATT_HANDLE_INVALID) { - mp_raise_ValueError(translate("No CCCD for this Characteristic")); - } - - if (!common_hal_bleio_service_get_is_remote(self->service)) { - mp_raise_ValueError(translate("Can't set CCCD on local Characteristic")); - } - - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); - common_hal_bleio_check_connected(conn_handle); - - uint16_t cccd_value = - (notify ? BLE_GATT_HVX_NOTIFICATION : 0) | - (indicate ? BLE_GATT_HVX_INDICATION : 0); - - ble_gattc_write_params_t write_params = { - .write_op = BLE_GATT_OP_WRITE_REQ, - .handle = self->cccd_handle, - .p_value = (uint8_t *) &cccd_value, - .len = 2, - }; - - while (1) { - uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); - if (err_code == NRF_SUCCESS) { - break; - } - - // Write with response will return NRF_ERROR_BUSY if the response has not been received. - // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending. - if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) { - // We could wait for an event indicating the write is complete, but just retrying is easier. - RUN_BACKGROUND_TASKS; - continue; - } - - // Some real error occurred. - mp_raise_OSError_msg_varg(translate("Failed to write CCCD, err 0x%04x"), err_code); - } - -} diff --git a/ports/nrf/common-hal/bleio/Characteristic.h b/ports/nrf/common-hal/bleio/Characteristic.h deleted file mode 100644 index fb6a4c6ce..000000000 --- a/ports/nrf/common-hal/bleio/Characteristic.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_COMMON_HAL_BLEIO_CHARACTERISTIC_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H - -#include "shared-bindings/bleio/Attribute.h" -#include "shared-module/bleio/Characteristic.h" -#include "common-hal/bleio/Service.h" -#include "common-hal/bleio/UUID.h" - -typedef struct { - mp_obj_base_t base; - // Will be MP_OBJ_NULL before being assigned to a Service. - bleio_service_obj_t *service; - bleio_uuid_obj_t *uuid; - mp_obj_t value; - uint16_t max_length; - bool fixed_length; - uint16_t handle; - bleio_characteristic_properties_t props; - bleio_attribute_security_mode_t read_perm; - bleio_attribute_security_mode_t write_perm; - mp_obj_list_t *descriptor_list; - uint16_t user_desc_handle; - uint16_t cccd_handle; - uint16_t sccd_handle; -} bleio_characteristic_obj_t; - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H diff --git a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c deleted file mode 100644 index 64ab5e8a7..000000000 --- a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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 -#include - -#include "ble_drv.h" -#include "ble_gatts.h" -#include "nrf_nvic.h" - -#include "lib/utils/interrupt_char.h" -#include "py/runtime.h" -#include "py/stream.h" - -#include "tick.h" - -#include "shared-bindings/bleio/__init__.h" -#include "common-hal/bleio/CharacteristicBuffer.h" - -STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) { - // Push all the data onto the ring buffer. - uint8_t is_nested_critical_region; - sd_nvic_critical_region_enter(&is_nested_critical_region); - for (size_t i = 0; i < len; i++) { - ringbuf_put(&self->ringbuf, data[i]); - } - sd_nvic_critical_region_exit(is_nested_critical_region); -} - -STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) { - bleio_characteristic_buffer_obj_t *self = (bleio_characteristic_buffer_obj_t *) param; - switch (ble_evt->header.evt_id) { - case BLE_GATTS_EVT_WRITE: { - // A client wrote to this server characteristic. - - 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) { - write_to_ringbuf(self, evt_write->data, evt_write->len); - } - break; - } - - case BLE_GATTC_EVT_HVX: { - // A remote service wrote to this characteristic. - - ble_gattc_evt_hvx_t* evt_hvx = &ble_evt->evt.gattc_evt.params.hvx; - // Must be a notification, and event handle must match the handle for my characteristic. - if (evt_hvx->type == BLE_GATT_HVX_NOTIFICATION && - evt_hvx->handle == self->characteristic->handle) { - write_to_ringbuf(self, evt_hvx->data, evt_hvx->len); - } - break; - } - } -} - -// 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. - // true means long-lived, so it won't be moved. - ringbuf_alloc(&self->ringbuf, buffer_size, true); - - ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self); - -} - -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) ) { - RUN_BACKGROUND_TASKS; - // Allow user to break out of a timeout with a KeyboardInterrupt. - if ( mp_hal_is_interrupted() ) { - return 0; - } - } - - // Copy received data. Lock out write interrupt handler while copying. - uint8_t is_nested_critical_region; - sd_nvic_critical_region_enter(&is_nested_critical_region); - - 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_nvic_critical_region_exit(is_nested_critical_region); - - return rx_bytes; -} - -uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) { - uint8_t is_nested_critical_region; - sd_nvic_critical_region_enter(&is_nested_critical_region); - uint16_t count = ringbuf_count(&self->ringbuf); - sd_nvic_critical_region_exit(is_nested_critical_region); - return count; -} - -void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) { - // prevent conflict with uart irq - uint8_t is_nested_critical_region; - sd_nvic_critical_region_enter(&is_nested_critical_region); - ringbuf_clear(&self->ringbuf); - sd_nvic_critical_region_exit(is_nested_critical_region); -} - -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) { - 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 deleted file mode 100644 index db8fd2fad..000000000 --- a/ports/nrf/common-hal/bleio/CharacteristicBuffer.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_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; -} bleio_characteristic_buffer_obj_t; - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTICBUFFER_H diff --git a/ports/nrf/common-hal/bleio/Descriptor.c b/ports/nrf/common-hal/bleio/Descriptor.c deleted file mode 100644 index 4a5974a06..000000000 --- a/ports/nrf/common-hal/bleio/Descriptor.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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/runtime.h" - -#include "shared-bindings/bleio/__init__.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -static volatile bleio_descriptor_obj_t *m_read_descriptor; - -void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { - self->characteristic = characteristic; - self->uuid = uuid; - self->handle = BLE_GATT_HANDLE_INVALID; - self->read_perm = read_perm; - self->write_perm = write_perm; - - const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; - if (max_length < 0 || max_length > max_length_max) { - mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), - max_length_max, fixed_length ? "True" : "False"); - } - self->max_length = max_length; - self->fixed_length = fixed_length; - - common_hal_bleio_descriptor_set_value(self, initial_value_bufinfo); -} - -bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) { - return self->uuid; -} - -bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) { - return self->characteristic; -} - -STATIC void descriptor_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { - switch (ble_evt->header.evt_id) { - - // More events may be handled later, so keep this as a switch. - - case BLE_GATTC_EVT_READ_RSP: { - ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; - if (m_read_descriptor) { - m_read_descriptor->value = mp_obj_new_bytearray(response->len, response->data); - } - // Indicate to busy-wait loop that we've read the attribute value. - m_read_descriptor = NULL; - break; - } - - default: - // For debugging. - // mp_printf(&mp_plat_print, "Unhandled descriptor event: 0x%04x\n", ble_evt->header.evt_id); - break; - } -} - -STATIC void descriptor_gattc_read(bleio_descriptor_obj_t *descriptor) { - const uint16_t conn_handle = - common_hal_bleio_device_get_conn_handle(descriptor->characteristic->service->device); - common_hal_bleio_check_connected(conn_handle); - - // Set to NULL in event loop after event. - m_read_descriptor = descriptor; - - ble_drv_add_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor); - - const uint32_t err_code = sd_ble_gattc_read(conn_handle, descriptor->handle, 0); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code); - } - - while (m_read_descriptor != NULL) { - MICROPY_VM_HOOK_LOOP; - } - - ble_drv_remove_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor); -} - -mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self) { - // Do GATT operations only if this descriptor has been registered - if (self->handle != BLE_GATT_HANDLE_INVALID) { - if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { - descriptor_gattc_read(self); - } else { - self->value = common_hal_bleio_gatts_read( - self->handle, common_hal_bleio_device_get_conn_handle(self->characteristic->service->device)); - } - } - - return self->value; -} - -void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) { - if (self->fixed_length && bufinfo->len != self->max_length) { - mp_raise_ValueError(translate("Value length != required fixed length")); - } - if (bufinfo->len > self->max_length) { - mp_raise_ValueError(translate("Value length > max_length")); - } - - self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len); - - // Do GATT operations only if this descriptor has been registered. - if (self->handle != BLE_GATT_HANDLE_INVALID) { - uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->characteristic->service->device); - if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { - // false means WRITE_REQ, not write-no-response - common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false); - } else { - common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); - } - } - -} diff --git a/ports/nrf/common-hal/bleio/Descriptor.h b/ports/nrf/common-hal/bleio/Descriptor.h deleted file mode 100644 index db1175146..000000000 --- a/ports/nrf/common-hal/bleio/Descriptor.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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_COMMON_HAL_BLEIO_DESCRIPTOR_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H - -#include "py/obj.h" - -#include "common-hal/bleio/Characteristic.h" -#include "common-hal/bleio/UUID.h" - -typedef struct { - mp_obj_base_t base; - // Will be MP_OBJ_NULL before being assigned to a Characteristic. - bleio_characteristic_obj_t *characteristic; - bleio_uuid_obj_t *uuid; - mp_obj_t value; - uint16_t max_length; - bool fixed_length; - uint16_t handle; - bleio_attribute_security_mode_t read_perm; - bleio_attribute_security_mode_t write_perm; -} bleio_descriptor_obj_t; - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H diff --git a/ports/nrf/common-hal/bleio/Peripheral.c b/ports/nrf/common-hal/bleio/Peripheral.c deleted file mode 100644 index 92dfc2da1..000000000 --- a/ports/nrf/common-hal/bleio/Peripheral.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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 -#include - -#include "ble.h" -#include "ble_drv.h" -#include "ble_hci.h" -#include "nrf_soc.h" -#include "py/gc.h" -#include "py/objlist.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/__init__.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Attribute.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Peripheral.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -#define BLE_ADV_LENGTH_FIELD_SIZE 1 -#define BLE_ADV_AD_TYPE_FIELD_SIZE 1 -#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 - -static const ble_gap_sec_params_t pairing_sec_params = { - .bond = 1, - .mitm = 0, - .lesc = 0, - .keypress = 0, - .oob = 0, - .io_caps = BLE_GAP_IO_CAPS_NONE, - .min_key_size = 7, - .max_key_size = 16, - .kdist_own = { .enc = 1, .id = 1}, - .kdist_peer = { .enc = 1, .id = 1}, -}; - -STATIC void check_data_fit(size_t data_len) { - if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) { - mp_raise_ValueError(translate("Data too large for advertisement packet")); - } -} - -STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { - bleio_peripheral_obj_t *self = (bleio_peripheral_obj_t*)self_in; - - // For debugging. - // mp_printf(&mp_plat_print, "Peripheral event: 0x%04x\n", ble_evt->header.evt_id); - - switch (ble_evt->header.evt_id) { - case BLE_GAP_EVT_CONNECTED: { - // Central has connected. - ble_gap_evt_connected_t* connected = &ble_evt->evt.gap_evt.params.connected; - - self->conn_handle = ble_evt->evt.gap_evt.conn_handle; - - // See if connection interval set by Central is out of range. - // If so, negotiate our preferred range. - ble_gap_conn_params_t conn_params; - sd_ble_gap_ppcp_get(&conn_params); - if (conn_params.min_conn_interval < connected->conn_params.min_conn_interval || - conn_params.min_conn_interval > connected->conn_params.max_conn_interval) { - sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params); - } - break; - } - - case BLE_GAP_EVT_DISCONNECTED: - // Central has disconnected. - self->conn_handle = BLE_CONN_HANDLE_INVALID; - break; - - case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { - ble_gap_phys_t const phys = { - .rx_phys = BLE_GAP_PHY_AUTO, - .tx_phys = BLE_GAP_PHY_AUTO, - }; - sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys); - break; - } - - case BLE_GAP_EVT_ADV_SET_TERMINATED: - // TODO: Someday may handle timeouts or limit reached. - break; - - case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST: - // SoftDevice will respond to a length update request. - sd_ble_gap_data_length_update(self->conn_handle, NULL, NULL); - break; - - case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: { - // We only handle MTU of size BLE_GATT_ATT_MTU_DEFAULT. - sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT); - break; - } - - case BLE_GATTS_EVT_SYS_ATTR_MISSING: - sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); - break; - - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { - ble_gap_sec_keyset_t keyset = { - .keys_own = { - .p_enc_key = &self->bonding_keys.own_enc, - .p_id_key = NULL, - .p_sign_key = NULL, - .p_pk = NULL - }, - - .keys_peer = { - .p_enc_key = &self->bonding_keys.peer_enc, - .p_id_key = &self->bonding_keys.peer_id, - .p_sign_key = NULL, - .p_pk = NULL - } - }; - - sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, - &pairing_sec_params, &keyset); - break; - } - - case BLE_GAP_EVT_LESC_DHKEY_REQUEST: - // TODO for LESC pairing: - // sd_ble_gap_lesc_dhkey_reply(...); - break; - - case BLE_GAP_EVT_AUTH_STATUS: { - // Pairing process completed - ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status; - if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) { - // TODO _ediv = bonding_keys->own_enc.master_id.ediv; - self->pair_status = PAIR_PAIRED; - } else { - self->pair_status = PAIR_NOT_PAIRED; - } - break; - } - - case BLE_GAP_EVT_SEC_INFO_REQUEST: { - // Peer asks for the stored keys. - // - load key and return if bonded previously. - // - Else return NULL --> Initiate key exchange - ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request; - (void) sec_info_request; - //if ( bond_load_keys(_role, sec_req->master_id.ediv, &bkeys) ) { - //sd_ble_gap_sec_info_reply(_conn_hdl, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL); - // - //_ediv = bkeys.own_enc.master_id.ediv; - // } else { - sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL); - // } - break; - } - - case BLE_GAP_EVT_CONN_SEC_UPDATE: { - ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec; - if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) { - // Security setup did not succeed: - // mode 0, level 0 means no access - // mode 1, level 1 means open link - // mode >=1 and/or level >=1 means encryption is set up - self->pair_status = PAIR_NOT_PAIRED; - } else { - //if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) { - if (true) { // TODO: no bonding yet - // Initialize system attributes fresh. - sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); - } - // Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS. - self->ediv = self->bonding_keys.own_enc.master_id.ediv; - } - break; - } - - - default: - // For debugging. - // mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id); - break; - } -} - -void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_t name) { - common_hal_bleio_adapter_set_enabled(true); - - self->service_list = mp_obj_new_list(0, NULL); - // Used only for discovery when acting as a client. - self->remote_service_list = mp_obj_new_list(0, NULL); - self->name = name; - - self->conn_handle = BLE_CONN_HANDLE_INVALID; - self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; - self->pair_status = PAIR_NOT_PAIRED; - - memset(&self->bonding_keys, 0, sizeof(self->bonding_keys)); -} - -void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio_service_obj_t *service) { - ble_uuid_t uuid; - bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); - - uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY; - if (common_hal_bleio_service_get_is_secondary(service)) { - service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; - } - - const uint32_t err_code = sd_ble_gatts_service_add(service_type, &uuid, &service->handle); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to add service, err 0x%04x"), err_code); - } - - mp_obj_list_append(self->service_list, MP_OBJ_FROM_PTR(service)); -} - -mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self) { - return self->service_list; -} - -bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) { - return self->conn_handle != BLE_CONN_HANDLE_INVALID; -} - -mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self) { - return self->name; -} - -void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *self, bool connectable, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo) { - - // interval value has already been validated. - - if (connectable) { - ble_drv_add_event_handler(peripheral_on_ble_evt, self); - } - - common_hal_bleio_adapter_set_enabled(true); - - uint32_t err_code; - - GET_STR_DATA_LEN(self->name, name_data, name_len); - if (name_len > 0) { - // Set device name, and make it available to anyone. - ble_gap_conn_sec_mode_t sec_mode; - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); - err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to set device name, err 0x%04x"), err_code); - - } - } - - check_data_fit(advertising_data_bufinfo->len); - // The advertising data buffers must not move, because the SoftDevice depends on them. - // So make them long-lived. - self->advertising_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true); - memcpy(self->advertising_data, advertising_data_bufinfo->buf, advertising_data_bufinfo->len); - - check_data_fit(scan_response_data_bufinfo->len); - self->scan_response_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true); - memcpy(self->scan_response_data, scan_response_data_bufinfo->buf, scan_response_data_bufinfo->len); - - - ble_gap_adv_params_t adv_params = { - .interval = SEC_TO_UNITS(interval, UNIT_0_625_MS), - .properties.type = connectable ? BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED - : BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED, - .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED, - .filter_policy = BLE_GAP_ADV_FP_ANY, - .primary_phy = BLE_GAP_PHY_1MBPS, - }; - - common_hal_bleio_peripheral_stop_advertising(self); - - const ble_gap_adv_data_t ble_gap_adv_data = { - .adv_data.p_data = self->advertising_data, - .adv_data.len = advertising_data_bufinfo->len, - .scan_rsp_data.p_data = scan_response_data_bufinfo-> len > 0 ? self->scan_response_data : NULL, - .scan_rsp_data.len = scan_response_data_bufinfo->len, - }; - - err_code = sd_ble_gap_adv_set_configure(&self->adv_handle, &ble_gap_adv_data, &adv_params); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to configure advertising, err 0x%04x"), err_code); - } - - err_code = sd_ble_gap_adv_start(self->adv_handle, BLE_CONN_CFG_TAG_CUSTOM); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code); - } -} - -void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *self) { - if (self->adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) - return; - - const uint32_t err_code = sd_ble_gap_adv_stop(self->adv_handle); - - if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { - mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code); - } -} - -void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *self) { - sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); -} - -mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist) { - common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist); - // Convert to a tuple and then clear the list so the callee will take ownership. - mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_service_list->len, - self->remote_service_list->items); - mp_obj_list_clear(self->remote_service_list); - return services_tuple; -} - -void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *self) { - self->pair_status = PAIR_WAITING; - - uint32_t err_code = sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to start pairing, error 0x%04x"), err_code); - } - - while (self->pair_status == PAIR_WAITING) { - RUN_BACKGROUND_TASKS; - } - - if (self->pair_status == PAIR_NOT_PAIRED) { - mp_raise_OSError_msg(translate("Failed to pair")); - } - - -} diff --git a/ports/nrf/common-hal/bleio/Peripheral.h b/ports/nrf/common-hal/bleio/Peripheral.h deleted file mode 100644 index 1620874ca..000000000 --- a/ports/nrf/common-hal/bleio/Peripheral.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_COMMON_HAL_BLEIO_PERIPHERAL_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H - -#include - -#include "ble.h" - -#include "py/obj.h" -#include "py/objlist.h" - -#include "common-hal/bleio/__init__.h" -#include "shared-module/bleio/Address.h" - -typedef enum { - PAIR_NOT_PAIRED, - PAIR_WAITING, - PAIR_PAIRED, -} pair_status_t; - -typedef struct { - mp_obj_base_t base; - mp_obj_t name; - volatile uint16_t conn_handle; - // Services provided by this peripheral. - mp_obj_list_t *service_list; - // Remote services discovered when this peripheral is acting as a client. - mp_obj_list_t *remote_service_list; - // The advertising data and scan response buffers are held by us, not by the SD, so we must - // maintain them and not change it. If we need to change the contents during advertising, - // there are tricks to get the SD to notice (see DevZone - TBS). - // EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing. - bonding_keys_t bonding_keys; - uint16_t ediv; - uint8_t* advertising_data; - uint8_t* scan_response_data; - uint8_t adv_handle; - pair_status_t pair_status; -} bleio_peripheral_obj_t; - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H diff --git a/ports/nrf/common-hal/bleio/Scanner.c b/ports/nrf/common-hal/bleio/Scanner.c deleted file mode 100644 index 57ce940ab..000000000 --- a/ports/nrf/common-hal/bleio/Scanner.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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 - -#include "ble_drv.h" -#include "ble_gap.h" -#include "py/mphal.h" -#include "py/objlist.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/ScanEntry.h" -#include "shared-bindings/bleio/Scanner.h" -#include "shared-module/bleio/ScanEntry.h" - -static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN]; - -static ble_data_t m_scan_buffer = { - m_scan_buffer_data, - BLE_GAP_SCAN_BUFFER_MIN -}; - -STATIC void scanner_on_ble_evt(ble_evt_t *ble_evt, void *scanner_in) { - bleio_scanner_obj_t *scanner = (bleio_scanner_obj_t*)scanner_in; - ble_gap_evt_adv_report_t *report = &ble_evt->evt.gap_evt.params.adv_report; - - if (ble_evt->header.evt_id != BLE_GAP_EVT_ADV_REPORT) { - return; - } - - bleio_scanentry_obj_t *entry = m_new_obj(bleio_scanentry_obj_t); - entry->base.type = &bleio_scanentry_type; - entry->rssi = report->rssi; - - bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t); - address->base.type = &bleio_address_type; - common_hal_bleio_address_construct(MP_OBJ_TO_PTR(address), - report->peer_addr.addr, report->peer_addr.addr_type); - entry->address = address; - - entry->data = mp_obj_new_bytes(report->data.p_data, report->data.len); - - mp_obj_list_append(scanner->scan_entries, MP_OBJ_FROM_PTR(entry)); - - const uint32_t err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to continue scanning, err 0x%04x"), err_code); - } -} - -void common_hal_bleio_scanner_construct(bleio_scanner_obj_t *self) { -} - -mp_obj_t common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_float_t timeout, mp_float_t interval, mp_float_t window) { - common_hal_bleio_adapter_set_enabled(true); - ble_drv_add_event_handler(scanner_on_ble_evt, self); - - ble_gap_scan_params_t scan_params = { - .interval = SEC_TO_UNITS(interval, UNIT_0_625_MS), - .window = SEC_TO_UNITS(window, UNIT_0_625_MS), - .scan_phys = BLE_GAP_PHY_1MBPS, - }; - - self->scan_entries = mp_obj_new_list(0, NULL); - - uint32_t err_code; - err_code = sd_ble_gap_scan_start(&scan_params, &m_scan_buffer); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to start scanning, err 0x%04x"), err_code); - } - - mp_hal_delay_ms(timeout * 1000); - sd_ble_gap_scan_stop(); - - // Return list, and don't hang on to it, so it can be GC'd. - mp_obj_t entries = self->scan_entries; - self->scan_entries = MP_OBJ_NULL; - return entries; -} diff --git a/ports/nrf/common-hal/bleio/Scanner.h b/ports/nrf/common-hal/bleio/Scanner.h deleted file mode 100644 index 3768a52cb..000000000 --- a/ports/nrf/common-hal/bleio/Scanner.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_COMMON_HAL_BLEIO_SCANNER_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SCANNER_H - -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - mp_obj_t scan_entries; - uint16_t interval; - uint16_t window; -} bleio_scanner_obj_t; - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SCANNER_H diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c deleted file mode 100644 index f69bbd89d..000000000 --- a/ports/nrf/common-hal/bleio/Service.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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 "ble_drv.h" -#include "ble.h" -#include "py/runtime.h" -#include "common-hal/bleio/__init__.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/Peripheral.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/Adapter.h" - -void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary) { - self->device = MP_OBJ_FROM_PTR(peripheral); - self->handle = 0xFFFF; - self->uuid = uuid; - self->characteristic_list = mp_obj_new_list(0, NULL); - self->is_remote = false; - self->is_secondary = is_secondary; -} - -bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { - return self->uuid; -} - -mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self) { - return self->characteristic_list; -} - -bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self) { - return self->is_remote; -} - -bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { - return self->is_secondary; -} - -void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic) { - ble_gatts_char_md_t char_md = { - .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0, - .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0, - .char_props.write_wo_resp = (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) ? 1 : 0, - .char_props.write = (characteristic->props & CHAR_PROP_WRITE) ? 1 : 0, - .char_props.notify = (characteristic->props & CHAR_PROP_NOTIFY) ? 1 : 0, - .char_props.indicate = (characteristic->props & CHAR_PROP_INDICATE) ? 1 : 0, - }; - - ble_gatts_attr_md_t cccd_md = { - .vloc = BLE_GATTS_VLOC_STACK, - }; - - ble_uuid_t char_uuid; - bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &char_uuid); - - ble_gatts_attr_md_t char_attr_md = { - .vloc = BLE_GATTS_VLOC_STACK, - .vlen = !characteristic->fixed_length, - }; - - if (char_md.char_props.notify || char_md.char_props.indicate) { - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); - // Make CCCD write permission match characteristic read permission. - bleio_attribute_gatts_set_security_mode(&cccd_md.write_perm, characteristic->read_perm); - - char_md.p_cccd_md = &cccd_md; - } - - bleio_attribute_gatts_set_security_mode(&char_attr_md.read_perm, characteristic->read_perm); - bleio_attribute_gatts_set_security_mode(&char_attr_md.write_perm, characteristic->write_perm); - - mp_buffer_info_t char_value_bufinfo; - mp_get_buffer_raise(characteristic->value, &char_value_bufinfo, MP_BUFFER_READ); - - ble_gatts_attr_t char_attr = { - .p_uuid = &char_uuid, - .p_attr_md = &char_attr_md, - .init_len = char_value_bufinfo.len, - .p_value = char_value_bufinfo.buf, - .init_offs = 0, - .max_len = characteristic->max_length, - }; - - ble_gatts_char_handles_t char_handles; - - uint32_t err_code; - err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &char_attr, &char_handles); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code); - } - - characteristic->user_desc_handle = char_handles.user_desc_handle; - characteristic->cccd_handle = char_handles.cccd_handle; - characteristic->sccd_handle = char_handles.sccd_handle; - characteristic->handle = char_handles.value_handle; - - mp_obj_list_append(self->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); -} diff --git a/ports/nrf/common-hal/bleio/Service.h b/ports/nrf/common-hal/bleio/Service.h deleted file mode 100644 index 03ac2bca8..000000000 --- a/ports/nrf/common-hal/bleio/Service.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_COMMON_HAL_BLEIO_SERVICE_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SERVICE_H - -#include "py/objlist.h" -#include "common-hal/bleio/UUID.h" - -typedef struct { - mp_obj_base_t base; - // Handle for this service. - uint16_t handle; - // True if created during discovery. - bool is_remote; - bool is_secondary; - bleio_uuid_obj_t *uuid; - // May be a Peripheral, Central, etc. - mp_obj_t device; - mp_obj_list_t *characteristic_list; - // Range of attribute handles of this service. - uint16_t start_handle; - uint16_t end_handle; -} bleio_service_obj_t; - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SERVICE_H diff --git a/ports/nrf/common-hal/bleio/UUID.c b/ports/nrf/common-hal/bleio/UUID.c deleted file mode 100644 index ebd6b6d1f..000000000 --- a/ports/nrf/common-hal/bleio/UUID.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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 - -#include "py/runtime.h" -#include "common-hal/bleio/UUID.h" -#include "shared-bindings/bleio/Adapter.h" - -#include "ble.h" -#include "ble_drv.h" -#include "nrf_error.h" - -// If uuid128 is NULL, this is a Bluetooth SIG 16-bit UUID. -// If uuid128 is not NULL, it's a 128-bit (16-byte) UUID, with bytes 12 and 13 zero'd out, where -// the 16-bit part goes. Those 16 bits are passed in uuid16. -void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, uint32_t uuid16, uint8_t uuid128[]) { - common_hal_bleio_adapter_set_enabled(true); - - self->nrf_ble_uuid.uuid = uuid16; - if (uuid128 == NULL) { - self->nrf_ble_uuid.type = BLE_UUID_TYPE_BLE; - } else { - ble_uuid128_t vs_uuid; - memcpy(vs_uuid.uuid128, uuid128, sizeof(vs_uuid.uuid128)); - - // Register this vendor-specific UUID. Bytes 12 and 13 will be zero. - const uint32_t err_code = sd_ble_uuid_vs_add(&vs_uuid, &self->nrf_ble_uuid.type); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to register Vendor-Specific UUID, err 0x%04x"), err_code); - } - } -} - -uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) { - return self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE ? 16 : 128; -} - -uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) { - return self->nrf_ble_uuid.uuid; -} - -// True if uuid128 has been successfully filled in. -bool common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) { - uint8_t length; - const uint32_t err_code = sd_ble_uuid_encode(&self->nrf_ble_uuid, &length, uuid128); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Could not decode ble_uuid, err 0x%04x"), err_code); - } - // If not 16 bytes, this is not a 128-bit UUID, so return. - return length == 16; -} - -// Returns 0 if this is a 16-bit UUID, otherwise returns a non-zero index -// into the 128-bit uuid registration table. -uint32_t common_hal_bleio_uuid_get_uuid128_reference(bleio_uuid_obj_t *self) { - return self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE ? 0 : self->nrf_ble_uuid.type; -} - - -void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_ble_uuid) { - if (nrf_ble_uuid->type == BLE_UUID_TYPE_UNKNOWN) { - mp_raise_RuntimeError(translate("Unexpected nrfx uuid type")); - } - self->nrf_ble_uuid.uuid = nrf_ble_uuid->uuid; - self->nrf_ble_uuid.type = nrf_ble_uuid->type; -} - -// Fill in a ble_uuid_t from my values. -void bleio_uuid_convert_to_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_ble_uuid) { - nrf_ble_uuid->uuid = self->nrf_ble_uuid.uuid; - nrf_ble_uuid->type = self->nrf_ble_uuid.type; -} diff --git a/ports/nrf/common-hal/bleio/UUID.h b/ports/nrf/common-hal/bleio/UUID.h deleted file mode 100644 index 7d3a6204e..000000000 --- a/ports/nrf/common-hal/bleio/UUID.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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_COMMON_HAL_BLEIO_UUID_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_UUID_H - -#include "py/obj.h" - -#include "ble.h" - -typedef struct { - mp_obj_base_t base; - // Use the native way of storing UUID's: - // - ble_uuid_t.uuid is a 16-bit uuid. - // - ble_uuid_t.type is BLE_UUID_TYPE_BLE if it's a 16-bit Bluetooth SIG UUID. - // or is BLE_UUID_TYPE_VENDOR_BEGIN and higher, which indexes into a table of registered - // 128-bit UUIDs. - ble_uuid_t nrf_ble_uuid; -} bleio_uuid_obj_t; - -void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid); -void bleio_uuid_convert_to_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_uuid); - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_UUID_H diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c deleted file mode 100644 index 4a7597e37..000000000 --- a/ports/nrf/common-hal/bleio/__init__.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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/runtime.h" -#include "shared-bindings/bleio/__init__.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Central.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/Peripheral.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -#include "common-hal/bleio/__init__.h" - -static volatile bool m_discovery_in_process; -static volatile bool m_discovery_successful; - -static bleio_service_obj_t *m_char_discovery_service; -static bleio_characteristic_obj_t *m_desc_discovery_characteristic; - -// Turn off BLE on a reset or reload. -void bleio_reset() { - if (common_hal_bleio_adapter_get_enabled()) { - common_hal_bleio_adapter_set_enabled(false); - } -} - -// The singleton bleio.Adapter object, bound to bleio.adapter -// It currently only has properties and no state -const super_adapter_obj_t common_hal_bleio_adapter_obj = { - .base = { - .type = &bleio_adapter_type, - }, -}; - -void common_hal_bleio_check_connected(uint16_t conn_handle) { - if (conn_handle == BLE_CONN_HANDLE_INVALID) { - mp_raise_OSError_msg(translate("Not connected")); - } -} - -uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { - if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { - return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; - } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { - return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; - } else { - return BLE_CONN_HANDLE_INVALID; - } -} - -mp_obj_list_t *common_hal_bleio_device_get_remote_service_list(mp_obj_t device) { - if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { - return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->remote_service_list; - } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { - return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->remote_service_list; - } else { - return NULL; - } -} - -// service_uuid may be NULL, to discover all services. -STATIC bool discover_next_services(mp_obj_t device, uint16_t start_handle, ble_uuid_t *service_uuid) { - m_discovery_successful = false; - m_discovery_in_process = true; - - uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); - uint32_t err_code = sd_ble_gattc_primary_services_discover(conn_handle, start_handle, service_uuid); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to discover services")); - } - - // Wait for a discovery event. - while (m_discovery_in_process) { - MICROPY_VM_HOOK_LOOP; - } - return m_discovery_successful; -} - -STATIC bool discover_next_characteristics(mp_obj_t device, bleio_service_obj_t *service, uint16_t start_handle) { - m_char_discovery_service = service; - - ble_gattc_handle_range_t handle_range; - handle_range.start_handle = start_handle; - handle_range.end_handle = service->end_handle; - - m_discovery_successful = false; - m_discovery_in_process = true; - - uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); - uint32_t err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range); - if (err_code != NRF_SUCCESS) { - return false; - } - - // Wait for a discovery event. - while (m_discovery_in_process) { - MICROPY_VM_HOOK_LOOP; - } - return m_discovery_successful; -} - -STATIC bool discover_next_descriptors(mp_obj_t device, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) { - m_desc_discovery_characteristic = characteristic; - - ble_gattc_handle_range_t handle_range; - handle_range.start_handle = start_handle; - handle_range.end_handle = end_handle; - - m_discovery_successful = false; - m_discovery_in_process = true; - - uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); - uint32_t err_code = sd_ble_gattc_descriptors_discover(conn_handle, &handle_range); - if (err_code != NRF_SUCCESS) { - return false; - } - - // Wait for a discovery event. - while (m_discovery_in_process) { - MICROPY_VM_HOOK_LOOP; - } - return m_discovery_successful; -} - -STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, mp_obj_t device) { - for (size_t i = 0; i < response->count; ++i) { - ble_gattc_service_t *gattc_service = &response->services[i]; - - bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); - service->base.type = &bleio_service_type; - - // Initialize several fields at once. - common_hal_bleio_service_construct(service, device, NULL, false); - - service->is_remote = true; - service->start_handle = gattc_service->handle_range.start_handle; - service->end_handle = gattc_service->handle_range.end_handle; - service->handle = gattc_service->handle_range.start_handle; - - if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) { - // Known service UUID. - bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid); - service->uuid = uuid; - } else { - // The discovery response contained a 128-bit UUID that has not yet been registered with the - // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. - // For now, just set the UUID to NULL. - service->uuid = NULL; - } - - mp_obj_list_append(common_hal_bleio_device_get_remote_service_list(device), service); - } - - if (response->count > 0) { - m_discovery_successful = true; - } - m_discovery_in_process = false; -} - -STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, mp_obj_t device) { - for (size_t i = 0; i < response->count; ++i) { - ble_gattc_char_t *gattc_char = &response->chars[i]; - - bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); - characteristic->base.type = &bleio_characteristic_type; - - bleio_uuid_obj_t *uuid = NULL; - - if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) { - // Known characteristic UUID. - uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid); - } else { - // The discovery response contained a 128-bit UUID that has not yet been registered with the - // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. - // For now, just leave the UUID as NULL. - } - - bleio_characteristic_properties_t props = - (gattc_char->char_props.broadcast ? CHAR_PROP_BROADCAST : 0) | - (gattc_char->char_props.indicate ? CHAR_PROP_INDICATE : 0) | - (gattc_char->char_props.notify ? CHAR_PROP_NOTIFY : 0) | - (gattc_char->char_props.read ? CHAR_PROP_READ : 0) | - (gattc_char->char_props.write ? CHAR_PROP_WRITE : 0) | - (gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0); - - // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler. - common_hal_bleio_characteristic_construct( - characteristic, m_char_discovery_service, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, - GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc - mp_obj_new_list(0, NULL)); - characteristic->handle = gattc_char->handle_value; - - mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); - } - - if (response->count > 0) { - m_discovery_successful = true; - } - m_discovery_in_process = false; -} - -STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, mp_obj_t device) { - for (size_t i = 0; i < response->count; ++i) { - ble_gattc_desc_t *gattc_desc = &response->descs[i]; - - // Remember handles for certain well-known descriptors. - switch (gattc_desc->uuid.uuid) { - case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG: - m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle; - break; - - case BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG: - m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle; - break; - - case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC: - m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle; - break; - - default: - // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors, - // so ignore those. - // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors - break; - } - - bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); - descriptor->base.type = &bleio_descriptor_type; - - bleio_uuid_obj_t *uuid = NULL; - - if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) { - // Known descriptor UUID. - uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid); - } else { - // The discovery response contained a 128-bit UUID that has not yet been registered with the - // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. - // For now, just leave the UUID as NULL. - } - - common_hal_bleio_descriptor_construct( - descriptor, m_desc_discovery_characteristic, uuid, - SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, - GATT_MAX_DATA_LENGTH, false, mp_const_empty_bytes); - descriptor->handle = gattc_desc->handle; - - mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor)); - } - - if (response->count > 0) { - m_discovery_successful = true; - } - m_discovery_in_process = false; -} - -STATIC void discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t device) { - switch (ble_evt->header.evt_id) { - case BLE_GAP_EVT_DISCONNECTED: - m_discovery_successful = false; - m_discovery_in_process = false; - break; - - case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: - on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, device); - break; - - case BLE_GATTC_EVT_CHAR_DISC_RSP: - on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, device); - break; - - case BLE_GATTC_EVT_DESC_DISC_RSP: - on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, device); - break; - - default: - // For debugging. - // mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id); - break; - } -} - - -void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) { - mp_obj_list_t *remote_services_list = common_hal_bleio_device_get_remote_service_list(device); - - ble_drv_add_event_handler(discovery_on_ble_evt, device); - - // Start over with an empty list. - mp_obj_list_clear(MP_OBJ_FROM_PTR(common_hal_bleio_device_get_remote_service_list(device))); - - if (service_uuids_whitelist == mp_const_none) { - // List of service UUID's not given, so discover all available services. - - uint16_t next_service_start_handle = BLE_GATT_HANDLE_START; - - while (discover_next_services(device, next_service_start_handle, MP_OBJ_NULL)) { - // discover_next_services() appends to remote_services_list. - - // Get the most recently discovered service, and then ask for services - // whose handles start after the last attribute handle inside that service. - const bleio_service_obj_t *service = - MP_OBJ_TO_PTR(remote_services_list->items[remote_services_list->len - 1]); - next_service_start_handle = service->end_handle + 1; - } - } else { - mp_obj_iter_buf_t iter_buf; - mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf); - mp_obj_t uuid_obj; - while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("non-UUID found in service_uuids_whitelist")); - } - bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); - - ble_uuid_t nrf_uuid; - bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid); - - // Service might or might not be discovered; that's ok. Caller has to check - // Central.remote_services to find out. - // We only need to call this once for each service to discover. - discover_next_services(device, BLE_GATT_HANDLE_START, &nrf_uuid); - } - } - - - for (size_t service_idx = 0; service_idx < remote_services_list->len; ++service_idx) { - bleio_service_obj_t *service = MP_OBJ_TO_PTR(remote_services_list->items[service_idx]); - - // Skip the service if it had an unknown (unregistered) UUID. - if (service->uuid == NULL) { - continue; - } - - uint16_t next_char_start_handle = service->start_handle; - - // Stop when we go past the end of the range of handles for this service or - // discovery call returns nothing. - // discover_next_characteristics() appends to the characteristic_list. - while (next_char_start_handle <= service->end_handle && - discover_next_characteristics(device, service, next_char_start_handle)) { - - - // Get the most recently discovered characteristic, and then ask for characteristics - // whose handles start after the last attribute handle inside that characteristic. - const bleio_characteristic_obj_t *characteristic = - MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]); - next_char_start_handle = characteristic->handle + 1; - } - - // Got characteristics for this service. Now discover descriptors for each characteristic. - size_t char_list_len = service->characteristic_list->len; - for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) { - bleio_characteristic_obj_t *characteristic = - MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]); - const bool last_characteristic = char_idx == char_list_len - 1; - bleio_characteristic_obj_t *next_characteristic = last_characteristic - ? NULL - : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]); - - // Skip the characteristic if it had an unknown (unregistered) UUID. - if (characteristic->uuid == NULL) { - continue; - } - - uint16_t next_desc_start_handle = characteristic->handle + 1; - - // Don't run past the end of this service or the beginning of the next characteristic. - uint16_t next_desc_end_handle = next_characteristic == NULL - ? service->end_handle - : next_characteristic->handle - 1; - - // Stop when we go past the end of the range of handles for this service or - // discovery call returns nothing. - // discover_next_descriptors() appends to the descriptor_list. - while (next_desc_start_handle <= service->end_handle && - next_desc_start_handle < next_desc_end_handle && - discover_next_descriptors(device, characteristic, - next_desc_start_handle, next_desc_end_handle)) { - - // Get the most recently discovered descriptor, and then ask for descriptors - // whose handles start after that descriptor's handle. - const bleio_descriptor_obj_t *descriptor = - MP_OBJ_TO_PTR(characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1]); - next_desc_start_handle = descriptor->handle + 1; - } - } - } - - // This event handler is no longer needed. - ble_drv_remove_event_handler(discovery_on_ble_evt, device); - -} - -// GATTS read of a Characteristic or Descriptor. -mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle) { - // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because - // we can still read and write the local value. - - mp_buffer_info_t bufinfo; - ble_gatts_value_t gatts_value = { - .p_value = NULL, - .len = 0, - }; - - // Read once to find out what size buffer we need, then read again to fill buffer. - - mp_obj_t value = mp_const_none; - uint32_t err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value); - if (err_code == NRF_SUCCESS) { - value = mp_obj_new_bytearray_of_zeros(gatts_value.len); - mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_WRITE); - gatts_value.p_value = bufinfo.buf; - - // Read again, with the correct size of buffer. - err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value); - } - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code); - } - - return value; -} - -void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { - // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because - // we can still read and write the local value. - - ble_gatts_value_t gatts_value = { - .p_value = bufinfo->buf, - .len = bufinfo->len, - }; - - const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code); - } -} - -void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { - common_hal_bleio_check_connected(conn_handle); - - ble_gattc_write_params_t write_params = { - .write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ, - .handle = handle, - .p_value = bufinfo->buf, - .len = bufinfo->len, - }; - - while (1) { - uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); - if (err_code == NRF_SUCCESS) { - break; - } - - // Write with response will return NRF_ERROR_BUSY if the response has not been received. - // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending. - if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) { - // We could wait for an event indicating the write is complete, but just retrying is easier. - MICROPY_VM_HOOK_LOOP; - continue; - } - - // Some real error occurred. - mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code); - } - -} diff --git a/ports/nrf/common-hal/bleio/__init__.h b/ports/nrf/common-hal/bleio/__init__.h deleted file mode 100644 index cf1a06945..000000000 --- a/ports/nrf/common-hal/bleio/__init__.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 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_COMMON_HAL_BLEIO_INIT_H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H - -void bleio_reset(void); - -typedef struct { - ble_gap_enc_key_t own_enc; - ble_gap_enc_key_t peer_enc; - ble_gap_id_key_t peer_id; -} bonding_keys_t; - -// We assume variable length data. -// 20 bytes max (23 - 3). -#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3) - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 03f787624..8e5583581 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -38,7 +38,7 @@ #include "shared-module/gamepad/__init__.h" #include "common-hal/microcontroller/Pin.h" -#include "common-hal/bleio/__init__.h" +#include "common-hal/_bleio/__init__.h" #include "common-hal/busio/I2C.h" #include "common-hal/busio/SPI.h" #include "common-hal/busio/UART.h" diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 3f593416e..b17c312f7 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -122,7 +122,7 @@ ifeq ($(CIRCUITPY_BITBANG_APA102),1) SRC_PATTERNS += bitbangio/SPI% endif ifeq ($(CIRCUITPY_BLEIO),1) -SRC_PATTERNS += bleio/% +SRC_PATTERNS += _bleio/% endif ifeq ($(CIRCUITPY_BOARD),1) SRC_PATTERNS += board/% @@ -223,6 +223,17 @@ endif # All possible sources are listed here, and are filtered by SRC_PATTERNS in SRC_COMMON_HAL SRC_COMMON_HAL_ALL = \ + _bleio/__init__.c \ + _bleio/Adapter.c \ + _bleio/Attribute.c \ + _bleio/Central.c \ + _bleio/Characteristic.c \ + _bleio/CharacteristicBuffer.c \ + _bleio/Descriptor.c \ + _bleio/Peripheral.c \ + _bleio/Scanner.c \ + _bleio/Service.c \ + _bleio/UUID.c \ analogio/AnalogIn.c \ analogio/AnalogOut.c \ analogio/__init__.c \ @@ -233,17 +244,6 @@ SRC_COMMON_HAL_ALL = \ audiopwmio/PWMAudioOut.c \ audioio/__init__.c \ audioio/AudioOut.c \ - bleio/__init__.c \ - bleio/Adapter.c \ - bleio/Attribute.c \ - bleio/Central.c \ - bleio/Characteristic.c \ - bleio/CharacteristicBuffer.c \ - bleio/Descriptor.c \ - bleio/Peripheral.c \ - bleio/Scanner.c \ - bleio/Service.c \ - bleio/UUID.c \ board/__init__.c \ busio/I2C.c \ busio/SPI.c \ @@ -284,9 +284,9 @@ SRC_COMMON_HAL = $(filter $(SRC_PATTERNS), $(SRC_COMMON_HAL_ALL)) # All possible sources are listed here, and are filtered by SRC_PATTERNS. SRC_BINDINGS_ENUMS = \ $(filter $(SRC_PATTERNS), \ - bleio/Address.c \ - bleio/Attribute.c \ - bleio/ScanEntry.c \ + _bleio/Address.c \ + _bleio/Attribute.c \ + _bleio/ScanEntry.c \ digitalio/Direction.c \ digitalio/DriveMode.c \ digitalio/Pull.c \ @@ -301,6 +301,9 @@ SRC_BINDINGS_ENUMS += \ util.c SRC_SHARED_MODULE_ALL = \ + _bleio/Address.c \ + _bleio/Attribute.c \ + _bleio/ScanEntry.c \ _pixelbuf/PixelBuf.c \ _pixelbuf/__init__.c \ _stage/Layer.c \ @@ -317,9 +320,6 @@ SRC_SHARED_MODULE_ALL = \ bitbangio/SPI.c \ bitbangio/__init__.c \ board/__init__.c \ - bleio/Address.c \ - bleio/Attribute.c \ - bleio/ScanEntry.c \ busio/OneWire.c \ displayio/Bitmap.c \ displayio/ColorConverter.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index c2e2e2d02..5ac66665a 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -259,7 +259,7 @@ extern const struct _mp_obj_module_t bitbangio_module; #endif #if CIRCUITPY_BLEIO -#define BLEIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_bleio), (mp_obj_t)&bleio_module }, +#define BLEIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__bleio), (mp_obj_t)&bleio_module }, extern const struct _mp_obj_module_t bleio_module; #else #define BLEIO_MODULE diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 356a8f95d..1af71a416 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -99,7 +99,7 @@ CIRCUITPY_BITBANGIO = $(CIRCUITPY_FULL_BUILD) endif CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO) -# Explicitly enabled for boards that support bleio. +# Explicitly enabled for boards that support _bleio. ifndef CIRCUITPY_BLEIO CIRCUITPY_BLEIO = 0 endif diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c new file mode 100644 index 000000000..0caca96b8 --- /dev/null +++ b/shared-bindings/_bleio/Adapter.c @@ -0,0 +1,125 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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/objproperty.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Adapter.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`Adapter` --- BLE adapter information +//| ---------------------------------------------------- +//| +//| Get current status of the BLE adapter +//| +//| Usage:: +//| +//| import _bleio +//| _bleio.adapter.enabled = True +//| print(_bleio.adapter.address) +//| + +//| .. class:: Adapter() +//| +//| You cannot create an instance of `_bleio.Adapter`. +//| Use `_bleio.adapter` to access the sole instance available. +//| + +//| .. attribute:: adapter.enabled +//| +//| State of the BLE adapter. +//| +STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) { + return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled()); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_enabled_obj, bleio_adapter_get_enabled); + +static mp_obj_t bleio_adapter_set_enabled(mp_obj_t self, mp_obj_t value) { + const bool enabled = mp_obj_is_true(value); + + common_hal_bleio_adapter_set_enabled(enabled); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_enabled_obj, bleio_adapter_set_enabled); + +const mp_obj_property_t bleio_adapter_enabled_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_adapter_get_enabled_obj, + (mp_obj_t)&bleio_adapter_set_enabled_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: adapter.address +//| +//| MAC address of the BLE adapter. (read-only) +//| +STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) { + return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_address()); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address); + +const mp_obj_property_t bleio_adapter_address_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_adapter_get_address_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: adapter.default_name +//| +//| default_name of the BLE adapter. (read-only) +//| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``, +//| to make it easy to distinguish multiple CircuitPython boards. +//| +STATIC mp_obj_t bleio_adapter_get_default_name(mp_obj_t self) { + return common_hal_bleio_adapter_get_default_name(); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_default_name_obj, bleio_adapter_get_default_name); + +const mp_obj_property_t bleio_adapter_default_name_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_adapter_get_default_name_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_adapter_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_default_name), MP_ROM_PTR(&bleio_adapter_default_name_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table); + +const mp_obj_type_t bleio_adapter_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Adapter, + .locals_dict = (mp_obj_t)&bleio_adapter_locals_dict, +}; diff --git a/shared-bindings/_bleio/Adapter.h b/shared-bindings/_bleio/Adapter.h new file mode 100644 index 000000000..932fc9c95 --- /dev/null +++ b/shared-bindings/_bleio/Adapter.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_ADAPTER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H + +#include "shared-module/_bleio/Address.h" + +const mp_obj_type_t bleio_adapter_type; + +extern bool common_hal_bleio_adapter_get_enabled(void); +extern void common_hal_bleio_adapter_set_enabled(bool enabled); +extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(void); +extern mp_obj_t common_hal_bleio_adapter_get_default_name(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H diff --git a/shared-bindings/_bleio/Address.c b/shared-bindings/_bleio/Address.c new file mode 100644 index 000000000..cdee02b5d --- /dev/null +++ b/shared-bindings/_bleio/Address.c @@ -0,0 +1,210 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 +#include + +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-module/_bleio/Address.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`Address` -- BLE address +//| ========================================================= +//| +//| Encapsulates the address of a BLE device. +//| + +//| .. class:: Address(address, address_type) +//| +//| Create a new Address object encapsulating the address value. +//| The value itself can be one of: +//| +//| :param buf address: The address value to encapsulate. A buffer object (bytearray, bytes) of 6 bytes. +//| :param int address_type: one of the integer values: `PUBLIC`, `RANDOM_STATIC`, +//| `RANDOM_PRIVATE_RESOLVABLE`, or `RANDOM_PRIVATE_NON_RESOLVABLE`. +//| +STATIC mp_obj_t bleio_address_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_address, ARG_address_type }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_address_type, MP_ARG_INT, {.u_int = BLEIO_ADDRESS_TYPE_PUBLIC } }, + }; + + 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); + + bleio_address_obj_t *self = m_new_obj(bleio_address_obj_t); + self->base.type = &bleio_address_type; + + const mp_obj_t address = args[ARG_address].u_obj; + mp_buffer_info_t buf_info; + mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); + if (buf_info.len != NUM_BLEIO_ADDRESS_BYTES) { + mp_raise_ValueError_varg(translate("Address must be %d bytes long"), NUM_BLEIO_ADDRESS_BYTES); + } + + const mp_int_t address_type = args[ARG_address_type].u_int; + if (address_type < BLEIO_ADDRESS_TYPE_MIN || address_type > BLEIO_ADDRESS_TYPE_MAX) { + mp_raise_ValueError(translate("Address type out of range")); + } + + common_hal_bleio_address_construct(self, buf_info.buf, address_type); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. attribute:: address_bytes +//| +//| The bytes that make up the device address (read-only). +//| +//| Note that the ``bytes`` object returned is in little-endian order: +//| The least significant byte is ``address_bytes[0]``. So the address will +//| appear to be reversed if you print the raw ``bytes`` object. If you print +//| or use `str()` on the :py:class:`~_bleio.Attribute` object itself, the address will be printed +//| in the expected order. For example: +//| +//| .. code-block:: pycon +//| +//| >>> import _bleio +//| >>> _bleio.adapter.address +//|
+//| >>> _bleio.adapter.address.address_bytes +//| b'5\xa8\xed\xf5\x1d\xc8' +//| +STATIC mp_obj_t bleio_address_get_address_bytes(mp_obj_t self_in) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_address_get_address_bytes(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_address_bytes_obj, bleio_address_get_address_bytes); + +const mp_obj_property_t bleio_address_address_bytes_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&bleio_address_get_address_bytes_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: type +//| +//| The address type (read-only). +//| One of the integer values: `PUBLIC`, `RANDOM_STATIC`, +//| `RANDOM_PRIVATE_RESOLVABLE`, or `RANDOM_PRIVATE_NON_RESOLVABLE`. +//| +STATIC mp_obj_t bleio_address_get_type(mp_obj_t self_in) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_address_get_type(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_type_obj, bleio_address_get_type); + +const mp_obj_property_t bleio_address_type_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&bleio_address_get_type_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. method:: __eq__(other) +//| +//| Two Address objects are equal if their addresses and address types are equal. +//| +STATIC mp_obj_t bleio_address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + // Two Addresses are equal if their address bytes and address_type are equal + case MP_BINARY_OP_EQUAL: + if (MP_OBJ_IS_TYPE(rhs_in, &bleio_address_type)) { + bleio_address_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); + bleio_address_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool( + mp_obj_equal(common_hal_bleio_address_get_address_bytes(lhs), + common_hal_bleio_address_get_address_bytes(rhs)) && + common_hal_bleio_address_get_type(lhs) == + common_hal_bleio_address_get_type(rhs)); + + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t buf_info; + mp_obj_t address_bytes = common_hal_bleio_address_get_address_bytes(self); + mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ); + + const uint8_t *buf = (uint8_t *) buf_info.buf; + mp_printf(print, "
", + buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]); +} + +//| .. data:: PUBLIC +//| +//| A publicly known address, with a company ID (high 24 bits)and company-assigned part (low 24 bits). +//| +//| .. data:: RANDOM_STATIC +//| +//| A randomly generated address that does not change often. It may never change or may change after +//| a power cycle. +//| +//| .. data:: RANDOM_PRIVATE_RESOLVABLE +//| +//| An address that is usable when the peer knows the other device's secret Identity Resolving Key (IRK). +//| +//| .. data:: RANDOM_PRIVATE_NON_RESOLVABLE +//| +//| A randomly generated address that changes on every connection. +//| +STATIC const mp_rom_map_elem_t bleio_address_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_address_bytes), MP_ROM_PTR(&bleio_address_address_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&bleio_address_type_obj) }, + // These match the BLE_GAP_ADDR_TYPES values used by the nRF library. + { MP_ROM_QSTR(MP_QSTR_PUBLIC), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_STATIC), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_RESOLVABLE), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_NON_RESOLVABLE), MP_ROM_INT(3) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_address_locals_dict, bleio_address_locals_dict_table); + +const mp_obj_type_t bleio_address_type = { + { &mp_type_type }, + .name = MP_QSTR_Address, + .make_new = bleio_address_make_new, + .print = bleio_address_print, + .binary_op = bleio_address_binary_op, + .locals_dict = (mp_obj_dict_t*)&bleio_address_locals_dict +}; diff --git a/shared-bindings/_bleio/Address.h b/shared-bindings/_bleio/Address.h new file mode 100644 index 000000000..98b6f80e0 --- /dev/null +++ b/shared-bindings/_bleio/Address.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_ADDRESS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H + +#include "py/objtype.h" +#include "shared-module/_bleio/Address.h" + +#define BLEIO_ADDRESS_TYPE_PUBLIC (0) +#define BLEIO_ADDRESS_TYPE_RANDOM_STATIC (1) +#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE (2) +#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE (3) + +#define BLEIO_ADDRESS_TYPE_MIN BLEIO_ADDRESS_TYPE_PUBLIC +#define BLEIO_ADDRESS_TYPE_MAX BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE + +extern const mp_obj_type_t bleio_address_type; + +extern void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type); +extern mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self); +extern uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H diff --git a/shared-bindings/_bleio/Attribute.c b/shared-bindings/_bleio/Attribute.c new file mode 100644 index 000000000..2d8b15b9f --- /dev/null +++ b/shared-bindings/_bleio/Attribute.c @@ -0,0 +1,94 @@ +/* + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/UUID.h" + +// + +//| .. currentmodule:: _bleio +//| +//| :class:`Attribute` -- BLE Attribute +//| ========================================================= +//| +//| Definitions associated with all BLE attributes: characteristics, descriptors, etc. +//| :py:class:`~_bleio.Attribute` is, notionally, a superclass of +//| :py:class:`~Characteristic` and :py:class:`~Descriptor`, +//| but is not defined as a Python superclass of those classes. +//| +//| .. class:: Attribute() +//| +//| You cannot create an instance of :py:class:`~_bleio.Attribute`. +//| + +STATIC const mp_rom_map_elem_t bleio_attribute_locals_dict_table[] = { + +//| .. data:: NO_ACCESS +//| +//| security mode: access not allowed +//| +//| .. data:: OPEN +//| +//| security_mode: no security (link is not encrypted) +//| +//| .. data:: ENCRYPT_NO_MITM +//| +//| security_mode: unauthenticated encryption, without man-in-the-middle protection +//| +//| .. data:: ENCRYPT_WITH_MITM +//| +//| security_mode: authenticated encryption, with man-in-the-middle protection +//| +//| .. data:: LESC_ENCRYPT_WITH_MITM +//| +//| security_mode: LESC encryption, with man-in-the-middle protection +//| +//| .. data:: SIGNED_NO_MITM +//| +//| security_mode: unauthenticated data signing, without man-in-the-middle protection +//| +//| .. data:: SIGNED_WITH_MITM +//| +//| security_mode: authenticated data signing, without man-in-the-middle protection +//| + { MP_ROM_QSTR(MP_QSTR_NO_ACCESS), MP_ROM_INT(SECURITY_MODE_NO_ACCESS) }, + { MP_ROM_QSTR(MP_QSTR_OPEN), MP_ROM_INT(SECURITY_MODE_OPEN) }, + { MP_ROM_QSTR(MP_QSTR_ENCRYPT_NO_MITM), MP_ROM_INT(SECURITY_MODE_ENC_NO_MITM) }, + { MP_ROM_QSTR(MP_QSTR_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_ENC_WITH_MITM) }, + { MP_ROM_QSTR(MP_QSTR_LESC_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_LESC_ENC_WITH_MITM) }, + { MP_ROM_QSTR(MP_QSTR_SIGNED_NO_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_NO_MITM) }, + { MP_ROM_QSTR(MP_QSTR_SIGNED_WITH_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_WITH_MITM) }, + +}; +STATIC MP_DEFINE_CONST_DICT(bleio_attribute_locals_dict, bleio_attribute_locals_dict_table); + +const mp_obj_type_t bleio_attribute_type = { + { &mp_type_type }, + .name = MP_QSTR_Attribute, + .locals_dict = (mp_obj_dict_t*)&bleio_attribute_locals_dict, +}; diff --git a/shared-bindings/_bleio/Attribute.h b/shared-bindings/_bleio/Attribute.h new file mode 100644 index 000000000..a0ce04500 --- /dev/null +++ b/shared-bindings/_bleio/Attribute.h @@ -0,0 +1,39 @@ +/* + * 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_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H + +#include "py/obj.h" + +#include "common-hal/_bleio/Attribute.h" +#include "shared-module/_bleio/Attribute.h" + +extern const mp_obj_type_t bleio_attribute_type; + +extern void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H diff --git a/shared-bindings/_bleio/Central.c b/shared-bindings/_bleio/Central.c new file mode 100644 index 000000000..084f40cd6 --- /dev/null +++ b/shared-bindings/_bleio/Central.c @@ -0,0 +1,207 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 +#include + +#include "ble_drv.h" +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Central.h" +#include "shared-bindings/_bleio/Service.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`Central` -- A BLE central device +//| ========================================================= +//| +//| Implement a BLE central, which runs locally. Can connect to a given address. +//| +//| Usage:: +//| +//| import _bleio +//| +//| scanner = _bleio.Scanner() +//| entries = scanner.scan(2.5) +//| +//| my_entry = None +//| for entry in entries: +//| if entry.name is not None and entry.name == 'InterestingPeripheral': +//| my_entry = entry +//| break +//| +//| if not my_entry: +//| raise Exception("'InterestingPeripheral' not found") +//| +//| central = _bleio.Central() +//| central.connect(my_entry.address, 10) # timeout after 10 seconds +//| remote_services = central.discover_remote_services() +//| + +//| .. class:: Central() +//| +//| Create a new Central object. +//| +STATIC mp_obj_t bleio_central_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 0, 0, false); + + bleio_central_obj_t *self = m_new_obj(bleio_central_obj_t); + self->base.type = &bleio_central_type; + + common_hal_bleio_central_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: connect(address, timeout, *, service_uuids_whitelist=None) +//| Attempts a connection to the remote peripheral. +//| +//| :param Address address: The address of the peripheral to connect to +//| :param float/int timeout: Try to connect for timeout seconds. +//| +STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_address, ARG_timeout, ARG_service_uuids_whitelist }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + if (!MP_OBJ_IS_TYPE(args[ARG_address].u_obj, &bleio_address_type)) { + mp_raise_ValueError(translate("Expected an Address")); + } + + bleio_address_obj_t *address = MP_OBJ_TO_PTR(args[ARG_address].u_obj); + mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + + // common_hal_bleio_central_connect() will validate that services is an iterable or None. + common_hal_bleio_central_connect(self, address, timeout); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_connect_obj, 3, bleio_central_connect); + + +//| .. method:: disconnect() +//| +//| Disconnects from the remote peripheral. +//| +STATIC mp_obj_t bleio_central_disconnect(mp_obj_t self_in) { + bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_central_disconnect(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_disconnect_obj, bleio_central_disconnect); + +//| .. method:: discover_remote_services(service_uuids_whitelist=None) +//| Do BLE discovery for all services or for the given service UUIDS, +//| to find their handles and characteristics, and return the discovered services. +//| `Peripheral.connected` must be True. +//| +//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services +//| provided by the peripheral that you want to use. +//| The peripheral may provide more services, but services not listed are ignored +//| and will not be returned. +//| +//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow. +//| +//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you +//| you must have already created a :py:class:~`UUID` object for that UUID in order for the +//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered +//| for use. (This restriction may be lifted in the future.) +//| +//| :return: A tuple of services provided by the remote peripheral. +//| +STATIC mp_obj_t bleio_central_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_service_uuids_whitelist }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + if (!common_hal_bleio_central_get_connected(self)) { + mp_raise_ValueError(translate("Not connected")); + } + + return MP_OBJ_FROM_PTR(common_hal_bleio_central_discover_remote_services( + MP_OBJ_FROM_PTR(self), + args[ARG_service_uuids_whitelist].u_obj)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_discover_remote_services_obj, 1, bleio_central_discover_remote_services); + +//| .. attribute:: connected +//| +//| True if connected to a remove peripheral. +//| +STATIC mp_obj_t bleio_central_get_connected(mp_obj_t self_in) { + bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_central_get_connected(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_get_connected_obj, bleio_central_get_connected); + +const mp_obj_property_t bleio_central_connected_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_central_get_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_central_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_central_discover_remote_services_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_central_connected_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_central_locals_dict, bleio_central_locals_dict_table); + +const mp_obj_type_t bleio_central_type = { + { &mp_type_type }, + .name = MP_QSTR_Central, + .make_new = bleio_central_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_central_locals_dict +}; diff --git a/shared-bindings/_bleio/Central.h b/shared-bindings/_bleio/Central.h new file mode 100644 index 000000000..85d788654 --- /dev/null +++ b/shared-bindings/_bleio/Central.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_CENTRAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H + +#include "py/objtuple.h" +#include "common-hal/_bleio/Central.h" +#include "common-hal/_bleio/Service.h" + +extern const mp_obj_type_t bleio_central_type; + +extern void common_hal_bleio_central_construct(bleio_central_obj_t *self); +extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout); +extern void common_hal_bleio_central_disconnect(bleio_central_obj_t *self); +extern bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self); +extern mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H diff --git a/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c new file mode 100644 index 000000000..198155589 --- /dev/null +++ b/shared-bindings/_bleio/Characteristic.c @@ -0,0 +1,341 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`Characteristic` -- BLE service characteristic +//| ========================================================= +//| +//| Stores information about a BLE service characteristic and allows reading +//| and writing of the characteristic's value. +//| +//| .. class:: Characteristic +//| +//| There is no regular constructor for a Characteristic. A new local Characteristic can be created +//| and attached to a Service by calling `add_to_service()`. +//| Remote Characteristic objects are created by `Central.discover_remote_services()` +//| or `Peripheral.discover_remote_services()` as part of remote Services. +//| + +//| .. method:: add_to_service(service, uuid, *, properties=0, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=None) +//| +//| Create a new Characteristic object, and add it to this Service. +//| +//| :param Service service: The service that will provide this characteristic +//| :param UUID uuid: The uuid of the characteristic +//| :param int properties: The properties of the characteristic, +//| specified as a bitmask of these values bitwise-or'd together: +//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. +//| :param int read_perm: Specifies whether the characteristic can be read by a client, and if so, which +//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, +//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. +//| :param int write_perm: Specifies whether the characteristic can be written by a client, and if so, which +//| security mode is required. Values allowed are the same as ``read_perm``. +//| :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed is +//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum +//| number of data bytes that fit in a single BLE 4.x ATT packet. +//| :param bool fixed_length: True if the characteristic value is of fixed length. +//| :param buf initial_value: The initial value for this characteristic. If not given, will be +//| filled with zeros. +//| +//| :return: the new Characteristic. +//| +STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_service, ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_properties, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, + { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + const mp_obj_t service_obj = args[ARG_service].u_obj; + if (!MP_OBJ_IS_TYPE(service_obj, &bleio_service_type)) { + mp_raise_ValueError(translate("Expected a Service")); + } + + const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("Expected a UUID")); + } + + const bleio_characteristic_properties_t properties = args[ARG_properties].u_int; + if (properties & ~CHAR_PROP_ALL) { + mp_raise_ValueError(translate("Invalid properties")); + } + + const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(read_perm); + + const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(write_perm); + + const mp_int_t max_length = args[ARG_max_length].u_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + // Length will be validated in common_hal. + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + characteristic->base.type = &bleio_characteristic_type; + + // Range checking on max_length arg is done by the common_hal layer, because + // it may vary depending on underlying BLE implementation. + common_hal_bleio_characteristic_construct( + characteristic, MP_OBJ_TO_PTR(service_obj), MP_OBJ_TO_PTR(uuid_obj), + properties, read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo); + + common_hal_bleio_service_add_characteristic(service_obj, characteristic); + + return MP_OBJ_FROM_PTR(characteristic); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_add_to_service_fun_obj, 3, bleio_characteristic_add_to_service); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_characteristic_add_to_service_obj, MP_ROM_PTR(&bleio_characteristic_add_to_service_fun_obj)); + + + +//| .. attribute:: properties +//| +//| An int bitmask representing which properties are set, specified as bitwise or'ing of +//| of these possible values. +//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. +//| +STATIC mp_obj_t bleio_characteristic_get_properties(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_get_properties(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_properties_obj, bleio_characteristic_get_properties); + +const mp_obj_property_t bleio_characteristic_properties_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_properties_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: uuid +//| +//| The UUID of this characteristic. (read-only) +//| Will be ``None`` if the 128-bit UUID for this characteristic is not known. +//| +STATIC mp_obj_t bleio_characteristic_get_uuid(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_characteristic_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_uuid_obj, bleio_characteristic_get_uuid); + +const mp_obj_property_t bleio_characteristic_uuid_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_uuid_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: value +//| +//| The value of this characteristic. +//| +STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_characteristic_get_value(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_value_obj, bleio_characteristic_get_value); + +STATIC mp_obj_t bleio_characteristic_set_value(mp_obj_t self_in, mp_obj_t value_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); + + common_hal_bleio_characteristic_set_value(self, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_value_obj, bleio_characteristic_set_value); + +const mp_obj_property_t bleio_characteristic_value_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_value_obj, + (mp_obj_t)&bleio_characteristic_set_value_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: descriptors +//| +//| A tuple of :py:class:`Descriptor` that describe this characteristic. (read-only) +//| +STATIC mp_obj_t bleio_characteristic_get_descriptors(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + // Return list as a tuple so user won't be able to change it. + mp_obj_list_t *char_list = common_hal_bleio_characteristic_get_descriptor_list(self); + return mp_obj_new_tuple(char_list->len, char_list->items); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_descriptors_obj, bleio_characteristic_get_descriptors); + +const mp_obj_property_t bleio_characteristic_descriptors_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_descriptors_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: service (read-only) +//| +//| The Service this Characteristic is a part of. +//| +STATIC mp_obj_t bleio_characteristic_get_service(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_characteristic_get_service(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_service_obj, bleio_characteristic_get_service); + +const mp_obj_property_t bleio_characteristic_service_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_characteristic_get_service_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. method:: set_cccd(*, notify=False, indicate=False) +//| +//| Set the remote characteristic's CCCD to enable or disable notification and indication. +//| +//| :param bool notify: True if Characteristic should receive notifications of remote writes +//| :param float indicate: True if Characteristic should receive indications of remote writes +//| +STATIC mp_obj_t bleio_characteristic_set_cccd(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_notify, ARG_indicate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_notify, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_indicate, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + 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); + + common_hal_bleio_characteristic_set_cccd(self, args[ARG_notify].u_bool, args[ARG_indicate].u_bool); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_characteristic_set_cccd); + + +STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) }, + { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_get_properties_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) }, + + // Bitmask constants to represent properties +//| .. data:: BROADCAST +//| +//| property: allowed in advertising packets +//| +//| .. data:: INDICATE +//| +//| property: server will indicate to the client when the value is set and wait for a response +//| +//| .. data:: NOTIFY +//| +//| property: server will notify the client when the value is set +//| +//| .. data:: READ +//| +//| property: clients may read this characteristic +//| +//| .. data:: WRITE +//| +//| property: clients may write this characteristic; a response will be sent back +//| +//| .. data:: WRITE_NO_RESPONSE +//| +//| property: clients may write this characteristic; no response will be sent back +//| + { MP_ROM_QSTR(MP_QSTR_BROADCAST), MP_ROM_INT(CHAR_PROP_BROADCAST) }, + { MP_ROM_QSTR(MP_QSTR_INDICATE), MP_ROM_INT(CHAR_PROP_INDICATE) }, + { MP_ROM_QSTR(MP_QSTR_NOTIFY), MP_ROM_INT(CHAR_PROP_NOTIFY) }, + { MP_ROM_QSTR(MP_QSTR_READ), MP_ROM_INT(CHAR_PROP_READ) }, + { MP_ROM_QSTR(MP_QSTR_WRITE), MP_ROM_INT(CHAR_PROP_WRITE) }, + { MP_ROM_QSTR(MP_QSTR_WRITE_NO_RESPONSE), MP_ROM_INT(CHAR_PROP_WRITE_NO_RESPONSE) }, + +}; +STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_locals_dict, bleio_characteristic_locals_dict_table); + +STATIC void bleio_characteristic_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Characteristic("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, ""); + } +} + +const mp_obj_type_t bleio_characteristic_type = { + { &mp_type_type }, + .name = MP_QSTR_Characteristic, + .print = bleio_characteristic_print, + .locals_dict = (mp_obj_dict_t*)&bleio_characteristic_locals_dict, +}; diff --git a/shared-bindings/_bleio/Characteristic.h b/shared-bindings/_bleio/Characteristic.h new file mode 100644 index 000000000..a816c6050 --- /dev/null +++ b/shared-bindings/_bleio/Characteristic.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H + +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-module/_bleio/Characteristic.h" +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Service.h" + +extern const mp_obj_type_t bleio_characteristic_type; + +extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo); +extern mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self); +extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); +extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self); +extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self); +extern mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self); +extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self); +extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor); +extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H diff --git a/shared-bindings/_bleio/CharacteristicBuffer.c b/shared-bindings/_bleio/CharacteristicBuffer.c new file mode 100644 index 000000000..9cc708eb7 --- /dev/null +++ b/shared-bindings/_bleio/CharacteristicBuffer.c @@ -0,0 +1,252 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/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 +//| +//| :class:`CharacteristicBuffer` -- BLE Service incoming values buffer. +//| ===================================================================== +//| +//| Accumulates a Characteristic's incoming values in a FIFO buffer. +//| +//| .. class:: CharacteristicBuffer(characteristic, *, timeout=1, buffer_size=64) +//| +//| Monitor the given Characteristic. Each time a new value is written to the Characteristic +//| add the newly-written bytes to a FIFO buffer. +//| +//| :param Characteristic characteristic: The Characteristic to monitor. +//| It may be a local Characteristic provided by a Peripheral Service, or a remote Characteristic +//| in a remote Service that a Central has connected to. +//| :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_timeout, ARG_buffer_size, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { 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; + + 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_varg(translate("%q must be >= 1"), MP_QSTR_buffer_size); + } + + if (!MP_OBJ_IS_TYPE(characteristic, &bleio_characteristic_type)) { + mp_raise_ValueError(translate("Expected a Characteristic")); + } + + bleio_characteristic_buffer_obj_t *self = m_new_obj(bleio_characteristic_buffer_obj_t); + self->base.type = &bleio_characteristic_buffer_type; + + common_hal_bleio_characteristic_buffer_construct(self, MP_OBJ_TO_PTR(characteristic), timeout, buffer_size); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void check_for_deinit(bleio_characteristic_buffer_obj_t *self) { + if (common_hal_bleio_characteristic_buffer_deinited(self)) { + raise_deinited_error(); + } +} + +// 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 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); + check_for_deinit(self); + raise_error_if_not_connected(self); + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + return common_hal_bleio_characteristic_buffer_read(self, buf, size, errcode); +} + +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); + check_for_deinit(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); + check_for_deinit(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); + check_for_deinit(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); + return mp_const_none; +} +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_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 new file mode 100644 index 000000000..83e6fef02 --- /dev/null +++ b/shared-bindings/_bleio/CharacteristicBuffer.h @@ -0,0 +1,42 @@ +/* + * 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_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H + +#include "common-hal/_bleio/CharacteristicBuffer.h" + +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, 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/_bleio/Descriptor.c b/shared-bindings/_bleio/Descriptor.c new file mode 100644 index 000000000..0ab9fe8e9 --- /dev/null +++ b/shared-bindings/_bleio/Descriptor.c @@ -0,0 +1,231 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/UUID.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`Descriptor` -- BLE descriptor +//| ========================================================= +//| +//| Stores information about a BLE descriptor. +//| Descriptors are attached to BLE characteristics and provide contextual +//| information about the characteristic. +//| +//| .. class:: Descriptor +//| +//| There is no regular constructor for a Descriptor. A new local Descriptor can be created +//| and attached to a Characteristic by calling `add_to_characteristic()`. +//| Remote Descriptor objects are created by `Central.discover_remote_services()` +//| or `Peripheral.discover_remote_services()` as part of remote Characteristics +//| in the remote Services that are discovered. +//| +//| .. classmethod:: add_to_characteristic(characteristic, uuid, *, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=b'') +//| +//| Create a new Descriptor object, and add it to this Service. +//| +//| :param Characteristic characteristic: The characteristic that will hold this descriptor +//| :param UUID uuid: The uuid of the descriptor +//| :param int read_perm: Specifies whether the descriptor can be read by a client, and if so, which +//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, +//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. +//| :param int write_perm: Specifies whether the descriptor can be written by a client, and if so, which +//| security mode is required. Values allowed are the same as ``read_perm``. +//| :param int max_length: Maximum length in bytes of the descriptor value. The maximum allowed is +//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum +//| number of data bytes that fit in a single BLE 4.x ATT packet. +//| :param bool fixed_length: True if the descriptor value is of fixed length. +//| :param buf initial_value: The initial value for this descriptor. +//| +//| :return: the new Descriptor. +//| +STATIC mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_characteristic, ARG_uuid, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, + { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, + }; + + 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); + + const mp_obj_t characteristic_obj = args[ARG_characteristic].u_obj; + if (!MP_OBJ_IS_TYPE(characteristic_obj, &bleio_characteristic_type)) { + mp_raise_ValueError(translate("Expected a Characteristic")); + } + + const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("Expected a UUID")); + } + + const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(read_perm); + + const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(write_perm); + + const mp_int_t max_length = args[ARG_max_length].u_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + // Length will be validated in common_hal. + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + + bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); + descriptor->base.type = &bleio_descriptor_type; + + // Range checking on max_length arg is done by the common_hal layer, because + // it may vary depending on underlying BLE implementation. + common_hal_bleio_descriptor_construct( + descriptor, MP_OBJ_TO_PTR(characteristic_obj), MP_OBJ_TO_PTR(uuid_obj), + read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo); + + common_hal_bleio_characteristic_add_descriptor(characteristic_obj, descriptor); + + return MP_OBJ_FROM_PTR(descriptor); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_descriptor_add_to_characteristic_fun_obj, 3, bleio_descriptor_add_to_characteristic); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_descriptor_add_to_characteristic_obj, MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_fun_obj)); + +//| .. attribute:: uuid +//| +//| The descriptor uuid. (read-only) +//| +STATIC mp_obj_t bleio_descriptor_get_uuid(mp_obj_t self_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_descriptor_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_uuid_obj, bleio_descriptor_get_uuid); + +const mp_obj_property_t bleio_descriptor_uuid_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&bleio_descriptor_get_uuid_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: characteristic (read-only) +//| +//| The Characteristic this Descriptor is a part of. +//| +STATIC mp_obj_t bleio_descriptor_get_characteristic(mp_obj_t self_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_descriptor_get_characteristic(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_characteristic_obj, bleio_descriptor_get_characteristic); + +const mp_obj_property_t bleio_descriptor_characteristic_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_descriptor_get_characteristic_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: value +//| +//| The value of this descriptor. +//| +STATIC mp_obj_t bleio_descriptor_get_value(mp_obj_t self_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_descriptor_get_value(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_value_obj, bleio_descriptor_get_value); + +STATIC mp_obj_t bleio_descriptor_set_value(mp_obj_t self_in, mp_obj_t value_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); + + common_hal_bleio_descriptor_set_value(self, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_descriptor_set_value_obj, bleio_descriptor_set_value); + +const mp_obj_property_t bleio_descriptor_value_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_descriptor_get_value_obj, + (mp_obj_t)&bleio_descriptor_set_value_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_to_characteristic), MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_descriptor_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_characteristic), MP_ROM_PTR(&bleio_descriptor_characteristic_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_descriptor_value_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_descriptor_locals_dict, bleio_descriptor_locals_dict_table); + +STATIC void bleio_descriptor_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Descriptor("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, ""); + } +} + +const mp_obj_type_t bleio_descriptor_type = { + { &mp_type_type }, + .name = MP_QSTR_Descriptor, + .print = bleio_descriptor_print, + .locals_dict = (mp_obj_dict_t*)&bleio_descriptor_locals_dict +}; diff --git a/shared-bindings/_bleio/Descriptor.h b/shared-bindings/_bleio/Descriptor.h new file mode 100644 index 000000000..7544bdb17 --- /dev/null +++ b/shared-bindings/_bleio/Descriptor.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H + +#include "shared-module/_bleio/Attribute.h" +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Descriptor.h" +#include "common-hal/_bleio/UUID.h" + +extern const mp_obj_type_t bleio_descriptor_type; + +extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo); +extern bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self); +extern bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self); +extern mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self); +extern void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H diff --git a/shared-bindings/_bleio/Peripheral.c b/shared-bindings/_bleio/Peripheral.c new file mode 100644 index 000000000..0bf292744 --- /dev/null +++ b/shared-bindings/_bleio/Peripheral.c @@ -0,0 +1,325 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 +#include + +#include "ble_drv.h" +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" + +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Peripheral.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-module/_bleio/ScanEntry.h" + +#include "common-hal/_bleio/Peripheral.h" + +#define ADV_INTERVAL_MIN (0.0020f) +#define ADV_INTERVAL_MIN_STRING "0.0020" +#define ADV_INTERVAL_MAX (10.24f) +#define ADV_INTERVAL_MAX_STRING "10.24" +// 20ms is recommended by Apple +#define ADV_INTERVAL_DEFAULT (0.1f) + +//| .. currentmodule:: _bleio +//| +//| :class:`Peripheral` -- A BLE peripheral device +//| ========================================================= +//| +//| Implement a BLE peripheral which runs locally. +//| Set up using the supplied services, and then allow advertising to be started and stopped. +//| +//| Usage:: +//| +//| from _bleio import Characteristic, Peripheral, Service +//| from adafruit_ble.advertising import ServerAdvertisement +//| +//| # Create a peripheral and start it up. +//| peripheral = _bleio.Peripheral() +//| +//| # Create a Service and add it to this Peripheral. +//| service = Service.add_to_peripheral(peripheral, _bleio.UUID(0x180f)) +//| +//| # Create a Characteristic and add it to the Service. +//| characteristic = Characteristic.add_to_service(service, +//| _bleio.UUID(0x2919), properties=Characteristic.READ | Characteristic.NOTIFY) +//| +//| adv = ServerAdvertisement(peripheral) +//| peripheral.start_advertising(adv.advertising_data_bytes, scan_response=adv.scan_response_bytes) +//| +//| while not peripheral.connected: +//| # Wait for connection. +//| pass +//| +//| .. class:: Peripheral(name=None) +//| +//| Create a new Peripheral object. +//| +//| :param str name: The name used when advertising this peripheral. If name is None, +//| _bleio.adapter.default_name will be used. +//| +STATIC mp_obj_t bleio_peripheral_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_name }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_name, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + bleio_peripheral_obj_t *self = m_new_obj(bleio_peripheral_obj_t); + self->base.type = &bleio_peripheral_type; + + mp_obj_t name = args[ARG_name].u_obj; + if (name == mp_const_none) { + name = common_hal_bleio_adapter_get_default_name(); + } else if (!MP_OBJ_IS_STR(name)) { + mp_raise_ValueError(translate("name must be a string")); + } + + common_hal_bleio_peripheral_construct(self, name); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. attribute:: connected (read-only) +//| +//| True if connected to a BLE Central device. +//| +STATIC mp_obj_t bleio_peripheral_get_connected(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_peripheral_get_connected(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_connected_obj, bleio_peripheral_get_connected); + +const mp_obj_property_t bleio_peripheral_connected_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_peripheral_get_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: services +//| +//| A tuple of :py:class:`Service` objects offered by this peripheral. (read-only) +//| +STATIC mp_obj_t bleio_peripheral_get_services(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + // Return list as a tuple so user won't be able to change it. + mp_obj_list_t *services_list = common_hal_bleio_peripheral_get_services(self); + return mp_obj_new_tuple(services_list->len, services_list->items); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services); + +const mp_obj_property_t bleio_peripheral_services_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_peripheral_get_services_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: name +//| +//| The peripheral's name, included when advertising. (read-only) +//| +STATIC mp_obj_t bleio_peripheral_get_name(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_peripheral_get_name(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_name_obj, bleio_peripheral_get_name); + +const mp_obj_property_t bleio_peripheral_name_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_peripheral_get_name_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. method:: start_advertising(data, *, scan_response=None, connectable=True, interval=0.1) +//| +//| Starts advertising the peripheral. The peripheral's name and +//| services are included in the advertisement packets. +//| +//| :param buf data: advertising data packet bytes +//| :param buf scan_response: scan response data packet bytes. ``None`` if no scan response is needed. +//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral. +//| :param float interval: advertising interval, in seconds +//| +STATIC mp_obj_t bleio_peripheral_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_interval }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_scan_response, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + 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); + + mp_buffer_info_t data_bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); + + // Pass an empty buffer if scan_response not provided. + mp_buffer_info_t scan_response_bufinfo = { 0 }; + if (args[ARG_scan_response].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_scan_response].u_obj, &scan_response_bufinfo, MP_BUFFER_READ); + } + + if (args[ARG_interval].u_obj == MP_OBJ_NULL) { + args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT); + } + + const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj); + if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) { + mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), + ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING); + } + + common_hal_bleio_peripheral_start_advertising(self, args[ARG_connectable].u_bool, interval, + &data_bufinfo, &scan_response_bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_start_advertising_obj, 2, bleio_peripheral_start_advertising); + +//| .. method:: stop_advertising() +//| +//| Stop sending advertising packets. +STATIC mp_obj_t bleio_peripheral_stop_advertising(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_peripheral_stop_advertising(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_stop_advertising_obj, bleio_peripheral_stop_advertising); + +//| .. method:: disconnect() +//| +//| Disconnects from the remote central. +//| Normally the central initiates a disconnection. Use this only +//| if necessary for your application. +//| +STATIC mp_obj_t bleio_peripheral_disconnect(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_peripheral_disconnect(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_disconnect_obj, bleio_peripheral_disconnect); + +//| .. method:: discover_remote_services(service_uuids_whitelist=None) +//| Do BLE discovery for all services or for the given service UUIDS, +//| to find their handles and characteristics, and return the discovered services. +//| `Peripheral.connected` must be True. +//| +//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services +//| provided by the peripheral that you want to use. +//| The peripheral may provide more services, but services not listed are ignored +//| and will not be returned. +//| +//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow. +//| +//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you +//| you must have already created a :py:class:~`UUID` object for that UUID in order for the +//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered +//| for use. (This restriction may be lifted in the future.) +//| +//| Thought it is unusual for a peripheral to act as a BLE client, it can do so, and +//| needs to be able to do discovery on its peer (a central). +//| Examples include a peripheral accessing a central that provides Current Time Service, +//| Apple Notification Center Service, or Battery Service. +//| +//| :return: A tuple of services provided by the remote central. +//| +STATIC mp_obj_t bleio_peripheral_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_service_uuids_whitelist }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + if (!common_hal_bleio_peripheral_get_connected(self)) { + mp_raise_ValueError(translate("Not connected")); + } + + return MP_OBJ_FROM_PTR(common_hal_bleio_peripheral_discover_remote_services( + MP_OBJ_FROM_PTR(self), + args[ARG_service_uuids_whitelist].u_obj)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_discover_remote_services_obj, 1, bleio_peripheral_discover_remote_services); + +//| .. method:: pair() +//| +//| Request pairing with connected central. +STATIC mp_obj_t bleio_peripheral_pair(mp_obj_t self_in) { + bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_peripheral_pair(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_pair_obj, bleio_peripheral_pair); + +STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_peripheral_discover_remote_services_obj) }, + { MP_ROM_QSTR(MP_QSTR_pair), MP_ROM_PTR(&bleio_peripheral_pair_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) }, + { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table); + +const mp_obj_type_t bleio_peripheral_type = { + { &mp_type_type }, + .name = MP_QSTR_Peripheral, + .make_new = bleio_peripheral_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_peripheral_locals_dict +}; diff --git a/shared-bindings/_bleio/Peripheral.h b/shared-bindings/_bleio/Peripheral.h new file mode 100644 index 000000000..bc56a9338 --- /dev/null +++ b/shared-bindings/_bleio/Peripheral.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_PERIPHERAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H + +#include "py/objtuple.h" +#include "common-hal/_bleio/Peripheral.h" +#include "common-hal/_bleio/Service.h" + +extern const mp_obj_type_t bleio_peripheral_type; + +extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_t name); +extern void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio_service_obj_t *service); +extern mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self); +extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self); +extern mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self); +extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, float interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo); +extern void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *device); +extern void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *device); +extern mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist); +extern void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *device); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H diff --git a/shared-bindings/_bleio/ScanEntry.c b/shared-bindings/_bleio/ScanEntry.c new file mode 100644 index 000000000..bec380d03 --- /dev/null +++ b/shared-bindings/_bleio/ScanEntry.c @@ -0,0 +1,111 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 + +#include "py/objproperty.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-module/_bleio/ScanEntry.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`ScanEntry` -- BLE scan response entry +//| ========================================================= +//| +//| Encapsulates information about a device that was received as a +//| response to a BLE scan request. This object may only be created +//| by a `_bleio.Scanner`: it has no user-visible constructor. +//| + +//| .. attribute:: address +//| +//| The address of the device (read-only), of type `_bleio.Address`. +//| +STATIC mp_obj_t bleio_scanentry_get_address(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_bleio_scanentry_get_address(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_address_obj, bleio_scanentry_get_address); + +const mp_obj_property_t bleio_scanentry_address_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_scanentry_get_address_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: advertisement_bytes +//| +//| All the advertisement data present in the packet, returned as a ``bytes`` object. (read-only) +//| +STATIC mp_obj_t scanentry_get_advertisement_bytes(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_bleio_scanentry_get_advertisement_bytes(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_advertisement_bytes_obj, scanentry_get_advertisement_bytes); + +const mp_obj_property_t bleio_scanentry_advertisement_bytes_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_scanentry_get_advertisement_bytes_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: rssi +//| +//| The signal strength of the device at the time of the scan, in integer dBm. (read-only) +//| +STATIC mp_obj_t scanentry_get_rssi(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_bleio_scanentry_get_rssi(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_rssi_obj, scanentry_get_rssi); + +const mp_obj_property_t bleio_scanentry_rssi_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_scanentry_get_rssi_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + + +STATIC const mp_rom_map_elem_t bleio_scanentry_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_scanentry_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_advertisement_bytes), MP_ROM_PTR(&bleio_scanentry_advertisement_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&bleio_scanentry_rssi_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_scanentry_locals_dict, bleio_scanentry_locals_dict_table); + +const mp_obj_type_t bleio_scanentry_type = { + { &mp_type_type }, + .name = MP_QSTR_ScanEntry, + .locals_dict = (mp_obj_dict_t*)&bleio_scanentry_locals_dict +}; diff --git a/shared-bindings/_bleio/ScanEntry.h b/shared-bindings/_bleio/ScanEntry.h new file mode 100644 index 000000000..8af1f050a --- /dev/null +++ b/shared-bindings/_bleio/ScanEntry.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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_SHARED_BINDINGS_BLEIO_SCANENTRY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H + +#include "py/obj.h" +#include "shared-module/_bleio/ScanEntry.h" + +extern const mp_obj_type_t bleio_scanentry_type; + +mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self); +mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self); +mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H diff --git a/shared-bindings/_bleio/Scanner.c b/shared-bindings/_bleio/Scanner.c new file mode 100644 index 000000000..94cec9752 --- /dev/null +++ b/shared-bindings/_bleio/Scanner.c @@ -0,0 +1,129 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/Scanner.h" + +#define INTERVAL_DEFAULT (0.1f) +#define INTERVAL_MIN (0.0025f) +#define INTERVAL_MIN_STRING "0.0025" +#define INTERVAL_MAX (40.959375f) +#define INTERVAL_MAX_STRING "40.959375" +#define WINDOW_DEFAULT (0.1f) + +//| .. currentmodule:: _bleio +//| +//| :class:`Scanner` -- scan for nearby BLE devices +//| ========================================================= +//| +//| Scan for nearby BLE devices. +//| +//| Usage:: +//| +//| import _bleio +//| scanner = _bleio.Scanner() +//| entries = scanner.scan(2.5) # Scan for 2.5 seconds +//| + +//| .. class:: Scanner() +//| +//| Create a new Scanner object. +//| +STATIC mp_obj_t bleio_scanner_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *all_args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 0, 0, false); + + bleio_scanner_obj_t *self = m_new_obj(bleio_scanner_obj_t); + self->base.type = type; + + common_hal_bleio_scanner_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: scan(timeout, \*, interval=0.1, window=0.1) +//| +//| Performs a BLE scan. +//| +//| :param float timeout: the scan timeout in seconds +//| :param float interval: the interval (in seconds) between the start of two consecutive scan windows +//| Must be in the range 0.0025 - 40.959375 seconds. +//| :param float window: the duration (in seconds) to scan a single BLE channel. +//| window must be <= interval. +//| :returns: an iterable of `ScanEntry` objects +//| :rtype: iterable +//| +STATIC mp_obj_t bleio_scanner_scan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_timeout, ARG_interval, ARG_window }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_window, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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); + + const mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + + if (args[ARG_interval].u_obj == MP_OBJ_NULL) { + args[ARG_interval].u_obj = mp_obj_new_float(INTERVAL_DEFAULT); + } + + if (args[ARG_window].u_obj == MP_OBJ_NULL) { + args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT); + } + + const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj); + if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { + mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING); + } + + const mp_float_t window = mp_obj_float_get(args[ARG_window].u_obj); + if (window > interval) { + mp_raise_ValueError(translate("window must be <= interval")); + } + + return common_hal_bleio_scanner_scan(self, timeout, interval, window); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_scanner_scan_obj, 2, bleio_scanner_scan); + + +STATIC const mp_rom_map_elem_t bleio_scanner_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&bleio_scanner_scan_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_scanner_locals_dict, bleio_scanner_locals_dict_table); + +const mp_obj_type_t bleio_scanner_type = { + { &mp_type_type }, + .name = MP_QSTR_Scanner, + .make_new = bleio_scanner_make_new, + .locals_dict = (mp_obj_dict_t*)&bleio_scanner_locals_dict +}; diff --git a/shared-bindings/_bleio/Scanner.h b/shared-bindings/_bleio/Scanner.h new file mode 100644 index 000000000..cbaa77866 --- /dev/null +++ b/shared-bindings/_bleio/Scanner.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_SCANNER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H + +#include "py/objtype.h" +#include "common-hal/_bleio/Scanner.h" + +extern const mp_obj_type_t bleio_scanner_type; + +extern void common_hal_bleio_scanner_construct(bleio_scanner_obj_t *self); +extern mp_obj_t common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_float_t timeout, mp_float_t interval, mp_float_t window); +extern void common_hal_bleio_scanner_stop(bleio_scanner_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H diff --git a/shared-bindings/_bleio/Service.c b/shared-bindings/_bleio/Service.c new file mode 100644 index 000000000..da5633f2a --- /dev/null +++ b/shared-bindings/_bleio/Service.c @@ -0,0 +1,201 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Peripheral.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`Service` -- BLE service +//| ========================================================= +//| +//| Stores information about a BLE service and its characteristics. +//| +//| .. class:: Service +//| +//| There is no regular constructor for a Service. A new local Service can be created +//| and attached to a Peripheral by calling `add_to_peripheral()`. +//| Remote Service objects are created by `Central.discover_remote_services()` +//| or `Peripheral.discover_remote_services()`. +//| +//| .. classmethod:: add_to_peripheral(peripheral, uuid, *, secondary=False) +//| +//| Create a new Service object, identitied by the specified UUID, and add it +//| to the given Peripheral. +//| +//| To mark the service as secondary, pass `True` as :py:data:`secondary`. +//| +//| :param Peripheral peripheral: The peripheral that will provide this service +//| :param UUID uuid: The uuid of the service +//| :param bool secondary: If the service is a secondary one +// +//| :return: the new Service +//| +STATIC mp_obj_t bleio_service_add_to_peripheral(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_peripheral, ARG_uuid, ARG_secondary }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_peripheral, MP_ARG_REQUIRED | MP_ARG_OBJ,}, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + 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); + + const mp_obj_t peripheral_obj = args[ARG_peripheral].u_obj; + if (!MP_OBJ_IS_TYPE(peripheral_obj, &bleio_peripheral_type)) { + mp_raise_ValueError(translate("Expected a Peripheral")); + } + + const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("Expected a UUID")); + } + + const bool is_secondary = args[ARG_secondary].u_bool; + + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + + common_hal_bleio_service_construct( + service, MP_OBJ_TO_PTR(peripheral_obj), MP_OBJ_TO_PTR(uuid_obj), is_secondary); + + common_hal_bleio_peripheral_add_service(peripheral_obj, service); + + return MP_OBJ_FROM_PTR(service); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_service_add_to_peripheral_fun_obj, 3, bleio_service_add_to_peripheral); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_service_add_to_peripheral_obj, MP_ROM_PTR(&bleio_service_add_to_peripheral_fun_obj)); + +//| .. attribute:: characteristics +//| +//| A tuple of :py:class:`Characteristic` designating the characteristics that are offered by this service. (read-only) +//| +STATIC mp_obj_t bleio_service_get_characteristics(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + // Return list as a tuple so user won't be able to change it. + mp_obj_list_t *char_list = common_hal_bleio_service_get_characteristic_list(self); + return mp_obj_new_tuple(char_list->len, char_list->items); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_characteristics_obj, bleio_service_get_characteristics); + +const mp_obj_property_t bleio_service_characteristics_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_service_get_characteristics_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: remote +//| +//| True if this is a service provided by a remote device. (read-only) +//| +STATIC mp_obj_t bleio_service_get_remote(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_service_get_is_remote(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_remote_obj, bleio_service_get_remote); + +const mp_obj_property_t bleio_service_remote_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_service_get_remote_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: secondary +//| +//| True if this is a secondary service. (read-only) +//| +STATIC mp_obj_t bleio_service_get_secondary(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_service_get_is_secondary(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_secondary_obj, bleio_service_get_secondary); + +const mp_obj_property_t bleio_service_secondary_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_service_get_secondary_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + +//| .. attribute:: uuid +//| +//| The UUID of this service. (read-only) +//| Will be ``None`` if the 128-bit UUID for this service is not known. +//| +STATIC mp_obj_t bleio_service_get_uuid(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_service_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_uuid_obj, bleio_service_get_uuid); + +const mp_obj_property_t bleio_service_uuid_obj = { + .base.type = &mp_type_property, + .proxy = { (mp_obj_t)&bleio_service_get_uuid_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj }, +}; + + +STATIC const mp_rom_map_elem_t bleio_service_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_to_peripheral), MP_ROM_PTR(&bleio_service_add_to_peripheral_obj) }, + { MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) }, + { MP_ROM_QSTR(MP_QSTR_secondary), MP_ROM_PTR(&bleio_service_secondary_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict_table); + +STATIC void bleio_service_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Service("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, ""); + } +} + +const mp_obj_type_t bleio_service_type = { + { &mp_type_type }, + .name = MP_QSTR_Service, + .print = bleio_service_print, + .locals_dict = (mp_obj_dict_t*)&bleio_service_locals_dict +}; diff --git a/shared-bindings/_bleio/Service.h b/shared-bindings/_bleio/Service.h new file mode 100644 index 000000000..e061bcffc --- /dev/null +++ b/shared-bindings/_bleio/Service.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_SERVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H + +#include "common-hal/_bleio/Peripheral.h" +#include "common-hal/_bleio/Service.h" + +const mp_obj_type_t bleio_service_type; + +extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary); +extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self); +extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self); +extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self); +extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self); +extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H diff --git a/shared-bindings/_bleio/UUID.c b/shared-bindings/_bleio/UUID.c new file mode 100644 index 000000000..3c0889aad --- /dev/null +++ b/shared-bindings/_bleio/UUID.c @@ -0,0 +1,280 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 + +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/UUID.h" + +//| .. currentmodule:: _bleio +//| +//| :class:`UUID` -- BLE UUID +//| ========================================================= +//| +//| A 16-bit or 128-bit UUID. Can be used for services, characteristics, descriptors and more. +//| + +//| .. class:: UUID(value) +//| +//| Create a new UUID or UUID object encapsulating the uuid value. +//| The value can be one of: +//| +//| - an `int` value in range 0 to 0xFFFF (Bluetooth SIG 16-bit UUID) +//| - a buffer object (bytearray, bytes) of 16 bytes in little-endian order (128-bit UUID) +//| - a string of hex digits of the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +//| +//| Creating a 128-bit UUID registers the UUID with the onboard BLE software, and provides a +//| temporary 16-bit UUID that can be used in place of the full 128-bit UUID. +//| +//| :param value: The uuid value to encapsulate +//| :type value: int or typing.ByteString +//| +STATIC mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 1, 1, false); + + bleio_uuid_obj_t *self = m_new_obj(bleio_uuid_obj_t); + self->base.type = type; + + const mp_obj_t value = pos_args[0]; + uint8_t uuid128[16]; + + if (MP_OBJ_IS_INT(value)) { + mp_int_t uuid16 = mp_obj_get_int(value); + if (uuid16 < 0 || uuid16 > 0xffff) { + mp_raise_ValueError(translate("UUID integer value must be 0-0xffff")); + } + + // NULL means no 128-bit value. + common_hal_bleio_uuid_construct(self, uuid16, NULL); + + } else { + if (MP_OBJ_IS_STR(value)) { + // 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + GET_STR_DATA_LEN(value, chars, len); + char hex[32]; + // Validate length, hyphens, and hex digits. + bool good_uuid = + len == 36 && chars[8] == '-' && chars[13] == '-' && chars[18] == '-' && chars[23] == '-'; + if (good_uuid) { + size_t hex_idx = 0; + for (size_t i = 0; i < len; i++) { + if (unichar_isxdigit(chars[i])) { + hex[hex_idx] = chars[i]; + hex_idx++; + } + } + good_uuid = hex_idx == 32; + } + if (!good_uuid) { + mp_raise_ValueError(translate("UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'")); + } + + size_t hex_idx = 0; + for (int i = 15; i >= 0; i--) { + uuid128[i] = (unichar_xdigit_value(hex[hex_idx]) << 4) | unichar_xdigit_value(hex[hex_idx + 1]); + hex_idx += 2; + } + } else { + // Last possibility is that it's a buf. + mp_buffer_info_t bufinfo; + if (!mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) { + mp_raise_ValueError(translate("UUID value is not str, int or byte buffer")); + } + + if (bufinfo.len != 16) { + mp_raise_ValueError(translate("Byte buffer must be 16 bytes.")); + } + + memcpy(uuid128, bufinfo.buf, 16); + } + + // Str and bytes both get constructed the same way here. + uint32_t uuid16 = (uuid128[13] << 8) | uuid128[12]; + uuid128[12] = 0; + uuid128[13] = 0; + common_hal_bleio_uuid_construct(self, uuid16, uuid128); + } + + return MP_OBJ_FROM_PTR(self); +} + +//| .. attribute:: uuid16 +//| +//| The 16-bit part of the UUID. (read-only) +//| +//| :type: int +//| +STATIC mp_obj_t bleio_uuid_get_uuid16(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid16_obj, bleio_uuid_get_uuid16); + +const mp_obj_property_t bleio_uuid_uuid16_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&bleio_uuid_get_uuid16_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: uuid128 +//| +//| The 128-bit value of the UUID +//| Raises AttributeError if this is a 16-bit UUID. (read-only) +//| +//| :type: bytes +//| +STATIC mp_obj_t bleio_uuid_get_uuid128(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uint8_t uuid128[16]; + if (!common_hal_bleio_uuid_get_uuid128(self, uuid128)) { + mp_raise_AttributeError(translate("not a 128-bit UUID")); + } + return mp_obj_new_bytes(uuid128, 16); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid128_obj, bleio_uuid_get_uuid128); + +const mp_obj_property_t bleio_uuid_uuid128_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&bleio_uuid_get_uuid128_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: size +//| +//| 128 if this UUID represents a 128-bit vendor-specific UUID. 16 if this UUID represents a +//| 16-bit Bluetooth SIG assigned UUID. (read-only) 32-bit UUIDs are not currently supported. +//| +//| :type: int +//| +STATIC mp_obj_t bleio_uuid_get_size(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_size(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_size_obj, bleio_uuid_get_size); + +const mp_obj_property_t bleio_uuid_size_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&bleio_uuid_get_size_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t bleio_uuid_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_uuid16), MP_ROM_PTR(&bleio_uuid_uuid16_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid128), MP_ROM_PTR(&bleio_uuid_uuid128_obj) }, + { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&bleio_uuid_size_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_uuid_locals_dict, bleio_uuid_locals_dict_table); + +STATIC mp_obj_t bleio_uuid_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_HASH: + if (common_hal_bleio_uuid_get_size(self) == 16) { + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); + } else { + union { + uint8_t uuid128_bytes[16]; + uint16_t uuid128_uint16[8]; + } uuid128; + common_hal_bleio_uuid_get_uuid128(self, uuid128.uuid128_bytes); + int hash = 0; + for (size_t i = 0; i < MP_ARRAY_SIZE(uuid128.uuid128_uint16); i++) { + hash += uuid128.uuid128_uint16[i]; + } + return MP_OBJ_NEW_SMALL_INT(hash); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| + +//| .. method:: __eq__(other) +//| +//| Two UUID objects are equal if their values match and they are both 128-bit or both 16-bit. +//| +STATIC mp_obj_t bleio_uuid_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + // Two UUID's are equal if their uuid16 values and uuid128 references match. + case MP_BINARY_OP_EQUAL: + if (MP_OBJ_IS_TYPE(rhs_in, &bleio_uuid_type)) { + return mp_obj_new_bool( + common_hal_bleio_uuid_get_uuid16(lhs_in) == common_hal_bleio_uuid_get_uuid16(rhs_in) && + common_hal_bleio_uuid_get_uuid128_reference(lhs_in) == + common_hal_bleio_uuid_get_uuid128_reference(rhs_in)); + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t size = common_hal_bleio_uuid_get_size(self); + if (size == 16) { + mp_printf(print, "UUID(0x%04x)", common_hal_bleio_uuid_get_uuid16(self)); + } else { + uint8_t uuid128[16]; + (void) common_hal_bleio_uuid_get_uuid128(self, uuid128); + mp_printf(print, "UUID('" + "%02x%02x%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x%02x%02x%02x%02x')", + uuid128[15], uuid128[14], uuid128[13], uuid128[12], + uuid128[11], uuid128[10], + uuid128[9], uuid128[8], + uuid128[7], uuid128[6], + uuid128[5], uuid128[4], uuid128[3], uuid128[2], uuid128[1], uuid128[0]); + } +} + +const mp_obj_type_t bleio_uuid_type = { + { &mp_type_type }, + .name = MP_QSTR_UUID, + .print = bleio_uuid_print, + .make_new = bleio_uuid_make_new, + .unary_op = bleio_uuid_unary_op, + .binary_op = bleio_uuid_binary_op, + .locals_dict = (mp_obj_dict_t*)&bleio_uuid_locals_dict, +}; diff --git a/shared-bindings/_bleio/UUID.h b/shared-bindings/_bleio/UUID.h new file mode 100644 index 000000000..46ac54ff3 --- /dev/null +++ b/shared-bindings/_bleio/UUID.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_BINDINGS_BLEIO_UUID_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H + +#include "common-hal/_bleio/UUID.h" + +void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); + +extern const mp_obj_type_t bleio_uuid_type; + +extern void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, uint8_t uuid128[]); +extern uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self); +extern bool common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]); +extern uint32_t common_hal_bleio_uuid_get_uuid128_reference(bleio_uuid_obj_t *self); +extern uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H diff --git a/shared-bindings/_bleio/__init__.c b/shared-bindings/_bleio/__init__.c new file mode 100644 index 000000000..f207be8cf --- /dev/null +++ b/shared-bindings/_bleio/__init__.c @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Central.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Peripheral.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/Scanner.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| :mod:`_bleio` --- Bluetooth Low Energy (BLE) communication +//| ================================================================ +//| +//| .. module:: _bleio +//| :synopsis: Bluetooth Low Energy functionality +//| :platform: nRF +//| +//| The `_bleio` module provides necessary low-level functionality for communicating +//| using Bluetooth Low Energy (BLE). The '_' prefix indicates this module is meant +//| for internal use by libraries but not by the end user. Its API may change incompatibly +//| between minor versions of CircuitPython. +//| Please use the +//| `adafruit_ble `_ +//| CircuitPython library instead, which builds on `_bleio`, and +//| provides higher-level convenience functionality, including predefined beacons, clients, +//| servers. +//| +//| Libraries +//| +//| .. toctree:: +//| :maxdepth: 3 +//| +//| Address +//| Adapter +//| Attribute +//| Central +//| Characteristic +//| CharacteristicBuffer +//| Descriptor +//| Peripheral +//| ScanEntry +//| Scanner +//| Service +//| UUID +//| +//| .. attribute:: adapter +//| +//| BLE Adapter information, such as enabled state as well as MAC +//| address. +//| This object is the sole instance of `_bleio.Adapter`. +//| + +STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__bleio) }, + { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, + { MP_ROM_QSTR(MP_QSTR_Attribute), MP_ROM_PTR(&bleio_attribute_type) }, + { MP_ROM_QSTR(MP_QSTR_Central), MP_ROM_PTR(&bleio_central_type) }, + { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, + { MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), MP_ROM_PTR(&bleio_characteristic_buffer_type) }, + { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, + { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&bleio_peripheral_type) }, + { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, + { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, + { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, + { MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_module_globals, bleio_module_globals_table); + +const mp_obj_module_t bleio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&bleio_module_globals, +}; diff --git a/shared-bindings/_bleio/__init__.h b/shared-bindings/_bleio/__init__.h new file mode 100644 index 000000000..67379ae2e --- /dev/null +++ b/shared-bindings/_bleio/__init__.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_SHARED_BINDINGS_BLEIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H + +#include "py/objlist.h" + +#include "shared-bindings/_bleio/Adapter.h" + +#include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/Adapter.h" + +extern const super_adapter_obj_t common_hal_bleio_adapter_obj; + +extern void common_hal_bleio_check_connected(uint16_t conn_handle); + +extern uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); +extern mp_obj_list_t *common_hal_bleio_device_get_remote_service_list(mp_obj_t device); +extern void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); + +extern mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle); +extern void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); +extern void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); + + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H diff --git a/shared-bindings/bleio/Adapter.c b/shared-bindings/bleio/Adapter.c deleted file mode 100644 index 2bb7f3465..000000000 --- a/shared-bindings/bleio/Adapter.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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/objproperty.h" -#include "shared-bindings/bleio/Address.h" -#include "shared-bindings/bleio/Adapter.h" - -//| .. currentmodule:: bleio -//| -//| :class:`Adapter` --- BLE adapter information -//| ---------------------------------------------------- -//| -//| Get current status of the BLE adapter -//| -//| Usage:: -//| -//| import bleio -//| bleio.adapter.enabled = True -//| print(bleio.adapter.address) -//| - -//| .. class:: Adapter() -//| -//| You cannot create an instance of `bleio.Adapter`. -//| Use `bleio.adapter` to access the sole instance available. -//| - -//| .. attribute:: adapter.enabled -//| -//| State of the BLE adapter. -//| -STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) { - return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled()); -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_enabled_obj, bleio_adapter_get_enabled); - -static mp_obj_t bleio_adapter_set_enabled(mp_obj_t self, mp_obj_t value) { - const bool enabled = mp_obj_is_true(value); - - common_hal_bleio_adapter_set_enabled(enabled); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_enabled_obj, bleio_adapter_set_enabled); - -const mp_obj_property_t bleio_adapter_enabled_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_adapter_get_enabled_obj, - (mp_obj_t)&bleio_adapter_set_enabled_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: adapter.address -//| -//| MAC address of the BLE adapter. (read-only) -//| -STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) { - return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_address()); - -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address); - -const mp_obj_property_t bleio_adapter_address_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_adapter_get_address_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: adapter.default_name -//| -//| default_name of the BLE adapter. (read-only) -//| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``, -//| to make it easy to distinguish multiple CircuitPython boards. -//| -STATIC mp_obj_t bleio_adapter_get_default_name(mp_obj_t self) { - return common_hal_bleio_adapter_get_default_name(); - -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_default_name_obj, bleio_adapter_get_default_name); - -const mp_obj_property_t bleio_adapter_default_name_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_adapter_get_default_name_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) }, - { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_adapter_address_obj) }, - { MP_ROM_QSTR(MP_QSTR_default_name), MP_ROM_PTR(&bleio_adapter_default_name_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table); - -const mp_obj_type_t bleio_adapter_type = { - .base = { &mp_type_type }, - .name = MP_QSTR_Adapter, - .locals_dict = (mp_obj_t)&bleio_adapter_locals_dict, -}; diff --git a/shared-bindings/bleio/Adapter.h b/shared-bindings/bleio/Adapter.h deleted file mode 100644 index 72382616c..000000000 --- a/shared-bindings/bleio/Adapter.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_ADAPTER_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H - -#include "shared-module/bleio/Address.h" - -const mp_obj_type_t bleio_adapter_type; - -extern bool common_hal_bleio_adapter_get_enabled(void); -extern void common_hal_bleio_adapter_set_enabled(bool enabled); -extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(void); -extern mp_obj_t common_hal_bleio_adapter_get_default_name(void); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H diff --git a/shared-bindings/bleio/Address.c b/shared-bindings/bleio/Address.c deleted file mode 100644 index baeb6d7c0..000000000 --- a/shared-bindings/bleio/Address.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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 -#include - -#include "py/objproperty.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Address.h" -#include "shared-module/bleio/Address.h" - -//| .. currentmodule:: bleio -//| -//| :class:`Address` -- BLE address -//| ========================================================= -//| -//| Encapsulates the address of a BLE device. -//| - -//| .. class:: Address(address, address_type) -//| -//| Create a new Address object encapsulating the address value. -//| The value itself can be one of: -//| -//| :param buf address: The address value to encapsulate. A buffer object (bytearray, bytes) of 6 bytes. -//| :param int address_type: one of the integer values: `PUBLIC`, `RANDOM_STATIC`, -//| `RANDOM_PRIVATE_RESOLVABLE`, or `RANDOM_PRIVATE_NON_RESOLVABLE`. -//| -STATIC mp_obj_t bleio_address_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_address, ARG_address_type }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_address, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_address_type, MP_ARG_INT, {.u_int = BLEIO_ADDRESS_TYPE_PUBLIC } }, - }; - - 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); - - bleio_address_obj_t *self = m_new_obj(bleio_address_obj_t); - self->base.type = &bleio_address_type; - - const mp_obj_t address = args[ARG_address].u_obj; - mp_buffer_info_t buf_info; - mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); - if (buf_info.len != NUM_BLEIO_ADDRESS_BYTES) { - mp_raise_ValueError_varg(translate("Address must be %d bytes long"), NUM_BLEIO_ADDRESS_BYTES); - } - - const mp_int_t address_type = args[ARG_address_type].u_int; - if (address_type < BLEIO_ADDRESS_TYPE_MIN || address_type > BLEIO_ADDRESS_TYPE_MAX) { - mp_raise_ValueError(translate("Address type out of range")); - } - - common_hal_bleio_address_construct(self, buf_info.buf, address_type); - - return MP_OBJ_FROM_PTR(self); -} - -//| .. attribute:: address_bytes -//| -//| The bytes that make up the device address (read-only). -//| -//| Note that the ``bytes`` object returned is in little-endian order: -//| The least significant byte is ``address_bytes[0]``. So the address will -//| appear to be reversed if you print the raw ``bytes`` object. If you print -//| or use `str()` on the :py:class:`~bleio.Attribute` object itself, the address will be printed -//| in the expected order. For example: -//| -//| .. code-block:: pycon -//| -//| >>> import bleio -//| >>> bleio.adapter.address -//|
-//| >>> bleio.adapter.address.address_bytes -//| b'5\xa8\xed\xf5\x1d\xc8' -//| -STATIC mp_obj_t bleio_address_get_address_bytes(mp_obj_t self_in) { - bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return common_hal_bleio_address_get_address_bytes(self); -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_address_bytes_obj, bleio_address_get_address_bytes); - -const mp_obj_property_t bleio_address_address_bytes_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&bleio_address_get_address_bytes_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: type -//| -//| The address type (read-only). -//| One of the integer values: `PUBLIC`, `RANDOM_STATIC`, -//| `RANDOM_PRIVATE_RESOLVABLE`, or `RANDOM_PRIVATE_NON_RESOLVABLE`. -//| -STATIC mp_obj_t bleio_address_get_type(mp_obj_t self_in) { - bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_address_get_type(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_type_obj, bleio_address_get_type); - -const mp_obj_property_t bleio_address_type_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&bleio_address_get_type_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. method:: __eq__(other) -//| -//| Two Address objects are equal if their addresses and address types are equal. -//| -STATIC mp_obj_t bleio_address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { - switch (op) { - // Two Addresses are equal if their address bytes and address_type are equal - case MP_BINARY_OP_EQUAL: - if (MP_OBJ_IS_TYPE(rhs_in, &bleio_address_type)) { - bleio_address_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); - bleio_address_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); - return mp_obj_new_bool( - mp_obj_equal(common_hal_bleio_address_get_address_bytes(lhs), - common_hal_bleio_address_get_address_bytes(rhs)) && - common_hal_bleio_address_get_type(lhs) == - common_hal_bleio_address_get_type(rhs)); - - } else { - return mp_const_false; - } - - default: - return MP_OBJ_NULL; // op not supported - } -} - -STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_buffer_info_t buf_info; - mp_obj_t address_bytes = common_hal_bleio_address_get_address_bytes(self); - mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ); - - const uint8_t *buf = (uint8_t *) buf_info.buf; - mp_printf(print, "
", - buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]); -} - -//| .. data:: PUBLIC -//| -//| A publicly known address, with a company ID (high 24 bits)and company-assigned part (low 24 bits). -//| -//| .. data:: RANDOM_STATIC -//| -//| A randomly generated address that does not change often. It may never change or may change after -//| a power cycle. -//| -//| .. data:: RANDOM_PRIVATE_RESOLVABLE -//| -//| An address that is usable when the peer knows the other device's secret Identity Resolving Key (IRK). -//| -//| .. data:: RANDOM_PRIVATE_NON_RESOLVABLE -//| -//| A randomly generated address that changes on every connection. -//| -STATIC const mp_rom_map_elem_t bleio_address_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_address_bytes), MP_ROM_PTR(&bleio_address_address_bytes_obj) }, - { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&bleio_address_type_obj) }, - // These match the BLE_GAP_ADDR_TYPES values used by the nRF library. - { MP_ROM_QSTR(MP_QSTR_PUBLIC), MP_ROM_INT(0) }, - { MP_ROM_QSTR(MP_QSTR_RANDOM_STATIC), MP_ROM_INT(1) }, - { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_RESOLVABLE), MP_ROM_INT(2) }, - { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_NON_RESOLVABLE), MP_ROM_INT(3) }, - -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_address_locals_dict, bleio_address_locals_dict_table); - -const mp_obj_type_t bleio_address_type = { - { &mp_type_type }, - .name = MP_QSTR_Address, - .make_new = bleio_address_make_new, - .print = bleio_address_print, - .binary_op = bleio_address_binary_op, - .locals_dict = (mp_obj_dict_t*)&bleio_address_locals_dict -}; diff --git a/shared-bindings/bleio/Address.h b/shared-bindings/bleio/Address.h deleted file mode 100644 index 5e02ee210..000000000 --- a/shared-bindings/bleio/Address.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_ADDRESS_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H - -#include "py/objtype.h" -#include "shared-module/bleio/Address.h" - -#define BLEIO_ADDRESS_TYPE_PUBLIC (0) -#define BLEIO_ADDRESS_TYPE_RANDOM_STATIC (1) -#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE (2) -#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE (3) - -#define BLEIO_ADDRESS_TYPE_MIN BLEIO_ADDRESS_TYPE_PUBLIC -#define BLEIO_ADDRESS_TYPE_MAX BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE - -extern const mp_obj_type_t bleio_address_type; - -extern void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type); -extern mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self); -extern uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H diff --git a/shared-bindings/bleio/Attribute.c b/shared-bindings/bleio/Attribute.c deleted file mode 100644 index 2cc9ef6d0..000000000 --- a/shared-bindings/bleio/Attribute.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/UUID.h" - -// - -//| .. currentmodule:: bleio -//| -//| :class:`Attribute` -- BLE Attribute -//| ========================================================= -//| -//| Definitions associated with all BLE attributes: characteristics, descriptors, etc. -//| :py:class:`~bleio.Attribute` is, notionally, a superclass of -//| :py:class:`~Characteristic` and :py:class:`~Descriptor`, -//| but is not defined as a Python superclass of those classes. -//| -//| .. class:: Attribute() -//| -//| You cannot create an instance of :py:class:`~bleio.Attribute`. -//| - -STATIC const mp_rom_map_elem_t bleio_attribute_locals_dict_table[] = { - -//| .. data:: NO_ACCESS -//| -//| security mode: access not allowed -//| -//| .. data:: OPEN -//| -//| security_mode: no security (link is not encrypted) -//| -//| .. data:: ENCRYPT_NO_MITM -//| -//| security_mode: unauthenticated encryption, without man-in-the-middle protection -//| -//| .. data:: ENCRYPT_WITH_MITM -//| -//| security_mode: authenticated encryption, with man-in-the-middle protection -//| -//| .. data:: LESC_ENCRYPT_WITH_MITM -//| -//| security_mode: LESC encryption, with man-in-the-middle protection -//| -//| .. data:: SIGNED_NO_MITM -//| -//| security_mode: unauthenticated data signing, without man-in-the-middle protection -//| -//| .. data:: SIGNED_WITH_MITM -//| -//| security_mode: authenticated data signing, without man-in-the-middle protection -//| - { MP_ROM_QSTR(MP_QSTR_NO_ACCESS), MP_ROM_INT(SECURITY_MODE_NO_ACCESS) }, - { MP_ROM_QSTR(MP_QSTR_OPEN), MP_ROM_INT(SECURITY_MODE_OPEN) }, - { MP_ROM_QSTR(MP_QSTR_ENCRYPT_NO_MITM), MP_ROM_INT(SECURITY_MODE_ENC_NO_MITM) }, - { MP_ROM_QSTR(MP_QSTR_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_ENC_WITH_MITM) }, - { MP_ROM_QSTR(MP_QSTR_LESC_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_LESC_ENC_WITH_MITM) }, - { MP_ROM_QSTR(MP_QSTR_SIGNED_NO_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_NO_MITM) }, - { MP_ROM_QSTR(MP_QSTR_SIGNED_WITH_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_WITH_MITM) }, - -}; -STATIC MP_DEFINE_CONST_DICT(bleio_attribute_locals_dict, bleio_attribute_locals_dict_table); - -const mp_obj_type_t bleio_attribute_type = { - { &mp_type_type }, - .name = MP_QSTR_Attribute, - .locals_dict = (mp_obj_dict_t*)&bleio_attribute_locals_dict, -}; diff --git a/shared-bindings/bleio/Attribute.h b/shared-bindings/bleio/Attribute.h deleted file mode 100644 index 39bef5057..000000000 --- a/shared-bindings/bleio/Attribute.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H - -#include "py/obj.h" - -#include "common-hal/bleio/Attribute.h" -#include "shared-module/bleio/Attribute.h" - -extern const mp_obj_type_t bleio_attribute_type; - -extern void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H diff --git a/shared-bindings/bleio/Central.c b/shared-bindings/bleio/Central.c deleted file mode 100644 index 1fc455d8d..000000000 --- a/shared-bindings/bleio/Central.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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 -#include - -#include "ble_drv.h" -#include "py/objarray.h" -#include "py/objproperty.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Address.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Central.h" -#include "shared-bindings/bleio/Service.h" - -//| .. currentmodule:: bleio -//| -//| :class:`Central` -- A BLE central device -//| ========================================================= -//| -//| Implement a BLE central, which runs locally. Can connect to a given address. -//| -//| Usage:: -//| -//| import bleio -//| -//| scanner = bleio.Scanner() -//| entries = scanner.scan(2.5) -//| -//| my_entry = None -//| for entry in entries: -//| if entry.name is not None and entry.name == 'InterestingPeripheral': -//| my_entry = entry -//| break -//| -//| if not my_entry: -//| raise Exception("'InterestingPeripheral' not found") -//| -//| central = bleio.Central() -//| central.connect(my_entry.address, 10) # timeout after 10 seconds -//| remote_services = central.discover_remote_services() -//| - -//| .. class:: Central() -//| -//| Create a new Central object. -//| -STATIC mp_obj_t bleio_central_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - mp_arg_check_num(n_args, kw_args, 0, 0, false); - - bleio_central_obj_t *self = m_new_obj(bleio_central_obj_t); - self->base.type = &bleio_central_type; - - common_hal_bleio_central_construct(self); - - return MP_OBJ_FROM_PTR(self); -} - -//| .. method:: connect(address, timeout, *, service_uuids_whitelist=None) -//| Attempts a connection to the remote peripheral. -//| -//| :param bleio.Address address: The address of the peripheral to connect to -//| :param float/int timeout: Try to connect for timeout seconds. -//| -STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_address, ARG_timeout, ARG_service_uuids_whitelist }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ }, - }; - - 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); - - if (!MP_OBJ_IS_TYPE(args[ARG_address].u_obj, &bleio_address_type)) { - mp_raise_ValueError(translate("Expected an Address")); - } - - bleio_address_obj_t *address = MP_OBJ_TO_PTR(args[ARG_address].u_obj); - mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); - - // common_hal_bleio_central_connect() will validate that services is an iterable or None. - common_hal_bleio_central_connect(self, address, timeout); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_connect_obj, 3, bleio_central_connect); - - -//| .. method:: disconnect() -//| -//| Disconnects from the remote peripheral. -//| -STATIC mp_obj_t bleio_central_disconnect(mp_obj_t self_in) { - bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in); - - common_hal_bleio_central_disconnect(self); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_disconnect_obj, bleio_central_disconnect); - -//| .. method:: discover_remote_services(service_uuids_whitelist=None) -//| Do BLE discovery for all services or for the given service UUIDS, -//| to find their handles and characteristics, and return the discovered services. -//| `Peripheral.connected` must be True. -//| -//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services -//| provided by the peripheral that you want to use. -//| The peripheral may provide more services, but services not listed are ignored -//| and will not be returned. -//| -//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow. -//| -//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you -//| you must have already created a :py:class:~`UUID` object for that UUID in order for the -//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered -//| for use. (This restriction may be lifted in the future.) -//| -//| :return: A tuple of services provided by the remote peripheral. -//| -STATIC mp_obj_t bleio_central_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_service_uuids_whitelist }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - 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); - - if (!common_hal_bleio_central_get_connected(self)) { - mp_raise_ValueError(translate("Not connected")); - } - - return MP_OBJ_FROM_PTR(common_hal_bleio_central_discover_remote_services( - MP_OBJ_FROM_PTR(self), - args[ARG_service_uuids_whitelist].u_obj)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_discover_remote_services_obj, 1, bleio_central_discover_remote_services); - -//| .. attribute:: connected -//| -//| True if connected to a remove peripheral. -//| -STATIC mp_obj_t bleio_central_get_connected(mp_obj_t self_in) { - bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return mp_obj_new_bool(common_hal_bleio_central_get_connected(self)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_get_connected_obj, bleio_central_get_connected); - -const mp_obj_property_t bleio_central_connected_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_central_get_connected_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -STATIC const mp_rom_map_elem_t bleio_central_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) }, - { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) }, - { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_central_discover_remote_services_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_central_connected_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_central_locals_dict, bleio_central_locals_dict_table); - -const mp_obj_type_t bleio_central_type = { - { &mp_type_type }, - .name = MP_QSTR_Central, - .make_new = bleio_central_make_new, - .locals_dict = (mp_obj_dict_t*)&bleio_central_locals_dict -}; diff --git a/shared-bindings/bleio/Central.h b/shared-bindings/bleio/Central.h deleted file mode 100644 index cbc437087..000000000 --- a/shared-bindings/bleio/Central.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_CENTRAL_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H - -#include "py/objtuple.h" -#include "common-hal/bleio/Central.h" -#include "common-hal/bleio/Service.h" - -extern const mp_obj_type_t bleio_central_type; - -extern void common_hal_bleio_central_construct(bleio_central_obj_t *self); -extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout); -extern void common_hal_bleio_central_disconnect(bleio_central_obj_t *self); -extern bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self); -extern mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H diff --git a/shared-bindings/bleio/Characteristic.c b/shared-bindings/bleio/Characteristic.c deleted file mode 100644 index 41d81087c..000000000 --- a/shared-bindings/bleio/Characteristic.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Attribute.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -//| .. currentmodule:: bleio -//| -//| :class:`Characteristic` -- BLE service characteristic -//| ========================================================= -//| -//| Stores information about a BLE service characteristic and allows reading -//| and writing of the characteristic's value. -//| -//| .. class:: Characteristic -//| -//| There is no regular constructor for a Characteristic. A new local Characteristic can be created -//| and attached to a Service by calling `add_to_service()`. -//| Remote Characteristic objects are created by `Central.discover_remote_services()` -//| or `Peripheral.discover_remote_services()` as part of remote Services. -//| - -//| .. method:: add_to_service(service, uuid, *, properties=0, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=None) -//| -//| Create a new Characteristic object, and add it to this Service. -//| -//| :param Service service: The service that will provide this characteristic -//| :param UUID uuid: The uuid of the characteristic -//| :param int properties: The properties of the characteristic, -//| specified as a bitmask of these values bitwise-or'd together: -//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. -//| :param int read_perm: Specifies whether the characteristic can be read by a client, and if so, which -//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, -//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, -//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. -//| :param int write_perm: Specifies whether the characteristic can be written by a client, and if so, which -//| security mode is required. Values allowed are the same as ``read_perm``. -//| :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed is -//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum -//| number of data bytes that fit in a single BLE 4.x ATT packet. -//| :param bool fixed_length: True if the characteristic value is of fixed length. -//| :param buf initial_value: The initial value for this characteristic. If not given, will be -//| filled with zeros. -//| -//| :return: the new Characteristic. -//| -STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - // class is arg[0], which we can ignore. - - enum { ARG_service, ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, - ARG_max_length, ARG_fixed_length, ARG_initial_value }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_service, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_properties, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, - { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - 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); - - const mp_obj_t service_obj = args[ARG_service].u_obj; - if (!MP_OBJ_IS_TYPE(service_obj, &bleio_service_type)) { - mp_raise_ValueError(translate("Expected a Service")); - } - - const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("Expected a UUID")); - } - - const bleio_characteristic_properties_t properties = args[ARG_properties].u_int; - if (properties & ~CHAR_PROP_ALL) { - mp_raise_ValueError(translate("Invalid properties")); - } - - const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(read_perm); - - const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(write_perm); - - const mp_int_t max_length = args[ARG_max_length].u_int; - const bool fixed_length = args[ARG_fixed_length].u_bool; - mp_obj_t initial_value = args[ARG_initial_value].u_obj; - - // Length will be validated in common_hal. - mp_buffer_info_t initial_value_bufinfo; - if (initial_value == mp_const_none) { - if (fixed_length && max_length > 0) { - initial_value = mp_obj_new_bytes_of_zeros(max_length); - } else { - initial_value = mp_const_empty_bytes; - } - } - mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - - bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); - characteristic->base.type = &bleio_characteristic_type; - - // Range checking on max_length arg is done by the common_hal layer, because - // it may vary depending on underlying BLE implementation. - common_hal_bleio_characteristic_construct( - characteristic, MP_OBJ_TO_PTR(service_obj), MP_OBJ_TO_PTR(uuid_obj), - properties, read_perm, write_perm, - max_length, fixed_length, &initial_value_bufinfo); - - common_hal_bleio_service_add_characteristic(service_obj, characteristic); - - return MP_OBJ_FROM_PTR(characteristic); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_add_to_service_fun_obj, 3, bleio_characteristic_add_to_service); -STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_characteristic_add_to_service_obj, MP_ROM_PTR(&bleio_characteristic_add_to_service_fun_obj)); - - - -//| .. attribute:: properties -//| -//| An int bitmask representing which properties are set, specified as bitwise or'ing of -//| of these possible values. -//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. -//| -STATIC mp_obj_t bleio_characteristic_get_properties(mp_obj_t self_in) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_get_properties(self)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_properties_obj, bleio_characteristic_get_properties); - -const mp_obj_property_t bleio_characteristic_properties_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_characteristic_get_properties_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: uuid -//| -//| The UUID of this characteristic. (read-only) -//| Will be ``None`` if the 128-bit UUID for this characteristic is not known. -//| -STATIC mp_obj_t bleio_characteristic_get_uuid(mp_obj_t self_in) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - - bleio_uuid_obj_t *uuid = common_hal_bleio_characteristic_get_uuid(self); - return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_uuid_obj, bleio_characteristic_get_uuid); - -const mp_obj_property_t bleio_characteristic_uuid_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_characteristic_get_uuid_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: value -//| -//| The value of this characteristic. -//| -STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return common_hal_bleio_characteristic_get_value(self); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_value_obj, bleio_characteristic_get_value); - -STATIC mp_obj_t bleio_characteristic_set_value(mp_obj_t self_in, mp_obj_t value_in) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); - - common_hal_bleio_characteristic_set_value(self, &bufinfo); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_value_obj, bleio_characteristic_set_value); - -const mp_obj_property_t bleio_characteristic_value_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_characteristic_get_value_obj, - (mp_obj_t)&bleio_characteristic_set_value_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: descriptors -//| -//| A tuple of :py:class:`Descriptor` that describe this characteristic. (read-only) -//| -STATIC mp_obj_t bleio_characteristic_get_descriptors(mp_obj_t self_in) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - // Return list as a tuple so user won't be able to change it. - mp_obj_list_t *char_list = common_hal_bleio_characteristic_get_descriptor_list(self); - return mp_obj_new_tuple(char_list->len, char_list->items); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_descriptors_obj, bleio_characteristic_get_descriptors); - -const mp_obj_property_t bleio_characteristic_descriptors_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_characteristic_get_descriptors_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: service (read-only) -//| -//| The Service this Characteristic is a part of. -//| -STATIC mp_obj_t bleio_characteristic_get_service(mp_obj_t self_in) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return common_hal_bleio_characteristic_get_service(self); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_service_obj, bleio_characteristic_get_service); - -const mp_obj_property_t bleio_characteristic_service_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_characteristic_get_service_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. method:: set_cccd(*, notify=False, indicate=False) -//| -//| Set the remote characteristic's CCCD to enable or disable notification and indication. -//| -//| :param bool notify: True if Characteristic should receive notifications of remote writes -//| :param float indicate: True if Characteristic should receive indications of remote writes -//| -STATIC mp_obj_t bleio_characteristic_set_cccd(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_notify, ARG_indicate }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_notify, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_indicate, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - }; - - 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); - - common_hal_bleio_characteristic_set_cccd(self, args[ARG_notify].u_bool, args[ARG_indicate].u_bool); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_characteristic_set_cccd); - - -STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) }, - { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_get_properties_obj) }, - { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, - { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) }, - - // Bitmask constants to represent properties -//| .. data:: BROADCAST -//| -//| property: allowed in advertising packets -//| -//| .. data:: INDICATE -//| -//| property: server will indicate to the client when the value is set and wait for a response -//| -//| .. data:: NOTIFY -//| -//| property: server will notify the client when the value is set -//| -//| .. data:: READ -//| -//| property: clients may read this characteristic -//| -//| .. data:: WRITE -//| -//| property: clients may write this characteristic; a response will be sent back -//| -//| .. data:: WRITE_NO_RESPONSE -//| -//| property: clients may write this characteristic; no response will be sent back -//| - { MP_ROM_QSTR(MP_QSTR_BROADCAST), MP_ROM_INT(CHAR_PROP_BROADCAST) }, - { MP_ROM_QSTR(MP_QSTR_INDICATE), MP_ROM_INT(CHAR_PROP_INDICATE) }, - { MP_ROM_QSTR(MP_QSTR_NOTIFY), MP_ROM_INT(CHAR_PROP_NOTIFY) }, - { MP_ROM_QSTR(MP_QSTR_READ), MP_ROM_INT(CHAR_PROP_READ) }, - { MP_ROM_QSTR(MP_QSTR_WRITE), MP_ROM_INT(CHAR_PROP_WRITE) }, - { MP_ROM_QSTR(MP_QSTR_WRITE_NO_RESPONSE), MP_ROM_INT(CHAR_PROP_WRITE_NO_RESPONSE) }, - -}; -STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_locals_dict, bleio_characteristic_locals_dict_table); - -STATIC void bleio_characteristic_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->uuid) { - mp_printf(print, "Characteristic("); - bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); - mp_printf(print, ")"); - } else { - mp_printf(print, ""); - } -} - -const mp_obj_type_t bleio_characteristic_type = { - { &mp_type_type }, - .name = MP_QSTR_Characteristic, - .print = bleio_characteristic_print, - .locals_dict = (mp_obj_dict_t*)&bleio_characteristic_locals_dict, -}; diff --git a/shared-bindings/bleio/Characteristic.h b/shared-bindings/bleio/Characteristic.h deleted file mode 100644 index 52df87b36..000000000 --- a/shared-bindings/bleio/Characteristic.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H - -#include "shared-bindings/bleio/Attribute.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-module/bleio/Characteristic.h" -#include "common-hal/bleio/Characteristic.h" -#include "common-hal/bleio/Service.h" - -extern const mp_obj_type_t bleio_characteristic_type; - -extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo); -extern mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self); -extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); -extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self); -extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self); -extern mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self); -extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self); -extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor); -extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H diff --git a/shared-bindings/bleio/CharacteristicBuffer.c b/shared-bindings/bleio/CharacteristicBuffer.c deleted file mode 100644 index 26f9e012b..000000000 --- a/shared-bindings/bleio/CharacteristicBuffer.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018 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/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 -//| -//| :class:`CharacteristicBuffer` -- BLE Service incoming values buffer. -//| ===================================================================== -//| -//| Accumulates a Characteristic's incoming values in a FIFO buffer. -//| -//| .. class:: CharacteristicBuffer(characteristic, *, timeout=1, buffer_size=64) -//| -//| Monitor the given Characteristic. Each time a new value is written to the Characteristic -//| add the newly-written bytes to a FIFO buffer. -//| -//| :param bleio.Characteristic characteristic: The Characteristic to monitor. -//| It may be a local Characteristic provided by a Peripheral Service, or a remote Characteristic -//| in a remote Service that a Central has connected to. -//| :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_timeout, ARG_buffer_size, }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { 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; - - 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_varg(translate("%q must be >= 1"), MP_QSTR_buffer_size); - } - - if (!MP_OBJ_IS_TYPE(characteristic, &bleio_characteristic_type)) { - mp_raise_ValueError(translate("Expected a Characteristic")); - } - - bleio_characteristic_buffer_obj_t *self = m_new_obj(bleio_characteristic_buffer_obj_t); - self->base.type = &bleio_characteristic_buffer_type; - - common_hal_bleio_characteristic_buffer_construct(self, MP_OBJ_TO_PTR(characteristic), timeout, buffer_size); - - return MP_OBJ_FROM_PTR(self); -} - -STATIC void check_for_deinit(bleio_characteristic_buffer_obj_t *self) { - if (common_hal_bleio_characteristic_buffer_deinited(self)) { - raise_deinited_error(); - } -} - -// 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 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); - check_for_deinit(self); - raise_error_if_not_connected(self); - byte *buf = buf_in; - - // make sure we want at least 1 char - if (size == 0) { - return 0; - } - - return common_hal_bleio_characteristic_buffer_read(self, buf, size, errcode); -} - -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); - check_for_deinit(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); - check_for_deinit(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); - check_for_deinit(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); - return mp_const_none; -} -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_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 deleted file mode 100644 index b45835ff3..000000000 --- a/shared-bindings/bleio/CharacteristicBuffer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H - -#include "common-hal/bleio/CharacteristicBuffer.h" - -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, 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/bleio/Descriptor.c b/shared-bindings/bleio/Descriptor.c deleted file mode 100644 index eba2ea1a8..000000000 --- a/shared-bindings/bleio/Descriptor.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Attribute.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/UUID.h" - -//| .. currentmodule:: bleio -//| -//| :class:`Descriptor` -- BLE descriptor -//| ========================================================= -//| -//| Stores information about a BLE descriptor. -//| Descriptors are attached to BLE characteristics and provide contextual -//| information about the characteristic. -//| -//| .. class:: Descriptor -//| -//| There is no regular constructor for a Descriptor. A new local Descriptor can be created -//| and attached to a Characteristic by calling `add_to_characteristic()`. -//| Remote Descriptor objects are created by `Central.discover_remote_services()` -//| or `Peripheral.discover_remote_services()` as part of remote Characteristics -//| in the remote Services that are discovered. -//| -//| .. classmethod:: add_to_characteristic(characteristic, uuid, *, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=b'') -//| -//| Create a new Descriptor object, and add it to this Service. -//| -//| :param Characteristic characteristic: The characteristic that will hold this descriptor -//| :param UUID uuid: The uuid of the descriptor -//| :param int read_perm: Specifies whether the descriptor can be read by a client, and if so, which -//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, -//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, -//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. -//| :param int write_perm: Specifies whether the descriptor can be written by a client, and if so, which -//| security mode is required. Values allowed are the same as ``read_perm``. -//| :param int max_length: Maximum length in bytes of the descriptor value. The maximum allowed is -//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum -//| number of data bytes that fit in a single BLE 4.x ATT packet. -//| :param bool fixed_length: True if the descriptor value is of fixed length. -//| :param buf initial_value: The initial value for this descriptor. -//| -//| :return: the new Descriptor. -//| -STATIC mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - // class is arg[0], which we can ignore. - - enum { ARG_characteristic, ARG_uuid, ARG_read_perm, ARG_write_perm, - ARG_max_length, ARG_fixed_length, ARG_initial_value }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, - { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, - }; - - 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); - - const mp_obj_t characteristic_obj = args[ARG_characteristic].u_obj; - if (!MP_OBJ_IS_TYPE(characteristic_obj, &bleio_characteristic_type)) { - mp_raise_ValueError(translate("Expected a Characteristic")); - } - - const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("Expected a UUID")); - } - - const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(read_perm); - - const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(write_perm); - - const mp_int_t max_length = args[ARG_max_length].u_int; - const bool fixed_length = args[ARG_fixed_length].u_bool; - mp_obj_t initial_value = args[ARG_initial_value].u_obj; - - // Length will be validated in common_hal. - mp_buffer_info_t initial_value_bufinfo; - if (initial_value == mp_const_none) { - if (fixed_length && max_length > 0) { - initial_value = mp_obj_new_bytes_of_zeros(max_length); - } else { - initial_value = mp_const_empty_bytes; - } - } - mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - - bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); - descriptor->base.type = &bleio_descriptor_type; - - // Range checking on max_length arg is done by the common_hal layer, because - // it may vary depending on underlying BLE implementation. - common_hal_bleio_descriptor_construct( - descriptor, MP_OBJ_TO_PTR(characteristic_obj), MP_OBJ_TO_PTR(uuid_obj), - read_perm, write_perm, - max_length, fixed_length, &initial_value_bufinfo); - - common_hal_bleio_characteristic_add_descriptor(characteristic_obj, descriptor); - - return MP_OBJ_FROM_PTR(descriptor); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_descriptor_add_to_characteristic_fun_obj, 3, bleio_descriptor_add_to_characteristic); -STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_descriptor_add_to_characteristic_obj, MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_fun_obj)); - -//| .. attribute:: uuid -//| -//| The descriptor uuid. (read-only) -//| -STATIC mp_obj_t bleio_descriptor_get_uuid(mp_obj_t self_in) { - bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); - - bleio_uuid_obj_t *uuid = common_hal_bleio_descriptor_get_uuid(self); - return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_uuid_obj, bleio_descriptor_get_uuid); - -const mp_obj_property_t bleio_descriptor_uuid_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&bleio_descriptor_get_uuid_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: characteristic (read-only) -//| -//| The Characteristic this Descriptor is a part of. -//| -STATIC mp_obj_t bleio_descriptor_get_characteristic(mp_obj_t self_in) { - bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return common_hal_bleio_descriptor_get_characteristic(self); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_characteristic_obj, bleio_descriptor_get_characteristic); - -const mp_obj_property_t bleio_descriptor_characteristic_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_descriptor_get_characteristic_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: value -//| -//| The value of this descriptor. -//| -STATIC mp_obj_t bleio_descriptor_get_value(mp_obj_t self_in) { - bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return common_hal_bleio_descriptor_get_value(self); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_value_obj, bleio_descriptor_get_value); - -STATIC mp_obj_t bleio_descriptor_set_value(mp_obj_t self_in, mp_obj_t value_in) { - bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); - - common_hal_bleio_descriptor_set_value(self, &bufinfo); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_descriptor_set_value_obj, bleio_descriptor_set_value); - -const mp_obj_property_t bleio_descriptor_value_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_descriptor_get_value_obj, - (mp_obj_t)&bleio_descriptor_set_value_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_add_to_characteristic), MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_obj) }, - { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_descriptor_uuid_obj) }, - { MP_ROM_QSTR(MP_QSTR_characteristic), MP_ROM_PTR(&bleio_descriptor_characteristic_obj) }, - { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_descriptor_value_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_descriptor_locals_dict, bleio_descriptor_locals_dict_table); - -STATIC void bleio_descriptor_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->uuid) { - mp_printf(print, "Descriptor("); - bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); - mp_printf(print, ")"); - } else { - mp_printf(print, ""); - } -} - -const mp_obj_type_t bleio_descriptor_type = { - { &mp_type_type }, - .name = MP_QSTR_Descriptor, - .print = bleio_descriptor_print, - .locals_dict = (mp_obj_dict_t*)&bleio_descriptor_locals_dict -}; diff --git a/shared-bindings/bleio/Descriptor.h b/shared-bindings/bleio/Descriptor.h deleted file mode 100644 index d12ebaab9..000000000 --- a/shared-bindings/bleio/Descriptor.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H - -#include "shared-module/bleio/Attribute.h" -#include "common-hal/bleio/Characteristic.h" -#include "common-hal/bleio/Descriptor.h" -#include "common-hal/bleio/UUID.h" - -extern const mp_obj_type_t bleio_descriptor_type; - -extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo); -extern bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self); -extern bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self); -extern mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self); -extern void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H diff --git a/shared-bindings/bleio/Peripheral.c b/shared-bindings/bleio/Peripheral.c deleted file mode 100644 index 206f1c9a8..000000000 --- a/shared-bindings/bleio/Peripheral.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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 -#include - -#include "ble_drv.h" -#include "py/objarray.h" -#include "py/objproperty.h" -#include "py/objstr.h" -#include "py/runtime.h" - -#include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Peripheral.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" -#include "shared-module/bleio/ScanEntry.h" - -#include "common-hal/bleio/Peripheral.h" - -#define ADV_INTERVAL_MIN (0.0020f) -#define ADV_INTERVAL_MIN_STRING "0.0020" -#define ADV_INTERVAL_MAX (10.24f) -#define ADV_INTERVAL_MAX_STRING "10.24" -// 20ms is recommended by Apple -#define ADV_INTERVAL_DEFAULT (0.1f) - -//| .. currentmodule:: bleio -//| -//| :class:`Peripheral` -- A BLE peripheral device -//| ========================================================= -//| -//| Implement a BLE peripheral which runs locally. -//| Set up using the supplied services, and then allow advertising to be started and stopped. -//| -//| Usage:: -//| -//| from bleio import Characteristic, Peripheral, Service -//| from adafruit_ble.advertising import ServerAdvertisement -//| -//| # Create a peripheral and start it up. -//| peripheral = bleio.Peripheral() -//| -//| # Create a Service and add it to this Peripheral. -//| service = Service.add_to_peripheral(peripheral, bleio.UUID(0x180f)) -//| -//| # Create a Characteristic and add it to the Service. -//| characteristic = Characterist.add_to_service(service, -//| bleio.UUID(0x2919), properties=Characteristic.READ | Characteristic.NOTIFY) -//| -//| adv = ServerAdvertisement(peripheral) -//| peripheral.start_advertising(adv.advertising_data_bytes, scan_response=adv.scan_response_bytes) -//| -//| while not peripheral.connected: -//| # Wait for connection. -//| pass -//| -//| .. class:: Peripheral(name=None) -//| -//| Create a new Peripheral object. -//| -//| :param str name: The name used when advertising this peripheral. If name is None, -//| bleio.adapter.default_name will be used. -//| -STATIC mp_obj_t bleio_peripheral_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_name }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_name, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - 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); - - bleio_peripheral_obj_t *self = m_new_obj(bleio_peripheral_obj_t); - self->base.type = &bleio_peripheral_type; - - mp_obj_t name = args[ARG_name].u_obj; - if (name == mp_const_none) { - name = common_hal_bleio_adapter_get_default_name(); - } else if (!MP_OBJ_IS_STR(name)) { - mp_raise_ValueError(translate("name must be a string")); - } - - common_hal_bleio_peripheral_construct(self, name); - - return MP_OBJ_FROM_PTR(self); -} - -//| .. attribute:: connected (read-only) -//| -//| True if connected to a BLE Central device. -//| -STATIC mp_obj_t bleio_peripheral_get_connected(mp_obj_t self_in) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return mp_obj_new_bool(common_hal_bleio_peripheral_get_connected(self)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_connected_obj, bleio_peripheral_get_connected); - -const mp_obj_property_t bleio_peripheral_connected_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_peripheral_get_connected_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: services -//| -//| A tuple of :py:class:`Service` objects offered by this peripheral. (read-only) -//| -STATIC mp_obj_t bleio_peripheral_get_services(mp_obj_t self_in) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - // Return list as a tuple so user won't be able to change it. - mp_obj_list_t *services_list = common_hal_bleio_peripheral_get_services(self); - return mp_obj_new_tuple(services_list->len, services_list->items); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services); - -const mp_obj_property_t bleio_peripheral_services_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_peripheral_get_services_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: name -//| -//| The peripheral's name, included when advertising. (read-only) -//| -STATIC mp_obj_t bleio_peripheral_get_name(mp_obj_t self_in) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return common_hal_bleio_peripheral_get_name(self); -} -MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_name_obj, bleio_peripheral_get_name); - -const mp_obj_property_t bleio_peripheral_name_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_peripheral_get_name_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. method:: start_advertising(data, *, scan_response=None, connectable=True, interval=0.1) -//| -//| Starts advertising the peripheral. The peripheral's name and -//| services are included in the advertisement packets. -//| -//| :param buf data: advertising data packet bytes -//| :param buf scan_response: scan response data packet bytes. ``None`` if no scan response is needed. -//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral. -//| :param float interval: advertising interval, in seconds -//| -STATIC mp_obj_t bleio_peripheral_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_interval }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_scan_response, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, - { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - }; - - 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); - - mp_buffer_info_t data_bufinfo; - mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); - - // Pass an empty buffer if scan_response not provided. - mp_buffer_info_t scan_response_bufinfo = { 0 }; - if (args[ARG_scan_response].u_obj != mp_const_none) { - mp_get_buffer_raise(args[ARG_scan_response].u_obj, &scan_response_bufinfo, MP_BUFFER_READ); - } - - if (args[ARG_interval].u_obj == MP_OBJ_NULL) { - args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT); - } - - const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj); - if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) { - mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), - ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING); - } - - common_hal_bleio_peripheral_start_advertising(self, args[ARG_connectable].u_bool, interval, - &data_bufinfo, &scan_response_bufinfo); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_start_advertising_obj, 2, bleio_peripheral_start_advertising); - -//| .. method:: stop_advertising() -//| -//| Stop sending advertising packets. -STATIC mp_obj_t bleio_peripheral_stop_advertising(mp_obj_t self_in) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - common_hal_bleio_peripheral_stop_advertising(self); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_stop_advertising_obj, bleio_peripheral_stop_advertising); - -//| .. method:: disconnect() -//| -//| Disconnects from the remote central. -//| Normally the central initiates a disconnection. Use this only -//| if necessary for your application. -//| -STATIC mp_obj_t bleio_peripheral_disconnect(mp_obj_t self_in) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - common_hal_bleio_peripheral_disconnect(self); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_disconnect_obj, bleio_peripheral_disconnect); - -//| .. method:: discover_remote_services(service_uuids_whitelist=None) -//| Do BLE discovery for all services or for the given service UUIDS, -//| to find their handles and characteristics, and return the discovered services. -//| `Peripheral.connected` must be True. -//| -//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services -//| provided by the peripheral that you want to use. -//| The peripheral may provide more services, but services not listed are ignored -//| and will not be returned. -//| -//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow. -//| -//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you -//| you must have already created a :py:class:~`UUID` object for that UUID in order for the -//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered -//| for use. (This restriction may be lifted in the future.) -//| -//| Thought it is unusual for a peripheral to act as a BLE client, it can do so, and -//| needs to be able to do discovery on its peer (a central). -//| Examples include a peripheral accessing a central that provides Current Time Service, -//| Apple Notification Center Service, or Battery Service. -//| -//| :return: A tuple of services provided by the remote central. -//| -STATIC mp_obj_t bleio_peripheral_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_service_uuids_whitelist }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - 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); - - if (!common_hal_bleio_peripheral_get_connected(self)) { - mp_raise_ValueError(translate("Not connected")); - } - - return MP_OBJ_FROM_PTR(common_hal_bleio_peripheral_discover_remote_services( - MP_OBJ_FROM_PTR(self), - args[ARG_service_uuids_whitelist].u_obj)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_discover_remote_services_obj, 1, bleio_peripheral_discover_remote_services); - -//| .. method:: pair() -//| -//| Request pairing with connected central. -STATIC mp_obj_t bleio_peripheral_pair(mp_obj_t self_in) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); - - common_hal_bleio_peripheral_pair(self); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_pair_obj, bleio_peripheral_pair); - -STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) }, - { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) }, - { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_peripheral_discover_remote_services_obj) }, - { MP_ROM_QSTR(MP_QSTR_pair), MP_ROM_PTR(&bleio_peripheral_pair_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) }, - { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) }, - { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table); - -const mp_obj_type_t bleio_peripheral_type = { - { &mp_type_type }, - .name = MP_QSTR_Peripheral, - .make_new = bleio_peripheral_make_new, - .locals_dict = (mp_obj_dict_t*)&bleio_peripheral_locals_dict -}; diff --git a/shared-bindings/bleio/Peripheral.h b/shared-bindings/bleio/Peripheral.h deleted file mode 100644 index a4bc9542b..000000000 --- a/shared-bindings/bleio/Peripheral.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_PERIPHERAL_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H - -#include "py/objtuple.h" -#include "common-hal/bleio/Peripheral.h" -#include "common-hal/bleio/Service.h" - -extern const mp_obj_type_t bleio_peripheral_type; - -extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_t name); -extern void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio_service_obj_t *service); -extern mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self); -extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self); -extern mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self); -extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, float interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo); -extern void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *device); -extern void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *device); -extern mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist); -extern void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *device); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H diff --git a/shared-bindings/bleio/ScanEntry.c b/shared-bindings/bleio/ScanEntry.c deleted file mode 100644 index 6eb02c716..000000000 --- a/shared-bindings/bleio/ScanEntry.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 - -#include "py/objproperty.h" -#include "shared-bindings/bleio/Address.h" -#include "shared-bindings/bleio/ScanEntry.h" -#include "shared-bindings/bleio/UUID.h" -#include "shared-module/bleio/ScanEntry.h" - -//| .. currentmodule:: bleio -//| -//| :class:`ScanEntry` -- BLE scan response entry -//| ========================================================= -//| -//| Encapsulates information about a device that was received as a -//| response to a BLE scan request. This object may only be created -//| by a `bleio.Scanner`: it has no user-visible constructor. -//| - -//| .. attribute:: address -//| -//| The address of the device (read-only), of type `bleio.Address`. -//| -STATIC mp_obj_t bleio_scanentry_get_address(mp_obj_t self_in) { - bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); - return common_hal_bleio_scanentry_get_address(self); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_address_obj, bleio_scanentry_get_address); - -const mp_obj_property_t bleio_scanentry_address_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_scanentry_get_address_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: advertisement_bytes -//| -//| All the advertisement data present in the packet, returned as a ``bytes`` object. (read-only) -//| -STATIC mp_obj_t scanentry_get_advertisement_bytes(mp_obj_t self_in) { - bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); - return common_hal_bleio_scanentry_get_advertisement_bytes(self); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_advertisement_bytes_obj, scanentry_get_advertisement_bytes); - -const mp_obj_property_t bleio_scanentry_advertisement_bytes_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_scanentry_get_advertisement_bytes_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: rssi -//| -//| The signal strength of the device at the time of the scan, in integer dBm. (read-only) -//| -STATIC mp_obj_t scanentry_get_rssi(mp_obj_t self_in) { - bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(common_hal_bleio_scanentry_get_rssi(self)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_rssi_obj, scanentry_get_rssi); - -const mp_obj_property_t bleio_scanentry_rssi_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_scanentry_get_rssi_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - - -STATIC const mp_rom_map_elem_t bleio_scanentry_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_scanentry_address_obj) }, - { MP_ROM_QSTR(MP_QSTR_advertisement_bytes), MP_ROM_PTR(&bleio_scanentry_advertisement_bytes_obj) }, - { MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&bleio_scanentry_rssi_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_scanentry_locals_dict, bleio_scanentry_locals_dict_table); - -const mp_obj_type_t bleio_scanentry_type = { - { &mp_type_type }, - .name = MP_QSTR_ScanEntry, - .locals_dict = (mp_obj_dict_t*)&bleio_scanentry_locals_dict -}; diff --git a/shared-bindings/bleio/ScanEntry.h b/shared-bindings/bleio/ScanEntry.h deleted file mode 100644 index 77e93175f..000000000 --- a/shared-bindings/bleio/ScanEntry.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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_SHARED_BINDINGS_BLEIO_SCANENTRY_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H - -#include "py/obj.h" -#include "shared-module/bleio/ScanEntry.h" - -extern const mp_obj_type_t bleio_scanentry_type; - -mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self); -mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self); -mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H diff --git a/shared-bindings/bleio/Scanner.c b/shared-bindings/bleio/Scanner.c deleted file mode 100644 index 269c42591..000000000 --- a/shared-bindings/bleio/Scanner.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/ScanEntry.h" -#include "shared-bindings/bleio/Scanner.h" - -#define INTERVAL_DEFAULT (0.1f) -#define INTERVAL_MIN (0.0025f) -#define INTERVAL_MIN_STRING "0.0025" -#define INTERVAL_MAX (40.959375f) -#define INTERVAL_MAX_STRING "40.959375" -#define WINDOW_DEFAULT (0.1f) - -//| .. currentmodule:: bleio -//| -//| :class:`Scanner` -- scan for nearby BLE devices -//| ========================================================= -//| -//| Scan for nearby BLE devices. -//| -//| Usage:: -//| -//| import bleio -//| scanner = bleio.Scanner() -//| entries = scanner.scan(2.5) # Scan for 2.5 seconds -//| - -//| .. class:: Scanner() -//| -//| Create a new Scanner object. -//| -STATIC mp_obj_t bleio_scanner_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *all_args, mp_map_t *kw_args) { - mp_arg_check_num(n_args, kw_args, 0, 0, false); - - bleio_scanner_obj_t *self = m_new_obj(bleio_scanner_obj_t); - self->base.type = type; - - common_hal_bleio_scanner_construct(self); - - return MP_OBJ_FROM_PTR(self); -} - -//| .. method:: scan(timeout, \*, interval=0.1, window=0.1) -//| -//| Performs a BLE scan. -//| -//| :param float timeout: the scan timeout in seconds -//| :param float interval: the interval (in seconds) between the start of two consecutive scan windows -//| Must be in the range 0.0025 - 40.959375 seconds. -//| :param float window: the duration (in seconds) to scan a single BLE channel. -//| window must be <= interval. -//| :returns: an iterable of `ScanEntry` objects -//| :rtype: iterable -//| -STATIC mp_obj_t bleio_scanner_scan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_timeout, ARG_interval, ARG_window }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_window, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - }; - - bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - 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); - - const mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); - - if (args[ARG_interval].u_obj == MP_OBJ_NULL) { - args[ARG_interval].u_obj = mp_obj_new_float(INTERVAL_DEFAULT); - } - - if (args[ARG_window].u_obj == MP_OBJ_NULL) { - args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT); - } - - const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj); - if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { - mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING); - } - - const mp_float_t window = mp_obj_float_get(args[ARG_window].u_obj); - if (window > interval) { - mp_raise_ValueError(translate("window must be <= interval")); - } - - return common_hal_bleio_scanner_scan(self, timeout, interval, window); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_scanner_scan_obj, 2, bleio_scanner_scan); - - -STATIC const mp_rom_map_elem_t bleio_scanner_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&bleio_scanner_scan_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_scanner_locals_dict, bleio_scanner_locals_dict_table); - -const mp_obj_type_t bleio_scanner_type = { - { &mp_type_type }, - .name = MP_QSTR_Scanner, - .make_new = bleio_scanner_make_new, - .locals_dict = (mp_obj_dict_t*)&bleio_scanner_locals_dict -}; diff --git a/shared-bindings/bleio/Scanner.h b/shared-bindings/bleio/Scanner.h deleted file mode 100644 index 3a0ce7eae..000000000 --- a/shared-bindings/bleio/Scanner.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_SCANNER_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H - -#include "py/objtype.h" -#include "common-hal/bleio/Scanner.h" - -extern const mp_obj_type_t bleio_scanner_type; - -extern void common_hal_bleio_scanner_construct(bleio_scanner_obj_t *self); -extern mp_obj_t common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_float_t timeout, mp_float_t interval, mp_float_t window); -extern void common_hal_bleio_scanner_stop(bleio_scanner_obj_t *self); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c deleted file mode 100644 index f3bfebb7b..000000000 --- a/shared-bindings/bleio/Service.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Peripheral.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -//| .. currentmodule:: bleio -//| -//| :class:`Service` -- BLE service -//| ========================================================= -//| -//| Stores information about a BLE service and its characteristics. -//| -//| .. class:: Service -//| -//| There is no regular constructor for a Service. A new local Service can be created -//| and attached to a Peripheral by calling `add_to_peripheral()`. -//| Remote Service objects are created by `Central.discover_remote_services()` -//| or `Peripheral.discover_remote_services()`. -//| -//| .. classmethod:: add_to_peripheral(peripheral, uuid, *, secondary=False) -//| -//| Create a new Service object, identitied by the specified UUID, and add it -//| to the given Peripheral. -//| -//| To mark the service as secondary, pass `True` as :py:data:`secondary`. -//| -//| :param Peripheral peripheral: The peripheral that will provide this service -//| :param UUID uuid: The uuid of the service -//| :param bool secondary: If the service is a secondary one -// -//| :return: the new Service -//| -STATIC mp_obj_t bleio_service_add_to_peripheral(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - // class is arg[0], which we can ignore. - - enum { ARG_peripheral, ARG_uuid, ARG_secondary }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_peripheral, MP_ARG_REQUIRED | MP_ARG_OBJ,}, - { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - }; - - 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); - - const mp_obj_t peripheral_obj = args[ARG_peripheral].u_obj; - if (!MP_OBJ_IS_TYPE(peripheral_obj, &bleio_peripheral_type)) { - mp_raise_ValueError(translate("Expected a Peripheral")); - } - - const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("Expected a UUID")); - } - - const bool is_secondary = args[ARG_secondary].u_bool; - - bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); - service->base.type = &bleio_service_type; - - common_hal_bleio_service_construct( - service, MP_OBJ_TO_PTR(peripheral_obj), MP_OBJ_TO_PTR(uuid_obj), is_secondary); - - common_hal_bleio_peripheral_add_service(peripheral_obj, service); - - return MP_OBJ_FROM_PTR(service); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_service_add_to_peripheral_fun_obj, 3, bleio_service_add_to_peripheral); -STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_service_add_to_peripheral_obj, MP_ROM_PTR(&bleio_service_add_to_peripheral_fun_obj)); - -//| .. attribute:: characteristics -//| -//| A tuple of :py:class:`Characteristic` designating the characteristics that are offered by this service. (read-only) -//| -STATIC mp_obj_t bleio_service_get_characteristics(mp_obj_t self_in) { - bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); - // Return list as a tuple so user won't be able to change it. - mp_obj_list_t *char_list = common_hal_bleio_service_get_characteristic_list(self); - return mp_obj_new_tuple(char_list->len, char_list->items); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_characteristics_obj, bleio_service_get_characteristics); - -const mp_obj_property_t bleio_service_characteristics_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_service_get_characteristics_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: remote -//| -//| True if this is a service provided by a remote device. (read-only) -//| -STATIC mp_obj_t bleio_service_get_remote(mp_obj_t self_in) { - bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return mp_obj_new_bool(common_hal_bleio_service_get_is_remote(self)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_remote_obj, bleio_service_get_remote); - -const mp_obj_property_t bleio_service_remote_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_service_get_remote_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: secondary -//| -//| True if this is a secondary service. (read-only) -//| -STATIC mp_obj_t bleio_service_get_secondary(mp_obj_t self_in) { - bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); - - return mp_obj_new_bool(common_hal_bleio_service_get_is_secondary(self)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_secondary_obj, bleio_service_get_secondary); - -const mp_obj_property_t bleio_service_secondary_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_service_get_secondary_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - -//| .. attribute:: uuid -//| -//| The UUID of this service. (read-only) -//| Will be ``None`` if the 128-bit UUID for this service is not known. -//| -STATIC mp_obj_t bleio_service_get_uuid(mp_obj_t self_in) { - bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); - - bleio_uuid_obj_t *uuid = common_hal_bleio_service_get_uuid(self); - return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_uuid_obj, bleio_service_get_uuid); - -const mp_obj_property_t bleio_service_uuid_obj = { - .base.type = &mp_type_property, - .proxy = { (mp_obj_t)&bleio_service_get_uuid_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj }, -}; - - -STATIC const mp_rom_map_elem_t bleio_service_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_add_to_peripheral), MP_ROM_PTR(&bleio_service_add_to_peripheral_obj) }, - { MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) }, - { MP_ROM_QSTR(MP_QSTR_secondary), MP_ROM_PTR(&bleio_service_secondary_obj) }, - { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) }, -}; -STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict_table); - -STATIC void bleio_service_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->uuid) { - mp_printf(print, "Service("); - bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); - mp_printf(print, ")"); - } else { - mp_printf(print, ""); - } -} - -const mp_obj_type_t bleio_service_type = { - { &mp_type_type }, - .name = MP_QSTR_Service, - .print = bleio_service_print, - .locals_dict = (mp_obj_dict_t*)&bleio_service_locals_dict -}; diff --git a/shared-bindings/bleio/Service.h b/shared-bindings/bleio/Service.h deleted file mode 100644 index ce4e51417..000000000 --- a/shared-bindings/bleio/Service.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_SERVICE_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H - -#include "common-hal/bleio/Peripheral.h" -#include "common-hal/bleio/Service.h" - -const mp_obj_type_t bleio_service_type; - -extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary); -extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self); -extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self); -extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self); -extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self); -extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H diff --git a/shared-bindings/bleio/UUID.c b/shared-bindings/bleio/UUID.c deleted file mode 100644 index dca7ea010..000000000 --- a/shared-bindings/bleio/UUID.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 - -#include "py/objproperty.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/bleio/UUID.h" - -//| .. currentmodule:: bleio -//| -//| :class:`UUID` -- BLE UUID -//| ========================================================= -//| -//| A 16-bit or 128-bit UUID. Can be used for services, characteristics, descriptors and more. -//| - -//| .. class:: UUID(value) -//| -//| Create a new UUID or UUID object encapsulating the uuid value. -//| The value can be one of: -//| -//| - an `int` value in range 0 to 0xFFFF (Bluetooth SIG 16-bit UUID) -//| - a buffer object (bytearray, bytes) of 16 bytes in little-endian order (128-bit UUID) -//| - a string of hex digits of the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' -//| -//| Creating a 128-bit UUID registers the UUID with the onboard BLE software, and provides a -//| temporary 16-bit UUID that can be used in place of the full 128-bit UUID. -//| -//| :param value: The uuid value to encapsulate -//| :type value: int or typing.ByteString -//| -STATIC mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - mp_arg_check_num(n_args, kw_args, 1, 1, false); - - bleio_uuid_obj_t *self = m_new_obj(bleio_uuid_obj_t); - self->base.type = type; - - const mp_obj_t value = pos_args[0]; - uint8_t uuid128[16]; - - if (MP_OBJ_IS_INT(value)) { - mp_int_t uuid16 = mp_obj_get_int(value); - if (uuid16 < 0 || uuid16 > 0xffff) { - mp_raise_ValueError(translate("UUID integer value must be 0-0xffff")); - } - - // NULL means no 128-bit value. - common_hal_bleio_uuid_construct(self, uuid16, NULL); - - } else { - if (MP_OBJ_IS_STR(value)) { - // 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - GET_STR_DATA_LEN(value, chars, len); - char hex[32]; - // Validate length, hyphens, and hex digits. - bool good_uuid = - len == 36 && chars[8] == '-' && chars[13] == '-' && chars[18] == '-' && chars[23] == '-'; - if (good_uuid) { - size_t hex_idx = 0; - for (size_t i = 0; i < len; i++) { - if (unichar_isxdigit(chars[i])) { - hex[hex_idx] = chars[i]; - hex_idx++; - } - } - good_uuid = hex_idx == 32; - } - if (!good_uuid) { - mp_raise_ValueError(translate("UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'")); - } - - size_t hex_idx = 0; - for (int i = 15; i >= 0; i--) { - uuid128[i] = (unichar_xdigit_value(hex[hex_idx]) << 4) | unichar_xdigit_value(hex[hex_idx + 1]); - hex_idx += 2; - } - } else { - // Last possibility is that it's a buf. - mp_buffer_info_t bufinfo; - if (!mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) { - mp_raise_ValueError(translate("UUID value is not str, int or byte buffer")); - } - - if (bufinfo.len != 16) { - mp_raise_ValueError(translate("Byte buffer must be 16 bytes.")); - } - - memcpy(uuid128, bufinfo.buf, 16); - } - - // Str and bytes both get constructed the same way here. - uint32_t uuid16 = (uuid128[13] << 8) | uuid128[12]; - uuid128[12] = 0; - uuid128[13] = 0; - common_hal_bleio_uuid_construct(self, uuid16, uuid128); - } - - return MP_OBJ_FROM_PTR(self); -} - -//| .. attribute:: uuid16 -//| -//| The 16-bit part of the UUID. (read-only) -//| -//| :type: int -//| -STATIC mp_obj_t bleio_uuid_get_uuid16(mp_obj_t self_in) { - bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); -} - -MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid16_obj, bleio_uuid_get_uuid16); - -const mp_obj_property_t bleio_uuid_uuid16_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&bleio_uuid_get_uuid16_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: uuid128 -//| -//| The 128-bit value of the UUID -//| Raises AttributeError if this is a 16-bit UUID. (read-only) -//| -//| :type: bytes -//| -STATIC mp_obj_t bleio_uuid_get_uuid128(mp_obj_t self_in) { - bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); - - uint8_t uuid128[16]; - if (!common_hal_bleio_uuid_get_uuid128(self, uuid128)) { - mp_raise_AttributeError(translate("not a 128-bit UUID")); - } - return mp_obj_new_bytes(uuid128, 16); -} - -MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid128_obj, bleio_uuid_get_uuid128); - -const mp_obj_property_t bleio_uuid_uuid128_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&bleio_uuid_get_uuid128_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -//| .. attribute:: size -//| -//| 128 if this UUID represents a 128-bit vendor-specific UUID. 16 if this UUID represents a -//| 16-bit Bluetooth SIG assigned UUID. (read-only) 32-bit UUIDs are not currently supported. -//| -//| :type: int -//| -STATIC mp_obj_t bleio_uuid_get_size(mp_obj_t self_in) { - bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_size(self)); -} - -MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_size_obj, bleio_uuid_get_size); - -const mp_obj_property_t bleio_uuid_size_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&bleio_uuid_get_size_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - -STATIC const mp_rom_map_elem_t bleio_uuid_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_uuid16), MP_ROM_PTR(&bleio_uuid_uuid16_obj) }, - { MP_ROM_QSTR(MP_QSTR_uuid128), MP_ROM_PTR(&bleio_uuid_uuid128_obj) }, - { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&bleio_uuid_size_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_uuid_locals_dict, bleio_uuid_locals_dict_table); - -STATIC mp_obj_t bleio_uuid_unary_op(mp_unary_op_t op, mp_obj_t self_in) { - bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); - switch (op) { - case MP_UNARY_OP_HASH: - if (common_hal_bleio_uuid_get_size(self) == 16) { - return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); - } else { - union { - uint8_t uuid128_bytes[16]; - uint16_t uuid128_uint16[8]; - } uuid128; - common_hal_bleio_uuid_get_uuid128(self, uuid128.uuid128_bytes); - int hash = 0; - for (size_t i = 0; i < MP_ARRAY_SIZE(uuid128.uuid128_uint16); i++) { - hash += uuid128.uuid128_uint16[i]; - } - return MP_OBJ_NEW_SMALL_INT(hash); - } - default: - return MP_OBJ_NULL; // op not supported - } -} - -//| - -//| .. method:: __eq__(other) -//| -//| Two UUID objects are equal if their values match and they are both 128-bit or both 16-bit. -//| -STATIC mp_obj_t bleio_uuid_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { - switch (op) { - // Two UUID's are equal if their uuid16 values and uuid128 references match. - case MP_BINARY_OP_EQUAL: - if (MP_OBJ_IS_TYPE(rhs_in, &bleio_uuid_type)) { - return mp_obj_new_bool( - common_hal_bleio_uuid_get_uuid16(lhs_in) == common_hal_bleio_uuid_get_uuid16(rhs_in) && - common_hal_bleio_uuid_get_uuid128_reference(lhs_in) == - common_hal_bleio_uuid_get_uuid128_reference(rhs_in)); - } else { - return mp_const_false; - } - - default: - return MP_OBJ_NULL; // op not supported - } -} - -void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); - uint32_t size = common_hal_bleio_uuid_get_size(self); - if (size == 16) { - mp_printf(print, "UUID(0x%04x)", common_hal_bleio_uuid_get_uuid16(self)); - } else { - uint8_t uuid128[16]; - (void) common_hal_bleio_uuid_get_uuid128(self, uuid128); - mp_printf(print, "UUID('" - "%02x%02x%02x%02x-" - "%02x%02x-" - "%02x%02x-" - "%02x%02x-" - "%02x%02x%02x%02x%02x%02x')", - uuid128[15], uuid128[14], uuid128[13], uuid128[12], - uuid128[11], uuid128[10], - uuid128[9], uuid128[8], - uuid128[7], uuid128[6], - uuid128[5], uuid128[4], uuid128[3], uuid128[2], uuid128[1], uuid128[0]); - } -} - -const mp_obj_type_t bleio_uuid_type = { - { &mp_type_type }, - .name = MP_QSTR_UUID, - .print = bleio_uuid_print, - .make_new = bleio_uuid_make_new, - .unary_op = bleio_uuid_unary_op, - .binary_op = bleio_uuid_binary_op, - .locals_dict = (mp_obj_dict_t*)&bleio_uuid_locals_dict, -}; diff --git a/shared-bindings/bleio/UUID.h b/shared-bindings/bleio/UUID.h deleted file mode 100644 index caf039aec..000000000 --- a/shared-bindings/bleio/UUID.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_BINDINGS_BLEIO_UUID_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H - -#include "common-hal/bleio/UUID.h" - -void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); - -extern const mp_obj_type_t bleio_uuid_type; - -extern void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, uint8_t uuid128[]); -extern uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self); -extern bool common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]); -extern uint32_t common_hal_bleio_uuid_get_uuid128_reference(bleio_uuid_obj_t *self); -extern uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c deleted file mode 100644 index 739f461a5..000000000 --- a/shared-bindings/bleio/__init__.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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 "shared-bindings/bleio/__init__.h" -#include "shared-bindings/bleio/Address.h" -#include "shared-bindings/bleio/Attribute.h" -#include "shared-bindings/bleio/Central.h" -#include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/CharacteristicBuffer.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/Peripheral.h" -#include "shared-bindings/bleio/ScanEntry.h" -#include "shared-bindings/bleio/Scanner.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -//| :mod:`bleio` --- Bluetooth Low Energy (BLE) communication -//| ================================================================ -//| -//| .. module:: bleio -//| :synopsis: Bluetooth Low Energy functionality -//| :platform: nRF -//| -//| The `bleio` module provides necessary low-level functionality for communicating -//| using Bluetooth Low Energy (BLE). We recommend you use `bleio` in conjunction -//| with the `adafruit_ble `_ -//| CircuitPython library, which builds on `bleio`, and -//| provides higher-level convenience functionality, including predefined beacons, clients, -//| servers. -//| -//| Libraries -//| -//| .. toctree:: -//| :maxdepth: 3 -//| -//| Address -//| Adapter -//| Attribute -//| Central -//| Characteristic -//| CharacteristicBuffer -//| Descriptor -//| Peripheral -//| ScanEntry -//| Scanner -//| Service -//| UUID -//| -//| .. attribute:: adapter -//| -//| BLE Adapter information, such as enabled state as well as MAC -//| address. -//| This object is the sole instance of `bleio.Adapter`. -//| - -STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) }, - { MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) }, - { MP_ROM_QSTR(MP_QSTR_Attribute), MP_ROM_PTR(&bleio_attribute_type) }, - { MP_ROM_QSTR(MP_QSTR_Central), MP_ROM_PTR(&bleio_central_type) }, - { MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) }, - { MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), MP_ROM_PTR(&bleio_characteristic_buffer_type) }, - { MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) }, - { MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&bleio_peripheral_type) }, - { MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) }, - { MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) }, - { MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) }, - { MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bleio_module_globals, bleio_module_globals_table); - -const mp_obj_module_t bleio_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&bleio_module_globals, -}; diff --git a/shared-bindings/bleio/__init__.h b/shared-bindings/bleio/__init__.h deleted file mode 100644 index c84895068..000000000 --- a/shared-bindings/bleio/__init__.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2016 Glenn Ruben Bakke - * - * 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_SHARED_BINDINGS_BLEIO___INIT___H -#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H - -#include "py/objlist.h" - -#include "shared-bindings/bleio/Adapter.h" - -#include "common-hal/bleio/__init__.h" -#include "common-hal/bleio/Adapter.h" - -extern const super_adapter_obj_t common_hal_bleio_adapter_obj; - -extern void common_hal_bleio_check_connected(uint16_t conn_handle); - -extern uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); -extern mp_obj_list_t *common_hal_bleio_device_get_remote_service_list(mp_obj_t device); -extern void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); - -extern mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle); -extern void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); -extern void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); - - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H diff --git a/shared-module/_bleio/Address.c b/shared-module/_bleio/Address.c new file mode 100644 index 000000000..8c8b043fe --- /dev/null +++ b/shared-module/_bleio/Address.c @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 + +#include "py/objstr.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-module/_bleio/Address.h" + +void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type) { + self->bytes = mp_obj_new_bytes(bytes, NUM_BLEIO_ADDRESS_BYTES); + self->type = address_type; +} + +mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self) { + return self->bytes; +} + +uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self) { + return self->type; +} diff --git a/shared-module/_bleio/Address.h b/shared-module/_bleio/Address.h new file mode 100644 index 000000000..39789842f --- /dev/null +++ b/shared-module/_bleio/Address.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_MODULE_BLEIO_ADDRESS_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H + +#include "py/obj.h" + +#define NUM_BLEIO_ADDRESS_BYTES 6 + +typedef struct { + mp_obj_base_t base; + uint8_t type; + mp_obj_t bytes; // a bytes() object +} bleio_address_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H diff --git a/shared-module/_bleio/Attribute.c b/shared-module/_bleio/Attribute.c new file mode 100644 index 000000000..3acfcf1f5 --- /dev/null +++ b/shared-module/_bleio/Attribute.c @@ -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. + */ + +#include + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Attribute.h" + +void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode) { + switch (security_mode) { + case SECURITY_MODE_NO_ACCESS: + case SECURITY_MODE_OPEN: + case SECURITY_MODE_ENC_NO_MITM: + case SECURITY_MODE_ENC_WITH_MITM: + case SECURITY_MODE_LESC_ENC_WITH_MITM: + case SECURITY_MODE_SIGNED_NO_MITM: + case SECURITY_MODE_SIGNED_WITH_MITM: + break; + default: + mp_raise_ValueError(translate("Invalid security_mode")); + break; + } +} diff --git a/shared-module/_bleio/Attribute.h b/shared-module/_bleio/Attribute.h new file mode 100644 index 000000000..a498a14a5 --- /dev/null +++ b/shared-module/_bleio/Attribute.h @@ -0,0 +1,41 @@ +/* + * 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_SHARED_MODULE_BLEIO_ATTRIBUTE_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H + +// BLE security modes: 0x +typedef enum { + SECURITY_MODE_NO_ACCESS = 0x00, + SECURITY_MODE_OPEN = 0x11, + SECURITY_MODE_ENC_NO_MITM = 0x21, + SECURITY_MODE_ENC_WITH_MITM = 0x31, + SECURITY_MODE_LESC_ENC_WITH_MITM = 0x41, + SECURITY_MODE_SIGNED_NO_MITM = 0x12, + SECURITY_MODE_SIGNED_WITH_MITM = 0x22, +} bleio_attribute_security_mode_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H diff --git a/shared-module/_bleio/Characteristic.h b/shared-module/_bleio/Characteristic.h new file mode 100644 index 000000000..409a57c76 --- /dev/null +++ b/shared-module/_bleio/Characteristic.h @@ -0,0 +1,43 @@ +/* + * 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_SHARED_MODULE_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H + +typedef enum { + CHAR_PROP_NONE = 0, + CHAR_PROP_BROADCAST = 1u << 0, + CHAR_PROP_INDICATE = 1u << 1, + CHAR_PROP_NOTIFY = 1u << 2, + CHAR_PROP_READ = 1u << 3, + CHAR_PROP_WRITE = 1u << 4, + CHAR_PROP_WRITE_NO_RESPONSE = 1u << 5, + CHAR_PROP_ALL = (CHAR_PROP_BROADCAST | CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY | + CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE) +} bleio_characteristic_properties_enum_t; +typedef uint8_t bleio_characteristic_properties_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H diff --git a/shared-module/_bleio/ScanEntry.c b/shared-module/_bleio/ScanEntry.c new file mode 100644 index 000000000..8dfa17f31 --- /dev/null +++ b/shared-module/_bleio/ScanEntry.c @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 + +#include "shared-bindings/_bleio/Address.h" +#include "shared-module/_bleio/Address.h" +#include "shared-module/_bleio/ScanEntry.h" + +mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self) { + return MP_OBJ_FROM_PTR(self->address); +} + +mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self) { + return self->data; +} + +mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self) { + return self->rssi; +} diff --git a/shared-module/_bleio/ScanEntry.h b/shared-module/_bleio/ScanEntry.h new file mode 100644 index 000000000..1e798d78f --- /dev/null +++ b/shared-module/_bleio/ScanEntry.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_SHARED_MODULE_BLEIO_SCANENTRY_H +#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H + +#include "py/obj.h" +#include "shared-bindings/_bleio/Address.h" + +typedef struct { + mp_obj_base_t base; + bool connectable; + int8_t rssi; + bleio_address_obj_t *address; + mp_obj_t data; +} bleio_scanentry_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H diff --git a/shared-module/bleio/Address.c b/shared-module/bleio/Address.c deleted file mode 100644 index 6bc458b7c..000000000 --- a/shared-module/bleio/Address.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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 - -#include "py/objstr.h" -#include "shared-bindings/bleio/Address.h" -#include "shared-module/bleio/Address.h" - -void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type) { - self->bytes = mp_obj_new_bytes(bytes, NUM_BLEIO_ADDRESS_BYTES); - self->type = address_type; -} - -mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self) { - return self->bytes; -} - -uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self) { - return self->type; -} diff --git a/shared-module/bleio/Address.h b/shared-module/bleio/Address.h deleted file mode 100644 index 39789842f..000000000 --- a/shared-module/bleio/Address.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_MODULE_BLEIO_ADDRESS_H -#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H - -#include "py/obj.h" - -#define NUM_BLEIO_ADDRESS_BYTES 6 - -typedef struct { - mp_obj_base_t base; - uint8_t type; - mp_obj_t bytes; // a bytes() object -} bleio_address_obj_t; - -#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ADDRESS_H diff --git a/shared-module/bleio/Attribute.c b/shared-module/bleio/Attribute.c deleted file mode 100644 index 2275003c9..000000000 --- a/shared-module/bleio/Attribute.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 - -#include "py/runtime.h" -#include "shared-bindings/bleio/Attribute.h" - -void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode) { - switch (security_mode) { - case SECURITY_MODE_NO_ACCESS: - case SECURITY_MODE_OPEN: - case SECURITY_MODE_ENC_NO_MITM: - case SECURITY_MODE_ENC_WITH_MITM: - case SECURITY_MODE_LESC_ENC_WITH_MITM: - case SECURITY_MODE_SIGNED_NO_MITM: - case SECURITY_MODE_SIGNED_WITH_MITM: - break; - default: - mp_raise_ValueError(translate("Invalid security_mode")); - break; - } -} diff --git a/shared-module/bleio/Attribute.h b/shared-module/bleio/Attribute.h deleted file mode 100644 index a498a14a5..000000000 --- a/shared-module/bleio/Attribute.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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_SHARED_MODULE_BLEIO_ATTRIBUTE_H -#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H - -// BLE security modes: 0x -typedef enum { - SECURITY_MODE_NO_ACCESS = 0x00, - SECURITY_MODE_OPEN = 0x11, - SECURITY_MODE_ENC_NO_MITM = 0x21, - SECURITY_MODE_ENC_WITH_MITM = 0x31, - SECURITY_MODE_LESC_ENC_WITH_MITM = 0x41, - SECURITY_MODE_SIGNED_NO_MITM = 0x12, - SECURITY_MODE_SIGNED_WITH_MITM = 0x22, -} bleio_attribute_security_mode_t; - -#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_ATTRIBUTE_H diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h deleted file mode 100644 index 409a57c76..000000000 --- a/shared-module/bleio/Characteristic.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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_SHARED_MODULE_BLEIO_CHARACTERISTIC_H -#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H - -typedef enum { - CHAR_PROP_NONE = 0, - CHAR_PROP_BROADCAST = 1u << 0, - CHAR_PROP_INDICATE = 1u << 1, - CHAR_PROP_NOTIFY = 1u << 2, - CHAR_PROP_READ = 1u << 3, - CHAR_PROP_WRITE = 1u << 4, - CHAR_PROP_WRITE_NO_RESPONSE = 1u << 5, - CHAR_PROP_ALL = (CHAR_PROP_BROADCAST | CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY | - CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE) -} bleio_characteristic_properties_enum_t; -typedef uint8_t bleio_characteristic_properties_t; - -#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H diff --git a/shared-module/bleio/ScanEntry.c b/shared-module/bleio/ScanEntry.c deleted file mode 100644 index 306ac33c5..000000000 --- a/shared-module/bleio/ScanEntry.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * 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 - -#include "shared-bindings/bleio/Address.h" -#include "shared-module/bleio/Address.h" -#include "shared-module/bleio/ScanEntry.h" - -mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self) { - return MP_OBJ_FROM_PTR(self->address); -} - -mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self) { - return self->data; -} - -mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self) { - return self->rssi; -} diff --git a/shared-module/bleio/ScanEntry.h b/shared-module/bleio/ScanEntry.h deleted file mode 100644 index b302460e6..000000000 --- a/shared-module/bleio/ScanEntry.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * - * 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_SHARED_MODULE_BLEIO_SCANENTRY_H -#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H - -#include "py/obj.h" -#include "shared-bindings/bleio/Address.h" - -typedef struct { - mp_obj_base_t base; - bool connectable; - int8_t rssi; - bleio_address_obj_t *address; - mp_obj_t data; -} bleio_scanentry_obj_t; - -#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H -- cgit v1.2.3 From b318896b8509df07630d7ee1d27ab46b6dcba1c1 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Tue, 3 Sep 2019 12:35:41 -0400 Subject: Capture rotation --- shared-module/displayio/display_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'shared-module') diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c index 0f449ea34..95fa08f54 100644 --- a/shared-module/displayio/display_core.c +++ b/shared-module/displayio/display_core.c @@ -85,6 +85,7 @@ void displayio_display_core_construct(displayio_display_core_t* self, self->ram_width = ram_width; self->ram_height = ram_height; rotation = rotation % 360; + self->rotation = rotation; self->transform.x = 0; self->transform.y = 0; self->transform.scale = 1; -- cgit v1.2.3 From b53f1698242ef797b7176e32a92b92f282d998d6 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Sep 2019 14:46:47 -0700 Subject: Fix I2CDisplay bus_free to not grab lock Fixes #2098 --- shared-module/displayio/Display.c | 2 +- shared-module/displayio/I2CDisplay.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index f210a80c3..212ee8c7b 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -90,7 +90,7 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, self->core.send(self->core.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, cmd, 1); self->core.send(self->core.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, data_size); } - self->core.end_transaction(self->core.bus); + displayio_display_core_end_transaction(&self->core); uint16_t delay_length_ms = 10; if (delay) { data_size++; diff --git a/shared-module/displayio/I2CDisplay.c b/shared-module/displayio/I2CDisplay.c index 87261dca0..a3b167a33 100644 --- a/shared-module/displayio/I2CDisplay.c +++ b/shared-module/displayio/I2CDisplay.c @@ -91,11 +91,13 @@ bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t obj) { if (!common_hal_busio_i2c_try_lock(self->bus)) { return false; } + common_hal_busio_i2c_unlock(self->bus); return true; } bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t obj) { - return common_hal_displayio_i2cdisplay_bus_free(obj); + displayio_i2cdisplay_obj_t* self = MP_OBJ_TO_PTR(obj); + return !common_hal_busio_i2c_try_lock(self->bus); } void common_hal_displayio_i2cdisplay_send(mp_obj_t obj, display_byte_type_t data_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length) { -- cgit v1.2.3