
Meetrip
テーマ:「旅」とは?
旅というものは、心と体、さらには人生全体に大きな豊かさをもたらしてくれる、かけがえのない体験だと感じています。
新しい景色を目にすることで感動が生まれ、旅先でのさまざまな体験や人との出会いを通じて、自分自身が大きく成長できる。
また、これかの経験が未来への糧となり、日常生活にも新しい活力や視点をもたらしてくれるものだと思います。
そんな旅の魅力を改めて感じながら、「旅」をテーマにした企画を考えました。
コンセプト:偶然の出会い
旅における「偶然の出会い」は、まるで魔法のように私たちの心深い影響を与えます。
それは、旅が単なる移動ではなく、心と人生を豊かにする、特別な体験へと変化する力を持っているからです。
計画通りに進める旅も魅力的ですが、予測できない出会いや出来事があることで、その旅は他の誰とも違う、完全に自分だけのかけがえのない体験となります。
こうした旅での偶然の出会いを通して、普段では触れられない異なる文化や考え方、人々と出会い、それが自分の視野を広げ、新たな価値観や気づきを生む大きなきっかけとなります。
このサービスでは、旅の「偶然の出会い」をテーマにし、旅後に自分自身と向き合わせる機会を提供します。
・自分が本当に大切にしていることは何か?
・何に心が動かされるのか?
・自分らしい生き方とは何か?
こうした問いを通して、自分の価値観や目標を再認識し、
自己成長や新たな人生の目標を発見できるようなサービスを作成しました。
イメージ:「旅」×「星空」
このサービスでは、旅の中での出会いを「星空の下で振り返る」というイメージで表現します。
星空と振り返りは深い関係があると感じるからです。
星空を見上げる瞬間、私たちは過去の出来事や自分自身を静かに見つめ直すことができ、その時間は非常に特別なものとなります。
広大な宇宙の中で自分の存在が小さく感じられる一方で、その一瞬一瞬がいかいに大切で貴重であるかを実感するのです。
つまり、星空は「内省」の時間をもたらし、心を解き放つことで、過去を整理し、新しい一歩を踏み出す準備を整える大切な機会を与えてくれます。
このように、「旅と星空」を結びつけて、心の整理と未来への一歩をサポートする内容をご提案します。
ターゲットユーザー
サービスを利用する主なターゲット層は下記です。
- まとまった休みで旅をするのが趣味の人
- 旅を通じて前向きな人生を送りたい人
サービス名
「Meetrip」
「Meetrip」の主な機能
サービスの主な機能は下記3つです。
①同じデバイスを持っている人同士がすれ違ったときに、相手の情報を取得することができる。
②デバイスを握ったときに、場所と時刻、圧力(握った強さ)を取得することができる。
③取得した情報(①・②)を星空で見ることができる。
サービスの構成
Meetripサービスは、下記の2つのデバイスで構成されています。
②「Reflection Space」
星空を見ることができるアプリです。
サービスのUI
①初期画面
→ボタン(自分のデバイスとBluetooth通信を行う)
→タップで遷移。
②星空画面
→パノラマで見ることができます。1画面で完結。四季ごとに分かれており、代表的な星座を表示。
<星の表現データ>
・圧力によって色が設定される。
取得データ | 星の表現 |
---|---|
圧力 | 光の強さ、大きさ、色 |
座標 | 位置(それぞれの距離で座標が作られる) |
年月日時分秒 | 位置(アルゴリズムで分けられる) |
サービスフロー
②自分自身の感情が動いたときに思いを込める(握る)
※Buddyを握ることでその時の感情の変化や出会いを記録できる。
握ったときの圧力 = 色
③Buddyを持っている人同士がすれ違うと光る
※相手の最後に設定した色(圧力)を受け取り、その色で光る。
④画面には星が四季ごとに分かれており、代表的な星座が表示され、星空の下で旅を振り返ることができる。
パノラマビューで楽しむことができる。
Buddyの構成
SPRESENSE本体にBluetooth通信用のBLE1507を取り付けています。
圧力センサーは、メインボードの1.8Vのピンから電力を供給し、A2ピンをINPUT(入力)モードにしました。
発光ダイオードはD22をOUTPUT(出力)モードにしてGNDと繋げています(ここに圧力センサーのケーブルも繋がっています)。
部品
名前 | 数量 | 備考 |
---|---|---|
SPRESENSE メインボード | 1 | (switch science)[https://www.switch-science.com/products/3900] |
20g-10kg薄膜圧力センサー | 1 | 秋月電子[https://akizukidenshi.com/catalog/g/g104158/] |
TNTOR モバイルバッテリー 軽量 超小型 持ち運び便利 5000mAh | 1 | https://x.gd/uGDAb |
[VOL-24]BLE1507 (BLE for Spresense) | 1 | クレイン電子[https://crane-elec.co.jp/products/vol-24/] |
サービスイメージ
データフロー
デバイスを握ると圧力センサーが反応し、値を読み取ります。同時に衛星ともBluetooth通信を行い、時刻と位置情報を取得します。これらのデータはまとめて配列に保存しています。
握る動作と値ですが、「2秒以上かつ閾値を超えているもの」で、「押してから離すまでの間で最も大きい値」を取得するようにしています。
このデバイスは常に自分のデバイス情報を発信し続けており、また常に取得状態になっています。そのBluetooth信号が受け取れる位置に来たときにお互いに送信・受信します。もらうデータは、相手のデバイスIDと、直近に相手が保存した圧力です。この時も衛星通信を行い、時刻と位置情報を取得して配列に保存します。
SPRESENSEに保存されたデータはスマホを通してBuddyに保存されます。星空を表示するには、ralabelAPIにてデバイスの固有IDで検索をかけて取得しています。
コード
SPRESENS本体のプログラム
//圧力(GPS)
#include <GNSS.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "BLE1507.h"
//定数宣言
#define UUID_SERVICE 0x3802
#define UUID_CHAR 0x4a02
#define STRING_BUFFER_SIZE 128 /**< %Buffer size */
#define RESTART_CYCLE (60 * 5) /**< positioning test term */
#define PRESSURE_ARRAY_SIZE 1000 // 配列サイズを1000に設定
static SpGnss Gnss; /**< SpGnss object */
static BT_ADDR addr = {{0x19, 0x84, 0x06, 0x14, 0xAB, 0xCD}};
static char ble_name[BT_NAME_LEN] = "SPR-PERIPHERAL";
BLE1507 *ble1507;
/**
衛星の種類。
* @enum ParamSat
* @brief Satellite system
*/
enum ParamSat {
eSatGps, /**< GPS World wide coverage */
eSatGlonass, /**< GLONASS World wide coverage */
eSatGpsSbas, /**< GPS+SBAS North America */
eSatGpsGlonass, /**< GPS+Glonass World wide coverage */
eSatGpsBeidou, /**< GPS+BeiDou World wide coverage */
eSatGpsGalileo, /**< GPS+Galileo World wide coverage */
eSatGpsQz1c, /**< GPS+QZSS_L1CA East Asia & Oceania */
eSatGpsGlonassQz1c, /**< GPS+Glonass+QZSS_L1CA East Asia & Oceania */
eSatGpsBeidouQz1c, /**< GPS+BeiDou+QZSS_L1CA East Asia & Oceania */
eSatGpsGalileoQz1c, /**< GPS+Galileo+QZSS_L1CA East Asia & Oceania */
eSatGpsQz1cQz1S, /**< GPS+QZSS_L1CA+QZSS_L1S Japan */
};
/* Set this parameter depending on your current region. */
static enum ParamSat satType = eSatGlonass;
//構造体
struct DateTime {
int year;
int month;
int date;
int hour;
int minute;
int second;
};
//nowという名前で(構造体の)dateTime型。
static DateTime now;
//圧力配列の構造体
struct pressure
{
int prss; //圧力
DateTime dt; //日付
};
static pressure pressures[PRESSURE_ARRAY_SIZE]; //1000個の配列作成
/****************************************************************************
*圧力データを配列に格納する関数
****************************************************************************/
void logPressureData(int prss) {
Serial.println(prss);
static int index = 0; // 配列のインデックス
static unsigned long pressStartTime = 0; // 握り始めた時刻
static int maxPrss = 0; // 最大圧力値
static bool isPressed = false; // 押しているかどうか
// 圧力センサーがしきい値より小さい場合(押していない場合)
if (prss <= 200) {
if (isPressed) {
// 握り終わった時(2秒以上握っていたら最大値を保存)
if (millis() - pressStartTime >= 2000) {
//LEDを消す
digitalWrite(PIN_D22, LOW);
// 最大圧力値を保存
pressures[index].prss = maxPrss;
pressures[index].dt = now; // 現在時刻を格納
Serial.print("Saved Pressure: ");
Serial.println(maxPrss);
index++;
if (index >= PRESSURE_ARRAY_SIZE) {
index = 0;
}
}
// リセット
isPressed = false;
maxPrss = 0;
}
} else {
// 圧力が200以上(押し始めた)
if (!isPressed) {
digitalWrite(PIN_D22, HIGH);
pressStartTime = millis(); // 握り始めた時刻を記録
maxPrss = prss; // 最大圧力を初期化
isPressed = true; // 握っている状態にする
} else {
// 握り続けているとき、最大圧力を更新
if (prss > maxPrss) {
maxPrss = prss;
}
}
}
}
/****************************************************************************
* 命令をもらって処理を行う
スマホと通信する
****************************************************************************/
bool needNotify = false;
void bleWriteCB(struct ble_gatt_char_s *ble_gatt_char) {
printf("write CB start...");
if (ble_gatt_char->value.length == 0) return;
if (ble_gatt_char->value.data[0] == 1) {
printf("write_callback!");
needNotify = true;
}
}
/****************************************************************************
* スマホに配列データを表示し、ずっとloopする
****************************************************************************/
void notifyLoop() {
printf("raise notify to the central : \n");
static uint8_t data = 0;
uint8_t str_data[20] = {0};
//for文でどんどん送信する
for(int i = 0; i < PRESSURE_ARRAY_SIZE; i++) {
if (pressures[i].prss != 0) {
sprintf((char*)str_data, "%03d,%03d,%02d%02d%02d%02d%02d%02d", i, pressures[i].prss, pressures[i].dt.year % 100, pressures[i].dt.month, pressures[i].dt.date, pressures[i].dt.hour, pressures[i].dt.minute, pressures[i].dt.second);
// sprintf((char*)str_data, "%03d", data++);
// data = data%100;
ble1507->writeNotify(str_data, 20); //20バイト分送信
printf("%s\n", str_data);
} else {
break;
}
}
}
/**
setup
初期化(起動時一回のみ)
*/
void setup() {
Serial.begin(115200);
//GPSの初期化
Gnss.setDebugMode(PrintInfo);
int result;
result = Gnss.begin();
if (result != 0)
{
Serial.println("Gnss begin error!!");
}
else
{
switch (satType)
{
case eSatGps:
Gnss.select(GPS);
break;
case eSatGpsSbas:
Gnss.select(GPS);
Gnss.select(SBAS);
break;
case eSatGlonass:
Gnss.select(GLONASS);
Gnss.deselect(GPS);
break;
case eSatGpsGlonass:
Gnss.select(GPS);
Gnss.select(GLONASS);
break;
case eSatGpsBeidou:
Gnss.select(GPS);
Gnss.select(BEIDOU);
break;
case eSatGpsGalileo:
Gnss.select(GPS);
Gnss.select(GALILEO);
break;
case eSatGpsQz1c:
Gnss.select(GPS);
Gnss.select(QZ_L1CA);
break;
case eSatGpsQz1cQz1S:
Gnss.select(GPS);
Gnss.select(QZ_L1CA);
Gnss.select(QZ_L1S);
break;
case eSatGpsBeidouQz1c:
Gnss.select(GPS);
Gnss.select(BEIDOU);
Gnss.select(QZ_L1CA);
break;
case eSatGpsGalileoQz1c:
Gnss.select(GPS);
Gnss.select(GALILEO);
Gnss.select(QZ_L1CA);
break;
case eSatGpsGlonassQz1c:
default:
Gnss.select(GPS);
Gnss.select(GLONASS);
Gnss.select(QZ_L1CA);
break;
}
/* Start positioning */
result = Gnss.start(COLD_START);
if (result != 0)
{
Serial.println("Gnss start error!!");
}
else
{
Serial.println("Gnss setup OK");
}
}
//圧力センサーの初期化
pinMode(A2, INPUT);//読み取りモード
Serial.println("setup finish");
//bluetooth通信呼び出し
ble1507 = BLE1507::getInstance();
ble1507->beginPeripheral(ble_name, addr, UUID_SERVICE, UUID_CHAR);
ble1507->setWritePeripheralCallback(bleWriteCB);//受け取ったらこの関数実行する
ble1507->startAdvertise();//起動
//LEDライト
pinMode(PIN_D22, OUTPUT);
}
/**
loop
繰り返し
*/
void loop() {
/*
*GPSの処理
*/
/* Check update. */
if (Gnss.waitUpdate(-1))
{
/* Get NaviData. */
SpNavData NavData;
Gnss.getNavData(&NavData);
now.year = NavData.time.year;
now.month = NavData.time.month;
now.date = NavData.time.day;
now.hour = NavData.time.hour;
now.minute = NavData.time.minute;
now.second = NavData.time.sec;
//printf("now:%04d/%02d/%02d %02d:%02d:%02d\n", now.year,now.month,now.date,now.hour,now.minute,now.second);
}
//A2を圧力センサーとして使う
int value = analogRead(A2);
//配列に圧力・今の時刻を保存する
logPressureData(value);
// Serial.println(value);
delay(100);
//needNotifyなら呼び出し
if(needNotify) {
notifyLoop();
}
}
SPRESENS本体のプログラムです。最初にGPSの衛星を選んで通信を行っています。通信して受け取った日時をDateTimeという専用の型を作って圧力とまとめて配列に保存します。配列は、1000個用意しています。
そして、圧力データは握った時の閾値を200として、200以上(握った時)かつ2秒握ったときに値を配列に保存しています。また、bleWriteCB()は、星空を表示するためのスマホ通信をしています。「BLE1507」を用いたBLE転送については「Spresense-Playground」を参照しました
HeartContoroller.php
<?php
namespace App\Http\Controllers;
use App\Models\Heart;
use Illuminate\Http\Request;
use App\Models\Passing;
use Illuminate\Support\Arr;
class HeartController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index($device_id_from)
{
// $list = Heart::get();
// return response(['list' => $list], 200);
/**
* デバイスIDが一致するデータのみとってくる
*/
$heart = Heart::where('device_id_from', $device_id_from)->get();
$passing = Passing::where('device_id_from', $device_id_from)->get();
//最悪これで
// $result = [
// 'heart' => $heart,
// 'passing' => $passing
// ];
$result = [
...$heart,//スプレット演算子
...$passing
];
//ソート
$result = Arr::sort($result, function($value){
return $value['created_at'];
});
return response($result, 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
// データを変数に保存
$req = $request->all(); //json部分を取り出す
// return 'ok';
foreach($req as $key => $value) {
$heart = new Heart();
$heart->lat = $value['lat'];
$heart->lng = $value['lng'];
$heart->pressure = $value['pressure'];
$heart->device_id = $value['device_id_from'];
$heart->device_id = $value['device_id_to'];
$heart->save();
}
//jsonデータで作成
return response()->json(['message' => 'Data stored successfully'], 200);
}
}
PassingController.php
<?php
namespace App\Http\Controllers;
use App\Models\Passing;
use Illuminate\Http\Request;
class PassingController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index($device_id_from)
{
// $list = Passing::get();
// return response(['list' => $list], 200);
/**
* デバイスIDが一致するデータのみとってくる
*/
$passing = Passing::where('device_id_from', $device_id_from)->get();
return $passing;
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
// データを変数に保存
$req = $request->all(); //json部分を取り出す
foreach($req as $key => $value) {
$passing = new Passing();
$passing->lat = $value['lat'];
$passing->lng = $value['lng'];
$passing->pressure = $value['pressure'];
$passing->device_id_from = $value['device_id_from'];
$passing->device_id_to = $value['device_id_to'];
$passing->save();
}
//jsonデータで作成
return response()->json(['message' => 'Data stored successfully'], 200);
}
}
Heart.php
<?php
namespace App\Models;
use Carbon\CarbonInterval;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
class Heart extends Model
{
use HasFactory;
// protected $table = 'hearts'; 勝手に参照してくれる
protected $fillable = [
'lat',
'lng',
'pressure',
'device_id_from',
'device_id_to'
];
protected function getCreatedAtAttribute($value) {
return Carbon::parse($value)->format('Y-m-d H:i:s');
}
protected function getUpdatedAtAttribute($value) {
return Carbon::parse($value)->format('Y-m-d H:i:s');
}
}
Passing.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
class Passing extends Model
{
use HasFactory;
protected $table = 'passings';
protected $fillable = [
'lat',
'lng',
'pressure',
'device_id_from',
'device_id_to'
];
protected function getCreatedAtAttribute($value) {
return Carbon::parse($value)->format('Y-m-d H:i:s');
}
protected function getUpdatedAtAttribute($value) {
return Carbon::parse($value)->format('Y-m-d H:i:s');
}
}
api.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HeartController;
use App\Http\Controllers\PassingController;
use App\Http\Controllers\Controller;
use App\Http\Controllers\UserController;
Route::get('/heart/{device_id}', [HeartController::class, 'index']);
Route::post('/heart-data', [HeartController::class, 'store']);
Route::get('/passing/{device_id}', [PassingController::class, 'index']);
Route::post('/passing-data', [PassingController::class, 'store']);
Route::get('/send/{device_id}', [Controller::class, 'index']);
Buddyとスマホを繋げるAPIです。フレームワークはlarabelを使用しました。上2つのコードがコントローラーで、storeメソッドがSPRESENSEから受け取ったデータをJSON形式でデータベースに保存しています。
indexメソッドは、スマホがデータベースからデータを持ってくるためにデバイスIDが一致するデータを送信するように作っています。そしてそのデータは一番下、api.phpにてURLを作成しています。3、4つ目はmodelです。
データベースのSQL
-- phpMyAdmin SQL Dump
-- version 5.2.1
-- https://www.phpmyadmin.net/
--
-- ホスト: localhost
-- 生成日時: 2025 年 1 月 30 日 06:24
-- サーバのバージョン: 10.6.14-MariaDB
-- PHP のバージョン: 8.2.7
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- データベース: `star2024`
--
-- --------------------------------------------------------
--
-- テーブルの構造 `hearts`
--
CREATE TABLE `hearts` (
`id` bigint(20) NOT NULL,
`lat` double NOT NULL,
`lng` double NOT NULL,
`pressure` float NOT NULL,
`device_id_from` bigint(20) NOT NULL,
`device_id_to` bigint(20) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
--
-- テーブルのデータのダンプ `hearts`
--
-- --------------------------------------------------------
--
-- テーブルの構造 `passings`
--
CREATE TABLE `passings` (
`id` bigint(20) NOT NULL,
`lat` double NOT NULL,
`lng` double NOT NULL,
`pressure` double NOT NULL,
`device_id_from` bigint(20) NOT NULL,
`device_id_to` bigint(20) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
--
-- テーブルのデータのダンプ `passings`
--
-- --------------------------------------------------------
--
-- テーブルの構造 `users`
--
CREATE TABLE `users` (
`id` bigint(20) NOT NULL,
`device_id` bigint(20) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
--
-- テーブルのデータのダンプ `users`
--
INSERT INTO `users` (`id`, `device_id`, `created_at`, `updated_at`) VALUES
(1, 1, '2024-12-04 08:39:24', '2024-12-04 08:39:24'),
(2, 2, '2024-12-04 08:43:40', '2024-12-04 08:43:40');
--
-- ダンプしたテーブルのインデックス
--
--
-- テーブルのインデックス `hearts`
--
ALTER TABLE `hearts`
ADD PRIMARY KEY (`id`);
--
-- テーブルのインデックス `passings`
--
ALTER TABLE `passings`
ADD PRIMARY KEY (`id`);
--
-- テーブルのインデックス `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`id`);
--
-- ダンプしたテーブルの AUTO_INCREMENT
--
--
-- テーブルの AUTO_INCREMENT `hearts`
--
ALTER TABLE `hearts`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
--
-- テーブルの AUTO_INCREMENT `passings`
--
ALTER TABLE `passings`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=11;
--
-- テーブルの AUTO_INCREMENT `users`
--
ALTER TABLE `users`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
データベースのSQLです。heartsがBuddyを握った時用のテーブル、passingsがすれ違った時用のテーブルです。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>星たびアプリ</title>
<link rel="stylesheet" href="assets/css/style.css" />
<script src="assets/js/script.js" type="module"></script>
</head>
<body>
<div class="wrapper">
<div class="content">
<h1>Reflection Space</h1>
<div class="text">
<p>星空の下で自分だけの特別な時間を</p>
<p>旅を振り返りましょう!</p>
</div>
<div class="btn">
<button type="button" id="connect-device">スタート</button>
<!-- <button type="button" id="get-records">旅の記録を取得する</button> -->
</div>
</div>
</div>
<!-- <a href="#">旅の記録を見る(Unityアプリ起動)</a>
<button type="button" id="debug-resend-deviceid">デバイスIDを再送信</button> -->
<p class="message"></p>
</body>
</html>
style.css
@charset "utf-8";
* {
padding: 0;
margin: 0;
list-style: none;
text-decoration: none;
}
body {
background-color: rgb(33, 33, 33);
}
h1,
p {
color: white;
}
.wrapper {
max-width: 852px;
height: 100vh;
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
margin: 0 auto;
}
.content {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
border-radius: 50px;
background: rgba(255, 255, 255, 0.1);
box-shadow: -5px 5px 10px 0px rgba(255, 255, 255, 0.2) inset,
5px -5px 10px 0px rgba(255, 255, 255, 0.2) inset,
-5px -5px 10px 0px rgba(255, 255, 255, 0.2) inset,
5px 5px 13px 0px rgba(255, 255, 255, 0.2) inset;
border: 1px solid white;
padding: 3em;
margin: 2em;
}
.text {
margin-block: 1.25em;
}
button {
border: none;
font-size: 2em;
font-weight: bold;
padding: .25em 1em;
color: white;
border-radius: 50px;
background: rgba(26, 62, 205, 0.5);
box-shadow: -5px 5px 10px 0px rgba(255, 255, 255, 0.2) inset,
5px -5px 10px 0px rgba(255, 255, 255, 0.2) inset,
-5px -5px 10px 0px rgba(255, 255, 255, 0.2) inset,
5px 5px 13px 0px rgba(255, 255, 255, 0.2) inset;
}
@media screen and (max-width: 353px) {
button{
font-size: 1.5em;
}
}
script.js
import BLE from './BLE.js';
(function(){
'usestrict';
//定数
const CMD_READ_PRESSURE = 1; //圧力一覧を受け取るコマンド。コマンドは1。
//BLE関連のイベントハンドラ
let ble = new BLE();
ble.onError = (error) => {
console.log(error)
};
ble.onScan = () => {
};
ble.onConnectGATT = () => {
};
ble.onWrite = () => {
};
//UI系のイベントハンドラ
const cnDevice = document.querySelector('#connect-device');
cnDevice.addEventListener('click', getDeviceId);
const btnGetRec = document.querySelector('#get-records');
btnGetRec.addEventListener('click', requestRecordings);
/**
* spresenseからデバイスIDを取得
*/
async function getDeviceId() {
console.log('scan start...');
await ble.scan();
console.log(ble.device.id);
}
/**
* spresenseに圧力データを送るよう指示する
*/
async function requestRecordings() {
console.log('送信リクエスト開始');
//指示完了の処理を事前に定義
ble.onWrite = (ret) => {
console.log('送信リクエスト完了')
}
ble.write(CMD_READ_PRESSURE, true);
}
})();
BLE.js
export default class {
/**
* コンストラクタ
*/
constructor() {
this.device = null;
this.server = null;
this.service = null;
this.notifyChar = null;
this.SERVICE_UUID = "00003802-0000-1000-8000-00805f9b34fb";
this.CHAR_UUID = "00004a02-0000-1000-8000-00805f9b34fb";
// this.Custom3_Characteristic_UUID = "00004a02-0000-1000-8000-00805f9b34fb";
this.alpsoptions = {
// acceptAllDevices: true,
filters: [
{ namePrefix: "SPR-" }
],
optionalServices: [this.SERVICE_UUID]
};
}
async scan() {
try {
console.log('Requesting Bluetooth Device...');
// 紐づけるデバイスを探索
this.device = await navigator.bluetooth.requestDevice(this.alpsoptions);
this.onScan(this.device.name);
this.onConnectGATT();
// より詳しい情報を取得するための接続(GATT)を開始
this.server = await this.device.gatt.connect();
console.log('Getting GAP Service...');
// 接続できたらサービスを取得
this.service = await this.server.getPrimaryService(this.SERVICE_UUID);
console.log('Got Service');
} catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
/**
* spresenseにコマンドを送信
* @param {*} command
* @param {*} isNumeric
*/
async write(command, isNumeric) {
try {
console.log('Execute : Write');
this.notifyChar = await this.service.getCharacteristic(this.CHAR_UUID);
let data = new Uint8Array(1);
// 送信データが数値ならそのまま、テキストなら変換
if (isNumeric) {
data[0] = command;
} else {
data = new TextEncoder().encode(command);
}
const ret = await this.notifyChar.writeValueWithResponse(data);
this.onWrite(ret);
}
catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
async startNotify(uuid) {
try {
console.log('Execute : StartNotify');
this.notifyChar = await this.service.getCharacteristic(uuid);
this.notifyChar.addEventListener('characteristicvaluechanged', this.DataChanged.bind(this));
await this.notifyChar.startNotifications();
}
catch (error) {
this.onError(error);
console.log('Argh! ' + error);
}
}
async stopNotify() {
try {
console.log('Execute : StopNotify');
await this.notifyChar.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();
}
}
apearPlanet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class apearPlanet : MonoBehaviour
{
public GameObject constellation ;
private bool drawFlag = false ;
// Update is called once per frame
void Update()
{
int i ;
GameObject obj ;
if ( GetTrajectory.constellationList != null && drawFlag == false )
{
for ( i = 0 ; i < GetTrajectory.constellationList.constellation.Length ; i ++ )
{
obj = Instantiate ( constellation ) ;
obj.GetComponent<drawConstellationLine>().id = i ;
drawFlag = true ;
}
}
}
}
ChangeColor.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeColor : MonoBehaviour
{
public MeshRenderer star1, star2, star3 ;
public Color atcolor ;
// Update is called once per frame
void Update()
{
star1.material.SetColor ( Shader.PropertyToID("_EMISSION_COLOR"), atcolor ) ;
star2.material.SetColor ( Shader.PropertyToID("_EMISSION_COLOR"), atcolor ) ;
star3.material.SetColor ( Shader.PropertyToID("_EMISSION_COLOR"), atcolor ) ;
}
}
Constellation.cs
using System ;
using UnityEngine;
[Serializable]
public class Constellation
{
public Planet [] planet ;
public Vector3 position ;
public string date ;
}
detectOcject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI ;
using UnityEngine.SceneManagement ;
public class detectObject : MonoBehaviour
{
private int count = 0 ;
private int oldid = -1 ;
public static int choiceId = 0 ;
public RawImage scope, black ;
public Text text ;
public GameObject camera ;
float r = 1.0f, g = 1.0f, b= 1.0f ,a = 0.0f ;
bool flag = false ;
// Update is called once per frame
void Update()
{
int i ;
Vector3 vp ;
if ( flag == false )
{
for ( i = 0 ; i < GetTrajectory.constellationList.constellation.Length ; i ++ )
{
vp = GetComponent<Camera>().WorldToViewportPoint ( GetTrajectory.constellationList.constellation[i].position );
if ( vp.x >= 0.4f && vp.x < 0.6f && vp.y >= 0.4f && vp.y < 0.6f )
{
if ( oldid != i )
{
count = 0 ;
oldid = i ;
flag = false ;
}
count ++ ;
if ( count == 50 )
{
a = 1.0f ;
}
if ( count > 50 && Random.Range ( 0.0f, 1.0f ) < 0.1f )
{
a = 0.0f ;
}
if ( count > 150 )
{
a = 1.0f ;
}
if ( count == 170 )
{
flag = true ;
r = 1.0f ;
g = 0.0f ;
b = 0.0f ;
}
a += Time.deltaTime * 10.0f ;
if ( a > 1.0f ) a = 1.0f ;
if ( count < 50 ) a = 0.0f ;
scope.color = new Color ( r, g, b, a ) ;
text.text = GetTrajectory.constellationList.constellation[oldid].date ;
text.color = new Color ( r, g, b*0.3f, a ) ;
}
}
}
else
{
if ( count >= 170 )
{
black.color = new Color ( 0.0f, 0.0f, 0.0f, ( float )( count - 170 ) / 30.0f ) ;
camera.transform.Translate ( 0.0f, 0.0f, 10.0f ) ;
}
if ( count > 200 )
{
scope.color = new Color ( 1.0f, 1.0f, 1.0f, 0.0f ) ;
text.text = GetTrajectory.constellationList.constellation[oldid].date ;
text.color = new Color ( 1.0f, 1.0f, 0.5f, 0.0f ) ;
choiceId = oldid ;
SceneManager.LoadScene ( "ConstellationView" ) ;
}
count ++ ;
}
}
}
drawConstellationLine.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class drawConstellationLine : MonoBehaviour
{
public int id = 0 ;
public GameObject planet ;
LineRenderer lineRenderer ;
private int count = 1 ;
public float alpha = 0.4f ;
private bool drawFlag = false ;
void drawLine ()
{
int i, j ;
float u, v ;
float seazon ;
Vector3 [] positions ;
Shader lineShader = Shader.Find("Hidden/Internal-Colored");
Material lineMaterial = new Material(lineShader);
lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = lineMaterial;
lineRenderer.startWidth = 0.5f ;
lineRenderer.endWidth = 0.5f ;
//曲がり角の丸み設定
lineRenderer.numCornerVertices = 10 ;
//線終端の丸み設定
lineRenderer.numCapVertices = 0 ;
seazon = GetTrajectory.constellationList.constellation[id].planet[0].seazon ;
Debug.Log ( "seazon = " + seazon ) ;
if ( seazon < 0.25f )
{
lineRenderer.startColor = new Color ( 0.3f, 0.7f, 1.0f, alpha ) ;
lineRenderer.endColor = new Color ( 0.3f, 0.7f, 1.0f, alpha ) ;
}
else if ( seazon < 0.5f )
{
lineRenderer.startColor = new Color ( 1.0f, 0.3f, 0.7f, alpha ) ;
lineRenderer.endColor = new Color ( 1.0f, 0.3f, 0.7f, alpha ) ;
}
else if ( seazon < 0.75f )
{
lineRenderer.startColor = new Color ( 1.0f, 1.0f, 1.0f, alpha ) ;
lineRenderer.endColor = new Color ( 1.0f, 1.0f, 1.0f, alpha ) ;
}
else
{
lineRenderer.startColor = new Color ( 1.0f, 0.5f, 0.3f, alpha ) ;
lineRenderer.endColor = new Color ( 1.0f, 0.5f, 0.3f, alpha ) ;
}
positions = new Vector3 [GetTrajectory.constellationList.constellation[id].planet.Length] ;
for ( i = 0 ; i < positions.Length ; i ++ )
{
u = GetTrajectory.constellationList.constellation[id].planet[i].u ;
v = GetTrajectory.constellationList.constellation[id].planet[i].v ;
u += GetTrajectory.constellationList.constellation[id].position.x ;
v += GetTrajectory.constellationList.constellation[id].position.y ;
positions[i].x = Mathf.Sin ( u * 2.0f * Mathf.PI ) *
Mathf.Cos ( v * Mathf.PI / 2.0f ) * 300.0f ;
positions[i].y = Mathf.Sin ( v * Mathf.PI / 2.0f ) * 300.0f ;
positions[i].z = Mathf.Cos ( u * 2.0f * Mathf.PI ) *
Mathf.Cos ( v * Mathf.PI / 2.0f ) * 300.0f ;
Instantiate ( planet, positions[i], Quaternion.identity ) ;
}
lineRenderer.positionCount = positions.Length ;
lineRenderer.SetPositions ( positions ) ;
u = GetTrajectory.constellationList.constellation[id].position.x ;
v = GetTrajectory.constellationList.constellation[id].position.y ;
GetTrajectory.constellationList.constellation[id].position.x = Mathf.Sin ( u * 2.0f * Mathf.PI ) *
Mathf.Cos ( v * Mathf.PI / 2.0f ) * 300.0f ;
GetTrajectory.constellationList.constellation[id].position.y = Mathf.Sin ( v * Mathf.PI / 2.0f ) * 300.0f ;
GetTrajectory.constellationList.constellation[id].position.z = Mathf.Cos ( u * 2.0f * Mathf.PI ) *
Mathf.Cos ( v * Mathf.PI / 2.0f ) * 300.0f ;
}
void Update ()
{
if ( GetTrajectory.constellationList != null && drawFlag == false )
{
drawLine () ;
drawFlag = true ;
}
}
}
drawConstellationView.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro ;
using UnityEngine.UI;
using UnityEngine.SceneManagement ;
public class drawConstellationView : MonoBehaviour
{
public GameObject [] planet ;
public Text text ;
LineRenderer lineRenderer ;
private int count = 1 ;
public float alpha = 0.4f ;
private bool drawFlag = false ;
Color [] planetCol ;
void Start ()
{
planetCol = new Color [4] ;
planetCol[0] = new Color ( 1.0f, 0.0f, 0.0f, 1.0f ) ;
planetCol[1] = new Color ( 1.0f, 1.0f, 0.0f, 1.0f ) ;
planetCol[2] = new Color ( 0.0f, 1.0f, 0.0f, 1.0f ) ;
planetCol[3] = new Color ( 0.0f, 0.0f, 1.0f, 1.0f ) ;
}
void drawLine ()
{
int i, j ;
float u, v ;
float seazon ;
Vector3 [] positions ;
int id ;
id = detectObject.choiceId ;
Shader lineShader = Shader.Find("Hidden/Internal-Colored");
Material lineMaterial = new Material(lineShader);
lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = lineMaterial;
lineRenderer.startWidth = 0.5f ;
lineRenderer.endWidth = 0.5f ;
//曲がり角の丸み設定
lineRenderer.numCornerVertices = 10 ;
//線終端の丸み設定
lineRenderer.numCapVertices = 0 ;
text.text = GetTrajectory.constellationList.constellation[id].date ;
seazon = GetTrajectory.constellationList.constellation[id].planet[0].seazon ;;
if ( seazon < 0.25f )
{
lineRenderer.startColor = new Color ( 0.3f, 0.7f, 1.0f, alpha ) ;
lineRenderer.endColor = new Color ( 0.3f, 0.7f, 1.0f, alpha ) ;
}
else if ( seazon < 0.5f )
{
lineRenderer.startColor = new Color ( 1.0f, 0.3f, 0.7f, alpha ) ;
lineRenderer.endColor = new Color ( 1.0f, 0.3f, 0.7f, alpha ) ;
}
else if ( seazon < 0.75f )
{
lineRenderer.startColor = new Color ( 1.0f, 1.0f, 1.0f, alpha ) ;
lineRenderer.endColor = new Color ( 1.0f, 1.0f, 1.0f, alpha ) ;
}
else
{
lineRenderer.startColor = new Color ( 1.0f, 0.5f, 0.3f, alpha ) ;
lineRenderer.endColor = new Color ( 1.0f, 0.5f, 0.3f, alpha ) ;
}
positions = new Vector3 [GetTrajectory.constellationList.constellation[id].planet.Length] ;
for ( i = 0 ; i < positions.Length ; i ++ )
{
GameObject obj ;
u = GetTrajectory.constellationList.constellation[id].planet[i].u * 4.0f ;
v = GetTrajectory.constellationList.constellation[id].planet[i].v ;
positions[i].x = u * 2000.0f ;
positions[i].y = v * 2000.0f ;
positions[i].z = 0.0f ;
obj = Instantiate ( planet[GetTrajectory.constellationList.constellation[id].planet[i].objectId], positions[i], Quaternion.identity ) ;
obj.transform.GetChild(1).gameObject.GetComponent<TextMeshPro>().text = GetTrajectory.constellationList.constellation[id].planet[i].date ;
if ( GetTrajectory.constellationList.constellation[id].planet[i].objectId == 0 )
{
obj.GetComponent<ChangeColor>().atcolor = new Color ( GetTrajectory.constellationList.constellation[id].planet[i].intensity * 2.0f,GetTrajectory.constellationList.constellation[id].planet[i].intensity * 2.0f,GetTrajectory.constellationList.constellation[id].planet[i].intensity * 2.0f,1.0f) ;
}
else
{
obj.GetComponent<ChangeColor>().atcolor = planetCol[Random.Range ( 0, 4 )] ;
}
}
lineRenderer.positionCount = positions.Length ;
lineRenderer.SetPositions ( positions ) ;
}
void Update ()
{
if ( GetTrajectory.constellationList != null && drawFlag == false )
{
drawLine () ;
drawFlag = true ;
}
if ( Input.GetMouseButtonDown(0) == true )
{
SceneManager.LoadScene ( "Planet" ) ;
}
}
}
GetTrajectory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using System;
public class GetTrajectory : MonoBehaviour
{
public string jsonData ;
public float constellationSize = 0.25f;
private const int REQUEST_DURATION = 1; // サーバにリクエストする頻度(秒)
private const int REDRAW_DURATION = 1; // 再描画する頻度(秒)
private static int requestTimer = 0;
private static TrajectoryList trajectoryList = null ;
private bool dataSet = false ;
public static ConstellationList constellationList = null ;
private void createConstellation ()
{
int oldId = -1 ;
int count = 0 ;
int id = -1 ;
constellationList = new ConstellationList () ;
foreach (var item in trajectoryList.data)
{
if ( oldId != item.id )
{
count ++ ;
oldId = item.id ;
}
}
constellationList.constellation = new Constellation [count] ;
oldId = -1 ;
id = -1 ;
foreach (var item in trajectoryList.data)
{
if ( oldId != item.id )
{
if ( id >= 0 )
{
constellationList.constellation[id] = new Constellation () ;
constellationList.constellation[id].planet = new Planet [count] ;
}
id ++ ;
count = 0 ;
oldId = item.id ;
}
count ++ ;
// Debug.Log ( id + ", " + count ) ;
}
// Debug.Log ( id + ", " + count ) ;
constellationList.constellation[id] = new Constellation () ;
constellationList.constellation[id].planet = new Planet [count] ;
/*
foreach (var item in trajectoryList.data)
{
Debug.Log ( item.id ) ;
Debug.Log ( item.lat ) ;
Debug.Log ( item.lng ) ;
Debug.Log ( item.pressure ) ;
Debug.Log ( item.devide_id_from ) ;
Debug.Log ( item.device_id_to ) ;
Debug.Log ( item.created_at ) ;
Debug.Log ( item.updated_at ) ;
}
*/
}
private void setConstellation ()
{
int i, j ;
int oldId = -1 ;
int cid = -1, pid = 0 ;
float minu = 0.0f, maxu = 0.0f, minv = 0.0f, maxv = 0.0f, cenu, cenv ;
float positionu, positionv ;
DateTime parseDate ;
foreach ( var item in trajectoryList.data )
{
if ( oldId != item.id )
{
cid ++ ;
pid = 0 ;
oldId = item.id ;
}
// Debug.Log ( "set = " + cid + ", " + pid ) ;
constellationList.constellation[cid].date = item.created_at ;
constellationList.constellation[cid].planet[pid] = new Planet () ;
constellationList.constellation[cid].planet[pid].u = item.lng ;
constellationList.constellation[cid].planet[pid].v = item.lat ;
constellationList.constellation[cid].planet[pid].intensity = item.pressure ;
Debug.Log ( "tra_int = " + constellationList.constellation[cid].planet[pid].intensity) ;
constellationList.constellation[cid].planet[pid].date = item.created_at ;
parseDate = DateTime.Parse ( item.created_at ) ;
constellationList.constellation[cid].planet[pid].seazon = ( float )( parseDate.Month % 12 ) / 11.0f ;
if ( item.device_id_to == 0 )
{
constellationList.constellation[cid].planet[pid].objectId = 0 ;
}
else
{
constellationList.constellation[cid].planet[pid].objectId = 1 ;
}
pid ++ ;
}
bool [,] cell ;
cell = new bool [4,36] ;
for ( i = 0 ; i < 4 ; i ++ )
{
for ( j = 0 ; j < 36 ; j ++ )
{
cell[i,j] = false ;
}
}
for ( i = 0 ; i < constellationList.constellation.Length ; i ++ )
{
for ( j = 0 ; j < constellationList.constellation[i].planet.Length ; j ++ )
{
if ( j == 0 || constellationList.constellation[i].planet[j].u < minu )
{
minu = constellationList.constellation[i].planet[j].u ;
}
if ( j == 0 || constellationList.constellation[i].planet[j].u > maxu )
{
maxu = constellationList.constellation[i].planet[j].u ;
}
if ( j == 0 || constellationList.constellation[i].planet[j].v < minv )
{
minv = constellationList.constellation[i].planet[j].v ;
}
if ( j == 0 || constellationList.constellation[i].planet[j].v > maxv )
{
maxv = constellationList.constellation[i].planet[j].v ;
}
}
int se = ( int )( constellationList.constellation[i].planet[0].seazon * 4.0f ) ;
if ( se >= 4 ) se = 3 ;
int ci = UnityEngine.Random.Range ( 0, 36 );
Debug.Log ( se + "," + ci ) ;
while ( cell[se,ci] == true )
{
ci = UnityEngine.Random.Range ( 0, 36 );
}
cell[se,ci] = true ;
cenu = ( minu + maxu ) / 2.0f;
cenv = ( minv + maxv ) / 2.0f ;
positionu = 0.041f * (float)( ci % 6 ) + UnityEngine.Random.Range ( 0.015f, 0.025666f ) ;
positionv = -0.6f + 0.2f * ( float ) ( ci / 6 ) + UnityEngine.Random.Range ( 0.07f, 0.13f ) ;
constellationList.constellation[i].position = new Vector3 ( positionu + constellationList.constellation[i].planet[0].seazon, positionv, 0.0f ) ;
for ( j = 0 ; j < constellationList.constellation[i].planet.Length ; j ++ )
{
constellationList.constellation[i].planet[j].u = ( ( constellationList.constellation[i].planet[j].u - cenu ) / ( maxu - minu ) ) * 0.25f * constellationSize ;
//constellationList.constellation[i].planet[j].u += positionu + constellationList.constellation[i].planet[j].seazon ;
constellationList.constellation[i].planet[j].v = ( ( constellationList.constellation[i].planet[j].v - cenv ) / ( maxv - minv ) ) * constellationSize ;
//constellationList.constellation[i].planet[j].v += positionv ;
}
}
/*
for ( i = 0 ; i < constellationList.constellation.Length ; i ++ )
{
for ( j = 0 ; j < constellationList.constellation.Length ; j ++ )
{
Debug.Log ( constellationList.constellation[i].planet[j].u ) ;
Debug.Log ( constellationList.constellation[i].planet[j].v ) ;
Debug.Log ( constellationList.constellation[i].planet[j].intensity ) ;
Debug.Log ( constellationList.constellation[i].planet[j].seazon ) ;
Debug.Log ( constellationList.constellation[i].planet[j].objectId ) ;
}
}*/
}
// 1フレームごとに処理
void Update()
{
/*
requestTimer = ++requestTimer % (60 * REQUEST_DURATION);
if (requestTimer == 0) {
// StartCoroutine(fetch("https://www.jz.jec.ac.jp/pjstub/ranking.json"));
StartCoroutine(fetch("http://10.40.112.44/star2024api_stub/hearts.json"));
}
*/
trajectoryList = JsonUtility.FromJson<TrajectoryList> ( jsonData ) ;
if ( trajectoryList != null && dataSet == false )
{
createConstellation () ;
setConstellation () ;
dataSet = true ;
}
}
IEnumerator fetch (string url)
{
Debug.Log("fetch start");
UnityWebRequest request = UnityWebRequest.Get(url);
// データをJSONで受け取りたいのでHeaderをセット
request.SetRequestHeader("Content-Type", "application/json");
yield return request.SendWebRequest();
Debug.Log("fetch complete");
if (request.result == UnityWebRequest.Result.ConnectionError)
{
// エラーが起きた場合はエラー内容を表示
Debug.Log(request.error);
}
else
{
// レスポンスをテキストで表示
Debug.Log(request.downloadHandler.text);
// JSONからRankList形式に変換
//trajectoryList = JsonUtility.FromJson<TrajectoryList>(request.downloadHandler.text);
//trajectoryList = JsonUtility.FromJson<TrajectoryList>(data);
// // もし画像データなどの場合はバイトデータとして受け取る
// byte[] results = request.downloadHandler.data;
}
}
}
Planet.cs
using System ;
[Serializable]
public class Planet
{
public float seazon ;
public float u ;
public float v ;
public float intensity ;
public string date ;
public int objectId ;
}
Trajectory.cs
using System ;
[Serializable]
public class Trajectory
{
public int id ;
public float lat ;
public float lng ;
public float pressure ;
public int devide_id_from ;
public int device_id_to ;
public string created_at ;
public string updated_at ;
}
TrajectoryList.cs
using System ;
using System.Collections.Generic;
[Serializable]
public class TrajectoryList
{
public List<Trajectory> data = new List<Trajectory>();
}
CameraGyro.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraGyro : MonoBehaviour
{
void Start()
{
Input.gyro.enabled = true;
}
void Update()
{
transform.rotation = Quaternion.Euler(0, 0, -180) * Quaternion.Euler(-90, 0, 0) * Input.gyro.attitude * Quaternion.Euler(0, 0, 180);
}
}
CameraRot.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraRot : MonoBehaviour
{
float t = 0.0f ;
// Update is called once per frame
void Update()
{
transform.localEulerAngles = new Vector3 ( 0.0f, Mathf.Sin ( t ) * 10.0f, 0.0f) ;
t += Time.deltaTime * 0.5f ;
}
}
ConstellationList.cs
using System ;
[Serializable]
public class ConstellationList
{
public Constellation [] constellation ;
}
スマホ画面です。星空を表示する前に、この画面でBuddyとスマホの紐付けを行っています(SPRESENSE本体のプログラム、bleWriteCBを呼び出し)。紐付けができたら、ついに星空を見ることができます。
Unityのプロジェクトは下記になります。
https://drive.google.com/file/d/1dNTf_limGGVAIlo7eJbtHGxOgMGMN_-t/view?usp=drive_link
最後に
企画・スケジュール調整
物理的に全員が集まることが難しい中、オンラインミーティングやチャットツールを活用し、限られた時間を有効に使いました。話し合いを重ねることで方向性が明確になり、着実に前進しました。特に「誰のためのサービスなのか」を深く考えたことで、企画の重要性を実感しました。この経験を通じて、しっかりとした企画の土台が開発をスムーズに進める鍵であることを学び、大きな成長につながりました。
技術的な課題
SPRESENSEのBluetooth通信や組み込み開発に挑戦し、C++の学習を進めながら試行錯誤を重ねました。思うように進まない場面もありましたが、衛星通信で日時を取得できた瞬間や、圧力センサーの値がシリアルモニタに表示されたときの感動は大きな励みになりました。現在も改善の余地はありますが、さらなるブラッシュアップを重ね、より完成度の高いものを目指していきます。
チーム開発の学び
今回のプロジェクトでは、各学科の専門性を活かし、得意分野を分担することで効率的に作業を進めました。方向性がブレないよう要件を明確にし、限られた期間内で成果を出すために、一人ひとりが責任を持って取り組む重要性を実感しました。
他学科とのグループ制作は初めてで、スケジュール調整や意見の擦り合わせの難しさを痛感しましたが、企画が行き詰まった際に根本から見直すことや、プレゼンを通じて情報を整理し伝える力の大切さを学びました。この経験を通じて、技術面だけでなく、チームでの進め方やコミュニケーションの重要性を深く理解することができました。
成果と今後の展望
将来的に多くの人にこのサービスを利用してもらい、心の整理と未来への一歩をサポートを提供するため、すれ違ったときにお互いのデータを交換して、相手のデータも星空に表示できるようにしたいです。
日本電子専門学校 学科横断プロジェクト Aチーム
- 高度情報処理科 佐野
- コンピューターグラフィックス科 黒川
- Webデザイン科 村田
-
muratatakumi
さんが
2025/01/31
に
編集
をしました。
(メッセージ: 初版)
-
muratatakumi
さんが
2025/01/31
に
編集
をしました。
ログインしてコメントを投稿する