編集履歴一覧に戻る
katiのアイコン画像

kati が 2024年01月22日05時54分43秒 に編集

コメント無し

本文の変更

# デモ動画

-

@[youtube](https://youtu.be/9LUNOc7TMnY)

+

@[youtube](https://https://youtu.be/pAzD3J1ZEbI)

# 部品 使用するハードウェアを次に示します。 | 部品名 | 個数| |:---:|:---| | 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を用いて可視化します。 「動き検知器」は、次に示すハードウェアにより構成されます。 ![キャプションを入力できます](https://camo.elchika.com/88537d7a02ac6e4ec55651f9422e6eed83beaf3a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f32376166663263352d333862312d343363632d386666362d6634376364396530343432352f62333935306561652d646238372d343330332d616262332d663139356363613736323234/) 「動き検知器」は、次のようにして開発しました。 - 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をそれぞれ次のように接続します。 ![キャプションを入力できます](https://camo.elchika.com/ba12d97d7835b0c3e632096c0f63cdcfbf584343/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f32376166663263352d333862312d343363632d386666362d6634376364396530343432352f32366637396262612d383461632d343030302d396263612d613837613664313566373138/) ## ハードウェア 液晶モジュールの背面からSpresense拡張ボードにSPIインタフェースにより接続します。Spresense拡張ボードに接続された加速度計が動くと、そのデータ値を液晶モジュールに表示し、同時にBLEによりWindowsパソコン上で動作しているブラウザに表示します。また加速度計の動きによって、カラーテープLEDの発行色が変化します。 ![キャプションを入力できます](https://camo.elchika.com/75fa059cf92bd92acdd826494b59cb9f0a72e147/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f32376166663263352d333862312d343363632d386666362d6634376364396530343432352f37646131633033632d373465372d343966312d613034612d663166313262336361663536/) # ソースコード プログラム「動き検知器」は、Spresenseメインボード上で動作するコード「 Arduinoソースコード」には、Arduino IDEを使用し、Windowsパソコン上のブラウザで動作するコード「 WEBアプリソースコード」には、テキストエディタを使ってコードを開発します。 - Arduinoソースコード - WEBアプリソースコード ## Arduinoソースコード Arduinoソースコードは、加速度計からのデータを液晶モジュールとカラーテープLEDに引き渡し、加速度計からのデータをBLE転送します。 - 「BLE1507」を用いたBLE転送については「[Spresense-Playground](https://github.com/TomonobuHayakawa/Spresense-Playground)」を参照しました。 - 「SK6812使用マイコン内蔵フルカラーテープLED」を用いたカラーテープLEDの表示については「[SpresenseNeoPixel](https://github.com/hideakitai/SpresenseNeoPixel)」を参照しました。 ```c: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); } ``` ```c: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; } ``` ```c: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 ``` ```c: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 ](https://developer.mozilla.org/ja/docs/Web/API/Web_Bluetooth_API)」を使用します。 - WEBアプリを起動すると次の画面が表示されます。 ![キャプションを入力できます](https://camo.elchika.com/cfa4b0c4cee167d9f1159f78a22fd2e55ef74c8c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f32376166663263352d333862312d343363632d386666362d6634376364396530343432352f66306239313537632d366233322d343662392d383134632d633562306639313461373835/) - 「Connect」ボタンを押すと次のダイアログが表示され、対応する「Spresense」を選択します。 ![キャプションを入力できます](https://camo.elchika.com/7eb8edeaa21313a09cc8fad8244c035e5b45187c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f32376166663263352d333862312d343363632d386666362d6634376364396530343432352f62646665623464392d313264342d346439362d616437312d616362396563323131343361/) ```c: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> ``` ```c: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; } ``` ```c: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(); } } ```