- 投稿日:2021-02-25T23:28:25+09:00
iPhoneでLEDを制御してみよう!
みなさん、こんばんは
【watnowテックカレンダーの25日目】
今日はwatnowのおかだが担当します
よろしくお願いしますはじめに
今回は私が卒業研究で触ったSpeech Recognition API(音声認識API)とESP-WROOM-32を用いてLEDを制御してみます.
動作環境及び開発環境
swift5.3.2
Xcode Version 12.4
MacOS Catalina
ios14.4Speechフレームワークとは
ios10から追加されたフレームワークで音声認識して文字起こししたりとか色々できちゃうやつです!
ESP-WROOM-32とは
通称ESP-32と呼ばれるArduinoとWIFI通信を兼ねたモジュールみたいなのです!
流れ
Arduino側でLEDの制御とWIFI通信の設定を行い,xcode側で認識した音声に応じたURLにアクセスすることで設定したLEDが光ります!!
Arduino側コード
#include <WiFi.h> const char* ssid = "あなたのSSID"; const char* password = "あなたのパス"; WiFiServer server(80); unsigned long time_data = 0; unsigned long second = 0; void setup() { Serial.begin(115200); pinMode(4, OUTPUT); //front pinMode(2, OUTPUT); //back pinMode(15, OUTPUT); //right pinMode(0, OUTPUT); //left delay(10); // We start by connecting to a WiFi network Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); server.begin(); } void loop(){ WiFiClient client = server.available(); // listen for incoming clients if (client) { // if you get a client, Serial.println("New Client."); // print a message out the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(); // the content of the HTTP response follows the header: client.print("Click <a href=\"/ST\">here</a> to turn the LED on pin All off.<br>"); //stop client.print("Click <a href=\"/FF\">here</a> to turn the LED on pin 4 on.<br>"); //front client.print("Click <a href=\"/BB\">here</a> to turn the LED on pin 2 on.<br>"); //back client.print("Click <a href=\"/RI\">here</a> to turn the LED on pin 15 on.<br>"); //right client.print("Click <a href=\"/LE\">here</a> to turn the LED on pin 0 on.<br>"); //left // The HTTP response ends with another blank line: client.println(); // break out of the while loop: break; } else { // if you got a newline, then clear currentLine: currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } if (currentLine.endsWith("GET /ST")) { stop(); } if (currentLine.endsWith("GET /FF")) { Serial.printf("前"); stop(); digitalWrite(4, HIGH); } if (currentLine.endsWith("GET /BB")) { Serial.printf("後"); stop(); digitalWrite(2, HIGH); } if (currentLine.endsWith("GET /RI")) { Serial.printf("右"); stop(); digitalWrite(15, HIGH); } if (currentLine.endsWith("GET /LE")) { Serial.printf("左"); stop(); digitalWrite(0, HIGH); } } } // close the connection: client.stop(); Serial.println("Client Disconnected."); } } void stop(){ digitalWrite(4, LOW); digitalWrite(2, LOW); digitalWrite(15, LOW); digitalWrite(0, LOW); }arduino側の操作
SSIDとPASSWORDを自分のWIFIのものに書き換えてコンパイルしてください.
次にWEBブラウザでIPアドレスにアクセスして設定したボタンを押してLEDが思い通りに点滅するか確認してください.
Xcode側コード
import UIKit import Speech class ViewController: UIViewController { //認識する言語 private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))! private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? private var recognitionTask: SFSpeechRecognitionTask? private var audioEngine = AVAudioEngine() @IBOutlet weak var textView: UITextView! @IBOutlet weak var fromtButton: UIButton! @IBOutlet weak var rightButton: UIButton! @IBOutlet weak var backButton: UIButton! @IBOutlet weak var leftButton: UIButton! @IBOutlet weak var stopButton: UIButton! override func viewDidLoad() { super.viewDidLoad() SFSpeechRecognizer.requestAuthorization { (status) in OperationQueue.main.addOperation { switch status { case .authorized: do { try self.startSpeech() } catch{ print("startError: \(error)") } self.startTimer() default: print("error") } } } } func stopOnButton() { let url = URL(string: "http://192.168.10.XX/ST")! var request = URLRequest(url: url) request.httpMethod = "GET" let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true) } func frontOnButton() { let url = URL(string: "http://192.168.10.XX/FF")! var request = URLRequest(url: url) request.httpMethod = "GET" let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true) } func backOnButton() { let url = URL(string: "http://192.168.10.XX/BB")! var request = URLRequest(url: url) request.httpMethod = "GET" let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true) } func rightOnButton() { let url = URL(string: "http://192.168.10.XX/RI")! var request = URLRequest(url: url) request.httpMethod = "GET" let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true) } func leftOnButton() { let url = URL(string: "http://192.168.10.XX/LE")! var request = URLRequest(url: url) request.httpMethod = "GET" let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true) } func startSpeech() throws { if let recognitionTask = recognitionTask { recognitionTask.cancel() self.recognitionTask = nil } let audioSession = AVAudioSession.sharedInstance() try audioSession.setCategory(.record, mode: .measurement, options: []) try audioSession.setActive(true, options: .notifyOthersOnDeactivation) let recognitionRequest = SFSpeechAudioBufferRecognitionRequest() self.recognitionRequest = recognitionRequest recognitionRequest.shouldReportPartialResults = true recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak self] (result, error) in guard let `self` = self else { return } if let result = result { self.textView.text = result.bestTranscription.formattedString print(result.bestTranscription.formattedString) //特定の文字を発話した際に処理を行う if result.bestTranscription.formattedString.contains("前のライト") { self.setBackgroundColor(color: .blue) self.frontOnButton() } else if result.bestTranscription.formattedString.contains("後のライト") { self.setBackgroundColor(color: .yellow) self.backOnButton() }else if result.bestTranscription.formattedString.contains("右のライト") { self.setBackgroundColor(color: .orange) self.rightOnButton() }else if result.bestTranscription.formattedString.contains("左のライト") { self.setBackgroundColor(color: .purple) self.leftOnButton() }else if result.bestTranscription.formattedString.contains("停止") { self.setBackgroundColor(color: .white) self.stopOnButton() } } } let recordingFormat = audioEngine.inputNode.outputFormat(forBus: 0) audioEngine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in self.recognitionRequest?.append(buffer) } audioEngine.prepare() try? audioEngine.start() } func stopSpeech() { guard let task = self.recognitionTask else { fatalError("Error") } task.cancel() task.finish() self.audioEngine.inputNode.removeTap(onBus: 0) self.audioEngine.stop() self.recognitionRequest?.endAudio() } private func setBackgroundColor(color: UIColor) { DispatchQueue.main.async { self.view.backgroundColor = color } self.stopSpeech() do { try self.startSpeech() } catch { print("startError: \(error)") } } //1分でタイムアウトして音声認識されなくなるのでタイマーでループ private func startTimer(){ Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in self.stopSpeech() do { try self.startSpeech() } catch{ print("startError: \(error)") } } } @IBAction func stopAction(_ sender: Any) { self.stopOnButton() } @IBAction func frontAction(_ sender: Any) { self.frontOnButton() } @IBAction func backAction(_ sender: Any) { self.backOnButton() } @IBAction func rightAction(_ sender: Any) { self.rightOnButton() } @IBAction func leftAction(_ sender: Any) { self.leftOnButton() } }xcode側の操作
各Butttonに設定してあるURLを各ボタンのURLに変えてください.
viewController
おわりに
どうでしたでしょうか.今回はiphoneで音声認識させてLEDを制御するということをやってみました.工夫次第で色々なことに応用できると思うのでぜひ色々やってみてください!!
watnowでは、チャレンジ精神旺盛な仲間を募集しています
興味を持たれた方はぜひ@watnowsにDMしてください
- 投稿日:2021-02-25T20:51:08+09:00
XMLを利用した汎用コンフィグレーションクラス
XMLを利用した汎用コンフィグレーションクラス
クラステンプレートを使って、次のようにConfigデータを管理する。
var config = Config<SetupParam>.Params.設定;
テンプレートクラス
#region テンプレートクラス public class Config<T> where T : new() { /// <summary> /// コンフィグファイルパスの設定 /// </summary> public static string Path { get; set; } = string.Empty; /// <summary> /// コンフィグパラメータプロパティ /// <para>このクラスがシリアライズ対象</para> /// </summary> public static T Params { get; private set; } /// <summary> /// コンフィグファイルから、Paramsプロパティへロードします(逆シリアライズ) /// </summary> public static void Load() { if (Path == string.Empty) throw new Exception("Pathが未設定です"); Load(Path); } /// <summary> /// コンフィグファイルから、Paramsプロパティへロードします(逆シリアライズ) /// </summary> /// <param name="path">ファイルパス(相対指定可能)</param> public static void Load(string path) { if (!File.Exists(path)) { Params = new T(); return; } XmlSerializer serializer = new XmlSerializer(typeof(T)); FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); Params = (T)serializer.Deserialize(fs); fs.Close(); } /// <summary> /// Paramsプロパティをファイルへ保存します(シリアライズ) /// </summary> public static void Save() { if (Path == string.Empty) throw new Exception("Pathが未設定です"); Save(Path); } /// <summary> /// Paramsプロパティをファイルへ保存します(シリアライズ) /// </summary> /// <param name="path">ファイルパス(相対指定可能)</param> public static void Save(string path) { XmlSerializer serializer = new XmlSerializer(typeof(T)); FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write); serializer.Serialize(fs, Params); fs.Close(); } } #endregion /// <summary> /// 保存パラメータ /// </summary> public class SetupParam { public SubSetup 設定 { get; set; } = new SubSetup(); }
- 投稿日:2021-02-25T18:06:34+09:00
新人研修後の講義資料
新人研修後の講義資料
少し前ですが、前職をやめる直前に行った講義の資料を共有してみます。
(すべてSlideshareです)
タイトル 概要 なぜオブジェクト指向? オブジェクト指向でつくる動機づけのための講義資料(部内でオブジェクト指向が浸透していなかったため) デザインパターン入門 まずはデザインパターンの一部でも使い、広めていくための講義資料 C#とJavaの違い 新人研修ではJavaしかやっておらず、配属後はC#なので勉強の動機づけのための講義資料 ドメイン駆動設計入門 設計スキル向上に対する動機づけのための講義資料 配属直後の新人に対する講義のふりをして、実は旧人たちに講義をするのが目的でした。
どの程度届いたかは分からないですが・・・
要は、プログラマーたるもの、常に勉強し続けようね、という内容です。
- 投稿日:2021-02-25T12:06:53+09:00
文字列連結時、大量の場合➡効率について
文字列連結時、大量の場合➡効率について、以下は参考になります。
sample.js//効率悪い例: detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //改善例: detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";sample.cs//効率悪い例: detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; detail += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //改善例: StringBuilder retSb = new StringBuilder(); retSb.Append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); retSb.Append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); retSb.Append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); //※大量の文字列に対し、正規表現を使わないように。非常に遅くなる。JavaはC#と同様。