Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • add_menu_vibration
  • blinkisync-as-preload
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • ch3/splashscreen
  • dualcore
  • dx/flatten-config-module
  • dx/meh-bdf-to-stm
  • freertos-btle
  • genofire/ble-follow-py
  • koalo/bhi160-works-but-dirty
  • koalo/factory-reset
  • koalo/wip/i2c-for-python
  • master
  • msgctl/faultscreen
  • msgctl/textbuffer_api
  • plaetzchen/ios-workaround
  • rahix/bhi
  • rahix/bluetooth-app-favorite
  • rahix/bma
  • rahix/user-space-ctx
  • renze/hatchery_apps
  • renze/safe_mode
  • schleicher-test
  • schneider/212-reset-hardware-when-entering-repl
  • schneider/ancs
  • schneider/ble-buffers
  • schneider/ble-central
  • schneider/ble-ecg-stream-visu
  • schneider/ble-fixes-2020-3
  • schneider/ble-mini-demo
  • schneider/ble-stability
  • schneider/ble-stability-new-phy
  • schneider/bonding
  • schneider/bonding-fail-if-full
  • schneider/bootloader-update-9a0d158
  • schneider/deepsleep
  • schneider/deepsleep2
  • schneider/deepsleep4
  • schneider/default-main
  • schneider/freertos-list-debug
  • schneider/fundamental-test
  • schneider/iaq-python
  • schneider/ir
  • schneider/max30001
  • schneider/max30001-epicaridum
  • schneider/max30001-pycardium
  • schneider/maxim-sdk-update
  • schneider/mp-exception-print
  • schneider/mp-for-old-bl
  • schneider/png
  • schneider/schleicher-test
  • schneider/sdk-0.2.1-11
  • schneider/sdk-0.2.1-7
  • schneider/sleep-display
  • schneider/spo2-playground
  • schneider/stream-locks
  • schneider/v1.17-changelog
  • bootloader-v1
  • release-1
  • v0.0
  • v1.0
  • v1.1
  • v1.10
  • v1.11
  • v1.12
  • v1.13
  • v1.14
  • v1.15
  • v1.16
  • v1.17
  • v1.18
  • v1.2
  • v1.3
  • v1.4
  • v1.5
  • v1.6
  • v1.7
  • v1.8
  • v1.9
82 results

Target

Select target project
  • card10/firmware
  • annejan/firmware
  • astro/firmware
  • fpletz/firmware
  • gerd/firmware
  • fleur/firmware
  • swym/firmware
  • l/firmware
  • uberardy/firmware
  • wink/firmware
  • madonius/firmware
  • mot/firmware
  • filid/firmware
  • q3k/firmware
  • hauke/firmware
  • Woazboat/firmware
  • pink/firmware
  • mossmann/firmware
  • omniskop/firmware
  • zenox/firmware
  • trilader/firmware
  • Danukeru/firmware
  • shoragan/firmware
  • zlatko/firmware
  • sistason/firmware
  • datenwolf/firmware
  • bene/firmware
  • amedee/firmware
  • martinling/firmware
  • griffon/firmware
  • chris007/firmware
  • adisbladis/firmware
  • dbrgn/firmware
  • jelly/firmware
  • rnestler/firmware
  • mh/firmware
  • ln/firmware
  • penguineer/firmware
  • monkeydom/firmware
  • jens/firmware
  • jnaulty/firmware
  • jeffmakes/firmware
  • marekventur/firmware
  • pete/firmware
  • h2obrain/firmware
  • DooMMasteR/firmware
  • jackie/firmware
  • prof_r/firmware
  • Draradech/firmware
  • Kartoffel/firmware
  • hinerk/firmware
  • abbradar/firmware
  • JustTB/firmware
  • LuKaRo/firmware
  • iggy/firmware
  • ente/firmware
  • flgr/firmware
  • Lorphos/firmware
  • matejo/firmware
  • ceddral7/firmware
  • danb/firmware
  • joshi/firmware
  • melle/firmware
  • fitch/firmware
  • deurknop/firmware
  • sargon/firmware
  • markus/firmware
  • kloenk/firmware
  • lucaswerkmeister/firmware
  • derf/firmware
  • meh/firmware
  • dx/card10-firmware
  • torben/firmware
  • yuvadm/firmware
  • AndyBS/firmware
  • klausdieter1/firmware
  • katzenparadoxon/firmware
  • xiretza/firmware
  • ole/firmware
  • techy/firmware
  • thor77/firmware
  • TilCreator/firmware
  • fuchsi/firmware
  • dos/firmware
  • yrlf/firmware
  • PetePriority/firmware
  • SuperVirus/firmware
  • sur5r/firmware
  • tazz/firmware
  • Alienmaster/firmware
  • flo_h/firmware
  • baldo/firmware
  • mmu_man/firmware
  • Foaly/firmware
  • sodoku/firmware
  • Guinness/firmware
  • ssp/firmware
  • led02/firmware
  • Stormwind/firmware
  • arist/firmware
  • coon/firmware
  • mdik/firmware
  • pippin/firmware
  • royrobotiks/firmware
  • zigot83/firmware
  • mo_k/firmware
106 results
Select Git revision
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • debug-queue-handling
  • dualcore
  • fix-issue_71
  • freertos-btle
  • issue104-UIError-update-fail
  • master
  • msgctl/faultscreen
  • msgctl/gfx_rle
  • msgctl/textbuffer_api
  • rahix/bhi
  • rahix/bma
  • schleicher-test
  • schneider/bonding
  • schneider/bootloader-update-9a0d158
  • schneider/bsec
  • schneider/mp-for-old-bl
  • schneider/schleicher-test
  • unique-advert-id
  • v0.0
23 results
Show changes
Showing
with 2879 additions and 151 deletions
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#include "hci_vs.h" #include "hci_vs.h"
#include <epicardium.h> #include <epicardium.h>
#include "modules/log.h" #include "os/core.h"
#include "util/bstream.h" #include "util/bstream.h"
#include "att_api.h" #include "att_api.h"
...@@ -143,7 +143,8 @@ static const attsAttr_t fileTransCfgList[] = { ...@@ -143,7 +143,8 @@ static const attsAttr_t fileTransCfgList[] = {
.pLen = NULL, .pLen = NULL,
.maxLen = 128, .maxLen = 128,
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN, .settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_AUTH, .permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
}, },
/* File transfer Central RX characteristic */ /* File transfer Central RX characteristic */
{ {
...@@ -161,7 +162,8 @@ static const attsAttr_t fileTransCfgList[] = { ...@@ -161,7 +162,8 @@ static const attsAttr_t fileTransCfgList[] = {
.pLen = &attRxChConfigValue_len, .pLen = &attRxChConfigValue_len,
.maxLen = sizeof(attRxChConfigValue), .maxLen = sizeof(attRxChConfigValue),
.settings = ATTS_SET_VARIABLE_LEN, .settings = ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_AUTH, .permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
}, },
/* File transfer Central RX notification channel */ /* File transfer Central RX notification channel */
{ {
...@@ -170,8 +172,9 @@ static const attsAttr_t fileTransCfgList[] = { ...@@ -170,8 +172,9 @@ static const attsAttr_t fileTransCfgList[] = {
.pLen = &attRxChConfigValue_len, .pLen = &attRxChConfigValue_len,
.maxLen = sizeof(attRxChConfigValue), .maxLen = sizeof(attRxChConfigValue),
.settings = ATTS_SET_CCC, .settings = ATTS_SET_CCC,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_AUTH | .permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_AUTH, ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_WRITE |
ATTS_PERMIT_WRITE_ENC | ATTS_PERMIT_WRITE_AUTH,
}, },
}; };
...@@ -393,9 +396,7 @@ static uint8_t writeCallback( ...@@ -393,9 +396,7 @@ static uint8_t writeCallback(
connId, handle, operation, offset, len, pValue, pAttr connId, handle, operation, offset, len, pValue, pAttr
); );
default: default:
LOG_ERR("filetrans", LOG_ERR("filetrans", "unsupported handle: %x\n", handle);
"unsupported characteristic: %c\n",
handle);
return ATT_ERR_HANDLE; return ATT_ERR_HANDLE;
} }
} }
......
/*
* Based on ble-profiles/sources/apps/hidapp/hidapp_main.c
*/
#include "cccd.h"
#include "hid.h"
#include "wsf_types.h"
#include "dm_api.h"
#include "att_api.h"
#include "svc_hid.h"
#include "hid/hid_api.h"
#include "os/core.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/*! HidApp Report Map (Descriptor) */
/* clang-format off */
/* Based on https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/master/adafruit_ble/services/standard/hid.py */
const uint8_t hidReportMap[] =
{
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x06, /* Usage (Keyboard) */
0xA1, 0x01, /* Collection (Application) */
0x85, HIDAPP_KEYBOARD_REPORT_ID, /* Report ID (1) */
0x05, 0x07, /* Usage Page (Kbrd/Keypad) */
0x19, 0xE0, /* Usage Minimum (\xE0) */
0x29, 0xE7, /* Usage Maximum (\xE7) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x08, /* Report Count (8) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x19, 0x00, /* Usage Minimum (\x00) */
0x29, 0x89, /* Usage Maximum (\x89) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x89, /* Logical Maximum (137) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x06, /* Report Count (6) */
0x81, 0x00, /* Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x08, /* Usage Page (LEDs) */
0x19, 0x01, /* Usage Minimum (Num Lock) */
0x29, 0x05, /* Usage Maximum (Kana) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x05, /* Report Count (5) */
0x91, 0x02, /* Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
0x95, 0x03, /* Report Count (3) */
0x91, 0x01, /* Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
0xC0, /* End Collection */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x85, HIDAPP_MOUSE_REPORT_ID, /* Report ID (2) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (\x01) */
0x29, 0x05, /* Usage Maximum (\x05) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x05, /* Report Count (5) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x03, /* Report Size (3) */
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, HIDAPP_CONSUMER_REPORT_ID, /* Report ID (3) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x01, /* Report Count (1) */
0x15, 0x01, /* Logical Minimum (1) */
0x26, 0x8C, 0x02, /* Logical Maximum (652) */
0x19, 0x01, /* Usage Minimum (Consumer Control) */
0x2A, 0x8C, 0x02, /* Usage Maximum (AC Send) */
0x81, 0x00, /* Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
#if 0
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x05, /* Usage (Game Pad) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x05, /* Report ID (5) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (\x01) */
0x29, 0x10, /* Usage Maximum (\x10) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x10, /* Report Count (16) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x32, /* Usage (Z) */
0x09, 0x35, /* Usage (Rz) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x04, /* Report Count (4) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
#endif
};
/* clang-format on */
const uint16_t hidReportMapLen = sizeof(hidReportMap);
/*! HID Report Type/ID and attribute handle map */
/* clang-format off */
static const hidReportIdMap_t hidAppReportIdSet[] =
{
/* type ID handle */
{HID_REPORT_TYPE_INPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_INPUT_REPORT_1_HDL}, /* Keyboard Input Report */
{HID_REPORT_TYPE_OUTPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_OUTPUT_REPORT_HDL}, /* Keyboard Output Report */
{HID_REPORT_TYPE_FEATURE, HIDAPP_KEYBOARD_REPORT_ID, HID_FEATURE_REPORT_HDL}, /* Keyboard Feature Report */
{HID_REPORT_TYPE_INPUT, HIDAPP_MOUSE_REPORT_ID, HID_INPUT_REPORT_2_HDL}, /* Mouse Input Report */
{HID_REPORT_TYPE_INPUT, HIDAPP_CONSUMER_REPORT_ID, HID_INPUT_REPORT_3_HDL}, /* Consumer Control Input Report */
{HID_REPORT_TYPE_INPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_IN_HDL}, /* Boot Keyboard Input Report */
{HID_REPORT_TYPE_OUTPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_OUT_HDL}, /* Boot Keyboard Output Report */
{HID_REPORT_TYPE_INPUT, HID_MOUSE_BOOT_ID, HID_MOUSE_BOOT_IN_HDL}, /* Boot Mouse Input Report */
};
/* clang-format on */
void hidAppOutputCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
);
void hidAppFeatureCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
);
void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value);
/*! HID Profile Configuration */
/* clang-format off */
static const hidConfig_t hidAppHidConfig =
{
(hidReportIdMap_t*) hidAppReportIdSet, /* Report ID to Attribute Handle map */
sizeof(hidAppReportIdSet)/sizeof(hidReportIdMap_t), /* Size of Report ID to Attribute Handle map */
&hidAppOutputCback, /* Output Report Callback */
&hidAppFeatureCback, /* Feature Report Callback */
&hidAppInfoCback /* Info Callback */
};
/* clang-format on */
static void hidAppReportInit(void)
{
uint8_t iKeyboardBuffer[HIDAPP_KEYBOARD_INPUT_REPORT_LEN];
uint8_t iMouseBuffer[HIDAPP_MOUSE_INPUT_REPORT_LEN];
uint8_t iConsumerBuffer[HIDAPP_CONSUMER_INPUT_REPORT_LEN];
uint8_t oBuffer[HIDAPP_OUTPUT_REPORT_LEN];
uint8_t fBuffer[HIDAPP_FEATURE_REPORT_LEN];
/* Keyboard Input report */
memset(iKeyboardBuffer, 0, HIDAPP_KEYBOARD_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_1_HDL,
HIDAPP_KEYBOARD_INPUT_REPORT_LEN,
iKeyboardBuffer
);
/* Mouse Input report */
memset(iMouseBuffer, 0, HIDAPP_MOUSE_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_2_HDL,
HIDAPP_MOUSE_INPUT_REPORT_LEN,
iMouseBuffer
);
/* Consumer Control Input report */
memset(iConsumerBuffer, 0, HIDAPP_CONSUMER_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_3_HDL,
HIDAPP_CONSUMER_INPUT_REPORT_LEN,
iConsumerBuffer
);
/* Output report */
memset(oBuffer, 0, HIDAPP_OUTPUT_REPORT_LEN);
AttsSetAttr(HID_OUTPUT_REPORT_HDL, HIDAPP_OUTPUT_REPORT_LEN, oBuffer);
/* Feature report */
memset(fBuffer, 0, HIDAPP_FEATURE_REPORT_LEN);
AttsSetAttr(HID_FEATURE_REPORT_HDL, HIDAPP_FEATURE_REPORT_LEN, fBuffer);
}
void hid_work_init(void);
void hid_init(void)
{
SvcHidAddGroup();
SvcHidRegister(HidAttsWriteCback, NULL);
/* Initialize the HID profile */
HidInit(&hidAppHidConfig);
/* Initialize the report attributes */
hidAppReportInit();
hid_work_init();
}
#pragma once
#include "wsf_types.h"
#include "wsf_os.h"
/* The input report fits in one byte */
#define HIDAPP_KEYBOARD_INPUT_REPORT_LEN 8
#define HIDAPP_MOUSE_INPUT_REPORT_LEN 4
#define HIDAPP_CONSUMER_INPUT_REPORT_LEN 2
#define HIDAPP_OUTPUT_REPORT_LEN 1
#define HIDAPP_FEATURE_REPORT_LEN 1
/* HID Report IDs */
#define HIDAPP_KEYBOARD_REPORT_ID 1
#define HIDAPP_MOUSE_REPORT_ID 2
#define HIDAPP_CONSUMER_REPORT_ID 3
void hid_init(void);
void HidProcMsg(wsfMsgHdr_t *pMsg);
#include "hid.h"
#include "cccd.h"
#include "epicardium.h"
#include "os/core.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "hid/hid_api.h"
#include "svc_hid.h"
#include "FreeRTOS.h"
#include "queue.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* HidApp TX path flags */
#define HIDAPP_TX_FLAGS_READY 0x01
/*! application control block */
/* clang-format off */
struct
{
uint8_t txFlags; /* transmit flags */
uint8_t protocolMode; /* current protocol mode */
uint8_t hostSuspended; /* TRUE if host suspended */
} hidAppCb;
/* clang-format on */
struct report {
uint8_t report_id;
uint8_t data[8];
uint8_t len;
};
#define QUEUE_SIZE 10
static QueueHandle_t queue;
static uint8_t buffer[sizeof(struct report) * QUEUE_SIZE];
static StaticQueue_t queue_data;
static int hid_queue_data(uint8_t report_id, uint8_t *data, uint8_t len)
{
struct report report;
if (report_id < 1 || report_id > 3) {
return -EINVAL;
}
report.report_id = report_id;
if (len > sizeof(report.data)) {
return -EINVAL;
}
memcpy(report.data, data, len);
report.len = len;
if (xQueueSend(queue, &report, 0) != pdTRUE) {
/* Likely full */
return -EAGAIN;
}
return 0;
}
static bool hid_dequeue_data(dmConnId_t connId)
{
uint8_t cccHandle;
struct report report;
//lock();
if (!(hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY)) {
//unlock();
return false;
}
// Loop until a CCC is enabled or the queue is empty
while (true) {
if (xQueueReceive(queue, &report, 0) != pdTRUE) {
break;
}
if (HidGetProtocolMode() == HID_PROTOCOL_MODE_BOOT) {
if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) {
report.report_id = HID_KEYBOARD_BOOT_ID;
cccHandle = HIDAPP_KBI_CCC_HDL;
} else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) {
report.report_id = HID_MOUSE_BOOT_ID;
cccHandle = HIDAPP_MBI_CCC_HDL;
} else {
break;
}
} else {
if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) {
cccHandle = HIDAPP_IN_KEYBOARD_CCC_HDL;
} else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) {
cccHandle = HIDAPP_IN_MOUSE_CCC_HDL;
} else if (report.report_id == HIDAPP_CONSUMER_REPORT_ID) {
cccHandle = HIDAPP_IN_CONSUMER_CCC_HDL;
} else {
break;
};
}
if (AttsCccEnabled(connId, cccHandle) || 1) {
hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY);
/* Send the message */
HidSendInputReport(
connId,
report.report_id,
report.len,
report.data
);
break;
}
}
//unlock();
return true;
}
/*************************************************************************************************/
/*!
* \brief Callback to handle an output report from the host.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
void hidAppOutputCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
) {
/* TODO: process output reports */
}
/*************************************************************************************************/
/*!
* \brief Callback to handle a feature report from the host.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
void hidAppFeatureCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
) {
/* TODO: process feature reports */
}
/*************************************************************************************************/
/*!
* \brief Callback to handle a change in protocol mode or control point from the host.
*
* \param connId The connection identifier.
* \param mode The type of information (HID_INFO_CONTROL_POINT or HID_INFO_PROTOCOL_MODE)
* \param value The value of the information
*
* \return None.
*/
/*************************************************************************************************/
void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value)
{
if (type == HID_INFO_PROTOCOL_MODE) {
LOG_INFO("hid", "protocol mode: %u\n", value);
hidAppCb.protocolMode = value;
} else if (type == HID_INFO_CONTROL_POINT) {
LOG_INFO("hid", "host suspended: %u\n", value);
hidAppCb.hostSuspended =
(value == HID_CONTROL_POINT_SUSPEND) ? TRUE : FALSE;
}
}
void HidProcMsg(wsfMsgHdr_t *pMsg)
{
if (!queue) {
/* Not initialized (yet). */
return;
}
if (pMsg->event == ATTS_HANDLE_VALUE_CNF) {
if (pMsg->status == ATT_SUCCESS) {
hidAppCb.txFlags |= HIDAPP_TX_FLAGS_READY;
hid_dequeue_data((dmConnId_t)pMsg->param);
}
}
if (pMsg->event == DM_CONN_OPEN_IND) {
hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
struct report report;
while (xQueueReceive(queue, &report, 0) == pdTRUE)
;
/* Todo: At this point the CCC descriptors are not set up yet
* and things which get sent until then are discarded. */
}
}
int epic_ble_hid_send_report(uint8_t report_id, uint8_t *data, uint8_t len)
{
dmConnId_t connId = AppConnIsOpen();
if (connId == DM_CONN_ID_NONE) {
return -EIO;
}
if (!queue) {
return -EIO;
}
int ret;
ret = hid_queue_data(report_id, data, len);
if (ret < 0) {
return ret;
}
if (hid_dequeue_data(connId)) {
return 0;
} else {
return 1;
}
}
void hid_work_init(void)
{
queue = xQueueCreateStatic(
QUEUE_SIZE, sizeof(struct report), buffer, &queue_data
);
hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
}
ble_sources = files( ble_sources = files(
'ble.c', 'ble.c',
'epic_ble_api.c',
'epic_att_api.c',
'stack.c', 'stack.c',
'ble_main.c', 'ble_main.c',
'ble_adv.c',
'ble_attc.c',
'profiles/tipc_main.c',
'profiles/gap_main.c',
'svc_dis.c', 'svc_dis.c',
'svc_core.c', 'svc_core.c',
'app/app_main.c', 'bondings.c',
'app/common/app_db.c',
'app/common/app_ui.c',
'uart.c', 'uart.c',
'card10.c', 'card10.c',
'ess.c',
'filetransfer.c', 'filetransfer.c',
'hid.c',
'hid_work.c',
) )
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* Copyright (c) 2015-2018 Arm Ltd. All Rights Reserved.
* Arm Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact Arm Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
#ifndef GAP_API_H
#define GAP_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GAP_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered */
enum
{
GAP_DN_HDL_IDX,
GAP_CAR_HDL_IDX, /*!< \brief Central Address Resolution */
GAP_RPAO_HDL_IDX, /*!< \brief Resolvable Private Address Only */
GAP_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
int GapGetDeviceName(char *buf, size_t buf_size);
void GapClearDeviceName(void);
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length \ref GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* GAP_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GAP_API_H */
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* Copyright (c) 2015-2018 Arm Ltd. All Rights Reserved.
* ARM Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "app_db.h"
#include "app_api.h"
#include "gap_api.h"
#include <string.h>
/* card10:
* copied from lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gap/gap_main.c
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
/**************************************************************************************************
Local Variables
**************************************************************************************************/
static char dn_value[ATT_DEFAULT_PAYLOAD_LEN + 1];
/*! GAP service characteristics for discovery */
static const attcDiscChar_t gapDn =
{
attDnChUuid,
0
};
/*! Central Address Resolution */
static const attcDiscChar_t gapCar =
{
attCarChUuid,
0
};
/*! Resolvable Private Address Only */
static const attcDiscChar_t gapRpao =
{
attRpaoChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *gapDiscCharList[] =
{
&gapDn,
&gapCar, /* Central Address Resolution */
&gapRpao /* Resolvable Private Address Only */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(GAP_HDL_LIST_LEN == ((sizeof(gapDiscCharList) / sizeof(attcDiscChar_t *))));
int GapGetDeviceName(char *buf, size_t buf_size)
{
memset(buf, 0, buf_size);
strncpy(buf, dn_value, buf_size - 1);
return 0;
}
void GapClearDeviceName(void)
{
dn_value[0] = 0;
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attGapSvcUuid,
GAP_HDL_LIST_LEN, (attcDiscChar_t **) gapDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* device name string */
if (pMsg->handle == pHdlList[GAP_DN_HDL_IDX])
{
int len = (pMsg->valueLen < (sizeof(dn_value) - 1)) ? pMsg->valueLen : (sizeof(dn_value) - 1);
memcpy(dn_value, pMsg->pValue, len);
dn_value[len] = '\0';
}
/* Central Address Resolution */
else if (pMsg->handle == pHdlList[GAP_CAR_HDL_IDX])
{
appDbHdl_t dbHdl;
/* if there's a device record */
if ((dbHdl = AppDbGetHdl((dmConnId_t)pMsg->hdr.param)) != APP_DB_HDL_NONE)
{
if ((pMsg->pValue[0] == FALSE) || (pMsg->pValue[0] == TRUE))
{
/* store value in device database */
AppDbSetPeerAddrRes(dbHdl, pMsg->pValue[0]);
}
else
{
/* invalid value */
status = ATT_ERR_RANGE;
}
APP_TRACE_INFO1("Central address resolution: %d", pMsg->pValue[0]);
}
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/* clang-format on */
/*************************************************************************************************/
/*!
* \file
*
* \brief Time profile client.
*
* Copyright (c) 2011-2018 Arm Ltd. All Rights Reserved.
* ARM Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
/* card10:
* copied from lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/tipc/tipc_main.c
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "tipc/tipc_api.h"
#include "epicardium.h"
#include <time.h>
#include <stdio.h>
/**************************************************************************************************
Local Variables
**************************************************************************************************/
static time_t s_time;
/*!
* Current Time service
*/
/* Characteristics for discovery */
/*! Current time */
static const attcDiscChar_t tipcCtsCt =
{
attCtChUuid,
ATTC_SET_REQUIRED
};
/*! Current time client characteristic configuration descriptor */
static const attcDiscChar_t tipcCtsCtCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Local time information */
static const attcDiscChar_t tipcCtsLti =
{
attLtiChUuid,
0
};
/*! Reference time information */
static const attcDiscChar_t tipcCtsRti =
{
attRtiChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *tipcCtsDiscCharList[] =
{
&tipcCtsCt, /* Current time */
&tipcCtsCtCcc, /* Current time client characteristic configuration descriptor */
&tipcCtsLti, /* Local time information */
&tipcCtsRti /* Reference time information */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(TIPC_CTS_HDL_LIST_LEN == ((sizeof(tipcCtsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Current Time service. Parameter
* pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void TipcCtsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attCtsSvcUuid,
TIPC_CTS_HDL_LIST_LEN, (attcDiscChar_t **) tipcCtsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t TipcCtsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
uint8_t *p;
uint16_t year;
uint8_t month, day, hour, min, sec, dayOfWeek, adjustReason;
uint8_t sec256 = 0;
int8_t timeZone;
uint8_t dstOffset, source, accuracy;
/* Suppress unused variable compile warning */
(void)month; (void)day; (void)hour; (void)min; (void)sec; (void)dayOfWeek; (void)adjustReason;
(void)year; (void)sec256; (void)dstOffset; (void)accuracy; (void)timeZone; (void)source;
/* current time */
if (pMsg->handle == pHdlList[TIPC_CTS_CT_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT16(year, p);
BSTREAM_TO_UINT8(month, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
BSTREAM_TO_UINT8(min, p);
BSTREAM_TO_UINT8(sec, p);
BSTREAM_TO_UINT8(dayOfWeek, p);
BSTREAM_TO_UINT8(sec256, p);
BSTREAM_TO_UINT8(adjustReason, p);
struct tm t;
t.tm_year = year - 1900;
t.tm_mon = month - 1;
t.tm_mday = day;
t.tm_hour = hour;
t.tm_min = min;
t.tm_sec = sec;
t.tm_isdst = -1; /* unknown */
s_time = mktime(&t);
APP_TRACE_INFO3("Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3("Time: %02d:%02d:%02d", hour, min, sec);
APP_TRACE_INFO3("dayOfWeek:%d sec256:%d adjustReason:%d", dayOfWeek, sec256, adjustReason);
}
/* local time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_LTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(timeZone, p);
BSTREAM_TO_UINT8(dstOffset, p);
APP_TRACE_INFO2("timeZone:%d dstOffset:%d", timeZone, dstOffset);
if(s_time) {
char buf[32];
snprintf(buf, sizeof(buf), "%+05d", (timeZone +dstOffset) / 4 * 100);
epic_config_set_string("timezone", buf);
epic_rtc_set_milliseconds(s_time * 1000 + (1000 * sec256) / 256 - (timeZone + dstOffset) * 15 * 60 * 1000);
}
}
/* reference time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_RTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(source, p);
BSTREAM_TO_UINT8(accuracy, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
APP_TRACE_INFO2("Ref. time source:%d accuracy:%d", source, accuracy);
APP_TRACE_INFO2("Last update days:%d hours:%d", day, hour);
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/* clang-format on */
...@@ -89,8 +89,8 @@ const LlRtCfg_t _ll_cfg = { ...@@ -89,8 +89,8 @@ const LlRtCfg_t _ll_cfg = {
/*dtmRxSyncMs*/ 10000, /*dtmRxSyncMs*/ 10000,
/* PHY */ /* PHY */
/*phy2mSup*/ TRUE, /*phy2mSup*/ FALSE,
/*phyCodedSup*/ TRUE, /*phyCodedSup*/ FALSE,
/*stableModIdxTxSup*/ FALSE, /*stableModIdxTxSup*/ FALSE,
/*stableModIdxRxSup*/ FALSE /*stableModIdxRxSup*/ FALSE
}; };
...@@ -105,16 +105,13 @@ const BbRtCfg_t _bb_cfg = { ...@@ -105,16 +105,13 @@ const BbRtCfg_t _bb_cfg = {
/*************************************************************************************************/ /*************************************************************************************************/
/*! /*!
* \brief Initialize stack. * \brief Initialize link layer part of the stack.
* *
* \return None. * \return None.
*/ */
/*************************************************************************************************/ /*************************************************************************************************/
void StackInit(void) void LlStackInit()
{ {
wsfHandlerId_t handlerId;
/* card10: We do not use the SDMA HCI at the moment. The block below is not compiled. */
#ifndef ENABLE_SDMA #ifndef ENABLE_SDMA
uint32_t memUsed; uint32_t memUsed;
...@@ -130,18 +127,31 @@ void StackInit(void) ...@@ -130,18 +127,31 @@ void StackInit(void)
.freeMemAvail = LL_MEMORY_FOOTPRINT .freeMemAvail = LL_MEMORY_FOOTPRINT
}; };
#ifdef DATS_APP_USE_LEGACY_API
memUsed = LlInitControllerExtInit(&ll_init_cfg); memUsed = LlInitControllerExtInit(&ll_init_cfg);
#else /* DATS_APP_USE_LEGACY_API */
memUsed = LlInitControllerExtInit(&ll_init_cfg);
#endif /* DATS_APP_USE_LEGACY_API */
if(memUsed != LL_MEMORY_FOOTPRINT) if(memUsed != LL_MEMORY_FOOTPRINT)
{ {
printf("Controller memory mismatch 0x%x != 0x%x\n", (unsigned int)memUsed, printf("Controller memory mismatch 0x%x != 0x%x\n", (unsigned int)memUsed,
(unsigned int)LL_MEMORY_FOOTPRINT); (unsigned int)LL_MEMORY_FOOTPRINT);
} }
#endif #endif
}
/*************************************************************************************************/
/*!
* \brief Initialize stack.
*
* \return None.
*/
/*************************************************************************************************/
void StackInit(void)
{
wsfHandlerId_t handlerId;
SecInit();
SecRandInit();
SecAesInit();
SecCmacInit();
SecEccInit();
/* card10: /* card10:
* These calls register a queue for callbacks in the OS abstraction * These calls register a queue for callbacks in the OS abstraction
...@@ -153,14 +163,10 @@ void StackInit(void) ...@@ -153,14 +163,10 @@ void StackInit(void)
handlerId = WsfOsSetNextHandler(HciHandler); handlerId = WsfOsSetNextHandler(HciHandler);
HciHandlerInit(handlerId); HciHandlerInit(handlerId);
SecInit();
SecAesInit();
SecCmacInit();
SecEccInit();
handlerId = WsfOsSetNextHandler(DmHandler); handlerId = WsfOsSetNextHandler(DmHandler);
DmDevVsInit(0); DmDevVsInit(0);
DmAdvInit(); DmAdvInit();
DmScanInit();
DmConnInit(); DmConnInit();
DmConnSlaveInit(); DmConnSlaveInit();
DmSecInit(); DmSecInit();
...@@ -178,6 +184,8 @@ void StackInit(void) ...@@ -178,6 +184,8 @@ void StackInit(void)
AttHandlerInit(handlerId); AttHandlerInit(handlerId);
AttsInit(); AttsInit();
AttsIndInit(); AttsIndInit();
AttsDynInit();
AttcInit();
handlerId = WsfOsSetNextHandler(SmpHandler); handlerId = WsfOsSetNextHandler(SmpHandler);
SmpHandlerInit(handlerId); SmpHandlerInit(handlerId);
...@@ -186,5 +194,8 @@ void StackInit(void) ...@@ -186,5 +194,8 @@ void StackInit(void)
/*TODO card10: Probably want to adjust this */ /*TODO card10: Probably want to adjust this */
HciSetMaxRxAclLen(100); HciSetMaxRxAclLen(100);
handlerId = WsfOsSetNextHandler(AppHandler);
AppHandlerInit(handlerId);
} }
/* clang-format off */ /* clang-format off */
...@@ -24,13 +24,17 @@ ...@@ -24,13 +24,17 @@
*/ */
/* clang-format off */ /* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */ /* clang-formet turned off for easier diffing against orginal file */
#include "card10-version.h"
#include "wsf_types.h" #include "wsf_types.h"
#include "att_api.h" #include "att_api.h"
#include "wsf_assert.h" #include "wsf_assert.h"
#include "wsf_trace.h" #include "wsf_trace.h"
#include "util/bstream.h" #include "util/bstream.h"
#define DIS_MAXSIZE_FWR_ATT 32
#include "svc_dis.h" #include "svc_dis.h"
#include "svc_cfg.h" #include "svc_cfg.h"
#include <string.h>
/************************************************************************************************** /**************************************************************************************************
Macros Macros
...@@ -60,10 +64,10 @@ ...@@ -60,10 +64,10 @@
#define DIS_DEFAULT_SERIAL_NUM_LEN 1 #define DIS_DEFAULT_SERIAL_NUM_LEN 1
/*! Default firmware revision */ /*! Default firmware revision */
#define DIS_DEFAULT_FW_REV "<git hash>" #define DIS_DEFAULT_FW_REV CARD10_VERSION
/*! Length of default firmware revision */ /*! Length of default firmware revision */
#define DIS_DEFAULT_FW_REV_LEN 10 #define DIS_DEFAULT_FW_REV_LEN strlen(CARD10_VERSION)
/*! Default hardware revision */ /*! Default hardware revision */
#define DIS_DEFAULT_HW_REV "1" #define DIS_DEFAULT_HW_REV "1"
...@@ -128,7 +132,7 @@ static const uint16_t disLenFwrCh = sizeof(disValFwrCh); ...@@ -128,7 +132,7 @@ static const uint16_t disLenFwrCh = sizeof(disValFwrCh);
/* Firmware revision string */ /* Firmware revision string */
static const uint8_t disUuFwr[] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)}; static const uint8_t disUuFwr[] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
static uint8_t disValFwr[DIS_MAXSIZE_FWR_ATT] = DIS_DEFAULT_FW_REV; static uint8_t disValFwr[DIS_MAXSIZE_FWR_ATT] = DIS_DEFAULT_FW_REV;
static uint16_t disLenFwr = DIS_DEFAULT_FW_REV_LEN; static uint16_t disLenFwr;
/* Hardware revision string characteristic */ /* Hardware revision string characteristic */
static const uint8_t disValHwrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_HWR_HDL), UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)}; static const uint8_t disValHwrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_HWR_HDL), UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
...@@ -319,6 +323,7 @@ WSF_CT_ASSERT(((sizeof(disList) / sizeof(disList[0])) == DIS_END_HDL - DIS_START ...@@ -319,6 +323,7 @@ WSF_CT_ASSERT(((sizeof(disList) / sizeof(disList[0])) == DIS_END_HDL - DIS_START
/*************************************************************************************************/ /*************************************************************************************************/
void SvcDisAddGroup(void) void SvcDisAddGroup(void)
{ {
disLenFwr = DIS_DEFAULT_FW_REV_LEN;
AttsAddGroup(&svcDisGroup); AttsAddGroup(&svcDisGroup);
} }
......
#include "uart.h"
#include "cccd.h"
#include "modules/modules.h" #include "modules/modules.h"
#include "drivers/drivers.h"
#include "wsf_types.h" #include "wsf_types.h"
#include "util/bstream.h" #include "util/bstream.h"
#include "att_api.h" #include "att_api.h"
#include "dm_api.h"
#include "app_api.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "timers.h" #include "timers.h"
...@@ -11,85 +17,91 @@ ...@@ -11,85 +17,91 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/************************************************************************************************** /**************************************************************************************************
Handles Handles
**************************************************************************************************/ **************************************************************************************************/
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
/**@}*/
/* clang-format off */ /* clang-format off */
static const uint8_t UARTSvc[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x01,0x00,0x40,0x6E}; static const uint8_t UARTSvc[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x01,0x00,0x40,0x6E};
static const uint16_t UARTSvc_len = sizeof(UARTSvc);
static const uint8_t uartRxCh[] = {ATT_PROP_WRITE, UINT16_TO_BYTES(UART_RX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E}; static const uint8_t uartRxCh[] = {ATT_PROP_WRITE, UINT16_TO_BYTES(UART_RX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E}; static const uint16_t uartRxCh_len = sizeof(uartRxCh);
static const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BYTES(UART_TX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E}; static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BYTES(UART_TX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E}; static const uint16_t uartTxCh_len = sizeof(uartTxCh);
/* clang-format on */ static const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
static void *SvcUARTAddGroupDyn(void) static uint8_t uartValTxChCcc[] = {UINT16_TO_BYTES(0x0000)};
{ static const uint16_t uartLenTxChCcc = sizeof(uartValTxChCcc);
void *pSHdl;
uint8_t initCcc[] = { UINT16_TO_BYTES(0x0000) };
/* Create the service */ static uint8_t ble_uart_tx_buf[20];
pSHdl = AttsDynCreateGroup(UART_START_HDL, UART_END_HDL); static uint16_t ble_uart_buf_tx_fill = 0;
/* clang-format on */
if (pSHdl != NULL) { /* Attribute list for uriCfg group */
/* clang-format off */ static const attsAttr_t uartAttrCfgList[] = {
/* Primary service */ /* Primary service */
AttsDynAddAttrConst( pSHdl, attPrimSvcUuid, UARTSvc, sizeof(UARTSvc), {
0, ATTS_PERMIT_READ); .pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UARTSvc,
.pLen = (uint16_t *)&UARTSvc_len,
.maxLen = sizeof(UARTSvc),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx characteristic */ /* UART rx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartRxCh, sizeof(uartRxCh), {
0, ATTS_PERMIT_READ); .pUuid = attChUuid,
// XXX: attUartRxChUuid is 16 bytes but nothing says so.... .pValue = (uint8_t *)uartRxCh,
.pLen = (uint16_t *)&uartRxCh_len,
.maxLen = sizeof(uartRxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx value */ /* UART rx value */
// XXX: not sure if max value of 128 is fine... {
AttsDynAddAttr( pSHdl, attUartRxChUuid, NULL, 0, 128, .pUuid = attUartRxChUuid,
ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN, ATTS_PERMIT_WRITE); .pValue = NULL,
.pLen = NULL,
.maxLen = 128,
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
/* UART tx characteristic */ /* UART tx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartTxCh, sizeof(uartTxCh), {
0, ATTS_PERMIT_READ); .pUuid = attChUuid,
.pValue = (uint8_t *)uartTxCh,
.pLen = (uint16_t *)&uartTxCh_len,
.maxLen = sizeof(uartTxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART tx value */ /* UART tx value */
/* TODO: do we need ATTS_SET_READ_CBACK ? */ {
AttsDynAddAttr( pSHdl, attUartTxChUuid, NULL, 0, sizeof(uint8_t), .pUuid = attUartTxChUuid,
ATTS_SET_READ_CBACK, ATTS_PERMIT_READ); .pValue = ble_uart_tx_buf,
.pLen = &ble_uart_buf_tx_fill,
.maxLen = sizeof(ble_uart_tx_buf),
.settings = 0,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* UART tx CCC descriptor */ /* UART tx CCC descriptor */
AttsDynAddAttr( pSHdl, attCliChCfgUuid, initCcc, sizeof(uint16_t), sizeof(uint16_t), {
ATTS_SET_CCC, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE); .pUuid = attCliChCfgUuid,
/* clang-format on */ .pValue = uartValTxChCcc,
} .pLen = (uint16_t *)&uartLenTxChCcc,
.maxLen = sizeof(uartValTxChCcc),
return pSHdl; .settings = ATTS_SET_CCC,
} .permissions =
(ATTS_PERMIT_READ |
dmConnId_t active_connection = 0; ATTS_PERMIT_WRITE) // How about security?
},
static uint8_t UARTReadCback( };
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
printf("read callback\n");
return ATT_SUCCESS;
}
static uint8_t UARTWriteCback( static uint8_t UARTWriteCback(
dmConnId_t connId, dmConnId_t connId,
...@@ -100,76 +112,107 @@ static uint8_t UARTWriteCback( ...@@ -100,76 +112,107 @@ static uint8_t UARTWriteCback(
uint8_t *pValue, uint8_t *pValue,
attsAttr_t *pAttr attsAttr_t *pAttr
) { ) {
active_connection = connId; static bool was_r = false;
//printf("UARTWriteCback %d: ", len);
int i; int i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
//printf("%c", pValue[i]); if (pValue[i] == '\n' && !was_r) {
serial_enqueue_char('\r');
}
was_r = pValue[i] == '\r';
serial_enqueue_char(pValue[i]); serial_enqueue_char(pValue[i]);
} }
serial_enqueue_char('\r');
//printf("\n");
#if 0
AttsSetAttr(UART_TX_HDL, len, pValue);
AttsHandleValueNtf(connId, UART_TX_HDL, len, pValue);
#endif
return ATT_SUCCESS; return ATT_SUCCESS;
} }
static uint8_t ble_uart_tx_buf[128]; static bool done;
static uint8_t ble_uart_buf_tx_fill; static bool again;
static int ble_uart_lasttick = 0;
void ble_uart_write(uint8_t *pValue, uint8_t len) static void ble_uart_flush(void)
{ {
for (int i = 0; i < len; i++) { if (ble_uart_buf_tx_fill == 0) {
if (pValue[i] >= 0x20 && pValue[i] < 0x7f) { return;
ble_uart_tx_buf[ble_uart_buf_tx_fill] = pValue[i];
ble_uart_buf_tx_fill++;
} }
if (ble_uart_buf_tx_fill == 128 || pValue[i] == '\r' || dmConnId_t connId = AppConnIsOpen();
pValue[i] == '\n') { if (connId != DM_CONN_ID_NONE) {
if (ble_uart_buf_tx_fill > 0) { if (AttsCccEnabled(connId, UART_TX_CH_CCC_IDX)) {
AttsSetAttr( done = false;
again = true;
int t0 = xTaskGetTickCount();
while (!done && ((xTaskGetTickCount() - t0) < 1000)) {
if (again) {
again = false;
AttsHandleValueNtf(
connId,
UART_TX_HDL, UART_TX_HDL,
ble_uart_buf_tx_fill, ble_uart_buf_tx_fill,
ble_uart_tx_buf ble_uart_tx_buf
); );
if (active_connection) { }
int x = xTaskGetTickCount() - /* This function is supposed to only be called
ble_uart_lasttick; * from the API scheduler with lowest priority.
if (x < 100) { *
/* * If that is not the case anymore, use a delay
* TODO: Ugly hack if we already * instead of the yield. Ideally refactor to avoid
* send something recently. * the delay.
* Figure out how fast we
* can send or use indications.
*/ */
vTaskDelay(100 - x); //vTaskDelay(5);
taskYIELD();
}
} }
AttsHandleValueNtf(
active_connection,
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
ble_uart_lasttick = xTaskGetTickCount();
} }
ble_uart_buf_tx_fill = 0; ble_uart_buf_tx_fill = 0;
} }
static void ble_uart_write_char(uint8_t c)
{
ble_uart_tx_buf[ble_uart_buf_tx_fill] = c;
ble_uart_buf_tx_fill++;
// TODO: increase buffer if configured MTU allows it
if (ble_uart_buf_tx_fill == sizeof(ble_uart_tx_buf)) {
ble_uart_flush();
} }
} }
void ble_uart_write(uint8_t *pValue, uint8_t len)
{
for (int i = 0; i < len; i++) {
ble_uart_write_char(pValue[i]);
} }
// TODO schedule timer in a few ms to flush the buffer
ble_uart_flush();
}
static attsGroup_t uartCfgGroup = {
.pAttr = (attsAttr_t *)uartAttrCfgList,
.writeCback = UARTWriteCback,
.startHandle = UART_START_HDL,
.endHandle = UART_END_HDL,
};
void bleuart_init(void) void bleuart_init(void)
{ {
/* Add the UART service dynamically */ /* Add the UART service */
void *pSHdl; AttsAddGroup(&uartCfgGroup);
pSHdl = SvcUARTAddGroupDyn(); }
AttsDynRegister(pSHdl, UARTReadCback, UARTWriteCback);
//AttsDynRegister(pSHdl, NULL, UARTWriteCback); void UartProcMsg(bleMsg_t *pMsg)
{
if (pMsg->hdr.event == ATTS_HANDLE_VALUE_CNF) {
if (pMsg->att.handle == UART_TX_HDL) {
if (pMsg->hdr.status == ATT_SUCCESS) {
done = true;
} else if (pMsg->hdr.status == ATT_ERR_OVERFLOW) {
again = true;
}
}
}
if (pMsg->hdr.event == DM_CONN_OPEN_IND) {
ble_uart_buf_tx_fill = 0;
}
} }
#pragma once
#include "ble_api.h"
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
void UartProcMsg(bleMsg_t *pMsg);
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "gpio.h"
#include "bhy_uc_driver.h"
#include "bhy.h"
#include "pmic.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "epicardium.h"
#include "os/core.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "modules/stream.h"
#include "user_core/interrupts.h"
/* BHI160 Firmware Blob. Contents are defined in libcard10. */
extern uint8_t bhy1_fw[];
/* Interrupt Pin */
static const gpio_cfg_t bhi160_interrupt_pin = {
PORT_0, PIN_13, GPIO_FUNC_IN, GPIO_PAD_PULL_UP
};
/* clang-format off */
/* Axis remapping matrices */
static const int8_t bhi160_mapping_matrix[3 * 3] = {
0, -1, 0,
1, 0, 0,
0, 0, 1,
};
static const int8_t bmm150_mapping_matrix[3 * 3] = {
-1, 0, 0,
0, 1, 0,
0, 0, -1,
};
/*
* From the official docs:
*
* The sic matrix should be calculated for customer platform by logging
* uncalibrated magnetometer data. The sic matrix here is only an example
* array (identity matrix). Customer should generate their own matrix. This
* affects magnetometer fusion performance.
*
* TODO: Get data for card10
*/
static const float bhi160_sic_array[3 * 3] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
};
/* clang-format on */
/* BHI160 Task ID */
static TaskHandle_t bhi160_task_id = NULL;
/* BHI160 Mutex */
static struct mutex bhi160_mutex = { 0 };
/* Streams */
static struct stream_info bhi160_streams[10];
/* Active */
static bool bhi160_sensor_active[10] = { 0 };
/*
* Driver State: A flag that is set when an unrecoverable error occurred.
* Effectively, this means the sensor will be disabled until next reboot and any
* API calls will fail immediately.
*/
static bool bhi160_driver_b0rked = false;
/* -- Utilities -------------------------------------------------------- {{{ */
/*
* Retrieve the data size for a sensor. This value is needed for the creation
* of the sensor's sample queue.
*/
static size_t bhi160_lookup_data_size(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
case BHI160_MAGNETOMETER:
case BHI160_ORIENTATION:
case BHI160_GYROSCOPE:
return sizeof(struct bhi160_data_vector);
default:
return 0;
}
}
/*
* Map a sensor type to the virtual sensor ID used by BHy1.
*/
static bhy_virtual_sensor_t bhi160_lookup_vs_id(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
return VS_ID_ACCELEROMETER;
case BHI160_MAGNETOMETER:
return VS_ID_MAGNETOMETER;
case BHI160_ORIENTATION:
return VS_ID_ORIENTATION;
case BHI160_GYROSCOPE:
return VS_ID_GYROSCOPE;
default:
return -1;
}
}
/*
* Map a sensor type to its stream descriptor.
*/
static int bhi160_lookup_sd(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
return SD_BHI160_ACCELEROMETER;
case BHI160_MAGNETOMETER:
return SD_BHI160_MAGNETOMETER;
case BHI160_ORIENTATION:
return SD_BHI160_ORIENTATION;
case BHI160_GYROSCOPE:
return SD_BHI160_GYROSCOPE;
default:
return -1;
}
}
/* }}} */
/* -- API -------------------------------------------------------------- {{{ */
int epic_bhi160_enable_sensor(
enum bhi160_sensor_type sensor_type,
struct bhi160_sensor_config *config
) {
int result = 0;
bhy_virtual_sensor_t vs_id = bhi160_lookup_vs_id(sensor_type);
if (vs_id == (bhy_virtual_sensor_t)-1) {
return -ENODEV;
}
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
if (bhi160_driver_b0rked) {
result = -ENODEV;
goto out_free;
}
struct stream_info *stream = &bhi160_streams[sensor_type];
stream->item_size = bhi160_lookup_data_size(sensor_type);
/* TODO: Sanity check length */
stream->queue =
xQueueCreate(config->sample_buffer_len, stream->item_size);
if (stream->queue == NULL) {
result = -ENOMEM;
goto out_free;
}
result = stream_register(bhi160_lookup_sd(sensor_type), stream);
if (result < 0) {
goto out_free;
}
result = bhy_enable_virtual_sensor(
vs_id,
VS_WAKEUP,
config->sample_rate,
0,
VS_FLUSH_NONE,
0,
config->dynamic_range /* dynamic range is sensor dependent */
);
if (result != BHY_SUCCESS) {
goto out_free;
}
bhi160_sensor_active[sensor_type] = true;
/* Return the sensor stream descriptor */
result = bhi160_lookup_sd(sensor_type);
out_free:
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
return result;
}
int epic_bhi160_disable_sensor(enum bhi160_sensor_type sensor_type)
{
int result = 0;
bhy_virtual_sensor_t vs_id = bhi160_lookup_vs_id(sensor_type);
if (vs_id == (bhy_virtual_sensor_t)-1) {
return -ENODEV;
}
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
if (bhi160_driver_b0rked) {
result = -ENODEV;
goto out_free;
}
struct stream_info *stream = &bhi160_streams[sensor_type];
result = stream_deregister(bhi160_lookup_sd(sensor_type), stream);
if (result < 0) {
goto out_free;
}
vQueueDelete(stream->queue);
stream->queue = NULL;
result = bhy_disable_virtual_sensor(vs_id, VS_WAKEUP);
if (result < 0) {
goto out_free;
}
bhi160_sensor_active[sensor_type] = false;
result = 0;
out_free:
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
return result;
}
void epic_bhi160_disable_all_sensors()
{
for (size_t i = 0; i < sizeof(bhi160_sensor_active); i++) {
if (bhi160_sensor_active[i]) {
epic_bhi160_disable_sensor(i);
}
}
}
/* }}} */
/* -- Driver ----------------------------------------------------------- {{{ */
/*
* Handle a single packet from the FIFO. For most sensors this means pushing
* the sample into its sample queue.
*/
static void
bhi160_handle_packet(bhy_data_type_t data_type, bhy_data_generic_t *sensor_data)
{
uint8_t sensor_id = sensor_data->data_vector.sensor_id;
struct bhi160_data_vector data_vector;
/*
* Timestamp of the next samples, counting at 32 kHz.
* Currently unused.
*/
static uint32_t timestamp = 0;
enum bhi160_sensor_type sensor_type = 0;
int epic_int = 0;
bool wakeup = false;
switch (sensor_id) {
case VS_ID_TIMESTAMP_MSW_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_TIMESTAMP_MSW:
assert(data_type == BHY_DATA_TYPE_SCALAR_U16);
timestamp = sensor_data->data_scalar_u16.data << 16;
break;
case VS_ID_TIMESTAMP_LSW_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_TIMESTAMP_LSW:
assert(data_type == BHY_DATA_TYPE_SCALAR_U16);
timestamp = (timestamp & 0xFFFF0000) |
sensor_data->data_scalar_u16.data;
break;
case VS_ID_ACCELEROMETER_WAKEUP:
case VS_ID_MAGNETOMETER_WAKEUP:
case VS_ID_ORIENTATION_WAKEUP:
case VS_ID_GYROSCOPE_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_ACCELEROMETER:
case VS_ID_MAGNETOMETER:
case VS_ID_ORIENTATION:
case VS_ID_GYROSCOPE:
switch (sensor_id) {
case VS_ID_ACCELEROMETER_WAKEUP:
case VS_ID_ACCELEROMETER:
sensor_type = BHI160_ACCELEROMETER;
epic_int = EPIC_INT_BHI160_ACCELEROMETER;
break;
case VS_ID_MAGNETOMETER_WAKEUP:
case VS_ID_MAGNETOMETER:
sensor_type = BHI160_MAGNETOMETER;
epic_int = EPIC_INT_BHI160_MAGNETOMETER;
break;
case VS_ID_ORIENTATION_WAKEUP:
case VS_ID_ORIENTATION:
sensor_type = BHI160_ORIENTATION;
epic_int = EPIC_INT_BHI160_ORIENTATION;
break;
case VS_ID_GYROSCOPE_WAKEUP:
case VS_ID_GYROSCOPE:
sensor_type = BHI160_GYROSCOPE;
epic_int = EPIC_INT_BHI160_GYROSCOPE;
break;
}
assert(data_type == BHY_DATA_TYPE_VECTOR);
if (bhi160_streams[sensor_type].queue == NULL) {
break;
}
data_vector.data_type = BHI160_DATA_TYPE_VECTOR;
data_vector.x = sensor_data->data_vector.x;
data_vector.y = sensor_data->data_vector.y;
data_vector.z = sensor_data->data_vector.z;
data_vector.status = sensor_data->data_vector.status;
/* Discard overflow. See discussion in !316. */
if (xQueueSend(
bhi160_streams[sensor_type].queue,
&data_vector,
0) != pdTRUE) {
if (!bhi160_streams[sensor_type].was_full) {
LOG_WARN(
"bhi160",
"queue full for %d",
sensor_type
);
}
bhi160_streams[sensor_type].was_full = true;
} else {
bhi160_streams[sensor_type].was_full = false;
}
if (wakeup) {
interrupt_trigger(epic_int);
}
break;
default:
break;
}
}
/*
* Fetch all data available from BHI160's FIFO buffer and handle all packets
* contained in it.
*/
static void bhi160_fetch_fifo(void)
{
/*
* Warning: The code from the BHy1 docs has some issues. This
* implementation looks similar, but has a few important differences.
* You'll probably be best of leaving it as it is ...
*/
/*
* FIFO buffer. Access to this static variable is safe because this
* function is guarded by the bhi160_mutex.
*/
static uint8_t bhi160_fifo[BHI160_FIFO_SIZE];
static size_t start_index = 0;
BHY_RETURN_FUNCTION_TYPE result = 0;
/* Number of bytes left in BHI160's FIFO buffer */
uint16_t bytes_left_in_fifo = 1;
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
while (bytes_left_in_fifo) {
/* Fill local FIFO buffer with as many bytes as possible */
uint16_t bytes_read;
result = bhy_read_fifo(
&bhi160_fifo[start_index],
BHI160_FIFO_SIZE - start_index,
&bytes_read,
&bytes_left_in_fifo
);
if (result != BHY_SUCCESS) {
/*
* Honestly not sure how we should handle these errors.
*
* Needs more digging to find out how bhy_read_fifo()
* behaves in error situations (a quick glance at the
* code shows that the function might have written
* _somethings_ into our buffer so we can't just retry
* blindly ...)
*
* For now, just abort everything and disable the
* sensor. Won't cause havoc at least ...
*/
LOG_ERR("bhi160",
"Error while reading fifo: %d. Disabling.",
result);
bhi160_driver_b0rked = true;
break;
}
/* Add the bytes left from the last transfer on top */
bytes_read += start_index;
/* Handle all full packets received in this transfer */
uint8_t *fifo_ptr = bhi160_fifo;
uint16_t bytes_left = bytes_read;
while (bytes_left > 0) {
bhy_data_generic_t sensor_data;
bhy_data_type_t data_type;
result = bhy_parse_next_fifo_packet(
&fifo_ptr,
&bytes_left,
&sensor_data,
&data_type
);
if (result == BHY_SUCCESS) {
bhi160_handle_packet(data_type, &sensor_data);
} else {
LOG_WARN(
"bhi160",
"Error in fifo packet: %d. Ignoring.",
result
);
break;
}
}
/* Shift the remaining bytes to the beginning */
for (int i = 0; i < bytes_left; i++) {
bhi160_fifo[i] =
bhi160_fifo[bytes_read - bytes_left + i];
}
start_index = bytes_left;
}
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
}
/*
* Callback for the BHI160 interrupt pin. This callback is called from the
* SDK's GPIO interrupt driver, in interrupt context.
*/
static void bhi160_interrupt_callback(void *_)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (bhi160_task_id != NULL && !bhi160_driver_b0rked) {
vTaskNotifyGiveFromISR(
bhi160_task_id, &xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/* }}} */
void vBhi160Task(void *pvParameters)
{
int ret;
bhi160_task_id = xTaskGetCurrentTaskHandle();
mutex_create(&bhi160_mutex);
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
memset(bhi160_streams, 0x00, sizeof(bhi160_streams));
/*
* The BHI160, coming out of power-on-reset will hold its interrupt line
* high until a firmware image is loaded. Once that firmware is loaded
* and running, the interrupt line is deasserted and from then on,
* interrupts are signaled using a rising edge.
*
* So, initially we need to configure the IRQ for a falling edge, load
* the firmware and then reconfigure for a rising edge.
*/
GPIO_Config(&bhi160_interrupt_pin);
GPIO_RegisterCallback(
&bhi160_interrupt_pin, bhi160_interrupt_callback, NULL
);
GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_FALLING);
GPIO_IntEnable(&bhi160_interrupt_pin);
NVIC_SetPriority(
(IRQn_Type)MXC_GPIO_GET_IRQ(bhi160_interrupt_pin.port), 2
);
NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(bhi160_interrupt_pin.port));
/* Upload firmware */
ret = bhy_driver_init(bhy1_fw);
if (ret) {
LOG_CRIT("bhi160", "BHy1 init failed! Disabling.");
/* Disable BHI160 until next reboot */
bhi160_driver_b0rked = true;
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
vTaskDelete(NULL);
}
/* Wait for first interrupt, a falling edge */
hwlock_release(HWLOCK_I2C);
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000)) == 0) {
LOG_CRIT(
"bhi160",
"Sensor firmware was not loaded correctly. Disabling."
);
/* Disable BHI160 until next reboot */
bhi160_driver_b0rked = true;
mutex_unlock(&bhi160_mutex);
vTaskDelete(NULL);
}
hwlock_acquire(HWLOCK_I2C);
/*
* The firmware is now loaded; as stated above, we now need to
* reconfigure the IRQ for a rising edge.
*/
GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_RISING);
/*
* Remap axes to match card10 layout.
*
* TODO: We set the matrix for the accelerometer twice because on some
* badges, the axis mapping is not applied properly the first time. We
* should fix this properly at some point.
*/
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_ACC, (int8_t *)bhi160_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_ACC, (int8_t *)bhi160_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_MAG, (int8_t *)bmm150_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_GYRO, (int8_t *)bhi160_mapping_matrix
);
/* Set "SIC" matrix. TODO: Find out what this is about */
bhy_set_sic_matrix((float *)bhi160_sic_array);
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
/* ----------------------------------------- */
while (1) {
bhi160_fetch_fifo();
/*
* Wait for interrupt. After two seconds, fetch FIFO anyway in
* case there are any diagnostics or errors.
*
* In the future, reads using epic_stream_read() might also
* trigger a FIFO fetch, from outside this task.
*/
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000));
}
}
#include "epicardium.h"
#include "modules/modules.h"
#include "os/core.h"
#include "drivers/drivers.h"
#include "card10.h"
#include "bme680.h"
#include "bosch.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#define HEATR_TEMP 320
#define HEATR_DUR 150
static bool initialized;
static struct bme680_dev bme;
static int convert_error(int8_t error)
{
switch (error) {
case BME680_OK:
return 0;
case BME680_E_NULL_PTR:
return EFAULT;
case BME680_E_COM_FAIL:
return EIO;
case BME680_E_DEV_NOT_FOUND:
return ENODEV;
case BME680_E_INVALID_LENGTH:
return EINVAL;
default:
return 1;
}
}
static int8_t
i2c_write(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_write(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static int8_t i2c_read(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_read(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static void delay(uint32_t msec)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
card10_bosch_delay(msec);
} else {
vTaskDelay(pdMS_TO_TICKS(msec));
}
}
int epic_bme680_init()
{
int8_t result = BME680_OK;
if (bsec_active()) {
/* If the proprietary Bosch BSEC libary is in use
* we redirect calls to that. It always runs
* in the background
*/
return 0;
}
if (initialized) {
return 0;
}
bme.dev_id = BME680_I2C_ADDR_PRIMARY;
bme.intf = BME680_I2C_INTF;
bme.read = i2c_read;
bme.write = i2c_write;
bme.delay_ms = delay;
/*
* amb_temp can be set to 25 prior to configuring the gas sensor
* or by performing a few temperature readings without operating
* the gas sensor.
*/
bme.amb_temp = 25;
result = bme680_init(&bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_init error: %d\n", result);
return -convert_error(result);
}
/*
* Select the power mode. Must be set before writing the sensor
* configuration
*/
bme.power_mode = BME680_FORCED_MODE;
/* Set the temperature, pressure and humidity settings */
bme.tph_sett.os_hum = BME680_OS_2X;
bme.tph_sett.os_pres = BME680_OS_4X;
bme.tph_sett.os_temp = BME680_OS_8X;
bme.tph_sett.filter = BME680_FILTER_SIZE_0;
/* Set the remaining gas sensor settings and link the heating profile */
bme.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
/* Create a ramp heat waveform in 3 steps */
bme.gas_sett.heatr_temp = HEATR_TEMP; /* degree Celsius */
bme.gas_sett.heatr_dur = HEATR_DUR; /* milliseconds */
/* Set the required sensor settings needed */
uint16_t settings_sel = BME680_OST_SEL | BME680_OSP_SEL |
BME680_OSH_SEL | BME680_FILTER_SEL |
BME680_GAS_SENSOR_SEL;
result = bme680_set_sensor_settings(settings_sel, &bme);
if (result != BME680_OK) {
LOG_ERR("bme680",
"bme680_set_sensor_settings error: %d\n",
result);
return -convert_error(result);
}
initialized = true;
return 0;
}
int epic_bme680_deinit()
{
/* This is an intentional NO OP to keep the BME680 always initialized.
*
* If it configured to foreced mode, there is no energy consumption
* penalty.
*/
if (bsec_active()) {
return 0;
}
#if 0
if (!initialized) {
return 0;
}
int8_t result = bme680_soft_reset(&bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_soft_reset error: %d\n", result);
}
initialized = false;
#endif
return 0;
}
int epic_bme680_read_sensors(struct bme680_sensor_data *data)
{
int8_t result = BME680_OK;
if (bsec_active()) {
return bsec_read_bme680(data);
}
if (!initialized) {
LOG_ERR("bme680", "bme680 sensor not initialized");
return -EINVAL;
}
if (data == NULL) {
return -EFAULT;
}
uint16_t profile_dur = 0;
bme680_get_profile_dur(&profile_dur, &bme);
result = bme680_set_sensor_mode(&bme); /* Trigger a measurement */
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_set_sensor_mode error: %d\n", result);
return -convert_error(result);
}
/*
* Wait for the measurement to complete. Release the I2C lock in the
* meantime.
*/
vTaskDelay(pdMS_TO_TICKS(profile_dur));
struct bme680_field_data raw_data;
result = bme680_get_sensor_data(&raw_data, &bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_get_sensor_data error: %d\n", result);
return -convert_error(result);
}
data->temperature = (float)raw_data.temperature / 100.0f;
data->humidity = raw_data.humidity / 1000.0f;
data->pressure = raw_data.pressure / 100.0f;
data->gas_resistance = raw_data.gas_resistance;
return 0;
}
/* Adapted from bsec_iot_example.c and bsec_iot_ulp_plus_example.c */
#include "card10.h"
#include "bosch.h"
#include "bsec_integration.h"
#include "ble/ess.h"
#include "epicardium.h"
#include "modules/modules.h"
#include "os/config.h"
#include "os/core.h"
#include "FreeRTOS.h"
#include "task.h"
#include "max32665.h"
#include "gcr_regs.h"
#include <string.h>
#include <stdio.h>
TaskHandle_t bsec_task_id;
static int64_t last_bme680_timestamp;
static bool bsec_task_active;
static bool debug;
static struct bsec_sensor_data last_bsec_data;
#define ULP 0
// From generic_18v_3s_4d/bsec_serialized_configurations_iaq.c
static const uint8_t bsec_config_generic_18v_3s_4d[454] = {
0, 8, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1,
0, 0, 48, 0, 1, 0, 0, 192, 168, 71, 64, 49, 119, 76,
0, 0, 225, 68, 137, 65, 0, 191, 205, 204, 204, 190, 0, 0,
64, 191, 225, 122, 148, 190, 0, 0, 0, 0, 216, 85, 0, 100,
0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 2, 0, 0, 244,
1, 225, 0, 25, 0, 0, 128, 64, 0, 0, 32, 65, 144, 1,
0, 0, 112, 65, 0, 0, 0, 63, 16, 0, 3, 0, 10, 215,
163, 60, 10, 215, 35, 59, 10, 215, 35, 59, 9, 0, 5, 0,
0, 0, 0, 0, 1, 88, 0, 9, 0, 7, 240, 150, 61, 0,
0, 0, 0, 0, 0, 0, 0, 28, 124, 225, 61, 52, 128, 215,
63, 0, 0, 160, 64, 0, 0, 0, 0, 0, 0, 0, 0, 205,
204, 12, 62, 103, 213, 39, 62, 230, 63, 76, 192, 0, 0, 0,
0, 0, 0, 0, 0, 145, 237, 60, 191, 251, 58, 64, 63, 177,
80, 131, 64, 0, 0, 0, 0, 0, 0, 0, 0, 93, 254, 227,
62, 54, 60, 133, 191, 0, 0, 64, 64, 12, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5,
48, 117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0,
92, 4, 144, 1, 64, 1, 64, 1, 144, 1, 48, 117, 48, 117,
48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117,
48, 117, 100, 0, 100, 0, 48, 117, 48, 117, 100, 0, 100, 0,
100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0,
100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 44, 1, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 8, 7, 8, 7,
8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 112, 23, 112, 23,
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23,
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255, 255, 255,
255, 255, 255, 255, 220, 5, 220, 5, 220, 5, 255, 255, 255, 255,
255, 255, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0,
0, 0, 83, 141, 0, 0
};
/*!
* @brief Capture the system time in microseconds
*
* @return system_current_time current system timestamp in microseconds
*/
static int64_t get_timestamp_us()
{
int64_t tick = xTaskGetTickCount();
return tick * 1000;
}
/*!
* @brief Handling of the ready outputs
*
* @param[in] timestamp time in nanoseconds
* @param[in] iaq IAQ signal
* @param[in] iaq_accuracy accuracy of IAQ signal
* @param[in] temperature temperature signal
* @param[in] humidity humidity signal
* @param[in] pressure pressure signal
* @param[in] raw_temperature raw temperature signal
* @param[in] raw_humidity raw humidity signal
* @param[in] gas raw gas sensor signal
* @param[in] bsec_status value returned by the bsec_do_steps() call
*
* @return none
*/
static void output_ready(
int64_t timestamp,
float iaq,
uint8_t iaq_accuracy,
float temperature,
float humidity,
float pressure,
float raw_temperature,
float raw_humidity,
float gas,
bsec_library_return_t bsec_status,
float static_iaq,
float co2_equivalent,
float breath_voc_equivalent
) {
last_bsec_data.temperature = temperature;
last_bsec_data.humidity = humidity;
last_bsec_data.pressure = pressure / 100.;
last_bsec_data.gas_resistance = gas;
last_bsec_data.timestamp = timestamp;
last_bsec_data.accuracy = iaq_accuracy;
last_bsec_data.indoor_air_quality = iaq;
last_bsec_data.static_indoor_air_quality = static_iaq;
last_bsec_data.co2_equivalent = co2_equivalent;
last_bsec_data.breath_voc_equivalent = breath_voc_equivalent;
__sync_synchronize();
last_bme680_timestamp = timestamp;
bleESS_update_from_bsec_data(&last_bsec_data);
if (debug) {
LOG_INFO(
"bsec",
"time[ms]: %u, IAQ: %u, IAQ ACC[0-3]: %u, T[.1C]: %u, Hum[.1%%]: %u, P[Pa]: %u, Raw T[.1C]: %u, Raw Hum[.1%%]: %u, Gas[Ohm]: %u, Static IAQ: %u, CO2[ppm]: %u, Breath VOC[ppb]: %u",
(unsigned int)(timestamp / 1e6),
(unsigned int)(iaq),
(unsigned int)(iaq_accuracy),
(unsigned int)(temperature * 10),
(unsigned int)(humidity * 10),
(unsigned int)(pressure),
(unsigned int)(raw_temperature * 10),
(unsigned int)(raw_humidity * 10),
(unsigned int)(gas),
(unsigned int)(static_iaq),
(unsigned int)(co2_equivalent),
(unsigned int)(breath_voc_equivalent * 1e3)
);
}
}
int epic_bsec_read_sensors(struct bsec_sensor_data *data)
{
if (data == NULL) {
return -EFAULT;
}
if (!bsec_task_active) {
return -ENODEV;
}
/* TODO: could also return -EINVAL */
while (last_bme680_timestamp == 0)
vTaskDelay(pdMS_TO_TICKS(10));
*data = last_bsec_data;
return 0;
}
static uint32_t bsec_load(char *path, uint8_t *buffer, uint32_t n_buffer)
{
uint32_t len = 0;
int fd, res;
LOG_DEBUG("bsec", "load %s %lu", path, n_buffer);
if ((fd = epic_file_open(path, "r")) < 0) {
LOG_DEBUG("bsec", "Open failed");
return 0;
}
uint32_t header;
if ((res = epic_file_read(fd, &header, sizeof(header))) !=
sizeof(header)) {
LOG_WARN("bsec", "Header failed");
goto done;
}
if (header > n_buffer) {
LOG_WARN("bsec", "Too large");
goto done;
}
if (epic_file_read(fd, buffer, header) != (int)header) {
LOG_WARN("bsec", "Read failed");
goto done;
}
len = header;
LOG_DEBUG("bsec", "Success");
done:
epic_file_close(fd);
return len;
}
/*!
* @brief Load previous library state from non-volatile memory
*
* @param[in,out] state_buffer buffer to hold the loaded state string
* @param[in] n_buffer size of the allocated state buffer
*
* @return number of bytes copied to state_buffer
*/
static uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer)
{
return bsec_load("bsec_iaq.state", state_buffer, n_buffer);
}
/*!
* @brief Save library state to non-volatile memory
*
* @param[in] state_buffer buffer holding the state to be stored
* @param[in] length length of the state string to be stored
*
* @return none
*/
static void state_save(const uint8_t *state_buffer, uint32_t length)
{
int fd, res;
LOG_DEBUG("bsec", "state_save %d", (int)length);
if ((fd = epic_file_open("bsec_iaq.state", "w")) < 0) {
LOG_WARN("bsec", "Open failed");
return;
}
uint32_t header = length;
if ((res = epic_file_write(fd, &header, sizeof(header))) !=
sizeof(header)) {
LOG_WARN("bsec", "Header failed");
goto done;
}
if (epic_file_write(fd, state_buffer, header) != (int)header) {
LOG_WARN("bsec", "Write failed");
goto done;
}
LOG_DEBUG("bsec", "stack high: %lu", uxTaskGetStackHighWaterMark(NULL));
done:
epic_file_close(fd);
}
/*!
* @brief Delete the library state from non-volatile memory
*
* @return none
*/
static void state_delete(void)
{
LOG_DEBUG("bsec", "state_delete");
epic_file_unlink("bsec_iaq.state");
}
static int8_t
i2c_write(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_write(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static int8_t i2c_read(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_read(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static void delay(uint32_t msec)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
/* We need to fall back to hardware waits if not running
* in a task context */
card10_bosch_delay(msec);
} else {
vTaskDelay(pdMS_TO_TICKS(msec));
}
}
/*!
* @brief Load library config from non-volatile memory
*
* @param[in,out] config_buffer buffer to hold the loaded state string
* @param[in] n_buffer size of the allocated state buffer
*
* @return number of bytes copied to config_buffer
*/
static uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer)
{
uint32_t len = bsec_load("bsec_iaq.config", config_buffer, n_buffer);
if (len == 0) {
LOG_INFO("bsec", "Using default bsec_config_generic_18v_3s_4d");
len = sizeof(bsec_config_generic_18v_3s_4d);
memcpy(config_buffer, bsec_config_generic_18v_3s_4d, len);
}
return len;
}
#if ULP
void ulp_plus_trigger_iaq()
{
/* We call bsec_update_subscription() in order to instruct BSEC to perform an extra measurement at the next
* possible time slot
*/
bsec_sensor_configuration_t requested_virtual_sensors[1];
uint8_t n_requested_virtual_sensors = 1;
bsec_sensor_configuration_t
required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
bsec_library_return_t status = BSEC_OK;
/* To trigger a ULP plus, we request the IAQ virtual sensor with a specific sample rate code */
requested_virtual_sensors[0].sensor_id = BSEC_OUTPUT_IAQ;
requested_virtual_sensors[0].sample_rate =
BSEC_SAMPLE_RATE_ULP_MEASUREMENT_ON_DEMAND;
/* Call bsec_update_subscription() to enable/disable the requested virtual sensors */
status = bsec_update_subscription(
requested_virtual_sensors,
n_requested_virtual_sensors,
required_sensor_settings,
&n_required_sensor_settings
);
/* The status code would tell is if the request was accepted. It will be rejected if the sensor is not already in
* ULP mode, or if the time difference between requests is too short, for example. */
}
#endif
bool bsec_active(void)
{
return bsec_task_active;
}
int bsec_read_bme680(struct bme680_sensor_data *data)
{
if (!bsec_task_active) {
return BME680_E_COM_FAIL;
}
if (data == NULL) {
return -EFAULT;
}
while (last_bme680_timestamp == 0)
vTaskDelay(pdMS_TO_TICKS(10));
data->temperature = last_bsec_data.temperature;
data->humidity = last_bsec_data.humidity;
data->pressure = last_bsec_data.pressure;
data->gas_resistance = last_bsec_data.gas_resistance;
return BME680_OK;
}
/**
* Checks config and activates the BSEC libary if requested.
*
* Initializes the BSEC library before starting the task to
* reduce the stack size needed for the task by at least 250 bytes
*/
int bsec_activate(void)
{
return_values_init ret;
#if ULP
float sample_rate = BSEC_SAMPLE_RATE_ULP;
#else
float sample_rate = BSEC_SAMPLE_RATE_LP;
#endif
if (!config_get_boolean_with_default("bsec_enable", false)) {
return -1;
}
debug = config_get_boolean_with_default("bsec_debug", false);
float temperature_offset =
config_get_integer_with_default("bsec_offset", -22) / 10.;
if (temperature_offset != 0.0) {
LOG_INFO(
"bsec",
"BSEC Temp offset %d/10 K",
(int)(temperature_offset * 10)
);
}
/* Puts AT LEAST 2 * #BSEC_MAX_PROPERTY_BLOB_SIZE = 2 * 454 = 908 bytes onto the stack */
ret = bsec_iot_init(
sample_rate,
-temperature_offset,
i2c_write,
i2c_read,
delay,
state_load,
config_load
);
if (ret.bsec_status == BSEC_E_CONFIG_VERSIONMISMATCH) {
/* BSEC version changed and old state is not compatible anymore */
/* If the config is also not valid anymore, the user will have
* to fix that. */
state_delete();
ret = bsec_iot_init(
sample_rate,
-temperature_offset,
i2c_write,
i2c_read,
delay,
state_load,
config_load
);
}
if (ret.bme680_status) {
LOG_WARN("bsec", "bme680 init failed: %d", ret.bme680_status);
/* Could not initialize BME680 */
return -1;
} else if (ret.bsec_status) {
LOG_WARN("bsec", "bsec init failed: %d", ret.bsec_status);
/* Could not initialize BSEC library */
return -1;
}
return 0;
}
void vBSECTask(void *pvParameters)
{
bsec_task_active = true;
bsec_task_id = xTaskGetCurrentTaskHandle();
#if ULP
/* State is saved every 100 samples, which means every 100 * 300 secs = 500 minutes */
const int stat_save_interval = 100;
#else
/* State is saved every 10.000 samples, which means every 10.000 * 3 secs = 500 minutes */
const int stat_save_interval = 10000;
#endif
/* Call to endless loop function which reads and processes data based on sensor settings */
/* Puts AT LEAST 2 * BSEC_MAX_STATE_BLOB_SIZE + 8 * sizeof(bsec_input_t) =
* 2 * 139 + 8 * 20 = 438 bytes onto the stack */
bsec_iot_loop(
delay,
get_timestamp_us,
output_ready,
state_save,
stat_save_interval
);
}
#include "epicardium.h" #include "epicardium.h"
#include "modules/modules.h" #include "modules/modules.h"
#include "modules/log.h" #include "os/core.h"
#include "portexpander.h" #include "portexpander.h"
#include "MAX77650-Arduino-Library.h" #include "MAX77650-Arduino-Library.h"
...@@ -16,19 +16,14 @@ static const uint8_t pin_mask[] = { ...@@ -16,19 +16,14 @@ static const uint8_t pin_mask[] = {
uint8_t epic_buttons_read(uint8_t mask) uint8_t epic_buttons_read(uint8_t mask)
{ {
uint8_t ret = 0; uint8_t ret = 0;
if (portexpander_detected() && (mask & 0x7)) {
if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
LOG_ERR("buttons", "Can't acquire I2C bus");
return 0;
}
hwlock_acquire(HWLOCK_I2C);
if (portexpander_detected() && (mask & 0x7)) {
/* /*
* Not using PB_Get() here as that performs one I2C transcation * Not using PB_Get() here as that performs one I2C transaction
* per button. * per button.
*/ */
uint8_t pin_status = ~portexpander_get(); uint8_t pin_status = ~portexpander_in_get(0xFF);
hwlock_release(HWLOCK_I2C);
for (uint8_t m = 1; m < 0x8; m <<= 1) { for (uint8_t m = 1; m < 0x8; m <<= 1) {
if (mask & m && pin_status & pin_mask[m]) { if (mask & m && pin_status & pin_mask[m]) {
...@@ -41,5 +36,6 @@ uint8_t epic_buttons_read(uint8_t mask) ...@@ -41,5 +36,6 @@ uint8_t epic_buttons_read(uint8_t mask)
ret |= BUTTON_RESET; ret |= BUTTON_RESET;
} }
hwlock_release(HWLOCK_I2C);
return ret; return ret;
} }
#include "epicardium.h"
#include "drivers/display/lcd.h"
#include "drivers/display/epic_ctx.h"
#include "FreeRTOS.h"
#include "LCD_Driver.h"
#include "gpio.h"
#include "task.h"
#include "tmr.h"
#include "tmr_utils.h"
#include <machine/endian.h>
#include <string.h>
static TaskHandle_t lock = NULL;
static int check_lock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (task != lock) {
return -EBUSY;
} else {
return 0;
}
}
static uint16_t rgb888_to_rgb565(uint8_t *bytes)
{
return ((bytes[0] & 0b11111000) << 8) | ((bytes[1] & 0b11111100) << 3) |
(bytes[2] >> 3);
}
static inline void
rgb565_to_rgb888(uint16_t pixel, uint8_t *red, uint8_t *green, uint8_t *blue)
{
*blue = (pixel & 31) << 3;
*green = ((pixel >> 5) & 63) << 2;
*red = ((pixel >> 11) & 31) << 3;
}
int epic_disp_print(
int16_t posx,
int16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
) {
return epic_disp_print_adv(DISP_FONT20, posx, posy, pString, fg, bg);
}
static const float font_map[] = {
[DISP_FONT8] = 8.0f, [DISP_FONT12] = 12.0f, [DISP_FONT16] = 16.0f,
[DISP_FONT20] = 20.0f, [DISP_FONT24] = 24.0f,
};
int epic_disp_print_adv(
uint8_t font,
int16_t posx,
int16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
) {
uint8_t r, g, b;
int cl = check_lock();
if (cl < 0) {
return cl;
}
if (font >= (sizeof(font_map) / sizeof(font_map[0]))) {
return -EINVAL;
}
float font_size = font_map[font];
ctx_font_size(epicardium_ctx, font_size);
if (fg != bg) {
/* non-transparent background */
rgb565_to_rgb888(bg, &r, &g, &b);
ctx_rgba8(epicardium_ctx, r, g, b, 255);
float width = ctx_text_width(epicardium_ctx, pString);
ctx_rectangle(epicardium_ctx, posx, posy, width, font_size);
ctx_fill(epicardium_ctx);
}
rgb565_to_rgb888(fg, &r, &g, &b);
ctx_rgba8(epicardium_ctx, r, g, b, 255);
ctx_move_to(epicardium_ctx, posx, (float)posy + font_size * 0.8f);
ctx_text(epicardium_ctx, pString);
return 0;
}
int epic_disp_clear(uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
/*
* We could use ctx for this but it's much easier to just clear the
* framebuffer directly.
*/
for (size_t i = 0; i < sizeof(epicardium_ctx_fb); i += 2) {
epicardium_ctx_fb[i] = color >> 8;
epicardium_ctx_fb[i + 1] = color & 0xff;
}
return 0;
}
int epic_disp_pixel(int16_t x, int16_t y, uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_set_pixel_u8(epicardium_ctx, x, y, r, g, b, 255);
return 0;
}
static uint16_t rgb565_pixel_from_buf(
uint8_t *img,
enum epic_rgb_format format,
int16_t width,
int16_t x,
int16_t y,
uint8_t *alpha
) {
uint16_t tmp16;
uint8_t rgba[4];
switch (format) {
case EPIC_RGB565:
*alpha = 255;
memcpy(&tmp16, &img[y * width * 2 + x * 2], 2);
return tmp16;
case EPIC_RGBA5551:
memcpy(&tmp16, &img[y * width * 2 + x * 2], 2);
*alpha = (tmp16 & 0x01) ? 255 : 0;
return (tmp16 & 0xFFC0) | ((tmp16 & 0x3E) >> 1);
case EPIC_RGB8:
*alpha = 255;
memcpy(rgba, &img[y * width * 3 + x * 3], 3);
return rgb888_to_rgb565(rgba);
case EPIC_RGBA8:
memcpy(rgba, &img[y * width * 4 + x * 4], 4);
*alpha = rgba[3];
return rgb888_to_rgb565(rgba);
default:
return 0xFFFF;
}
}
int epic_disp_blit(
int16_t pos_x,
int16_t pos_y,
int16_t width,
int16_t height,
void *img,
enum epic_rgb_format format
) {
int cl = check_lock();
if (cl < 0) {
return cl;
}
for (int16_t xsrc = 0; xsrc < width; xsrc += 1) {
for (int16_t ysrc = 0; ysrc < height; ysrc += 1) {
int16_t xscreen = pos_x + xsrc;
int16_t yscreen = pos_y + ysrc;
size_t offset = yscreen * 160 * 2 + xscreen * 2;
if (xscreen < 0 || xscreen >= 160 || yscreen < 0 ||
yscreen >= 80) {
continue;
}
uint8_t alpha = 255;
uint16_t pixel = rgb565_pixel_from_buf(
img, format, width, xsrc, ysrc, &alpha
);
if (alpha == 0) {
continue;
}
epicardium_ctx_fb[offset] = (pixel & 0xFF00) >> 8;
epicardium_ctx_fb[offset + 1] = pixel & 0xFF;
}
}
return 0;
}
int epic_disp_line(
int16_t xstart,
int16_t ystart,
int16_t xend,
int16_t yend,
uint16_t color,
enum disp_linestyle linestyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0) {
return cl;
}
float xstartf = xstart, ystartf = ystart, xendf = xend, yendf = yend;
/*
* For odd line widths, shift the line by half a pixel so it aligns
* perfectly with the pixel grid.
*/
if (pixelsize % 2 == 1) {
xstartf += 0.5f;
ystartf += 0.5f;
yendf += 0.5f;
xendf += 0.5f;
}
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
ctx_line_width(epicardium_ctx, pixelsize);
ctx_move_to(epicardium_ctx, xstartf, ystartf);
ctx_line_to(epicardium_ctx, xendf, yendf);
ctx_stroke(epicardium_ctx);
return 0;
}
int epic_disp_rect(
int16_t xstart,
int16_t ystart,
int16_t xend,
int16_t yend,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_rectangle(
epicardium_ctx,
xstart,
ystart,
xend - xstart + 1,
yend - ystart + 1
);
switch (fillstyle) {
case FILLSTYLE_EMPTY:
ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
ctx_line_width(epicardium_ctx, pixelsize);
ctx_stroke(epicardium_ctx);
break;
case FILLSTYLE_FILLED:
ctx_rgba8(epicardium_ctx, r, g, b, 255);
ctx_fill(epicardium_ctx);
break;
}
return 0;
}
int epic_disp_circ(
int16_t x,
int16_t y,
uint16_t rad,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_arc(epicardium_ctx, x, y, rad, 0.0f, CTX_PI * 1.95, 0);
switch (fillstyle) {
case FILLSTYLE_EMPTY:
ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
ctx_line_width(epicardium_ctx, pixelsize);
ctx_stroke(epicardium_ctx);
break;
case FILLSTYLE_FILLED:
ctx_rgba8(epicardium_ctx, r, g, b, 255);
ctx_fill(epicardium_ctx);
break;
}
return 0;
}
int epic_disp_update()
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
lcd_write_fb(epicardium_ctx_fb);
return 0;
}
int epic_disp_framebuffer(union disp_framebuffer *fb)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
/*
* Flip the screen because that's what this API call historically
* expects.
*/
lcd_set_screenflip(true);
lcd_write_fb(fb->raw);
lcd_set_screenflip(false);
return 0;
}
int epic_disp_backlight(uint16_t brightness)
{
/* TODO: lock? */
if (brightness == 0) {
lcd_set_sleep(true);
} else {
lcd_set_sleep(false);
}
LCD_SetBacklight(brightness);
return 0;
}
int epic_disp_open()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (lock == task) {
return 0;
} else if (lock == NULL) {
lock = task;
return 0;
} else {
return -EBUSY;
}
}
int epic_disp_close()
{
if (check_lock() < 0 && lock != NULL) {
return -EBUSY;
} else {
lock = NULL;
return 0;
}
}
void disp_update_backlight_clock(void)
{
LCD_UpdateBacklightClock();
}
void disp_forcelock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
lock = task;
}
#pragma once
#include "ctx.h"
extern Ctx *epicardium_ctx;
extern uint8_t epicardium_ctx_fb[160 * 80 * 2];
#include "drivers/display/lcd.h"
#include "drivers/display/epic_ctx.h"
#include "drivers/drivers.h"
#include "display.h"
#include "ctx.h"
#include <stdint.h>
#include <machine/endian.h>
#if BYTE_ORDER == LITTLE_ENDIAN
#define CARD10_CTX_FORMAT CTX_FORMAT_RGB565_BYTESWAPPED
#else
#define CARD10_CTX_FORMAT CTX_FORMAT_RGB565
#endif
uint8_t epicardium_ctx_fb[160 * 80 * 2] = { 0 };
Ctx *epicardium_ctx = NULL;
void disp_init(void)
{
/*
* The bootloader has already initialized the display, so we only need
* to do the bare minimum here.
*/
lcd_reconfigure();
/*
* Initialize the graphics context.
*/
disp_ctx_reinit();
}
void disp_ctx_reinit(void)
{
if (epicardium_ctx != NULL) {
ctx_free(epicardium_ctx);
}
epicardium_ctx = ctx_new_for_framebuffer(
epicardium_ctx_fb, 160, 80, 160 * 2, CARD10_CTX_FORMAT
);
/* set some defaults */
ctx_rgba(epicardium_ctx, 1.0f, 1.0f, 1.0f, 1.0f);
ctx_rgba_stroke(epicardium_ctx, 1.0f, 1.0f, 1.0f, 1.0f);
ctx_font(epicardium_ctx, "ctx-mono");
}