Zařízení ve formě prstenu slouží jako bezdrátový ovladač aplikace bMyVision, se kterou komunikuje prostřednictvím technologie Bluetooth. Uživatel má prsten na ukazováčku a palcem může stisknout tlačítko, tím lze ovládat jednotlivé funkce aplikace, přičemž krátký stisk přepíná mezi režimy a dvojklik slouží k jejich potvrzení. V aktivním režimu umožňuje krátké stisknutí pořízení snímku a následné rozpoznání obsahu obrazu pomocí funkcí aplikace. Dlouhé stisknutí pak zajišťuje návrat do základního menu, čímž je ovládání intuitivní a snadno zapamatovatelné. Řešení je navrženo tak, aby maximálně zjednodušilo interakci zrakově postižených uživatelů s aplikací a zvýšilo jejich samostatnost při každodenních činnostech.
Elektronika inteligentního asistenčního prstenu je založena na číslicovém obvodu EFR32BG22C222F352GN32. Schéma zapojení:
Program pro číslicový obvod:
/***************************************************************************//**
* @file
* @brief Core application logic.
*******************************************************************************
* # License
* <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include "sl_bt_api.h"
#include "sl_main_init.h"
#include "app_assert.h"
#include "app.h"
#include "em_gpio.h"
#include "sl_sleeptimer.h"
#include "sl_gpio.h"
#include "sl_power_manager.h"
#include "sl_bt_api.h"
#include "em_emu.h"
#include "em_cmu.h"
#include "sl_simple_button_instances.h"
/*IDLE
└─ press → PRESSED
PRESSED
├─ release → WAIT_MULTI
└─ timeout → LONG_PRESS
WAIT_MULTI
├─ press → PRESSED
└─ timeout → EVALUATE_CLICKS*/
#define DEBOUNCE_MS 30
#define LONG_PRESS_MS 600 //800
#define MULTI_CLICK_TIMEOUT_MS 350 //400
#define IDLE_CLICK_TIMEOUT_MS 1500 //zadne kliknuti
#define INACTIVITY_MS 20000 // spadne do hlubokeho spanku - reset
// The advertising set handle allocated from Bluetooth stack.
static uint8_t advertising_set_handle = 0xff;
static uint8_t adv_data[] = { //data pro advertising
2, 0x01, 0x06, // flags
10, 0x16, // length + type (service data 0x16)
0xD2, 0xFC, // service UUID 0x180F
64, 0, 175, 1, 61, 58, 0 //adv_data[13]= button_STATUS - 1;
};
typedef enum
{
BUTTON_IDLE,
BUTTON_PRESSED,
BUTTON_WAIT_MULTI,
BUTTON_LONG_PRESS
} button_state_t;
static volatile button_state_t button_state = BUTTON_IDLE;
static volatile int sleep_state_ = 0;
static sl_sleeptimer_timer_handle_t debounce_timer;
static sl_sleeptimer_timer_handle_t long_timer;
static sl_sleeptimer_timer_handle_t multi_timer;
static sl_sleeptimer_timer_handle_t idle_timer;
static sl_sleeptimer_timer_handle_t inactivity_timer;
static uint8_t click_count = 0;
static volatile bool need_change_advertising = false;
static uint8_t new_click_type = 0; // 1=short, 2=double, 3=triple, 4=long atd.
static void inactivity_timeout_cb(sl_sleeptimer_timer_handle_t *handle, void *data)
{ (void)data; (void)handle;
sleep_state_=1; // prechod do deep sleep
}
static void evaluate_clicks(void)
{
switch(click_count)
{
case 1: // button_event_short(); adv_data[13]= 1;
new_click_type = 1;
need_change_advertising = true;
sl_bt_external_signal(1); // signál pro změnu reklamy
break;
case 2: // button_event_double(); adv_data[13]= 2;
new_click_type = 2; need_change_advertising = true;
sl_bt_external_signal(1); // signál pro změnu reklamy
break;
case 3: // button_event_triple(); adv_data[13]= 3;
new_click_type = 3; need_change_advertising = true;
sl_bt_external_signal(1); // signál pro změnu reklamy
break;
default:
break;
}
click_count = 0;
}
static void long_press_timeout(sl_sleeptimer_timer_handle_t *handle, void *data)
{ (void) handle; (void) data;
if(button_state == BUTTON_PRESSED)
{
button_state = BUTTON_LONG_PRESS;
click_count = 0;
//button_event_long();
new_click_type = 4; need_change_advertising = true;
sl_bt_external_signal(1); // signál pro změnu reklamy
}
}
static void multi_click_timeout(sl_sleeptimer_timer_handle_t *handle, void *data)
{ (void) handle; (void) data;
if(button_state == BUTTON_WAIT_MULTI)
{
evaluate_clicks();
button_state = BUTTON_IDLE;
}
}
static void idle_press_timeout(sl_sleeptimer_timer_handle_t *handle, void *data)
{ (void) handle; (void) data;
if(button_state == BUTTON_IDLE)
{
new_click_type = 0; need_change_advertising = true;
sl_bt_external_signal(1); // signál pro změnu reklamy
}
}
// test na zakmity
static void debounce_timeout(sl_sleeptimer_timer_handle_t *handle, void *data)
{ (void) handle; (void) data;
//bool pressed = !GPIO_PinInGet(BUTTON_PORT, BUTTON_PIN);// aktivni v nule
bool pressed = sl_button_get_state(SL_SIMPLE_BUTTON_INSTANCE(0));
switch(button_state)
{
case BUTTON_IDLE:
if(pressed)
{
button_state = BUTTON_PRESSED;
sl_sleeptimer_start_timer_ms( &long_timer, LONG_PRESS_MS, long_press_timeout,NULL,0,0);
sl_sleeptimer_restart_timer_ms(&inactivity_timer, INACTIVITY_MS, inactivity_timeout_cb, NULL, 0, 0);
}
break;
case BUTTON_PRESSED:
if(!pressed)
{
sl_sleeptimer_stop_timer(&long_timer);
click_count++;
button_state = BUTTON_WAIT_MULTI;
sl_sleeptimer_start_timer_ms( &multi_timer, MULTI_CLICK_TIMEOUT_MS, multi_click_timeout,NULL,0,0);
}
break;
case BUTTON_WAIT_MULTI:
if(pressed)
{
button_state = BUTTON_PRESSED;
sl_sleeptimer_start_timer_ms(&long_timer, LONG_PRESS_MS, long_press_timeout,NULL,0,0);
}
break;
case BUTTON_LONG_PRESS:
if(!pressed)
{
button_state = BUTTON_IDLE;
}
break;
}
}
/***************************************************************************//**
* Simple Button
* Button state changed callback
* @param[in] handle Button event handle
******************************************************************************/
void sl_button_on_change(const sl_button_t *handle)
{
if (SL_SIMPLE_BUTTON_INSTANCE(0) == handle) {
if(sl_button_get_state(handle)) {
app_proceed();//vzestupna hrana
}
else {
app_proceed();// sestupna hrana
}
}
}
void button_init(void)
{
sl_sleeptimer_start_timer_ms(&inactivity_timer, INACTIVITY_MS, inactivity_timeout_cb, NULL, 0, 0);
GPIO_EM4EnablePinWakeup(GPIO_IEN_EM4WUIEN5, 0);
GPIO_EM4SetPinRetention(true);
GPIO_PinModeSet(gpioPortC, 0, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortC, 1, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortC, 2, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortC, 3, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortC, 4, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortC, 5, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortA, 0, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortA, 4, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortA, 6, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortB, 0, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortB, 1, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortB, 2, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortD, 0, gpioModePushPull, 0);
GPIO_PinModeSet(gpioPortD, 1, gpioModePushPull, 0);
}
// Application Init.
void app_init(void)
{
EMU_UnlatchPinRetention();
button_init();
sl_sleeptimer_start_timer_ms( &debounce_timer, DEBOUNCE_MS, debounce_timeout, NULL, 0, 0);
}
// Application Process Action.
void app_process_action(void)
{
if (app_is_process_required()) {
sl_sleeptimer_start_timer_ms( &debounce_timer, DEBOUNCE_MS, debounce_timeout, NULL, 0, 0);
}
if(sleep_state_)
{
sleep_state_=0;
sl_bt_advertiser_stop(advertising_set_handle);
sl_bt_system_halt(1);
sl_power_manager_enter_em4();
}
}
/**************************************************************************//**
* Bluetooth stack event handler.
*
* @param[in] evt Event coming from the Bluetooth stack.
*****************************************************************************/
void sl_bt_on_event(sl_bt_msg_t *evt)
{
sl_status_t sc;
switch (SL_BT_MSG_ID(evt->header)) {
case sl_bt_evt_system_boot_id:
sc = sl_bt_advertiser_create_set(&advertising_set_handle);
app_assert_status(sc);
sc = sl_bt_legacy_advertiser_generate_data(advertising_set_handle,
sl_bt_advertiser_general_discoverable);
app_assert_status(sc);
sc = sl_bt_advertiser_set_timing(
advertising_set_handle,
160,
160,
0,
0);
app_assert_status(sc);
sc = sl_bt_legacy_advertiser_set_data(advertising_set_handle, sl_bt_advertiser_general_discoverable, sizeof(adv_data), adv_data);
sc = sl_bt_legacy_advertiser_start(advertising_set_handle,
sl_bt_legacy_advertiser_connectable);
sl_sleeptimer_start_periodic_timer_ms(&idle_timer, IDLE_CLICK_TIMEOUT_MS , idle_press_timeout, NULL, 0, 0);
app_assert_status(sc);
break;
case sl_bt_evt_connection_opened_id:
break;
case sl_bt_evt_connection_closed_id:
sc = sl_bt_legacy_advertiser_generate_data(advertising_set_handle,
sl_bt_advertiser_general_discoverable);
app_assert_status(sc);
sc = sl_bt_legacy_advertiser_start(advertising_set_handle,
sl_bt_legacy_advertiser_scannable);
app_assert_status(sc);
break;
case sl_bt_evt_system_external_signal_id:
if (evt->data.evt_system_external_signal.extsignals & 1) {
if (need_change_advertising) {
need_change_advertising = false;
adv_data[13]=new_click_type;
sl_bt_legacy_advertiser_set_data(advertising_set_handle,
sl_bt_advertiser_general_discoverable, sizeof(adv_data), adv_data);
sl_bt_legacy_advertiser_start(advertising_set_handle, sl_bt_legacy_advertiser_scannable);
if(new_click_type)
sl_sleeptimer_restart_periodic_timer_ms(&idle_timer, IDLE_CLICK_TIMEOUT_MS , idle_press_timeout, NULL, 0, 0);
else
sl_sleeptimer_stop_timer(&idle_timer);
}
}
break;
default:
break;
}
}
Prototyp inteligentního asistenčního prstenu byl vyvinut s využitím moderního 3D tisku plošných spojů na tiskárně Dragonfly od firmy Nanodimension.