Files
M5Stack_MicroPython/MicroPython_BUILD/components/micropython/esp32/machine_rfcomm.c
T
sakabin 070e06db00 update micropython
add m5stack.py
add m5ui.py
2018-12-04 14:06:15 +08:00

1153 lines
43 KiB
C

/*
* This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo
*
* The MIT License (MIT)
*
* Copyright (c) 2018 LoBo (https://github.com/loboris)
*
* 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 "sdkconfig.h"
#ifdef CONFIG_MICROPY_USE_RFCOMM
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "api/esp_bt_main.h"
#include "api/esp_gap_bt_api.h"
#include "api/esp_bt_device.h"
#include "api/esp_spp_api.h"
#include "machine_uart.h"
#include "py/stream.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#include "modmachine.h"
#define SPP_TAG "RFCOMM"
#define RFCOMM_CB_TYPE_DATA 1
#define RFCOMM_CB_TYPE_PATTERN 2
#define RFCOMM_CB_TYPE_STATUS 3
#define RFCOMM_MAX_CLIENTS 8
typedef struct _rfcomm_cb_obj_t {
int data_cb_size;
uint8_t pattern[16];
uint8_t pattern_len;
uint32_t *data_cb;
uint32_t *pattern_cb;
uint8_t lineend[3];
} rfcomm_cb_obj_t;
typedef struct _rfcomm_client_t {
uint8_t client_btaddr[ESP_BD_ADDR_LEN];
uint32_t handle;
uint32_t wr_handle;
uint16_t size;
uint16_t iget;
uint16_t iput;
rfcomm_cb_obj_t cb;
uint8_t *buf;
} rfcomm_client_t;
typedef struct _machine_rfcomm_obj_t {
mp_obj_base_t base;
bool init;
int8_t channel;
int8_t connected;
esp_spp_sec_t sec_mask;
esp_spp_role_t role;
char server_name[32];
char device_name[32];
rfcomm_cb_obj_t cb;
uint16_t timeout; // timeout waiting for first char (in ms)
uint16_t buffer_size;
uint32_t *status_cb;
} machine_rfcomm_obj_t;
static machine_rfcomm_obj_t *rfcomm_obj = NULL;
static QueueHandle_t rfcomm_mutex = NULL;
static bool bt_controller_is_init = false;
static rfcomm_client_t * clients[RFCOMM_MAX_CLIENTS] = {NULL};
static const char* const rfcomm_events[] = {
"SPP initialized", // 0
"SDP discovery complete", // 1
"SPP client connection open", // 2
"SPP connection closed, client disconnected", // 3
"SPP server started", // 4
"SPP client initiated a connection", // 5
"SPP connection received data", // 6
"SPP connection congestion status changed", // 7
"SPP write operation complete", // 8
"SPP connection open, client connected", // 9
"Data received, buffer overflow", // 10
"SPP server start failed", // 11
};
//-----------------------------------------------------------------
static int rfcomm_buf_get(uint8_t idx, uint8_t *dest, uint16_t len)
{
if ((clients[idx] == NULL) || (clients[idx]->buf == NULL)) return -1;
if (clients[idx]->iget == clients[idx]->iput) return -1; // input buffer empty
int res = 0;
for (int i=0; i<len; i++) {
dest[i] = clients[idx]->buf[clients[idx]->iget++];
res++;
if (clients[idx]->iget == clients[idx]->iput) break;
}
// move the buffer and adjust the pointers
memmove(clients[idx], clients[idx]->buf+res, clients[idx]->iput - res);
clients[idx]->iget -= res;
clients[idx]->iput -= res;
return res;
}
//-------------------------------------------------------------------
static int rfcomm_buf_put(uint8_t idx, uint8_t *source, uint16_t len)
{
if ((clients[idx] == NULL) || (clients[idx]->buf == NULL)) return 1;
int res = 0;
for (int i=0; i<len; i++) {
if (clients[idx]->iput >= clients[idx]->size) return 1; // overflow
clients[idx]->buf[clients[idx]->iput++] = source[i];
}
return res;
}
//--------------------------------------------------------------------------------------------------------------
static void _sched_callback(mp_obj_t function, int8_t client, int type, int iarglen, uint8_t *sarg, char *param)
{
mp_sched_carg_t *carg = make_cargs(MP_SCHED_CTYPE_TUPLE);
if (carg == NULL) return;
if (client >= 0) {
if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_INT, client, NULL, NULL)) return;
}
else {
if (!make_carg_entry(carg, 0, MP_SCHED_ENTRY_TYPE_NONE, 0, NULL, NULL)) return;
}
if (!make_carg_entry(carg, 1, MP_SCHED_ENTRY_TYPE_INT, type, NULL, NULL)) return;
if (sarg) {
if (iarglen == 0) {
if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_STR, strlen((char *)sarg), sarg, NULL)) return;
}
else {
if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_STR, iarglen, sarg, NULL)) return;
}
}
else {
if (!make_carg_entry(carg, 2, MP_SCHED_ENTRY_TYPE_INT, iarglen, NULL, NULL)) return;
}
if (param) {
if (!make_carg_entry(carg, 3, MP_SCHED_ENTRY_TYPE_STR, strlen(param), (uint8_t *)param, NULL)) return;
}
mp_sched_schedule(function, mp_const_none, carg);
}
//----------------------------------------------------------------------
static void _rfcomm_event_status(int8_t client, int nevent, char *param)
{
if (rfcomm_obj->status_cb) {
_sched_callback(rfcomm_obj->status_cb, client, nevent, 0, (uint8_t *)rfcomm_events[nevent], param);
}
else {
if (param) {
if (client >= 0) {
ESP_LOGD(SPP_TAG, "[client_%d] %s (%s)", client, rfcomm_events[nevent], param);
}
else {
ESP_LOGD(SPP_TAG, "%s (%s)", rfcomm_events[nevent], param);
}
}
else {
if (client >= 0) {
ESP_LOGD(SPP_TAG, "[client_%d] %s", client, rfcomm_events[nevent]);
}
else {
ESP_LOGD(SPP_TAG, "%s", rfcomm_events[nevent]);
}
}
}
}
//============================================================================
static void rfcomm_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
if (rfcomm_obj == NULL) return;
int res;
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
switch (event) {
case ESP_SPP_INIT_EVT:
{
esp_bt_dev_set_device_name((const char *)rfcomm_obj->device_name);
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
esp_spp_start_srv(rfcomm_obj->sec_mask, rfcomm_obj->role, rfcomm_obj->channel, (const char *)rfcomm_obj->server_name);
_rfcomm_event_status(-1, 0, NULL);
break;
}
case ESP_SPP_DISCOVERY_COMP_EVT:
_rfcomm_event_status(-1, 1, NULL);
break;
case ESP_SPP_OPEN_EVT:
_rfcomm_event_status(-1, 2, NULL);
break;
case ESP_SPP_CLOSE_EVT:
{
int client = -1;
for (int i=0; i<RFCOMM_MAX_CLIENTS; i++) {
if ((clients[i]) && (clients[i]->handle == param->close.handle)) {
if (clients[i]->buf) free(clients[i]->buf);
free(clients[i]);
clients[i] = NULL;
client = i;
break;
}
}
if (rfcomm_obj->connected) rfcomm_obj->connected--;
_rfcomm_event_status(client, 3, NULL);
break;
}
case ESP_SPP_START_EVT:
if (param->start.status != ESP_SPP_SUCCESS) {
_rfcomm_event_status(-1, 11, NULL);
rfcomm_obj->channel++;
if (rfcomm_obj->channel > 79) rfcomm_obj->channel = 1;
ESP_LOGW(SPP_TAG, "Fail reason: %d, retry on channel %d", param->start.status, rfcomm_obj->channel);
esp_spp_start_srv(rfcomm_obj->sec_mask, rfcomm_obj->role, rfcomm_obj->channel, (const char *)rfcomm_obj->server_name);
}
else {
_rfcomm_event_status(-1, 4, NULL);
}
break;
case ESP_SPP_CL_INIT_EVT:
_rfcomm_event_status(-1, 5, NULL);
break;
case ESP_SPP_DATA_IND_EVT:
{
// Data received
int cidx = -1;
for (int i=0; i<RFCOMM_MAX_CLIENTS; i++) {
if ((clients[i]) && (clients[i]->handle == param->data_ind.handle)) {
cidx = i;
break;
}
}
if (cidx >= 0) {
res = rfcomm_buf_put(cidx, param->data_ind.data, param->data_ind.len);
if (res) _rfcomm_event_status(cidx, 10, NULL);
else {
bool serviced = false;
if ((clients[cidx]->cb.data_cb) || (rfcomm_obj->cb.data_cb)) {
rfcomm_cb_obj_t *pcb = &rfcomm_obj->cb;
if (clients[cidx]->cb.data_cb) pcb = &clients[cidx]->cb;
if ((pcb->data_cb_size > 0) && (clients[cidx]->iput >= pcb->data_cb_size)) {
// ** callback on data length
uint8_t *dtmp = malloc(pcb->data_cb_size);
if (dtmp) {
rfcomm_buf_get(cidx, dtmp, pcb->data_cb_size);
_sched_callback(pcb->data_cb, cidx, RFCOMM_CB_TYPE_DATA, pcb->data_cb_size, dtmp, NULL);
free(dtmp);
}
serviced = true;
}
}
else if ((!serviced) && ((clients[cidx]->cb.pattern_cb) || (rfcomm_obj->cb.pattern_cb))) {
// ** callback on pattern received
rfcomm_cb_obj_t *pcb = &rfcomm_obj->cb;
if (clients[cidx]->cb.pattern_cb) pcb = &clients[cidx]->cb;
res = match_pattern(clients[cidx]->buf, clients[cidx]->iput, pcb->pattern, pcb->pattern_len);
if (res >= 0) {
// found, pull data, including pattern from buffer
uint8_t *dtmp = malloc(res + pcb->pattern_len);
if (dtmp) {
rfcomm_buf_get(cidx, dtmp, res + pcb->pattern_len);
_sched_callback(pcb->pattern_cb, cidx, RFCOMM_CB_TYPE_PATTERN, res, dtmp, NULL);
free(dtmp);
}
}
}
else {
ESP_LOGD(SPP_TAG, "[client %d] Data received: len=%d, in buffer=%d", cidx, param->data_ind.len, clients[cidx]->iput);
}
}
}
break;
}
case ESP_SPP_CONG_EVT:
_rfcomm_event_status(-1, 7, NULL);
break;
case ESP_SPP_WRITE_EVT:
_rfcomm_event_status(-1, 8, NULL);
break;
case ESP_SPP_SRV_OPEN_EVT:
{
char btaddr[13] = {0};
for (res = 0; res < 6; res++) {
sprintf(btaddr+(res*2), "%02X", param->srv_open.rem_bda[res]);
}
// Add the new client to clients list
int idx;
for (idx=0; idx<RFCOMM_MAX_CLIENTS; idx++) {
if (clients[idx] == NULL) {
clients[idx] = malloc(sizeof(rfcomm_client_t));
if (clients[idx] == NULL) {
esp_spp_disconnect(param->srv_open.handle);
ESP_LOGE(SPP_TAG, "Error allocating client's data for client %d [%s]", idx, btaddr);
break;
}
memset(clients[idx], 0, sizeof(rfcomm_client_t));
// Allocate the client's buffer
clients[idx]->buf = malloc(rfcomm_obj->buffer_size);
if (clients[idx]->buf == NULL) {
free(clients[idx]);
clients[idx] = NULL;
esp_spp_disconnect(param->srv_open.handle);
ESP_LOGE(SPP_TAG, "Error allocating data buffer for client %d [%s]", idx, btaddr);
break;
}
clients[idx]->size = rfcomm_obj->buffer_size;
clients[idx]->handle = param->srv_open.handle;
clients[idx]->wr_handle = param->write.handle;
memcpy(clients[idx]->client_btaddr, param->srv_open.rem_bda, ESP_BD_ADDR_LEN);
memcpy(clients[idx]->cb.lineend, rfcomm_obj->cb.lineend, sizeof(rfcomm_obj->cb.lineend));
_rfcomm_event_status(idx, 9, btaddr);
rfcomm_obj->connected++;
break;
}
}
if (idx >= RFCOMM_MAX_CLIENTS) {
esp_spp_disconnect(param->srv_open.handle);
ESP_LOGW(SPP_TAG, "Maximal number of clients (%d) already connected", RFCOMM_MAX_CLIENTS);
}
break;
}
default:
break;
}
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
}
//--------------------------------------------------------------------------------
static char *_rfcomm_read(uint8_t client, int timeout, char *lnend, char *lnstart)
{
char *rdstr = NULL;
int rdlen = -1;
int minlen = strlen(lnend);
if (lnstart) minlen += strlen(lnstart);
if (timeout == 0) {
if (rfcomm_mutex) {
if (xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) {
return NULL;
}
}
// check for minimal length
if (clients[client]->iput < minlen) {
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
return NULL;
}
while (1) {
rdlen = match_pattern(clients[client]->buf, clients[client]->iput, (uint8_t *)lnend, strlen(lnend));
if (rdlen >= 0) {
// found, pull data, including pattern from buffer
rdlen += 2;
rdstr = calloc(rdlen+1, 1);
if (rdstr) {
rfcomm_buf_get(client, (uint8_t *)rdstr, rdlen);
rdstr[rdlen] = 0;
if (lnstart) {
// Match beginning string
char *start_ptr = strstr(rdstr, lnstart);
if (start_ptr) {
if (start_ptr != rdstr) {
char *new_rdstr = strdup(start_ptr);
free(rdstr);
rdstr = new_rdstr;
}
break;
}
else {
free(rdstr);
rdstr = NULL;
rdlen = -1;
break;
}
}
else break;
}
else {
rdlen = -1;
break;
}
}
else break;
}
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
if (rdlen < 0) return NULL;
}
else {
// wait until lnend received or timeout
int wait = timeout;
int buflen = 0;
mp_hal_set_wdt_tmo();
while (wait > 0) {
if (rfcomm_mutex) {
if (xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) {
vTaskDelay(10 / portTICK_PERIOD_MS);
wait -= 10;
mp_hal_reset_wdt();
continue;
}
}
if (buflen < clients[client]->iput) {
// ** new data received, reset timeout
buflen = clients[client]->iput;
wait = timeout;
}
if (clients[client]->iput < minlen) {
// ** too few characters received
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
vTaskDelay(10 / portTICK_PERIOD_MS);
wait -= 10;
mp_hal_reset_wdt();
continue;
}
while (1) {
// * Check if lineend pattern is received
rdlen = match_pattern(clients[client]->buf, clients[client]->iput, (uint8_t *)lnend, strlen(lnend));
if (rdlen >= 0) {
rdlen += 2;
// * found, pull data, including pattern from buffer
rdstr = calloc(rdlen+1, 1);
if (rdstr) {
rfcomm_buf_get(client, (uint8_t *)rdstr, rdlen);
rdstr[rdlen] = 0;
if (lnstart) {
// * Find beginning of the sentence
char *start_ptr = strstr(rdstr, lnstart);
if (start_ptr) {
// === received string ending with lnend and starting with lnstart
if (start_ptr != rdstr) {
char *new_rdstr = strdup(start_ptr);
free(rdstr);
rdstr = new_rdstr;
}
break;
}
else {
free(rdstr);
rdstr = NULL;
break;
}
}
else break; // === received string ending with lineend
}
else {
// error allocating buffer, finish
wait = 0;
break;
}
}
else break;
}
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
if (rdstr) break;
if (wait > 0) {
vTaskDelay(10 / portTICK_PERIOD_MS);
wait -= 10;
mp_hal_reset_wdt();
}
}
}
return rdstr;
}
//-----------------------------------------------------------------------------------------------
STATIC int _machine_rfcomm_read(mp_obj_t self_in, uint8_t client, void *buf_in, mp_uint_t size) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
// make sure we want at least 1 char
if (size == 0) return 0;
int bytes_read = 0;
if (self->timeout == 0) {
// just return the buffer content
if (rfcomm_mutex) {
if (xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) {
return 0;
}
}
bytes_read = rfcomm_buf_get(client, (uint8_t *)buf_in, size);
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
if (bytes_read < 0) bytes_read = 0;
}
else {
// wait until data received or timeout
mp_hal_set_wdt_tmo();
int wait = self->timeout;
MP_THREAD_GIL_EXIT();
while (wait > 0) {
if (rfcomm_mutex) {
if (xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS) != pdTRUE) {
vTaskDelay(2 / portTICK_PERIOD_MS);
wait -= 2;
mp_hal_reset_wdt();
continue;
}
}
if (clients[client]->iput < size) {
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
vTaskDelay(2 / portTICK_PERIOD_MS);
wait -= 2;
mp_hal_reset_wdt();
continue;
}
bytes_read = rfcomm_buf_get(client, (uint8_t *)buf_in, size);
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
break;
}
MP_THREAD_GIL_ENTER();
}
return bytes_read;
}
/******************************************************************************/
// MicroPython bindings for UART
//-------------------------------------------------------------------
static void _check_rfcomm(machine_rfcomm_obj_t *self, int chk_client)
{
if ((!self->init) || (rfcomm_obj == NULL)) {
mp_raise_ValueError("RFCOMM not initialized");
}
if (chk_client >= -1) {
if (self->connected <= 0) {
mp_raise_ValueError("No client connected");
}
if (chk_client >= 0) {
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
if (clients[chk_client] == NULL) {
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
mp_raise_ValueError("Client not connected");
}
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
}
}
}
//-------------------------------------------------------------------------------------------------
STATIC void machine_rfcomm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
if ((!self->init) || (rfcomm_obj == NULL)) {
mp_printf(print, "RFCOMM(%s: Deinitialized )", self->device_name);
return;
}
char tmps[16] = {'\0'};
int lnend_idx = 0;
for (int i=0; i < strlen((char *)self->cb.lineend); i++) {
if (self->cb.lineend[i] == 0) break;
if ((self->cb.lineend[i] < 32) || (self->cb.lineend[i] > 126)) {
if (self->cb.lineend[i] == '\r') {
sprintf(tmps+lnend_idx, "\\r");
lnend_idx += 2;
}
else if (self->cb.lineend[i] == '\n') {
sprintf(tmps+lnend_idx, "\\n");
lnend_idx += 2;
}
else {
sprintf(tmps+lnend_idx, "\\x%2x", self->cb.lineend[i]);
lnend_idx += 4;
}
}
else {
sprintf(tmps+lnend_idx, "%c", self->cb.lineend[i]);
lnend_idx++;
}
}
mp_printf(print, "RFCOMM(%s: channel=%d, timeout=%u, buf_size=%u, lineend=b'%s')", self->device_name, self->channel, self->timeout, self->buffer_size, tmps);
if (self->connected) {
mp_printf(print, "\n Connected clients: %d (", self->connected);
int nclients = 0;
for (int client=0; client < RFCOMM_MAX_CLIENTS; client++) {
if (clients[client]) {
memset(tmps, 0, 16);
for (int i = 0; i<6; i++) {
sprintf(tmps+(i*2), "%02X", clients[client]->client_btaddr[i]);
}
if (nclients > 0) mp_printf(print, ", ");
nclients++;
mp_printf(print, "%s [id=%d]", tmps, client);
}
}
mp_printf(print, ")", tmps);
}
if (self->cb.data_cb) {
mp_printf(print, "\n data CB: True, on len: %d", self->cb.data_cb_size);
}
if (self->cb.pattern_cb) {
char pattern[80] = {'\0'};
for (int i=0; i<self->cb.pattern_len; i++) {
if ((self->cb.pattern[i] >= 0x20) && (self->cb.pattern[i] < 0x7f)) pattern[strlen(pattern)] = self->cb.pattern[i];
else sprintf(pattern+strlen(pattern), "\\x%02x", self->cb.pattern[i]);
}
mp_printf(print, "\n pattern CB: True, pattern: b'%s'", pattern);
}
if (self->status_cb) {
mp_printf(print, "\n status CB: True");
}
}
//----------------------------------------------------------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args)
{
enum { ARG_channel, ARG_device, ARG_server, ARG_timeout, ARG_buffer_size, ARG_lineend };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_device, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_server, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 512} },
{ MP_QSTR_lineend, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
if (rfcomm_obj) {
mp_raise_msg(&mp_type_OSError, "RFCOMM object already created, only one allowed");
}
if (rfcomm_mutex == NULL) {
rfcomm_mutex = xSemaphoreCreateMutex();
}
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if ((args[ARG_channel].u_int < 0) || (args[ARG_channel].u_int < 0)) {
mp_raise_ValueError("invalid channel (0-79 allowed)");
}
// Create UART instance, set defaults
machine_rfcomm_obj_t *self = m_new_obj(machine_rfcomm_obj_t);
self->base.type = &machine_rfcomm_type;
self->timeout = 0;
self->cb.pattern[0] = 0;
self->cb.pattern_len = 0;
self->cb.data_cb = NULL;
self->cb.pattern_cb = NULL;
self->status_cb = NULL;
self->cb.data_cb_size = 0;
self->role = ESP_SPP_ROLE_SLAVE;
self->sec_mask = ESP_SPP_SEC_NONE;
self->init = false;
self->channel = (uint8_t)args[ARG_channel].u_int;
// Set names
if (args[ARG_device].u_obj != mp_const_none) {
const char *device = mp_obj_str_get_str(args[ARG_device].u_obj);
memset(self->device_name, 0, sizeof(self->device_name));
snprintf(self->device_name, sizeof(self->device_name), "%s", device);
}
else {
sprintf(self->device_name, "MicroPython_RFCOMM");
}
if (args[ARG_server].u_obj != mp_const_none) {
const char *device = mp_obj_str_get_str(args[ARG_server].u_obj);
memset(self->server_name, 0, sizeof(self->server_name));
snprintf(self->server_name, sizeof(self->server_name)-1, "%s", device);
}
else {
sprintf(self->server_name, "ESP32_SPP_Server");
}
// set timeout
if (args[ARG_timeout].u_int >= 0) self->timeout = args[ARG_timeout].u_int;
// set line end
sprintf((char *)self->cb.lineend, "\r\n");
mp_buffer_info_t lnend_buff;
mp_obj_type_t *lne_type = mp_obj_get_type(args[ARG_lineend].u_obj);
if (lne_type->buffer_p.get_buffer != NULL) {
int ret = lne_type->buffer_p.get_buffer(args[ARG_lineend].u_obj, &lnend_buff, MP_BUFFER_READ);
if (ret == 0) {
if ((lnend_buff.len > 0) && (lnend_buff.len < sizeof(self->cb.lineend))) {
memset(self->cb.lineend, 0, sizeof(self->cb.lineend));
memcpy(self->cb.lineend, lnend_buff.buf, lnend_buff.len);
}
}
}
// Set buffer size
int bufsize = args[ARG_buffer_size].u_int;
if (bufsize < 512) bufsize = 512;
if (bufsize > 8192) bufsize = 8192;
self->buffer_size = bufsize;
self->init = true;
rfcomm_obj = self;
if (!bt_controller_is_init) {
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if (esp_bt_controller_init(&bt_cfg) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, "BT controller initialization failed");
}
bt_controller_is_init = true;
}
if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, "BT controller enable failed");
}
if (esp_bluedroid_init() != ESP_OK) {
mp_raise_msg(&mp_type_OSError, "Bluedroid initialization failed");
}
if (esp_bluedroid_enable() != ESP_OK) {
mp_raise_msg(&mp_type_OSError, "Bluedroid enable failed");
}
if (esp_spp_register_callback(rfcomm_spp_cb) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, "BT event callback registration failed");
}
if (esp_spp_init(ESP_SPP_MODE_CB) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, "BT SPP initialization failed");
}
return MP_OBJ_FROM_PTR(self);
}
//---------------------------------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
{
enum { ARG_client, ARG_timeout, ARG_buffer_size, ARG_lineend };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_client, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_lineend, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
machine_rfcomm_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);
_check_rfcomm(self, -2);
// set timeout
if (args[ARG_timeout].u_int >= 0) self->timeout = args[ARG_timeout].u_int;
// set line end
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
rfcomm_cb_obj_t *pcb = &self->cb;
if ((args[ARG_client].u_int >= 0) && (clients[args[ARG_client].u_int])) pcb = &clients[args[ARG_client].u_int]->cb;
mp_buffer_info_t lnend_buff;
mp_obj_type_t *lne_type = mp_obj_get_type(args[ARG_lineend].u_obj);
if (lne_type->buffer_p.get_buffer != NULL) {
int ret = lne_type->buffer_p.get_buffer(args[ARG_lineend].u_obj, &lnend_buff, MP_BUFFER_READ);
if (ret == 0) {
if ((lnend_buff.len > 0) && (lnend_buff.len < sizeof(pcb->lineend))) {
memset(pcb->lineend, 0, sizeof(pcb->lineend));
memcpy(pcb->lineend, lnend_buff.buf, lnend_buff.len);
}
}
}
// Set buffer size
if ((args[ARG_buffer_size].u_int >= 512) && (args[ARG_buffer_size].u_int <= 8192)) {
if (args[ARG_buffer_size].u_int != self->buffer_size) {
self->buffer_size = args[ARG_buffer_size].u_int;
}
}
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_rfcomm_init_obj, 1, machine_rfcomm_init);
//-------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_deinit(mp_obj_t self_in) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self->init) {
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
for (int i=0; i<RFCOMM_MAX_CLIENTS; i++) {
if (clients[i]) {
if (clients[i]->buf) free(clients[i]->buf);
free(clients[i]);
clients[i] = NULL;
break;
}
}
rfcomm_obj = NULL;
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
esp_spp_deinit();
esp_bluedroid_disable();
esp_bluedroid_deinit();
esp_bt_controller_disable();
//esp_bt_controller_deinit();
self->init = false;
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_rfcomm_deinit_obj, machine_rfcomm_deinit);
//---------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_any(mp_obj_t self_in, mp_obj_t client) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
int idx = mp_obj_get_int(client);
_check_rfcomm(self, idx);
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
int res = clients[idx]->iput;
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
return MP_OBJ_NEW_SMALL_INT(res);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_rfcomm_any_obj, machine_rfcomm_any);
//-------------------------------------------------------------------
mp_obj_t machine_rfcomm_readln(size_t n_args, const mp_obj_t *args) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
int idx = mp_obj_get_int(args[1]);
_check_rfcomm(self, idx);
int timeout = self->timeout;
if (n_args > 1) timeout = mp_obj_get_int(args[2]);
const char *startstr = NULL;
if (n_args > 3) startstr = mp_obj_str_get_str(args[3]);
MP_THREAD_GIL_EXIT();
char *rdstr = _rfcomm_read(idx, timeout, (char *)clients[idx]->cb.lineend, (char *)startstr);
MP_THREAD_GIL_ENTER();
if (rdstr == NULL) return mp_const_none;
mp_obj_t res_str = mp_obj_new_str((const char *)rdstr, strlen(rdstr));
if (rdstr != NULL) free(rdstr);
return res_str;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rfcomm_readln_obj, 2, 4, machine_rfcomm_readln);
//-------------------------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_read(mp_obj_t self_in, mp_obj_t client, mp_obj_t len_in)
{
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
int idx = mp_obj_get_int(client);
_check_rfcomm(self, idx);
vstr_t vstr;
int len = mp_obj_get_int(len_in);
vstr_init_len(&vstr, len);
int res = _machine_rfcomm_read(self, idx, vstr.buf, len);
if (res <= 0) return mp_const_empty_bytes;
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_rfcomm_read_obj, machine_rfcomm_read);
//------------------------------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_readinto(mp_obj_t self_in, mp_obj_t client, mp_obj_t buf_out)
{
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
int idx = mp_obj_get_int(client);
_check_rfcomm(self, idx);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf_out, &bufinfo, MP_BUFFER_WRITE);
int res = _machine_rfcomm_read(self, idx, bufinfo.buf, bufinfo.len);
if (res <= 0) return MP_OBJ_NEW_SMALL_INT(0);
return MP_OBJ_NEW_SMALL_INT(res);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_rfcomm_readinto_obj, machine_rfcomm_readinto);
//----------------------------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_write(mp_obj_t self_in, mp_obj_t client, mp_obj_t buf_in) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
int idx = mp_obj_get_int(client);
_check_rfcomm(self, idx);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
int res = -1;
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
res = esp_spp_write(clients[idx]->wr_handle, bufinfo.len, (uint8_t *)bufinfo.buf);
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
if (res != ESP_OK) return MP_OBJ_NEW_SMALL_INT(0);
return MP_OBJ_NEW_SMALL_INT(bufinfo.len);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_rfcomm_write_obj, machine_rfcomm_write);
//----------------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_disconnect(mp_obj_t self_in, mp_obj_t client) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
int idx = mp_obj_get_int(client);
_check_rfcomm(self, idx);
int res = -1;
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
res = esp_spp_disconnect(clients[idx]->handle);
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
return (res == ESP_OK) ? mp_const_true : mp_const_false;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_rfcomm_disconnect_obj, machine_rfcomm_disconnect);
//----------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_connected(mp_obj_t self_in) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
_check_rfcomm(self, -2);
mp_obj_t tuple[2];
tuple[0] = mp_obj_new_int(self->connected);
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
if (self->connected) {
char btaddr[13] = {0};
mp_obj_t clients_tuple[self->connected];
mp_obj_t client_tuple[2];
int client = 0;
for (int idx=0; idx<RFCOMM_MAX_CLIENTS; idx++) {
if (clients[idx]) {
for (int i = 0; i<6; i++) {
sprintf(btaddr+(i*2), "%02X", clients[idx]->client_btaddr[i]);
}
client_tuple[0] = mp_obj_new_int(idx);
client_tuple[1] = mp_obj_new_str(btaddr, strlen(btaddr));
clients_tuple[client++] = mp_obj_new_tuple(2, client_tuple);
}
}
tuple[1] = mp_obj_new_tuple(self->connected, clients_tuple);
}
else tuple[1] = mp_const_none;
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
return mp_obj_new_tuple(2, tuple);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_rfcomm_connected_obj, machine_rfcomm_connected);
//--------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_channel(mp_obj_t self_in) {
machine_rfcomm_obj_t *self = MP_OBJ_TO_PTR(self_in);
_check_rfcomm(self, -2);
return MP_OBJ_NEW_SMALL_INT(self->channel);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_rfcomm_channel_obj, machine_rfcomm_channel);
//-------------------------------------------------------------------------------------------------
STATIC mp_obj_t machine_rfcomm_callback(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
{
enum { ARG_type, ARG_func, ARG_client, ARG_pattern, ARG_datalen };
const mp_arg_t allowed_args[] = {
{ MP_QSTR_type, MP_ARG_REQUIRED | MP_ARG_INT, { .u_int = 0 } },
{ MP_QSTR_func, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
{ MP_QSTR_client, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = -2 } },
{ MP_QSTR_pattern, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } },
{ MP_QSTR_data_len, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = -1 } },
};
machine_rfcomm_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);
int client = args[ARG_client].u_int;
if ((client != -2) && ((client < 0) || (client >= RFCOMM_MAX_CLIENTS))) {
mp_raise_ValueError("invalid client id");
}
_check_rfcomm(self, client);
int datalen = -1;
mp_buffer_info_t pattern_buff;
int cbtype = args[ARG_type].u_int;
if ((cbtype != RFCOMM_CB_TYPE_DATA) && (cbtype != RFCOMM_CB_TYPE_PATTERN) && (cbtype != RFCOMM_CB_TYPE_STATUS)) {
mp_raise_ValueError("invalid callback type");
}
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
rfcomm_cb_obj_t *pcb = &self->cb;
if ((args[ARG_client].u_int >= 0) && (cbtype != RFCOMM_CB_TYPE_STATUS) && (clients[args[ARG_client].u_int])) pcb = &clients[args[ARG_client].u_int]->cb;
if ((!MP_OBJ_IS_FUN(args[ARG_func].u_obj)) && (!MP_OBJ_IS_METH(args[ARG_func].u_obj))) {
// === CB function not given, disable callback ===
switch(cbtype) {
case RFCOMM_CB_TYPE_DATA:
pcb->data_cb = NULL;
pcb->data_cb_size = 0;
break;
case RFCOMM_CB_TYPE_PATTERN:
pcb->pattern_cb = NULL;
pcb->pattern[0] = 0;
pcb->pattern_len = 0;
break;
case RFCOMM_CB_TYPE_STATUS:
self->status_cb = NULL;
break;
default:
break;
}
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
return mp_const_none;
}
// Get and check callback parameters
switch(cbtype) {
case RFCOMM_CB_TYPE_DATA:
if ((args[ARG_datalen].u_int <= 0) || (args[ARG_datalen].u_int >= self->buffer_size)) {
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
mp_raise_ValueError("invalid data length");
}
datalen = args[ARG_datalen].u_int;
break;
case RFCOMM_CB_TYPE_PATTERN:
{
bool has_pattern = false;
mp_obj_type_t *type = mp_obj_get_type(args[ARG_pattern].u_obj);
if (type->buffer_p.get_buffer != NULL) {
int ret = type->buffer_p.get_buffer(args[ARG_pattern].u_obj, &pattern_buff, MP_BUFFER_READ);
if (ret == 0) {
if ((pattern_buff.len > 0) && (pattern_buff.len <= sizeof(pcb->pattern))) has_pattern = true;
}
}
if (!has_pattern) {
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
mp_raise_ValueError("invalid pattern");
}
}
break;
default:
break;
}
// Set the callback
if (rfcomm_mutex) xSemaphoreTake(rfcomm_mutex, 200 / portTICK_PERIOD_MS);
switch(cbtype) {
case RFCOMM_CB_TYPE_DATA:
pcb->data_cb_size = datalen;
pcb->data_cb = args[ARG_func].u_obj;
break;
case RFCOMM_CB_TYPE_PATTERN:
memcpy(pcb->pattern, pattern_buff.buf, pattern_buff.len);
pcb->pattern_len = pattern_buff.len;
pcb->pattern_cb = args[ARG_func].u_obj;
break;
case RFCOMM_CB_TYPE_STATUS:
self->status_cb = args[ARG_func].u_obj;
break;
default:
break;
}
if (rfcomm_mutex) xSemaphoreGive(rfcomm_mutex);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_rfcomm_callback_obj, 2, machine_rfcomm_callback);
//===================================================================
STATIC const mp_rom_map_elem_t machine_rfcomm_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_rfcomm_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_rfcomm_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_rfcomm_any_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_rfcomm_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&machine_rfcomm_readln_obj) },
{ MP_ROM_QSTR(MP_QSTR_readln), MP_ROM_PTR(&machine_rfcomm_readln_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_rfcomm_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_rfcomm_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&machine_rfcomm_callback_obj) },
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&machine_rfcomm_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&machine_rfcomm_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_channel), MP_ROM_PTR(&machine_rfcomm_channel_obj) },
// class constants
{ MP_ROM_QSTR(MP_QSTR_CBTYPE_DATA), MP_ROM_INT(RFCOMM_CB_TYPE_DATA) },
{ MP_ROM_QSTR(MP_QSTR_CBTYPE_PATTERN), MP_ROM_INT(RFCOMM_CB_TYPE_PATTERN) },
{ MP_ROM_QSTR(MP_QSTR_CBTYPE_STATUS), MP_ROM_INT(RFCOMM_CB_TYPE_STATUS) },
};
STATIC MP_DEFINE_CONST_DICT(machine_rfcomm_locals_dict, machine_rfcomm_locals_dict_table);
//=========================================
const mp_obj_type_t machine_rfcomm_type = {
{ &mp_type_type },
.name = MP_QSTR_RFCOMM,
.print = machine_rfcomm_print,
.make_new = machine_rfcomm_make_new,
.locals_dict = (mp_obj_dict_t*)&machine_rfcomm_locals_dict,
};
#endif