@@ -42,40 +42,22 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self,
42
42
bleio_attribute_security_mode_t read_perm , bleio_attribute_security_mode_t write_perm ,
43
43
mp_int_t max_length , bool fixed_length , mp_buffer_info_t * initial_value_bufinfo ,
44
44
const char * user_description ) {
45
- mp_raise_NotImplementedError (NULL );
46
45
self -> service = service ;
47
46
self -> uuid = uuid ;
48
47
self -> handle = BLEIO_HANDLE_INVALID ;
48
+ self -> cccd_handle = BLEIO_HANDLE_INVALID ;
49
+ self -> sccd_handle = BLEIO_HANDLE_INVALID ;
49
50
self -> props = props ;
50
51
self -> read_perm = read_perm ;
51
52
self -> write_perm = write_perm ;
52
- self -> initial_value_len = 0 ;
53
- self -> initial_value = NULL ;
54
- if (initial_value_bufinfo != NULL ) {
55
- // Copy the initial value if it's on the heap. Otherwise it's internal and we may not be able
56
- // to allocate.
57
- self -> initial_value_len = initial_value_bufinfo -> len ;
58
- if (gc_alloc_possible ()) {
59
- if (gc_nbytes (initial_value_bufinfo -> buf ) > 0 ) {
60
- uint8_t * initial_value = m_malloc (self -> initial_value_len , false);
61
- memcpy (initial_value , initial_value_bufinfo -> buf , self -> initial_value_len );
62
- self -> initial_value = initial_value ;
63
- } else {
64
- self -> initial_value = initial_value_bufinfo -> buf ;
65
- }
66
- self -> descriptor_list = mp_obj_new_list (0 , NULL );
67
- } else {
68
- self -> initial_value = initial_value_bufinfo -> buf ;
69
- self -> descriptor_list = NULL ;
70
- }
53
+ common_hal_bleio_characteristic_set_value (self , initial_value_bufinfo );
54
+
55
+ if (gc_alloc_possible ()) {
56
+ self -> descriptor_list = mp_obj_new_list (0 , NULL );
57
+ } else {
58
+ self -> descriptor_list = NULL ;
71
59
}
72
60
73
- // const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX;
74
- // if (max_length < 0 || max_length > max_length_max) {
75
- // mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"),
76
- // max_length_max, fixed_length ? "True" : "False");
77
- // }
78
- // TODO: Implement this.
79
61
self -> max_length = max_length ;
80
62
self -> fixed_length = fixed_length ;
81
63
@@ -97,17 +79,123 @@ bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_character
97
79
return self -> service ;
98
80
}
99
81
82
+ typedef struct {
83
+ TaskHandle_t task ;
84
+ uint8_t * buf ;
85
+ uint16_t len ;
86
+ } _read_info_t ;
87
+
88
+ STATIC int _read_cb (uint16_t conn_handle ,
89
+ const struct ble_gatt_error * error ,
90
+ struct ble_gatt_attr * attr ,
91
+ void * arg ) {
92
+ _read_info_t * read_info = (_read_info_t * )arg ;
93
+ switch (error -> status ) {
94
+ case 0 : {
95
+ int len = MIN (read_info -> len , OS_MBUF_PKTLEN (attr -> om ));
96
+ os_mbuf_copydata (attr -> om , attr -> offset , len , read_info -> buf );
97
+ read_info -> len = len ;
98
+ }
99
+ MP_FALLTHROUGH ;
100
+
101
+ default :
102
+ #if CIRCUITPY_VERBOSE_BLE
103
+ // For debugging.
104
+ mp_printf (& mp_plat_print , "Read status: %d\n" , error -> status );
105
+ #endif
106
+ xTaskNotify (read_info -> task , error -> status , eSetValueWithOverwrite );
107
+ break ;
108
+ }
109
+
110
+ return 0 ;
111
+ }
112
+
100
113
size_t common_hal_bleio_characteristic_get_value (bleio_characteristic_obj_t * self , uint8_t * buf , size_t len ) {
101
- // TODO: Implement this.
114
+ // Do GATT operations only if this characteristic has been added to a registered service.
115
+ if (self -> handle == BLEIO_HANDLE_INVALID ) {
116
+ return 0 ;
117
+ }
118
+ uint16_t conn_handle = bleio_connection_get_conn_handle (self -> service -> connection );
119
+ if (common_hal_bleio_service_get_is_remote (self -> service )) {
120
+ _read_info_t read_info = {
121
+ .task = xTaskGetCurrentTaskHandle (),
122
+ .buf = buf ,
123
+ .len = len
124
+ };
125
+ CHECK_NIMBLE_ERROR (ble_gattc_read (conn_handle , self -> handle , _read_cb , & read_info ));
126
+ int error_code ;
127
+ xTaskNotifyWait (0 , 0 , (uint32_t * )& error_code , 200 );
128
+ CHECK_BLE_ERROR (error_code );
129
+ return read_info .len ;
130
+ } else {
131
+ len = MIN (self -> current_value_len , len );
132
+ memcpy (buf , self -> current_value , len );
133
+ return len ;
134
+ }
135
+
102
136
return 0 ;
103
137
}
104
138
105
139
size_t common_hal_bleio_characteristic_get_max_length (bleio_characteristic_obj_t * self ) {
106
140
return self -> max_length ;
107
141
}
108
142
143
+ STATIC int _write_cb (uint16_t conn_handle ,
144
+ const struct ble_gatt_error * error ,
145
+ struct ble_gatt_attr * attr ,
146
+ void * arg ) {
147
+ TaskHandle_t task = (TaskHandle_t )arg ;
148
+ xTaskNotify (task , error -> status , eSetValueWithOverwrite );
149
+
150
+ return 0 ;
151
+ }
152
+
109
153
void common_hal_bleio_characteristic_set_value (bleio_characteristic_obj_t * self , mp_buffer_info_t * bufinfo ) {
110
- // TODO: Implement this.
154
+ if (common_hal_bleio_service_get_is_remote (self -> service )) {
155
+ uint16_t conn_handle = bleio_connection_get_conn_handle (self -> service -> connection );
156
+ if ((self -> props & CHAR_PROP_WRITE_NO_RESPONSE ) != 0 ) {
157
+ CHECK_NIMBLE_ERROR (ble_gattc_write_no_rsp_flat (conn_handle , self -> handle , bufinfo -> buf , bufinfo -> len ));
158
+ } else {
159
+ CHECK_NIMBLE_ERROR (ble_gattc_write_flat (conn_handle , self -> handle , bufinfo -> buf , bufinfo -> len , _write_cb , xTaskGetCurrentTaskHandle ()));
160
+ int error_code ;
161
+ xTaskNotifyWait (0 , 0 , (uint32_t * )& error_code , 200 );
162
+ CHECK_BLE_ERROR (error_code );
163
+ }
164
+ } else {
165
+ // Validate data length for local characteristics only.
166
+ // TODO: Test this once we can get servers going.
167
+ if (self -> fixed_length && bufinfo -> len != self -> max_length ) {
168
+ mp_raise_ValueError (translate ("Value length != required fixed length" ));
169
+ }
170
+ if (bufinfo -> len > self -> max_length ) {
171
+ mp_raise_ValueError (translate ("Value length > max_length" ));
172
+ }
173
+
174
+ if (bufinfo == NULL ) {
175
+ self -> current_value_len = 0 ;
176
+ ble_gatts_chr_updated (self -> handle );
177
+ return ;
178
+ }
179
+
180
+ self -> current_value_len = bufinfo -> len ;
181
+ // If we've already allocated an internal buffer or the provided buffer
182
+ // is on the heap, then copy into the internal buffer.
183
+ if (self -> current_value_alloc > 0 || gc_nbytes (bufinfo -> buf ) > 0 ) {
184
+ if (self -> current_value_alloc < bufinfo -> len ) {
185
+ self -> current_value = m_realloc (self -> current_value , bufinfo -> len );
186
+ // Get the number of bytes from the heap because it may be more
187
+ // than the len due to gc block size.
188
+ self -> current_value_alloc = gc_nbytes (self -> current_value );
189
+ }
190
+ memcpy (self -> current_value , bufinfo -> buf , bufinfo -> len );
191
+ } else {
192
+ // Otherwise, use the provided buffer to delay any heap allocation.
193
+ self -> current_value = bufinfo -> buf ;
194
+ self -> current_value_alloc = 0 ;
195
+ }
196
+
197
+ ble_gatts_chr_updated (self -> handle );
198
+ }
111
199
}
112
200
113
201
bleio_uuid_obj_t * common_hal_bleio_characteristic_get_uuid (bleio_characteristic_obj_t * self ) {
@@ -118,10 +206,32 @@ bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties
118
206
return self -> props ;
119
207
}
120
208
121
- void common_hal_bleio_characteristic_add_descriptor (bleio_characteristic_obj_t * self , bleio_descriptor_obj_t * descriptor ) {
209
+ void common_hal_bleio_characteristic_add_descriptor (bleio_characteristic_obj_t * self ,
210
+ bleio_descriptor_obj_t * descriptor ) {
122
211
// TODO: Implement this.
212
+
213
+ mp_obj_list_append (MP_OBJ_FROM_PTR (self -> descriptor_list ),
214
+ MP_OBJ_FROM_PTR (descriptor ));
123
215
}
124
216
125
217
void common_hal_bleio_characteristic_set_cccd (bleio_characteristic_obj_t * self , bool notify , bool indicate ) {
126
- // TODO: Implement this.
218
+ if (self -> cccd_handle == BLEIO_HANDLE_INVALID ) {
219
+ mp_raise_bleio_BluetoothError (translate ("No CCCD for this Characteristic" ));
220
+ }
221
+
222
+ if (!common_hal_bleio_service_get_is_remote (self -> service )) {
223
+ mp_raise_bleio_RoleError (translate ("Can't set CCCD on local Characteristic" ));
224
+ }
225
+
226
+ const uint16_t conn_handle = bleio_connection_get_conn_handle (self -> service -> connection );
227
+ common_hal_bleio_check_connected (conn_handle );
228
+
229
+ uint16_t cccd_value =
230
+ (notify ? 1 << 0 : 0 ) |
231
+ (indicate ? 1 << 1 : 0 );
232
+
233
+ CHECK_NIMBLE_ERROR (ble_gattc_write_flat (conn_handle , self -> cccd_handle , & cccd_value , 2 , _write_cb , xTaskGetCurrentTaskHandle ()));
234
+ int error_code ;
235
+ xTaskNotifyWait (0 , 0 , (uint32_t * )& error_code , 200 );
236
+ CHECK_BLE_ERROR (error_code );
127
237
}
0 commit comments