デモ動画
部品
使用するハードウェアを次に示します。
部品名 | 個数 |
---|---|
Spresenseメインボード | 1 |
Spresense拡張ボード | 1 |
BLE1507(BLE serialization firmware) | 1 |
ILI9341搭載2.8インチSPI制御タッチパネル付TFT液晶 MSP2807 | 1 |
9軸 加速度計・ジャイロ・コンパスセンサ (BMI270・AK09918) Addon ボード | 1 |
SK6812使用マイコン内蔵フルカラーテープLED 1m 30LED IP67 | 1 |
使用するソフトウェアを次に示します。
ソフトウェア名 |
---|
Arduino IDE |
Web Bluetooth API |
設計図
「動き検知器」は、加速度計から定期的にセンサー情報を入力して、その情報を液晶モジュールに表示し、BLE(Bluetooth Low Energy)転送により遠隔地のパソコン等のブラウザに表示し、またその動きの変化をカラーテープLEDを用いて可視化します。
「動き検知器」は、次に示すハードウェアにより構成されます。
「動き検知器」は、次のようにして開発しました。
- Spresenseメインボード上で動作するプログラムはArduino IDEを使って開発します。
- BLE転送により転送されるデータをブラウザに表示するために、Windowsパソコン上で動作するWebアプリを開発します。
- 定期的に加速度計からのデータを液晶モジュールに表示し、BLE転送によりWindowsパソコンに転送し、カラーテープLEDで可視化します。
接続図
Spresenseメインボードに BLE1507(BLE serialization firmware) をAddonし、Spresenseメインボードと接続したSpresense拡張ボードに、SPIインタフェースを持つ液晶モジュール、I2Cインタフェースを持つ9軸 加速度計・ジャイロ・コンパスセンサ (BMI270・AK09918) Addon ボード、PWMにより接続するSK6812使用マイコン内蔵フルカラーテープLED 1m 30LED IP67をそれぞれ次のように接続します。
ハードウェア
液晶モジュールの背面からSpresense拡張ボードにSPIインタフェースにより接続します。Spresense拡張ボードに接続された加速度計が動くと、そのデータ値を液晶モジュールに表示し、同時にBLEによりWindowsパソコン上で動作しているブラウザに表示します。また加速度計の動きによって、カラーテープLEDの発行色が変化します。
ソースコード
プログラム「動き検知器」は、Spresenseメインボード上で動作するコード「 Arduinoソースコード」には、Arduino IDEを使用し、Windowsパソコン上のブラウザで動作するコード「 WEBアプリソースコード」には、テキストエディタを使ってコードを開発します。
- Arduinoソースコード
- WEBアプリソースコード
Arduinoソースコード
Arduinoソースコードは、加速度計からのデータを液晶モジュールとカラーテープLEDに引き渡し、加速度計からのデータをBLE転送します。
- 「BLE1507」を用いたBLE転送については「Spresense-Playground」を参照しました。
- 「SK6812使用マイコン内蔵フルカラーテープLED」を用いたカラーテープLEDの表示については「SpresenseNeoPixel」を参照しました。
SpresenseAccel.ino
#include "SpresenseBle.h"
#include "SpresenseSensor.h"
#include "SPI.h"
#include "Adafruit_ILI9341.h"
#include "SpresenseNeoPixel.h"
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
extern float accx;
extern float accy;
extern float accz;
char report[80];
// const uint16_t PIN = 6;
const uint16_t PIN = 3;
const uint16_t NUM_PIXELS = 30;
SpresenseNeoPixel<PIN, NUM_PIXELS> neopixel;
void setup() {
Serial.begin(115200);
Serial.println("Spresense start!");
BleInitialize();
Serial.println("setup00");
tft.begin();
tft.setRotation(3);
tft.fillScreen(ILI9341_BLACK);
pinMode(LED0, OUTPUT);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
digitalWrite(LED0, HIGH);
delay(100);
digitalWrite(LED1, HIGH);
delay(100);
digitalWrite(LED2, HIGH);
delay(1000);
digitalWrite(LED0, LOW);
delay(100);
digitalWrite(LED1, LOW);
delay(100);
digitalWrite(LED2, LOW);
/*
Serial.println(LED0);
Serial.println(LED1);
Serial.println(LED2);
*/
//float x = 123.56;
//testText(x);
//int8_t rslt = BMI270.begin(BMI270_I2C,BMI2_I2C_SEC_ADDR);
Serial.println("setup05");
int8_t rslt = BMI270.begin(BMI270_I2C, BMI2_I2C_PRIM_ADDR);
print_rslt(rslt);
Serial.println("setup10");
rslt = configure_sensor();
print_rslt(rslt);
neopixel.clear();
neopixel.framerate(40); // default framerate is 40[fps]
delay(1000);
}
void loop(void) {
int ret = 0;
static uint8_t ble_notify_data = 0;
uint8_t data[BLE_MAX_CHAR_SIZE];
int k = 0;
struct bmi2_sens_float sensor_data;
int8_t rslt = BMI270.bmi2_get_sensor_float(&sensor_data);
print_rslt(rslt);
Serial.print(micros()); // Comment out this line if using the Serial plotter
Serial.print(","); // Comment out this line if using the Serial plotter
Serial.print(sensor_data.acc.x);
Serial.print(",");
Serial.print(sensor_data.acc.y);
Serial.print(",");
Serial.print(sensor_data.acc.z);
Serial.println();
float accx = sensor_data.acc.x;
float accy = sensor_data.acc.y;
float accz = sensor_data.acc.z;
testText(accx, accy, accz);
uint leddata = LedConv(accx, accy, accz);
Serial.print(","); // Comment out this line if using the Serial plotter
Serial.print(leddata & 0xff, HEX);
Serial.print(",");
Serial.print((leddata & 0xff00) >> 8, HEX);
Serial.print(",");
Serial.print((leddata & 0xff0000) >> 16, HEX);
Serial.println();
neopixel.set(leddata & 0xff, (leddata & 0xff00) >> 8, (leddata & 0xff0000) >> 16);
neopixel.show();
byte* bytes = (byte*)&accx; // float値の先頭アドレスをbyteポインタにキャストする
for (int i = 0; i < sizeof(accx); i++) {
byte b = bytes[i]; // ポインタからbyte値を取得する
// 取得したbyte値を何らかの方法で格納する
data[k] = b;
k++;
}
bytes = (byte*)&accy; // float値の先頭アドレスをbyteポインタにキャストする
for (int i = 0; i < sizeof(accy); i++) {
byte b = bytes[i]; // ポインタからbyte値を取得する
// 取得したbyte値を何らかの方法で格納する
data[k] = b;
k++;
}
bytes = (byte*)&accz; // float値の先頭アドレスをbyteポインタにキャストする
for (int i = 0; i < sizeof(accz); i++) {
byte b = bytes[i]; // ポインタからbyte値を取得する
// 取得したbyte値を何らかの方法で格納する
data[k] = b;
k++;
}
/*
String s = "x=19.25 y=31.54 z=78.96";
s.getBytes(data, BLE_MAX_CHAR_SIZE);
*/
if (ble_is_notify_enabled) {
ret = ble_characteristic_notify(ble_conn_handle,
&g_ble_gatt_char,
data,
k);
if (ret != BT_SUCCESS) {
printf("%s [BLE] Send data failed. ret = %d\n", __func__, ret);
}
}
sleep(1);
}
uint LedConv(float x, float y, float z) {
uint work;
if (x > 0) {
if (y > 0) {
if (z > 10) {
work = 0x000000ff;
} else {
work = 0x0000ff00;
}
} else {
if (z > 10) {
work = 0x000000ff;
} else {
work = 0x00ff0000;
}
}
} else {
if (y > 0) {
if (z > 10) {
work = 0x000000ff;
} else {
work = 0x00ff0000;
}
} else {
if (z > 10) {
work = 0x000000ff;
} else {
work = 0x0000ff00;
}
}
}
Serial.print(work, HEX);
Serial.println();
return work;
}
void testText(float x, float y, float z) {
tft.fillRect(80, 10, 210, 80, ILI9341_BLACK);
tft.setCursor(0, 10);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(3);
tft.print("Accx: ");
tft.println(x);
tft.print("Accy: ");
tft.println(y);
tft.print("Accz: ");
tft.println(z);
}
SpresenseBle.h
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bluetooth/ble_gatt.h>
#define BLE_UUID_SDS_SERVICE_IN 0x3802
#define BLE_UUID_SDS_CHAR_IN 0x4a02
#define BONDINFO_FILENAME "/mnt/spif/BONDINFO"
/****************************************************************************
Private Function Prototypes
****************************************************************************/
/* BLE common callbacks */
/* Connection status change */
static void onLeConnectStatusChanged(struct ble_state_s *ble_state,
bool connected, uint8_t reason);
/* Device name change */
static void onConnectedDeviceNameResp(const char *name);
/* Save bonding information */
static void onSaveBondInfo(int num, struct ble_bondinfo_s *bond);
/* Load bonding information */
static int onLoadBondInfo(int num, struct ble_bondinfo_s *bond);
/* Negotiated MTU size */
static void onMtuSize(uint16_t handle, uint16_t sz);
/* Encryption result */
static void onEncryptionResult(uint16_t, bool result);
/* BLE GATT callbacks */
/* Write request */
static void onWrite(struct ble_gatt_char_s *ble_gatt_char);
/* Read request */
static void onRead(struct ble_gatt_char_s *ble_gatt_char);
/* Notify request */
static void onNotify(struct ble_gatt_char_s *ble_gatt_char, bool enable);
/****************************************************************************
Private Data
****************************************************************************/
static struct ble_common_ops_s ble_common_ops = {
.connect_status_changed = onLeConnectStatusChanged,
.connected_device_name_resp = onConnectedDeviceNameResp,
.mtusize = onMtuSize,
.save_bondinfo = onSaveBondInfo,
.load_bondinfo = onLoadBondInfo,
.encryption_result = onEncryptionResult,
};
static struct ble_gatt_peripheral_ops_s ble_gatt_peripheral_ops = {
.write = onWrite,
.read = onRead,
.notify = onNotify
};
static BT_ADDR local_addr = { { 0x19, 0x84, 0x06, 0x14, 0xAB, 0xCD } };
static char local_ble_name[BT_NAME_LEN] = "SPR-PERIPHERAL";
static uint16_t ble_conn_handle;
static struct ble_gatt_service_s *g_ble_gatt_service;
static BLE_ATTR_PERM attr_param = {
.readPerm = BLE_SEC_MODE1LV2_NO_MITM_ENC,
.writePerm = BLE_SEC_MODE1LV2_NO_MITM_ENC
};
static uint8_t char_data[BLE_MAX_CHAR_SIZE];
static BLE_CHAR_VALUE char_value = {
.length = BLE_MAX_CHAR_SIZE
};
static BLE_CHAR_PROP char_property = {
.read = 1,
.write = 1,
.notify = 1
};
static struct ble_gatt_char_s g_ble_gatt_char = {
.handle = 0,
.ble_gatt_peripheral_ops = &ble_gatt_peripheral_ops
};
static int g_ble_bonded_device_num;
static struct ble_cccd_s **g_cccd = NULL;
static bool ble_is_notify_enabled = false;
/****************************************************************************
Private Functions
****************************************************************************/
static void onLeConnectStatusChanged(struct ble_state_s *ble_state,
bool connected, uint8_t reason) {
BT_ADDR addr = ble_state->bt_target_addr;
/* If receive connected status data, this function will call. */
printf("[BLE_GATT] Connect status ADDR:%02X:%02X:%02X:%02X:%02X:%02X, status:%s, reason:%d\n",
addr.address[5], addr.address[4], addr.address[3],
addr.address[2], addr.address[1], addr.address[0],
connected ? "Connected" : "Disconnected", reason);
ble_conn_handle = ble_state->ble_connect_handle;
}
static void onConnectedDeviceNameResp(const char *name) {
/* If receive connected device name data, this function will call. */
printf("%s [BLE] Receive connected device name = %s\n", __func__, name);
}
static void onSaveBondInfo(int num, struct ble_bondinfo_s *bond) {
int i;
FILE *fp;
int sz;
/* In this example, save the parameter `num` and each members of
the parameter `bond` in order to the file.
*/
fp = fopen(BONDINFO_FILENAME, "wb");
if (fp == NULL) {
printf("Error: could not create file %s\n", BONDINFO_FILENAME);
return;
}
fwrite(&num, 1, sizeof(int), fp);
for (i = 0; i < num; i++) {
fwrite(&bond[i], 1, sizeof(struct ble_bondinfo_s), fp);
/* Because only cccd is pointer member, save it individually. */
sz = bond[i].cccd_num * sizeof(struct ble_cccd_s);
fwrite(bond[i].cccd, 1, sz, fp);
}
fclose(fp);
}
static int onLoadBondInfo(int num, struct ble_bondinfo_s *bond) {
int i;
FILE *fp;
int stored_num;
int sz;
size_t ret;
fp = fopen(BONDINFO_FILENAME, "rb");
if (fp == NULL) {
return 0;
}
ret = fread(&stored_num, 1, sizeof(int), fp);
if (ret != sizeof(int)) {
printf("Error: could not load due to %s read error.\n",
BONDINFO_FILENAME);
fclose(fp);
return 0;
}
g_ble_bonded_device_num = (stored_num < num) ? stored_num : num;
sz = g_ble_bonded_device_num * sizeof(struct ble_cccd_s *);
g_cccd = (struct ble_cccd_s **)malloc(sz);
if (g_cccd == NULL) {
printf("Error: could not load due to malloc error.\n");
g_ble_bonded_device_num = 0;
}
for (i = 0; i < g_ble_bonded_device_num; i++) {
ret = fread(&bond[i], 1, sizeof(struct ble_bondinfo_s), fp);
if (ret != sizeof(struct ble_bondinfo_s)) {
printf("Error: could not load all data due to %s read error.\n",
BONDINFO_FILENAME);
printf("The number of loaded device is %d\n", i);
g_ble_bonded_device_num = i;
break;
}
if (bond[i].cccd_num > 1) {
printf("Error: could not load all data due to invalid data.\n");
printf("cccd_num does not exceed the number of characteristics\n");
printf("that is set by this application.\n");
printf("The number of loaded device is %d\n", i);
g_ble_bonded_device_num = i;
break;
}
/* Because only cccd is pointer member, load it individually. */
sz = bond[i].cccd_num * sizeof(struct ble_cccd_s);
g_cccd[i] = (struct ble_cccd_s *)malloc(sz);
if (g_cccd[i] == NULL) {
printf("Error: could not load all data due to malloc error.");
printf("The number of loaded device is %d\n", i);
g_ble_bonded_device_num = i;
break;
}
bond[i].cccd = g_cccd[i];
ret = fread(bond[i].cccd, 1, sz, fp);
if (ret != sz) {
printf("Error: could not load all data due to %s read error.\n",
BONDINFO_FILENAME);
printf("The number of loaded device is %d\n", i);
g_ble_bonded_device_num = i;
break;
}
}
fclose(fp);
return g_ble_bonded_device_num;
}
static void free_cccd(void) {
int i;
if (g_cccd) {
for (i = 0; i < g_ble_bonded_device_num; i++) {
if (g_cccd[i]) {
free(g_cccd[i]);
}
}
free(g_cccd);
g_cccd = NULL;
}
}
static void onMtuSize(uint16_t handle, uint16_t sz) {
printf("negotiated MTU size(connection handle = %d) : %d\n", handle, sz);
}
static void onEncryptionResult(uint16_t handle, bool result) {
printf("Encryption result(connection handle = %d) : %s\n",
handle, (result) ? "Success" : "Fail");
}
static void show_uuid(BLE_UUID *uuid) {
int i;
printf("uuid : ");
switch (uuid->type) {
case BLE_UUID_TYPE_UUID128:
/* UUID format YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY */
for (i = 0; i < BT_UUID128_LEN; i++) {
printf("%02x", uuid->value.uuid128.uuid128[BT_UUID128_LEN - i - 1]);
if ((i == 3) || (i == 5) || (i == 7) || (i == 9)) {
printf("-");
}
}
printf("\n");
break;
case BLE_UUID_TYPE_BASEALIAS_BTSIG:
case BLE_UUID_TYPE_BASEALIAS_VENDOR:
/* UUID format XXXX */
printf("%04x\n", uuid->value.alias.uuidAlias);
break;
default:
printf("Irregular UUID type.\n");
break;
}
}
static void onWrite(struct ble_gatt_char_s *ble_gatt_char) {
int i;
/* If receive connected device name data, this function will call. */
printf("%s [BLE] start\n", __func__);
printf("handle : %d\n", ble_gatt_char->handle);
show_uuid(&ble_gatt_char->uuid);
printf("value_len : %d\n", ble_gatt_char->value.length);
printf("value : ");
for (i = 0; i < ble_gatt_char->value.length; i++) {
printf("%02x ", ble_gatt_char->value.data[i]);
}
digitalWrite(ble_gatt_char->value.data[0], HIGH);
delay(150);
digitalWrite(ble_gatt_char->value.data[0], LOW);
printf("\n");
printf("%s [BLE] end\n", __func__);
}
static void onRead(struct ble_gatt_char_s *ble_gatt_char) {
/* If receive connected device name data, this function will call. */
printf("%s [BLE] \n", __func__);
}
static void onNotify(struct ble_gatt_char_s *ble_gatt_char, bool enable) {
/* If receive connected device name data, this function will call. */
printf("%s [BLE] start \n", __func__);
printf("handle : %d\n", ble_gatt_char->handle);
show_uuid(&ble_gatt_char->uuid);
if (enable) {
printf("notification enabled\n");
ble_is_notify_enabled = true;
} else {
printf("notification disabled\n");
ble_is_notify_enabled = false;
}
printf("%s [BLE] end \n", __func__);
}
/* Do not used now */
static void ble_peripheral_exit(void) {
int ret;
/* Turn OFF BT */
ret = bt_disable();
if (ret != BT_SUCCESS) {
printf("%s [BT] BT disable failed. ret = %d\n", __func__, ret);
}
/* Finalize BT */
ret = bt_finalize();
if (ret != BT_SUCCESS) {
printf("%s [BT] BT finalize failed. ret = %d\n", __func__, ret);
}
}
void BleInitialize() {
//printf("setup00\n");
int ret = 0;
BLE_UUID *s_uuid;
BLE_UUID *c_uuid;
/* Initialize BT HAL */
ret = bt_init();
if (ret != BT_SUCCESS) {
printf("%s [BT] Initialization failed. ret = %d\n", __func__, ret);
goto error;
}
//printf("setup01\n");
/* Register BLE common callbacks */
ret = ble_register_common_cb(&ble_common_ops);
if (ret != BT_SUCCESS) {
printf("%s [BLE] Register common call back failed. ret = %d\n", __func__, ret);
goto error;
}
/* Turn ON BT */
// printf("setup02\n");
ret = bt_enable();
if (ret != BT_SUCCESS) {
printf("%s [BT] Enabling failed. ret = %d\n", __func__, ret);
goto error;
}
/* Free memory that is allocated in onLoadBond() callback function. */
free_cccd();
/* BLE set name */
//printf("setup03\n");
ret = ble_set_name(local_ble_name);
if (ret != BT_SUCCESS) {
printf("%s [BLE] Set name failed. ret = %d\n", __func__, ret);
goto error;
}
/* BLE set address */
//printf("setup04\n");
ret = ble_set_address(&local_addr);
if (ret != BT_SUCCESS) {
printf("%s [BLE] Set address failed. ret = %d\n", __func__, ret);
goto error;
}
/* BLE enable */
//printf("setup05\n");
ret = ble_enable();
if (ret != BT_SUCCESS) {
printf("%s [BLE] Enable failed. ret = %d\n", __func__, ret);
goto error;
}
/* BLE create GATT service instance */
ret = ble_create_service(&g_ble_gatt_service);
if (ret != BT_SUCCESS) {
printf("%s [BLE] Create GATT service failed. ret = %d\n", __func__, ret);
goto error;
}
//printf("setup06\n");
/* Setup Service */
/* Get Service UUID pointer */
s_uuid = &g_ble_gatt_service->uuid;
/* Setup Service UUID */
s_uuid->type = BLE_UUID_TYPE_BASEALIAS_BTSIG;
s_uuid->value.alias.uuidAlias = BLE_UUID_SDS_SERVICE_IN;
/* Setup Characteristic */
//printf("setup07\n");
/* Get Characteristic UUID pointer */
c_uuid = &g_ble_gatt_char.uuid;
/* Setup Characteristic UUID */
//printf("setup08\n");
c_uuid->type = BLE_UUID_TYPE_BASEALIAS_BTSIG;
c_uuid->value.alias.uuidAlias = BLE_UUID_SDS_CHAR_IN;
/* Set data point */
//printf("setup09\n");
char_value.data = char_data;
/* Setup Characteristic BLE_ATTR_PERM */
memcpy(&char_value.attrPerm, &attr_param, sizeof(BLE_ATTR_PERM));
/* Setup Characteristic BLE_CHAR_VALUE */
memcpy(&g_ble_gatt_char.value, &char_value, sizeof(BLE_CHAR_VALUE));
/* Setup Characteristic BLE_CHAR_PROP */
memcpy(&g_ble_gatt_char.property, &char_property, sizeof(BLE_CHAR_PROP));
/* BLE add GATT characteristic into service */
ret = ble_add_characteristic(g_ble_gatt_service, &g_ble_gatt_char);
if (ret != BT_SUCCESS) {
printf("%s [BLE] Add GATT characteristic failed. ret = %d\n", __func__, ret);
goto error;
}
/* BLE register GATT service */
ret = ble_register_servce(g_ble_gatt_service);
if (ret != BT_SUCCESS) {
printf("%s [BLE] Register GATT service failed. ret = %d\n", __func__, ret);
goto error;
}
/* BLE start advertise */
ret = ble_start_advertise();
if (ret != BT_SUCCESS) {
printf("%s [BLE] Start advertise failed. ret = %d\n", __func__, ret);
goto error;
}
return ret;
error:
return ret;
}
SpresenseNeoPixel.h
#pragma once
#ifndef NEOPIXELSPRESENSE_H
#define NEOPIXELSPRESENSE_H
#include <Arduino.h>
#include "wiring_private.h"
template<uint8_t PIN, uint32_t N_PIXELS>
class SpresenseNeoPixel {
uint32_t reg;
uint32_t reg_val;
uint8_t pixels[N_PIXELS * 3];
uint32_t interval_us{ 25000 }; // 40fps
uint32_t prev_us{ 0 };
float scale{ 1.f };
const uint32_t n_wait_cycles_t0h_t1l;
const uint32_t n_wait_cycles_t1h_t0l;
const uint32_t n_wait_cycles_reset;
public:
SpresenseNeoPixel()
: n_wait_cycles_t0h_t1l(1), n_wait_cycles_t1h_t0l(28), n_wait_cycles_reset(1) {
pinMode(PIN, OUTPUT);
digitalWrite(PIN, LOW);
analog_stop(PIN);
reg = get_gpio_regaddr(pin_convert(PIN));
reg_val = *(volatile uint32_t*)reg;
clear();
}
inline void show() {
uint32_t curr_us = micros();
if (curr_us >= prev_us + interval_us) {
noInterrupts();
for (uint32_t pixel = 0; pixel < N_PIXELS; ++pixel) {
write(LOW);
wait_cycles(n_wait_cycles_reset);
for (uint32_t rgb = 0; rgb < 3; ++rgb) {
uint32_t i = pixel * 3 + rgb;
(pixels[i] & 0x80) ? one() : zero();
(pixels[i] & 0x40) ? one() : zero();
(pixels[i] & 0x20) ? one() : zero();
(pixels[i] & 0x10) ? one() : zero();
(pixels[i] & 0x08) ? one() : zero();
(pixels[i] & 0x04) ? one() : zero();
(pixels[i] & 0x02) ? one() : zero();
(pixels[i] & 0x01) ? one() : zero();
}
}
interrupts();
prev_us = curr_us;
}
}
inline void clear() {
memset(pixels, 0, N_PIXELS * 3);
}
inline void set(uint32_t n, uint8_t r, uint8_t g, uint8_t b) {
if (n < N_PIXELS) {
pixels[n * 3 + 0] = (uint8_t)((float)g * scale);
pixels[n * 3 + 1] = (uint8_t)((float)r * scale);
pixels[n * 3 + 2] = (uint8_t)((float)b * scale);
}
}
inline void set(uint8_t r, uint8_t g, uint8_t b) {
for (size_t i = 0; i < N_PIXELS; ++i) set(i, r, g, b);
}
inline void set(uint8_t brightness) {
set(brightness, brightness, brightness);
}
inline void brightness(uint8_t b) {
scale = (float)b / 255.f;
}
inline void framerate(float fps) {
interval_us = (uint32_t)(1000000.f / fps);
}
inline void interval(uint32_t us) {
interval_us = us;
}
private:
inline void zero() {
write(HIGH);
wait_cycles(n_wait_cycles_t0h_t1l);
write(LOW);
wait_cycles(n_wait_cycles_t1h_t0l);
}
inline void one() {
write(HIGH);
wait_cycles(n_wait_cycles_t1h_t0l);
write(LOW);
wait_cycles(n_wait_cycles_t0h_t1l);
}
inline void write(uint8_t value) {
bitWrite(reg_val, GPIO_OUTPUT_SHIFT, value);
*(volatile uint32_t*)reg = reg_val;
}
inline void wait_cycles(uint32_t n) // 4 clocks per cycle
{
asm volatile(
"0:"
"SUBS %[count], 1;"
"BNE 0b;"
: [count] "+r"(n));
}
};
#endif // NEOPIXELSPRESENSE_H
SpresenseSensor.h
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "BMI270_Arduino.h"
BMI270Class BMI270;
/* Other functions */
int8_t configure_sensor(struct bmi2_dev *dev);
void panic_led_trap(void);
void print_rslt(int8_t rslt);
/* Sensor configuration */
int8_t configure_sensor()
{
int8_t rslt;
uint8_t sens_list[1] = { BMI2_ACCEL };
struct bmi2_sens_config config;
/* Configure the type of feature. */
config.type = BMI2_ACCEL;
/* NOTE: The user can change the following configuration parameters according to their requirement. */
/* Set Output Data Rate */
config.cfg.acc.odr = BMI2_ACC_ODR_200HZ;
/* Gravity range of the sensor (+/- 2G, 4G, 8G, 16G). */
config.cfg.acc.range = BMI2_ACC_RANGE_2G;
/* The bandwidth parameter is used to configure the number of sensor samples that are averaged
* if it is set to 2, then 2^(bandwidth parameter) samples
* are averaged, resulting in 4 averaged samples.
* Note1 : For more information, refer the datasheet.
* Note2 : A higher number of averaged samples will result in a lower noise level of the signal, but
* this has an adverse effect on the power consumed.
*/
config.cfg.acc.bwp = BMI2_ACC_NORMAL_AVG4;
/* Enable the filter performance mode where averaging of samples
* will be done based on above set bandwidth and ODR.
* There are two modes
* 0 -> Ultra low power mode
* 1 -> High performance mode(Default)
* For more info refer datasheet.
*/
config.cfg.acc.filter_perf = BMI2_PERF_OPT_MODE;
/* Set the accel configurations. */
rslt = BMI270.set_sensor_config(&config, 1);
if (rslt != BMI2_OK) return rslt;
rslt = BMI270.sensor_enable(sens_list, 1);
if (rslt != BMI2_OK) return rslt;
return rslt;
}
void panic_led_trap(void)
{
while (1)
{
digitalWrite(LED_BUILTIN, LOW);
delay(100);
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
}
}
void print_rslt(int8_t rslt)
{
switch (rslt)
{
case BMI2_OK: return; /* Do nothing */ break;
case BMI2_E_NULL_PTR:
Serial.println("Error [" + String(rslt) + "] : Null pointer");
panic_led_trap();
break;
case BMI2_E_COM_FAIL:
Serial.println("Error [" + String(rslt) + "] : Communication failure");
panic_led_trap();
break;
case BMI2_E_DEV_NOT_FOUND:
Serial.println("Error [" + String(rslt) + "] : Device not found");
panic_led_trap();
break;
case BMI2_E_OUT_OF_RANGE:
Serial.println("Error [" + String(rslt) + "] : Out of range");
panic_led_trap();
break;
case BMI2_E_ACC_INVALID_CFG:
Serial.println("Error [" + String(rslt) + "] : Invalid accel configuration");
panic_led_trap();
break;
case BMI2_E_GYRO_INVALID_CFG:
Serial.println("Error [" + String(rslt) + "] : Invalid gyro configuration");
panic_led_trap();
break;
case BMI2_E_ACC_GYR_INVALID_CFG:
Serial.println("Error [" + String(rslt) + "] : Invalid accel/gyro configuration");
panic_led_trap();
break;
case BMI2_E_INVALID_SENSOR:
Serial.println("Error [" + String(rslt) + "] : Invalid sensor");
panic_led_trap();
break;
case BMI2_E_CONFIG_LOAD:
Serial.println("Error [" + String(rslt) + "] : Configuration loading error");
panic_led_trap();
break;
case BMI2_E_INVALID_PAGE:
Serial.println("Error [" + String(rslt) + "] : Invalid page ");
panic_led_trap();
break;
case BMI2_E_INVALID_FEAT_BIT:
Serial.println("Error [" + String(rslt) + "] : Invalid feature bit");
panic_led_trap();
break;
case BMI2_E_INVALID_INT_PIN:
Serial.println("Error [" + String(rslt) + "] : Invalid interrupt pin");
panic_led_trap();
break;
case BMI2_E_SET_APS_FAIL:
Serial.println("Error [" + String(rslt) + "] : Setting advanced power mode failed");
panic_led_trap();
break;
case BMI2_E_AUX_INVALID_CFG:
Serial.println("Error [" + String(rslt) + "] : Invalid auxilliary configuration");
panic_led_trap();
break;
case BMI2_E_AUX_BUSY:
Serial.println("Error [" + String(rslt) + "] : Auxilliary busy");
panic_led_trap();
break;
case BMI2_E_SELF_TEST_FAIL:
Serial.println("Error [" + String(rslt) + "] : Self test failed");
panic_led_trap();
break;
case BMI2_E_REMAP_ERROR:
Serial.println("Error [" + String(rslt) + "] : Remapping error");
panic_led_trap();
break;
case BMI2_E_GYR_USER_GAIN_UPD_FAIL:
Serial.println("Error [" + String(rslt) + "] : Gyro user gain update failed");
panic_led_trap();
break;
case BMI2_E_SELF_TEST_NOT_DONE:
Serial.println("Error [" + String(rslt) + "] : Self test not done");
panic_led_trap();
break;
case BMI2_E_INVALID_INPUT:
Serial.println("Error [" + String(rslt) + "] : Invalid input");
panic_led_trap();
break;
case BMI2_E_INVALID_STATUS:
Serial.println("Error [" + String(rslt) + "] : Invalid status");
panic_led_trap();
break;
case BMI2_E_CRT_ERROR:
Serial.println("Error [" + String(rslt) + "] : CRT error");
panic_led_trap();
break;
case BMI2_E_ST_ALREADY_RUNNING:
Serial.println("Error [" + String(rslt) + "] : Self test already running");
panic_led_trap();
break;
case BMI2_E_CRT_READY_FOR_DL_FAIL_ABORT:
Serial.println("Error [" + String(rslt) + "] : CRT ready for DL fail abort");
panic_led_trap();
break;
case BMI2_E_DL_ERROR:
Serial.println("Error [" + String(rslt) + "] : DL error");
panic_led_trap();
break;
case BMI2_E_PRECON_ERROR:
Serial.println("Error [" + String(rslt) + "] : PRECON error");
panic_led_trap();
break;
case BMI2_E_ABORT_ERROR:
Serial.println("Error [" + String(rslt) + "] : Abort error");
panic_led_trap();
break;
case BMI2_E_GYRO_SELF_TEST_ERROR:
Serial.println("Error [" + String(rslt) + "] : Gyro self test error");
panic_led_trap();
break;
case BMI2_E_GYRO_SELF_TEST_TIMEOUT:
Serial.println("Error [" + String(rslt) + "] : Gyro self test timeout");
panic_led_trap();
break;
case BMI2_E_WRITE_CYCLE_ONGOING:
Serial.println("Error [" + String(rslt) + "] : Write cycle ongoing");
panic_led_trap();
break;
case BMI2_E_WRITE_CYCLE_TIMEOUT:
Serial.println("Error [" + String(rslt) + "] : Write cycle timeout");
panic_led_trap();
break;
case BMI2_E_ST_NOT_RUNING:
Serial.println("Error [" + String(rslt) + "] : Self test not running");
panic_led_trap();
break;
case BMI2_E_DATA_RDY_INT_FAILED:
Serial.println("Error [" + String(rslt) + "] : Data ready interrupt failed");
panic_led_trap();
break;
case BMI2_E_INVALID_FOC_POSITION:
Serial.println("Error [" + String(rslt) + "] : Invalid FOC position");
panic_led_trap();
break;
default:
Serial.println("Error [" + String(rslt) + "] : Unknown error code");
panic_led_trap();
break;
}
}
WEBアプリソースコード
WEBアプリソースコードは、BLE転送による加速度計からのデータを、Windowsパソコンで動作しているブラウザで受け取って表示します。BLE転送による表示には、「Web Bluetooth API 」を使用します。
- WEBアプリを起動すると次の画面が表示されます。
- 「Connect」ボタンを押すと次のダイアログが表示され、対応する「Spresense」を選択します。
index.html.h
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css">
<title>Spresense BLE Interface</title>
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-10 pt-2 mb-2">
<h3>Spresense BLE Interface</h3>
<div id="error">Error : </div>
</div>
</div>
<div class="row justify-content-center bg-light">
<div class="col-6 pt-2">
<div id="device_name">Device Name : </div>
<div id="connect_status">Status : Disconnect</div>
<div class="mt-2">
<button type="button" class="btn btn-secondary scanBtn">Connect</button>
<button type="button" class="btn btn-secondary resetBtn">Disconnect</button>
</div>
</div>
<div class="col-6 pt-2">
<div id="read_data">加速度 :</div>
<div class="col-12 pt-2">
<button type="button" class="btn btn-secondary notifyBtn">Notify Start</button>
</div>
<div class="col-12 pt-2 mb-2">
<div>
<button type="button" class="btn btn-secondary commandBtn1">LED0</button>
<button type="button" class="btn btn-secondary commandBtn2">LED1</button>
<button type="button" class="btn btn-secondary commandBtn3">LED2</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="module" src="./Spresense.js"></script>
</body>
</html>
Spresense.js
import spresense_ble from './Spresense_ble.js';
let Spresense_ble = new spresense_ble();
/*/////////////////////////////////
console.log("float test05");
function toFloat(v) {
var a = new ArrayBuffer(4),
b = new Uint32Array(a),
f = new Float32Array(a);
b[0] = v;
return f[0];
}
console.log(toFloat(0x419A0000)); // -> 19.25
console.log(toFloat(0x41FC51EC)); // -> 31.54
console.log(toFloat(0x429DEB85)); // -> 78.96
////////////////////////////////*/
//Connectボタンの処理
const scanBtn = document.querySelector('.scanBtn');
scanBtn.addEventListener('click', function (clickEvent) {
//this.EGRequest = 0; //暫定的に記載
Spresense_ble.Blecan();
})
//コマンド書き込み
const onBtn1 = document.querySelector('.commandBtn1');
onBtn1.addEventListener('click', function (clickEvent) {
//セットしたコマンドを送信
Spresense_ble.Write(Spresense_ble.Custom3_Characteristic_UUID, Spresense_ble.Command1);
})
const onBtn2 = document.querySelector('.commandBtn2');
onBtn2.addEventListener('click', function (clickEvent) {
//セットしたコマンドを送信
Spresense_ble.Write(Spresense_ble.Custom3_Characteristic_UUID, Spresense_ble.Command2);
})
const onBtn3 = document.querySelector('.commandBtn3');
onBtn3.addEventListener('click', function (clickEvent) {
//セットしたコマンドを送信
Spresense_ble.Write(Spresense_ble.Custom3_Characteristic_UUID, Spresense_ble.Command3);
})
//データ通知
const notifyBtn = document.querySelector('.notifyBtn');
notifyBtn.addEventListener('click', function (clickEvent) {
Spresense_ble.StartNotify(Spresense_ble.Custom1_Characteristic_UUID);
})
//Disconnectボタンの処理
const resetBtn = document.querySelector('.resetBtn');
resetBtn.addEventListener('click', function (clickEvent) {
Spresense_ble.Reset();
})
//HTMLに値を表示
Spresense_ble.onScan = function (deviceName) {
document.getElementById('device_name').innerHTML = 'Device Name : ' + deviceName;
document.getElementById('error').innerHTML = "Error : ";
}
Spresense_ble.onConnectGATT = function () {
document.getElementById('connect_status').innerHTML = 'Status : Connected';
document.getElementById('error').innerHTML = "Error : ";
};
Spresense_ble.onWrite = function () {
document.getElementById('connect_status').innerHTML = 'Status : Sended command'
document.getElementById('error').innerHTML = "Error : ";
}
Spresense_ble.onData = function (data) {
document.getElementById('read_data').innerHTML = '加速度 : ' + data;
document.getElementById('error').innerHTML = "Error : ";
}
Spresense_ble.onError = function (error) {
document.getElementById('error').innerHTML = 'Error : ' + error;
}
Spresense_ble.js
export default class {
//constructor
constructor() {
this.device = null;
this.server = null;
this.service = null;
this.Commanduuid = null;
this.Readuuid = null;
this.Notifyuuid = null;
this.Service1_UUID = "00003802-0000-1000-8000-00805f9b34fb";
this.Custom1_Characteristic_UUID = "00004a02-0000-1000-8000-00805f9b34fb";
this.Custom3_Characteristic_UUID = "00004a02-0000-1000-8000-00805f9b34fb";
// モーション(動き)検知 100ms間隔(モーション系センサのみ)
this.Command1 = [[64]];
this.Command2 = [[65]];
this.Command3 = [[66]];
this.alpsoptions = {
acceptAllDevices: true,
optionalServices: [this.Service1_UUID]
};
}
async Blecan() {
try {
console.log('Requesting Bluetooth Device...');
this.device = await navigator.bluetooth.requestDevice(this.alpsoptions);
this.onScan(this.device.name);
this.onConnectGATT();
this.server = await this.device.gatt.connect();
console.log('Getting GAP Service...');
this.service = await this.server.getPrimaryService(this.Service1_UUID);
console.log('Got Service');
} catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
async Write(uuid, moveCommand) {
try {
console.log('Execute : Write');
this.Commanduuid = await this.service.getCharacteristic(uuid);
//this.Readuuid = await this.service.getCharacteristic(this.Custom1_Characteristic_UUID);
for (let i = 0; i < moveCommand.length; i++) {
//console.log(moveCommand[i]);
let uint8array = new Uint8Array(moveCommand[i].length);
for (let Offset = 0; Offset < moveCommand[i].length; Offset++) {
uint8array[Offset] = moveCommand[i][Offset];
}
//console.log(uint8array);
await this.Commanduuid.writeValue(uint8array, true);
}
this.onWrite();
}
catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
toFloat(v) {
var a = new ArrayBuffer(4),
b = new Uint32Array(a),
f = new Float32Array(a);
b[0] = v;
return f[0];
}
DataChanged(event) {
try {
console.log('Execute8 : DataChanged');
let value = event.target.value;
const data = new ArrayBuffer(4);
let accx = 0;
let accy = 0;
let accz = 0;
for (let k = 0; k < 4; k++) {
accx += value.getUint8(k, false) << (8 * k);
}
for (let k = 0; k < 4; k++) {
accy += value.getUint8(k + 4, false) << (8 * k);
}
for (let k = 0; k < 4; k++) {
accz += value.getUint8(k + 8, false) << (8 * k);
}
/*
console.log(accx.toString(16));
console.log(this.toFloat(accx)); // -> 19.25
console.log(this.toFloat(0x419A0000)); // -> 19.25
console.log(this.toFloat(0x41FC51EC)); // -> 31.54
console.log(this.toFloat(0x429DEB85)); // -> 78.96
*/
this.onData("x=" + this.toFloat(accx).toFixed(2) +
" y=" + this.toFloat(accy).toFixed(2) +
" z=" + this.toFloat(accz).toFixed(2)
);
}
catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
async StartNotify(uuid) {
try {
console.log('Execute : StartNotify');
this.Notifyuuid = await this.service.getCharacteristic(uuid);
this.Notifyuuid.addEventListener('characteristicvaluechanged', this.DataChanged.bind(this));
await this.Notifyuuid.startNotifications();
}
catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
async StopNotify() {
try {
console.log('Execute : StopNotify');
await this.Notifyuuid.stopNotifications();
}
catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
async Disconnect() {
if (!this.device) {
var error = "No Bluetooth Device";
console.log('Error : ' + error);
this.onError(error);
return;
}
if (this.device.gatt.connected) {
console.log('Execute : disconnect');
this.isNotify = false;
this.device.gatt.disconnect();
} else {
var error = "Bluetooth Device is already disconnected";
console.log('Error : ' + error);
this.onError(error);
return;
}
}
Clear() {
console.log('Excute : Clear Device and Characteristic');
this.device = null;
}
//reset
Reset() {
console.log('Excute : reset');
this.Disconnect(); //GNDisconnect() is not Promise Object
this.Clear();
}
}
投稿者の人気記事
-
kati
さんが
2024/01/22
に
編集
をしました。
(メッセージ: 初版)
-
kati
さんが
2024/01/22
に
編集
をしました。
-
kati
さんが
2024/01/22
に
編集
をしました。
-
kati
さんが
2024/01/22
に
編集
をしました。
-
kati
さんが
2024/03/29
に
編集
をしました。
ログインしてコメントを投稿する