作成の動機
- クリスマスの飾りつけをしていて、何か物足りないと思い、
電子工作好きなら、家庭にもあるかもしれないもので、動くサンタの玩具を作ってみました。
家族団らんの場で、父の威厳を取り戻すこと間違いなし!?(責任はとれませんのであしからず)
できたもの「綱渡りサンタ」
- M5Stick Cを搭載したサンタが、綱を渡っていきます。
操作は、Androidアプリケーションを使い、BLE通信でON/OFFを切替えさせます。
M5Santa 動画
- BLE Start ボタンで、接続を開始
- ToggleSwitch ボタンを、押す毎に、サンタの動作のON/OFFの切替え
パーツ
- 作成に使用したパーツは下記となります。
品目 | 価格 |
---|---|
M5StickC | 1,650 |
電動ロボットキット | 850 |
GROVE - リレー | 440 |
単三電池2本 | 110 |
プラ板 | 110 |
カラー紙 | 50 |
Android(操作用) | - |
Bluetooth LE for iOS, tvOS and Android | 2400 |
合計 | 5610 |
- 同じものを買わなくても、DCモーターと、電池ボックスを使って、
動かす部分を自作できれば良し。
綱渡りサンタ2020の電子工作
-
電子工作部分は、M5StickCとGROVEのリレーが中心となります。
他の部品は家にあるもので自由に置き換えてもらって構いません。 -
【作成手順】
1.電動ロボットのキットを組み立てます
・キットの説明書を参考に組み立ててください
2.M5Stick CとGROVEリレーを付属のGROVEの専用線でつなげます
・GROVEの専用線はコネクタの形に合わせて接続するため、向きのさし間違えはありません
3.電子部品を接着します
・DCモーターの全面に、両面テープで、M5Stick Cを接着します
・DCモーターの背面に、両面テープで、GROVEリレーを接着します
4.DCモーターと電池ボックス、GROVEリレーをつなげます
5.デコレーション
・サンタのイラストを作成し、印刷してください
・印刷した紙をプラ板に張り付け、カットしてください
・カットしたプラ板を電子工作したものに貼り付けてください
綱渡りサンタ2020の全プログラム
Github
-
BLE通信は、電子工作のサンタが「ペリフェラル」、Androidアプリケーションが「セントラル」となります。
BLE通信は、ペリフェラルとセントラルで、UUIDを揃えてください。 -
UUIDの作成は、Online UUID Generatorを使い生成しました。
値を変更する場合は、URL内の「Version 1 UUID Generator」の「How Many?」を「3」にし、Generateボタンで3つUUIDを作成してください。
綱渡りサンタ2020のマイコンプログラム
- プログラムは、Arudino IDEを使用しています。
下記コードで完結しているため、そのまま利用可能です。
注意点は、GROVEのリレー操作は、ピン番号が「33」です。「32」ではありません。
「BLE_NAME 」「SERVICE_UUID 」「CHARACTERISTIC_UUID_NOTIFY」「CHARACTERISTIC_UUID_RX 」の値は、「M5SantaBleScript.cs」と合わせて下さい
M5Stick-Cのプログラム(M5Santa.ino)
#include <M5StickC.h>
// Bluetooth LE
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define BLE_NAME "M5Santa"
#define SERVICE_UUID "8f6156ca-48cc-11eb-b378-0242ac130002"
#define CHARACTERISTIC_UUID_NOTIFY "8f61590e-48cc-11eb-b378-0242ac130002"
#define CHARACTERISTIC_UUID_RX "8f615b70-48cc-11eb-b378-0242ac130002"
//Relay
#define RELAY_PIN 33
//-----------------------------------------------------------------------------
//Relay
//-----------------------------------------------------------------------------
//button
bool _isRelay = false;
uint16_t getColor(uint8_t red, uint8_t green, uint8_t blue)
{
return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
}
void RelayON()
{
digitalWrite(RELAY_PIN, HIGH);
M5.Lcd.fillScreen(getColor(255,0,0));
Serial.println("ON");
}
void RelayOFF()
{
digitalWrite(RELAY_PIN, LOW);
M5.Lcd.fillScreen(getColor(0,0,255));
Serial.println("OFF");
}
//-----------------------------------------------------------------------------
//BLE
//-----------------------------------------------------------------------------
BLEServer *pServer = NULL;
BLECharacteristic * pNotifyCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
// Bluetooth LE Change Connect State
class SantaBleServerCallbacks: public BLEServerCallbacks
{
void onConnect(BLEServer* pServer)
{
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer)
{
deviceConnected = false;
}
};
// Bluetooth LE Recive
class SantaBleCallbacks: public BLECharacteristicCallbacks
{
void onWrite(BLECharacteristic *pCharacteristic)
{
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() <= 0)
{
return;
}
String cmd = String(rxValue.c_str());
Serial.print("Received Value: ");
Serial.println(cmd);
//Function
if (cmd == "1")
{
RelayON();
}
else if (cmd == "0")
{
RelayOFF();
}
}
};
// Bluetooth LE initialize
void InitBLE()
{
// Create the BLE Device
BLEDevice::init(BLE_NAME);
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new SantaBleServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pNotifyCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_NOTIFY,
BLECharacteristic::PROPERTY_NOTIFY
);
pNotifyCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pRxCharacteristic->setCallbacks(new SantaBleCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
}
// Bluetooth LE loop
void LoopBLE()
{
// disconnecting
if (!deviceConnected && oldDeviceConnected)
{
// give the bluetooth stack the chance to get things ready
delay(500);
// restart advertising
pServer->startAdvertising();
Serial.println("startAdvertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected)
{
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}
//-----------------------------------------------------------------------------
//Main
//-----------------------------------------------------------------------------
void setup()
{
M5.begin();
//Relay
pinMode(RELAY_PIN, OUTPUT);
RelayOFF();
//Ble
InitBLE();
}
void loop()
{
M5.update();
//Button
if (M5.BtnA.wasReleased())
{
_isRelay = !_isRelay;
if (_isRelay)
{
RelayON();
}
else
{
RelayOFF();
}
}
//BLE
LoopBLE();
}
綱渡りサンタ2020のAndroidプログラム
-
プログラムは、Unityを使用しています。
事前にUnityがインストールされた環境での説明となります。
BLE通信は、有料のAssetを使用して実現しています。
腕に自信がある人は、自作しても構いませんが、日曜プログラマーは、お金で時間を買うのも選択肢としてありだと思います。 -
【作成手順】
1.Githubからソースコードをダウンロードしてください
2.Unityを起動し、プロジェクトを読み込んでください
フォルダ階層は、ダウンロードした「M5Santa\Unity」になります
3.AssetStoreで購入した「Bluetooth LE for iOS, tvOS and Android」を、プロジェクトにImportしてください。
4.Unityのプログラムシーン「Assets\Scenes」を開いてください
5.Androidプロジェクトで出力してください
6.Androidスマートフォンにインストールしてください -
コードの説明
・購入したAssetのSampleコードを流用し、少ないプログラム数で実現させるようにしているため、コードが煩雑となっています。
・「DeviceName」「ServiceUUID」「SubscribeCharacteristic」「WriteCharacteristic」の値は、「M5Santa.ino」と合わせて下さい
・BLE接続開始:UGUIのボタンに「StartProcess」をアタッチして実現しています。
・操作のON/OFF:UGUIのボタンに「WriteToggleData」をアタッチして実現しています。
M5Santaのメインスクリプト(M5SantaBleScript.cs)
using UnityEngine;
using UnityEngine.UI;
public class M5SantaBleScript : MonoBehaviour
{
protected string DeviceName = "M5Santa";
protected string ServiceUUID = "8f6156ca-48cc-11eb-b378-0242ac130002";
protected string SubscribeCharacteristic = "8f61590e-48cc-11eb-b378-0242ac130002";
protected string WriteCharacteristic = "8f615b70-48cc-11eb-b378-0242ac130002";
public Text StateText;
public Text LogText;
public Text Log2Text;
protected string _deviceName = string.Empty;
protected string _serviceName = string.Empty;
private bool _connected = false;
private float _timeout = 0f;
private States _state = States.None;
private string _deviceAddress;
private bool _foundSubscribeID = false;
private bool _foundWriteID = false;
private byte[] _dataBytes = null;
private bool _rssiOnly = false;
private int _rssi = 0;
private bool _isON = false;
public AudioSource audioSound;
/// <summary>
/// States
/// </summary>
public enum States
{
None,
Scan,
ScanRSSI,
Connect,
Subscribe,
Unsubscribe,
Disconnect,
Write,
}
public States State
{
get
{
return _state;
}
}
/// <summary>
/// Update
/// </summary>
void Update()
{
if (_timeout > 0f)
{
_timeout -= Time.deltaTime;
if (_timeout > 0f)
{
return;
}
_timeout = 0f;
switch (_state)
{
//None
case States.None:
break;
//Scan
case States.Scan:
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null, (address, name) =>
{
// デバイスがrssiおよびメーカー固有のデータをアドバタイズしない場合、次のコールバックはメーカー固有のデータがある場合にのみ呼び出されるため、このコールバックを使用する必要があります。
_deviceName += string.Format(",{0} ", name);
if (!_rssiOnly)
{
if (name.Contains(DeviceName))
{
_deviceName += string.Format("[FIND]-{0}\n", _deviceAddress);
BluetoothLEHardwareInterface.StopScan();
_deviceAddress = address;
SetState(States.Connect, 0.5f);
}
else
{
_deviceName += "\n";
}
}
if (LogText != null)
{
LogText.text = _deviceName;
}
}, (address, name, rssi, bytes) =>
{
// デバイスがメーカー固有のデータとrssiで応答する場合、これを使用します
if (name.Contains(DeviceName))
{
if (_rssiOnly)
{
_rssi = rssi;
}
else
{
BluetoothLEHardwareInterface.StopScan();
_deviceAddress = address;
SetState(States.Connect, 0.5f);
}
}
}, _rssiOnly);
if (_rssiOnly)
{
SetState(States.ScanRSSI, 0.5f);
}
break;
//ScanRSSI
case States.ScanRSSI:
break;
//Connect
case States.Connect:
// set these flags
_foundSubscribeID = false;
_foundWriteID = false;
// 最初のパラメータは名前ではなくアドレスであることに注意してください。後方互換性のため、これは修正していません。
// また、最初の2つのコールバックを使用していることに注意してください。特定の特性を探していない場合は、最初の2つのいずれかを使用できますが、
// デバイスはすべてを列挙するので、サブスクライブする前に列挙が完了するまでタイムアウトを大きくする必要があることに注意してください。他の操作を行います。
BluetoothLEHardwareInterface.ConnectToPeripheral(_deviceAddress, null, null, (address, serviceUUID, characteristicUUID) =>
{
_serviceName += string.Format("{0} ", serviceUUID);
if (IsEqual(serviceUUID, ServiceUUID))
{
_serviceName += string.Format("[FIND]-{0}\n", _deviceAddress);
_foundSubscribeID = _foundSubscribeID || IsEqual(characteristicUUID, SubscribeCharacteristic);
_foundWriteID = _foundWriteID || IsEqual(characteristicUUID, WriteCharacteristic);
_serviceName += string.Format("<{0}>,{1},{2}\n", characteristicUUID, _foundSubscribeID, _foundWriteID);
if (_foundSubscribeID)
{
_connected = true;
SetState(States.Subscribe, 2f);
}
}
else
{
_serviceName += "\n";
}
if (Log2Text != null)
{
Log2Text.text = _serviceName;
}
});
break;
// Subscribe
case States.Subscribe:
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(_deviceAddress, ServiceUUID, SubscribeCharacteristic, null, (address, characteristicUUID, bytes) =>
{
_state = States.None;
// we received some data from the device
_dataBytes = bytes;
});
break;
// Unsubscribe
case States.Unsubscribe:
BluetoothLEHardwareInterface.UnSubscribeCharacteristic(_deviceAddress, ServiceUUID, SubscribeCharacteristic, null);
SetState(States.Disconnect, 4f);
break;
// Disconnect
case States.Disconnect:
if (_connected)
{
BluetoothLEHardwareInterface.DisconnectPeripheral(_deviceAddress, (address) =>
{
BluetoothLEHardwareInterface.DeInitialize(() =>
{
_connected = false;
_state = States.None;
});
});
}
else
{
BluetoothLEHardwareInterface.DeInitialize(() =>
{
_state = States.None;
});
}
break;
}
}
}
/// <summary>
/// Reset
/// </summary>
void Reset()
{
_deviceName = string.Empty;
_serviceName = string.Empty;
_connected = false;
_timeout = 0f;
_state = States.None;
_deviceAddress = null;
_foundSubscribeID = false;
_foundWriteID = false;
_dataBytes = null;
_rssi = 0;
}
/// <summary>
/// SetState
/// </summary>
/// <param name="newState"></param>
/// <param name="timeout"></param>
void SetState(States newState, float timeout)
{
_state = newState;
_timeout = timeout;
//State
if (StateText != null)
{
StateText.text = _state.ToString();
}
}
/// <summary>
/// StartProcess
/// </summary>
public void StartProcess()
{
Reset();
BluetoothLEHardwareInterface.Initialize(true, false, () =>
{
SetState(States.Scan, 0.1f);
}, (error) =>
{
BluetoothLEHardwareInterface.Log("Error during initialize: " + error);
});
}
/// <summary>
/// StopProcess
/// </summary>
public void StopProcess()
{
if (_state == States.ScanRSSI)
{
BluetoothLEHardwareInterface.StopScan();
SetState(States.Disconnect, 0.5f);
}
}
/// <summary>
/// WriteData
/// </summary>
/// <param name="str"></param>
public void WriteToggleData()
{
_isON = !_isON;
var strData = _isON ? "1" : "0";
//BGM
if (audioSound != null)
{
if (_isON)
{
audioSound.Play();
}
else
{
audioSound.Stop();
}
}
//Send Device
WriteData(strData);
}
/// <summary>
/// WriteData
/// </summary>
/// <param name="str"></param>
public bool WriteData(string str)
{
if (!_connected)
{
return false;
}
SetState(States.Write, 0.5f);
var data = System.Text.Encoding.UTF8.GetBytes(str);
SendBytes(data);
return true;
}
string FullUUID(string uuid)
{
return "0000" + uuid + "-0000-1000-8000-00805f9b34fb";
}
bool IsEqual(string uuid1, string uuid2)
{
if (uuid1.Length == 4)
{
uuid1 = FullUUID(uuid1);
}
if (uuid2.Length == 4)
{
uuid2 = FullUUID(uuid2);
}
return (uuid1.ToUpper().CompareTo(uuid2.ToUpper()) == 0);
}
void SendBytes(byte[] data)
{
BluetoothLEHardwareInterface.WriteCharacteristic(_deviceAddress, ServiceUUID, WriteCharacteristic, data, data.Length, true, (characteristicUUID) =>
{
BluetoothLEHardwareInterface.Log("Write Succeeded");
});
}
void SendByte(byte value)
{
var data = new byte[] { value };
BluetoothLEHardwareInterface.WriteCharacteristic(_deviceAddress, ServiceUUID, WriteCharacteristic, data, data.Length, true, (characteristicUUID) =>
{
BluetoothLEHardwareInterface.Log("Write Succeeded");
});
}
}
最後に
- この内容をもとに、ちょっとした改造でオリジナリティを出してみてはいかがでしょうか。
例えば、以下のものが考えられます。是非チャレンジしてみてください。
【応用編】
・M5StickCにHATを追加する
・スピーカで音を鳴らす。
・Androidアプリケーションの操作方法に工夫を加える
・スマホを振ることで、ON/OFFを切り替える
・マイク音声のレベルが基準値以上になった場合にONに切り替える
投稿者の人気記事
-
tktk360
さんが
2020/12/29
に
編集
をしました。
(メッセージ: 初版)
-
tktk360
さんが
2020/12/29
に
編集
をしました。
-
tktk360
さんが
2020/12/29
に
編集
をしました。
-
tktk360
さんが
2020/12/30
に
編集
をしました。
ログインしてコメントを投稿する