年末までにReact+Reduxで何か作る その3

続き
年末までにReact+Reduxで何か作る その1 - ぷろろろぐ
年末までにReact+Reduxで何か作る その2 - ぷろろろぐ

react-redux

前の記事でとりあえずreducer作ったけど,store作ってなかった.こんな感じでreactコンポーネントとreduxを繋ぐ.

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducers from 'reducers';
import Top from 'container/Top';

const store = createStore(reducers);

ReactDOM.render(
  <Provider store={store}>
    <Top store={store}/>
  </Provider>,
  document.getElementById('root')
);

container/Top.js

import { connect } from 'react-redux';
import Top from 'components/Top'

const mapStateToProps = (state, ownProps) => ({
});
const mapDispatchToProps = (dispatch) => ({
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(Top);

components/Top.js

import React, { Component } from 'react';

class Top extends Component {
  render() {
    return (
      <div className="Top">
        hello world.
      </div>
    );
  }
}
export default Top;

まず,index.jsでcreateStore関数にreducerを渡しstoreを生成する.その後,描画したいコンポーネントをProviderで囲うことでstoreの情報を渡す.

次にcontainerなんだけど,これはReactコンポーネントであるcomponents/Top.jsをReduxに接続する処理を行っている.そのために必要なのがconnect関数.

connect関数はmapStateToPropsとmapDispatchToPropsの2つを引数として取る.
storeに格納されたstateを,コンポーネントに対してpropsとして渡すことになるけど,そのstateは全体を表す巨大なものになる.だから,mapStateToProps内で,そのコンポーネントで使うものをstoreから抜粋してあげる,という処理を書く.state to propsだからそういうことなんだろう.よくわからんけど

次にmapDispatchToProps.storeを更新したい時はActionを発行してReducerに送信(dispatch)するのがreduxのフローなのは第1回で書いた気がするけど,じゃあどうやってdispatchするんですか,というと,dispatch関数にActionを渡すことで行う.この「dispatch()関数にActionを渡す」という,propsとしてコンポーネントに渡すコールバック関数を定義するのがmapDispatchToProps.

で,connect関数にmapStateToPropsとmapDispatchToPropsを渡すと関数が返ってくるので,対象のコンポーネントを引数として実行することで,Reduxと紐付いたコンポーネントとすることができる.これで無事ReactコンポーネントでReduxが使えるようになりました.と言う感じ.合ってるかな…

ちなみに,components/Top.js内にこのconnect処理を書いちゃっても良いんだけど,

qiita.com

こういうのがあって,「見た目を定義するコンポーネント(Presentational component)」と「処理を記述するコンポーネント(Container component)」を分ける考え方があるらしい.というかconnectはそのための機能で,Presentational component内にstateとのアレコレやdispatchの処理を書くと複雑になり,コンポーネントの再利用もしづらくなってしまうため,そこらへんのロジカルな部分を別のところで定義して,propsとしてコンポーネントに渡しましょうね.という考え方.

多分これどの分野でも言えるんだけど,先人が試行錯誤して考案したベストプラクティスは,僕みたいな初学者からしたら「なぜこういうやり方なのか」という過程が見えないままモヤモヤしてしまうというのがあって,当初は「connectって何!!!」って嘆いてた.

要は「Reactコンポーネントで直接storeを読んで,dispatchの処理を書く」っていうやり方だと,Reactコンポーネントにロジカルな処理をやらせるのはコンポーネント再利用性の観点から見てよろしくないというのは簡単に想像できる.

なので,見た目と処理の分離をしたという話.Container Componentの方でまず巨大なstoreの中からPresentational componentが必要としている情報を抜き出してあげて,ついでにPresentational componentが使うdispatchもコールバック関数として渡してあげる.こうすることでPresentational componentはそこらへんのロジカルな部分を全部propsやコールバック関数として受け取るだけになるので,reduxのやりとりを気にせずに見た目に専念できる.

こんな感じでページを作っていく.

routerの設定

react-router-reduxを使うアレコレ.調べつつやってたからどこか違う気もするけど,そこは追々直す.

react-router

だいたいここでまとめてある.
frogwell.hatenablog.jp

Reactでアプリケーションを作る時,ページ遷移をイイカンジにやってくれるライブラリ.URLに応じて描画するコンポーネントを切り替えてくれる.
なんか最近react-router-domになったらしい.従来バージョンの資料が多くて少々混乱してしまった.

とりあえずルーティング機能を動かすためにはここに書いてる感じで書く.
qiita.com

まずreactとreact-routerだけ用いてルーティングするとなると,

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import Top from 'containers/Top';

const history = createBrowserHistory();

ReactDOM.render(
  <BrowserRouter history={history}>
    <Switch>
      <Route path="/" component={Top} />
    </Switch>
  </BrowserRouter>
  document.getElementById('root')
);

みたいに書く.
BrowserRouterコンポーネントで囲って,Routeコンポーネントを増やしていってページを追加していく.ちなみにSwitchで囲わないとpathに指定された条件に合致しているコンポーネントを全て一気に表示してしまう.

historyは↓
github.com

react-router-redux

react-routerでReactアプリにおいてURLで画面遷移をできるようになったけど,URLが変わったという情報もReduxで管理したい時に使う.Reduxはあくまでもstateを管理するものであってURLの変更を知る術は無いわけだから,何かしらかの手段を用いる必要がある.それがこれ.何故今回使ったかって,自分もよく分からんけど色々使い方試したかったからだよ…

で,react-router-reduxを使うと,前章のReduxの項目で示したぶんと合わせて以下のようになる.多分.

index.html

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';
import createBrowserHistory from 'history/createBrowserHistory';
import 'index.css';
import App from 'App';
import registerServiceWorker from 'registerServiceWorker';
import reducers from 'reducers';

const store = createStore(
  combineReducers({
    reducers,
    routing: routerReducer,
  })
);
const history = syncHistoryWithStore(createBrowserHistory(), store);

ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter history={history}>
      <App store={store}/>
    </BrowserRouter>
  </Provider>,
  document.getElementById('root')
);

App.js

import React, { Component } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';
import Top from 'containers/Top'

class App extends Component {
  render() {
    return (
      <div className="App">
        <Switch>
          <Route path="/" component={Top} />
        </Switch>
      </div>
    );
  }
}

export default App;

最初から説明していこうと思う.
reducerにrouterReducerというものを追加したいので,既存のreducersにcombineReducersをかけてstoreを定義している.

historyにはsyncHistoryWithStoreを用いてstoreを繋ぎこみをしている.

あとは一緒かな… ルーティングする部分をApp.jsとして分割したくらい.


多分これで基本的なところの整備が終わったので,頭が悪くて理解に時間がかかっているのでさっさと先へ進めたい.レポート片付けながら作業します.

年末までにReact+Reduxで何か作る その2

続きです.

年末までにReact+Reduxで何か作る その1 - ぷろろろぐ

環境構築

よくわからんしとりあえず

$ npm install -g create-react-app 

からの

$ create-react-app perfect-victory

でお手軽Reactアプリ生成./src/ に諸々を入れれば良い.画像とか参照したいやつは/public/ へ.

これをつかうと

$ npm start

localhost:3000でプレビューできるのでなかなか捗る.

Firebaseを使う

まずは https://console.firebase.google.com へ行き,アプリケーションを新規登録.「ウェブアプリに Firebase を追加」を見れば色々出てくるのであとで使う.

このapiKeyとか見られたらやばくね?と思ったが

testpy.hatenablog.com

らしい.公式チュートリアルもコピペして使ってね!って言ってるし気にしないでおこう…

まず

$ npm install firebase

次に/src/firebase/ にconfig.jsとindex.jsを作って接続情報だけ作る.

firebase/config.js

export const config = {
  apiKey: "*****",
  authDomain: "*****",
  databaseURL: "*****",
  projectId: "*****",
  storageBucket: "*****",
  messagingSenderId: "*****"
};

firebase/index.js

import firebase from 'firebase';
import { config } from './config';
export const firebaseApp = firebase.initializeApp(config);
export const firebaseDb = firebaseApp.database();

使う時は firebase/index.js をimport.その後

const messagesRef = firebaseDb.ref('messages');

みたいにrefを定義して,messagesRef.Push()でJSONを送信.受信もできる.とりあえず今は先に進まずfirebaseの接続情報だけ用意して次に行く.

色々installする

create-react-app直後のpackage.jsonには

  • react
  • react-dom
  • react-scripts

が登録されている.reactとreact-domはReactの根幹ですね.react-scriptsは…→知らん

今回はReduxを用いるので

  • redux
  • react-redux

をinstall.

SPAのためにルーティングしてくれる何かが欲しい.

frogwell.hatenablog.jp

とりあえずreact-routerというものがある.ただしreact-router v4からはreact-router-domとreact-router-nativeに分岐したようで,使う時はreact-router-domをinstallする.あとhistoryというライブラリも使うらしい(?).曖昧なので調べておこう.

ここでReduxを使うならreact-router-reduxというものがあるらしい.というわけで

  • react-router-dom
  • react-router-redux
  • history

をinstall.

Reducerをつくる

何はともあれStoreがないといけない.StoreはReducerによって更新されていくからまずReducerを作らないといけない.

reducers/index.js

import { combineReducers } from 'redux';
export default combineReducers({});

reducerはいくつかに分割して,importしてcombineReducersでひとつにまとめることができるらしい.

www.wazalab.com

今後を考えたら分けたほうが見通しが立てやすそうなので,最初から分割することにする.とりあえずcombineReducersには何も入れていないので空.

相対パスつらい

ところでimportするときにいちいち相対パスで指定していくのは後々ヤバそうなので,絶対パスで指定したい.

webpackでワチャワチャやればできそうなんだけど,create-react-appはそこらへんまとめてやってくれるからブラックボックスだ.と思ってたら,

npm run eject

でcreate-react-appとしてラップされている諸々が表に出てくる.とりあえず実行したら,package.jsonが大変なことになったのと,webpackとかなんだとかのconfigが出てきたのでこれをいじればなんとかなりそう.

構成はこんな感じらしい

mikesorae.hatenablog.jp

とりあえずwebpack.config.dev.jsを読もうにもよくわからんので,webpackのconfigについて色々調べる.

dackdive.hateblo.jp

zukucode.com

webpack.config.dev.js内のresolve.modulesにpaths.jsに定義されているpaths.appSrcを追加したら動いた.

modules: ['node_modules', paths.appNodeModules, paths.appSrc]

これで相対パス地獄を見ずに住むようになったので,index.jsとApp.jsをカラにして,不要なファイルを消して次に至る.

年末までにReact+Reduxで何か作る その1

はじめに

今年で3年目となる完全勝利20xxですが,今回はReact+Reduxの勉強も兼ねてちゃんと作っていこうと思う.ついでに作業メモとして記事も残す.

ちなみに完全勝利とは…?という方のために一応説明すると,僕が2015年の年末に10分くらいで作った糞サイト.ガンダムUCの楽曲"UNICORN"(多分完全勝利の曲と言ったほうが伝わると思う.ググって)の盛り上がるところでちょうど年を越すというだけのもの.身内でSkype年越ししながらみんなで開いて遊んだ.絶対近いうちに曲使ってるアレで怒られが発生しそうなのでなんとかしたい………

完全勝利2016

完全勝利2017

実は知り合いのアレでフロントのインターンをやらせてもらったのだけど,大学が忙しくてあまり開発に携われていないというのと,ガッツリフロントをやったことがないので自分の理解が足りていないなぁという場面が多々あって,それの勉強も兼ねて自分でひとつサービスを作ってみようと思ったという話.

作業記録なのでだらだら書いていきます.用語とかもなるべく説明していこうと思う.なので長くなると思う…理解不足気味なので間違ったところがあったら指摘してください…!

やりたいこと

  • とりあえず「React+ReduxでSPA」
  • ホスティングはFirebase.赤字兄貴チャット機能とか付けられるといいですね
  • Twitter連携とかできるともっと良いのだが…

そもそもReactって何

ここを読むのが早い.

qiita.com

 上の記事の最初に書いてあるとおり,画面に表示するUIパーツ(コンポーネント)を作るためのライブラリ.ボタンとかなんだとか.最終的にそれらを配置していってページを作るみたいな流れになる.

とりあえずWebページ作って軽くDOM操作したいって時はjQuery使うのが速いけど,いちいち要素指定して中身いじって…っていうのを大規模アプリケーションでやると早いうちに死ぬのは目に見えている.

Reactは,親から渡されるpropsと自分の内部状態を示すstateによって自動で内容を書き換えてくれる.便利ですね.

超単純に言うと,TLに流れるツイートのコンポーネントを考えたとき,そのコンポーネントはpropsとしてユーザIDとかユーザ名とかツイート内容とかを受け取る.まずコンポーネントの見た目とかを定義しておいて,ユーザIDの部分に{this.props.userid}みたいに入れておく.その後,画面上に生成するときについでにツイートに関するpropsを渡すと書き換えを勝手にやってくれるぞい.という話.

propsとstateの話はこことかわかりやすいかもしれない.

qiita.com

Reduxって何

わからん.俺達は雰囲気でReduxをやっている.

これは半分冗談ですが残りの半分は本気で,理解しているとは言い難い.

mae.chab.in

ほん,ReduxはFluxを発展させたようなやつなんやな.Fluxってなんや.

qiita.com

よくわからないけど,急激なフロントエンドの進化によってどんどん構造が複雑化していき,フロントでやる内容がとても多くなった.色々デザインパターンはあるけどつらくなってきた… そこでFacebookがFluxというデザインパターンを提唱した.

saneyukis.hatenablog.com

とはいっても別に完全に新しい概念ではなく,従来からあるMVCのイイカンジに落ち着くけど名前がついてないデザインパターンに名前をつけて定義しただけ.らしい.名前がついていることは大切ですね.シンゴジラでも似たようなこと言ってます.

Fluxは以下の図のような一方通行のデータフローを持つ.

f:id:protocol1964:20171119143606p:plain

図の出典: https://github.com/facebook/flux

mae.chab.in

まず,アプリケーションが持つ状態(state)はStoreに格納されている.ViewはStoreを見ているのでStoreが更新されるとスッ…とViewが更新される.コンポーネント毎に状態持ってると管理つらくて死ぬからどっかにまとめたろ,といった感じでしょうか.

ただ,Viewが直接Storeをいじることはなく,更新内容を格納したActionを発行し,そいつを受け取ったDispatcherがStoreをいじる.

もうちょっと詳しく書くと,Viewで何かしらかの操作(onClickとか)が発生したとき,ActionCreatorがそれに合わせてActionを発行する.操作が何であれ,Dispatcherが読める形にするのがActionCreatorのお仕事.そうして発行されたActionはStoreの管理人Dispatcherに渡され,DispatcherはStoreを更新する.最後に,更新されたStoreの情報を元にViewが再構成され…という流れ(合ってる…?)

なるほど.自分で書いててわかってきたぞ.やっぱ記事にするのは大切だね.

 

で,ようやくReduxの話.Reduxとは… これを読んだほうが早い.

qiita.com

まずFluxではStoreを複数持つことを許していたけれど,Reduxは1つのStoreにアプリケーションの全てのstateを格納する.ReduxのStoreは1つだけ.

Viewでの操作からActionCreatorがActionを発行するとこまではFluxと同様.ただ,Actionの送り先はDispatcherではなくReducerという名前になっている.Reducerは現在のStoreの状態と受け取ったActionから,新しいStoreを生成する部分.らしい.よくわからんけど.Storeを更新するという意味ではDispatcherと似たような動きではある.

これは実際にちゃんと書いたこと無いので曖昧なんだけど,DispatcherはActionをStoreに投げる仲介者であるのに対し,ReducerはActionで受け取った情報をもとにStoreを新しく生成するもの.という違い?合ってるかな.

簡単に書くとこのような感じの説明になると思う.ReactでReduxを使う時はReactのstateをReduxで管理するためにreact-reduxを使ったり色々あるらしいが,そこらへんは追々.

 

長くなってしまったのでとりあえずここで区切って,次から実際に作り始めようと思う.というか既に作り始めているのでさっさと書いてしまおう.

DQ11をやって思ったこと【ネタバレ有】

普段全くテレビを見ない(というかテレビがない)のでDQ11の存在を発売3日前くらいに知り,DL版を予約して発売日からちょいちょいプレイしています.

一応表ENDは終わり,今裏ENDに向けて進めているところですが,ちょっと思うところがあったので書きます.

 

以下ネタバレ有り

 

 

 

 

 

 

 

表ENDまではよかったんですよ.世界崩壊~表ENDの間に各PTキャラは精神的に大きく成長します.カミュの妹を助け出すくだりとか,ベロニカの死とか,色々あるじゃん.最後の砦のくだりとかメッチャ興奮しましたよ.世界崩壊~表ENDまでの間で,世界観やキャラクターの肉付けされていく量がすごい大きかったと思うんですよね.

そこでラスボスを倒して世界には平和が訪れるわけです.ここまでの評価なら文句なしの★5です.ベロニカ居ないけど…

で,ベロニカが死んでしまったということで,どうせベロニカ復活に関するイベントでもあるのかなと思っていたのですが,まさかね,時間遡行でそれが実現されるとは思っていませんでしたね.

時間遡行は使いようによっては物凄く強いツールとなります(シュタゲとかそうですね)が,物凄く扱いが難しいツールです.

表ENDが終わると,ちょっとしたイベントの後に世界崩壊直前まで時間遡行してしまいます.これはベロニカが死ぬ直前なので,数十時間ぶりにベロニカを見た時はちょっと涙出そうになりました.

でも,前述したとおり,世界崩壊~表ENDの間でのキャラクターの肉付けが大きかったんですよ.それらが全て”無かったこと”にされてしまうわけです.

例えばホメロス.どちらにせよ魔物側の人間ですが,命の大樹のイベント戦で主人公にあっさり倒されてしまいます.時間遡行前ではホメロスが何故魔物側についたのか知るイベントがありましたが,時間遡行後の世界の流れとしてみると主人公以外の人間はこれを知る術がないわけです.グレイグ可哀想…

カミュの妹も主人公の勇者パワーでわりとあっさりと救出されます.時間遡行前の同イベント時のカミュすごいよかったのに…なんというか拍子抜け.レベルや能力は時間遡行前と一緒だけど,あのイベントが有ると無いとではなんかキャラが別人なんだよな…

…まだプレイ途中なので全部把握していませんが,時間遡行によって”無かったこと”にされてしまう部分があまりにも大きすぎると感じました.僕はむしろ世界崩壊~表ENDまでが本編と思ってる人間なので,とても残念です.

ローシュのくだりやニズゼルファのくだりなど,色々なストーリー展開があって時間遡行を用いたのかと思いますが,時間遡行使わない手はなかったのかなぁ…

あ,でも時間遡行後の世界で拝めるベロニカ(おとなのすがた)とか女賢者セニカちゃんは色々と良かった.

あとNier:automataをプレイしたばかりだったので,ニズゼルファが完全に機械生命体にしか見えない.

こんなところかなぁ.まだLv55なのであと15近く上げるのは流石に厳しい.コツコツやっていきます.

Macintosh SE/30を魔改造した話

こんにちは.はじめてクレカを作ったら2日で給料分の買い物をしてしまい既に八方塞がりの私です.

先日,私の大学のとある研究室からMacintosh SE/30が廃棄品としてやってきました.

f:id:protocol1964:20170417234403j:plain

部室にやってきた瞬間に部員による解体作業の憂き目にあってしまい(一応起動しないことは確認しました),現物の画像は残っておりませんが,解体中の写真がこちらです.

 ロジックボードは先輩が,電源回路は後輩が取り出し,残るは筐体.これを何か活用できないだろうか…そう考えました.

 

「…モニタ一体型PCを組めばいいのでは?」

 

Macintosh SE/30の筐体はそのまま,内部にPCパーツを組み込んで魔改造することに.

さて,SE/30の表示部はブラウン管ですから,それなりに筐体の容積は確保されているとはいえ,やはりPCを組み込むには若干小さめ.

f:id:protocol1964:20170417235517j:plain

ゲーミングPCから引っ張り出してきた750Wの電源装置とCPU,メモリ,SSDはコスト削減の為に維持したい…とし,手元にある部品から行き当たりばったりに組み込んでいきます.

SSDは下部スペースに.ここの有効活用はこれしかない.

f:id:protocol1964:20170417235712j:plain

電源は背面の穴に合わせて設置…したのですが,やはりファンが外装に隠れて吸気できないとまずいので,最終的には内側にファンがくるようにしてあります.結束バンド有能すぎる…(固定の手法として正しいとは言っていない)

f:id:protocol1964:20170418000049j:plain

今回の改造の目安のひとつに,ケース加工は一切行わない,というものがあります.やはりMacintoshの筐体に穴は開けたくないですからね〜〜〜〜()

というか,エアフローがクソすぎるのでケースに穴開けようと思ってたら,先輩方に「ケース加工は甘え」と言われてしまったので,最初からある穴で頑張ることにしました.

次にマザボその他を組み込んでいきます.今回は手持ちのi7 3770を流用したかったので,LGA1155でMicro-ATXなボードを探した結果,ASUSのB75M-PLUSが丁度よく売っていたので採用しました.とはいえ筐体にはMicro-ATXでも入らなかったので,Micro-ATXより一回り小さいボードを探した結果,これしかなかったという感じです(Flex-ATXというらしい?公式サイトではMicro-ATXと呼んでいるけど).

グラボはZOTACのGTX1050Ti.値段的には玄人志向が最安値だったのですが,筐体の横幅を考えると少しでも小型のほうが良かったので採用.結果としてこの判断は正解でした.

f:id:protocol1964:20170418000758j:plain

筐体に組み込みます.グラボは重力などによって垂れ下がらないように無理やり固定.魔改造感が出てきました…

f:id:protocol1964:20170418001039j:plain

ちなみに言い忘れていましたが,今回作ろうとしているのはモニタ一体型の「ゲーミングPC」です.小型のレトロな筐体で最新のゲームが動いている様子はロマンありますよね…(なお発熱

さて,最低限必要な部品は揃ったので,液晶とコントローラ基板が届くまでの間にOSのインストールと設定を済ませておきます.

f:id:protocol1964:20170418001857j:plain

ジョブズに呪い殺されるのを覚悟でインストールしています.

Mac「シテ……コ…ロシテ…」

 

さて,あとは液晶を搭載すれば一体型PCと呼べるものになります.このMacのディスプレイは9インチです.9インチ前後の液晶で入手が容易なもの…といえば,一つしかありませんね.

iPadRetinaディスプレイです.

Retinaは解像度2048*1536.申し分ないレベルです.Amazonで4000円くらいで売られていました.また,Retina用のコントローラ基板も売られており,AbuseMarKにて販売されています.

abusemark.com

コントローラは比較的すぐ届いたのですが,液晶は中国からの発送だったので,少々時間がかかりました.届き次第,早速搭載します.

f:id:protocol1964:20170418002829j:plain

デェェェン

メチャクチャ綺麗で驚きました.UIの倍率を1倍にしていると文字が小さすぎてつらいものがあります.あと画面枠に対して大きい(9.7インチ)ので,四辺が見えなくなってしまいます.ここはグラボのドライバでなんとかなるのかな?調べていないけど.

f:id:protocol1964:20170418003351j:plain

うおぉ

まだ試してないけどこの画面でEliteとかNFSとかやりたいですね…

 

画面を取り付けてテンションが上がっていましたが,現実的な問題がいくつか残っています.マザボインターフェイス部がちょうど筐体の壁面で隠れてしまうので,あそこらへんのポートは全て使用できません.何かしらかの手段を用いて少なくともUSB,オーディオ端子を筐体の穴から出さないと色々と厳しいPCとなってしまいます.

あとうまくいったと思った液晶ですが,バックライトにそれなりに電力を食われるらしく,端子によっては電力不足.また筐体の背面のせいでマザボのUSBが使えなくなってしまうので,何かしらかの方法を用いて電源装置から5Vを引っ張らないといけません.これについてはペリフェラル端子を適当に分解すればいけるかな〜と考えています.

とりあえず,取り急ぎ筐体に納めてみました.

f:id:protocol1964:20170418004201j:plain

チョロっと出てる電源スイッチもしっかり作らねば…まだまだやることは多いです.

完成したら講義に持参するなどのガイジ行為に手を染めるかちょっと悩んでいますが,とりあえず完成度を高めていこうと思います.

放射温度計MLX90614をmbedで使った話 と I2Cの解説のようなメモ

こんにちは.春休みに入った瞬間に生活リズムの位相がπ/2ほど遅れました.直していきたいです.

最近,友人にとあるロボットの大会に誘われたので,機体の設計を友人に任せ,電装系(かっこいい響きですね)を色々と作ったりしています.

というわけで,このロボットに使用するつもりの放射温度計MLX90614をmbedで使った話です.ちなみに使用するマイコンはみんな大好きLPC1114です.

もともとは,秋月で売っている焦電センサ D203Bを用いて,自分で放射温度計を作成するつもりだったのですが,色々と厳しい感じになったので,少々高いですが市販されている放射温度計モジュールを使用することにしました.

赤外線温度センサ MLX90614 - MLX90614BAA - ネット販売

こちらのセンサですが,I2C(厳密にはSMBus)インターフェイスで通信を行える大変便利な物となっております.ただ,日本語の情報があまり存在しないんですよね.センサを動かすにあたって,こちらの記事を参考にさせていただきました.

温度センサMLX90614を使ってみる : Hoge-Puge-Foo
情報が少ない放射温度計センサMLX90614を使ってみた
Sensor de temperatura infrarrojo Melexis | Espacio de Victor Pasilla

まず,データシートも英語だしよく分からんからとりあえずi2c.read()で読むゾ とやっても読めなかったりします.そりゃそうだ.

というわけでデータシートを見ると,通信のタイミングは次のようになっております.

f:id:protocol1964:20170220221818p:plain

まず,スタートコンディションを発行し,続いて7bitのスレーブアドレス(このセンサのアドレスは0x5aです),書き込みを示すWr(0)を送信します.ちゃんと接続されていればスレーブからアクノリッジ(ACK)が送られてくるので,続いて8bitのコマンドを送信します.再びACKがスレーブから帰ってくるので,リピートスタートコンディション(Sr)を発行します.再び7bitのスレーブアドレスと,今度は読み取りを示すRd(1)を送信し,ACKが返ってくると,スレーブから3バイトのデータが送られてきます.3バイト目を受信し,ACKを返してからストップコンディションを発行して,一連の通信は終了します.

…と,図に書いてあることをつらつらと文章に起こしましたが,ここで用語の解説を挟みます.自分も最近勉強したばかりなので間違えていたら教えてください…


まずスタートコンディションとは,マスタが「今から俺がマスタになって通信するゾ」といった感じで,バスに接続されているスレーブ全体に対して送信するメッセージです.実際の信号では,SCLが上がっている時にSDAを下げることを示します.

オシロスコープで見るとこんな感じ.上がSCL,下がSDAです.
f:id:protocol1964:20170220223754j:plain

最初,SCLが上がっている時にSDAが下がり,SCLが動き出してデータのやり取りをしているのが分かりますね.


次にアクノリッジ
簡単に言うと,受信側がしっかりとデータを受信できているか,その確認のために発行されます.
上の図で言うと,一瞬だけピョコッと出ている信号に相当します.送信側は受信側のACKを受信してから次の1バイトを送信するので,ACKが帰ってこないと通信が進みません.


続いてリピートスタートコンディション.
I2Cの通信は,前述のスタートコンディションから始まり,スレーブアドレスとR/W情報の送信,データのやり取りの後,ストップコンディションという,スタートコンディションとは反対にSCLが上がっている時にSDAを上げることで,「通信終了ォ!」とマスタが宣言し,終了します.
この一連のやりとりの中で,最初にスレーブアドレスと同時にR/W情報も送信しているので,一連の過程において送信者,受信者の立場は固定されています.ここで,通信中に立場を変えたい場合(最初に読みたいアドレスを送り,帰ってくる情報を見たい等)があります.
この時は,ストップコンディションを発行せずに,再びスタートコンディションを発行し,R/Wビットを変更することで実現できます.この時に発行するスタートコンディションの事を,リピートスタートコンディションと呼びます.

赤枠の部分,SCLが上がっているタイミングでSDAが下がり,スタートコンディションが発行されていますね.

f:id:protocol1964:20170220231727j:plain


I2Cよく知らんけどとりあえず使ってるという人でも,これで前述の通信タイミングの説明を理解できるかと思います.

さて,まずは実際に動いたソースがこちら

#include "mbed.h"

Serial pc(dp16, dp15);
I2C i2c(dp5, dp27);

int main() {
    i2c.frequency(20000);
    while(1){
        char recieve[3];
        i2c.start();
        if(i2c.write(0xb4)){
            if(i2c.write(0x07)){
                i2c.read(0xb5, recieve, 3);
            }
        }
        i2c.stop();
        int h = recieve[1] << 8;
        int l = recieve[0];
        int tmp = h+l;
        float temp = (((float)tmp * 2.0) - 27315.0)/100.0;
        printf("temp:%f\n\r", temp);
        wait(0.5);
    }
}

ifの中でwriteを実行していますが,多分ちょっと違うと思うのであまり気にしないでください.実行順序の参考程度に.

まず,I2Cのクロック周波数について,データシートにはこう書いてあります.

f:id:protocol1964:20170220231138p:plain

100kHz〜10kHzで使ってね!とのことですが,10kHzだと通信がうまく行きませんでした.適当に設定しましょう.

あとは指示されたタイミング通りに関数を叩くだけです.


まずスタートコンディションを発行するために,i2c.start()を実行.

次にスレーブアドレス0x5aと,書き込みを示す0を送信するため,i2c.write(0xb4)を実行します.0xb4とは,要はこういうことです.

f:id:protocol1964:20170220233546p:plain

次に,アドレス0x07を送信します.詳細はデータシート8.3.4項まで.


ここで,i2c.write()の連続送信を使って0xb4と0x07を送ればええやん!と言われる気がします.
i2c.write()の連続送信を用いると,スタートコンディションとストップコンディションが自動発行され,送信するだけならこの関数1つ叩けば終わります.ただ,送信後にリセットスタートコンディションを発行したいので,ストップコンディションが自動発行されるのはマズイ.

i2c.write()の4つ目の引数をtrueにすれば,連続送信後にストップコンディションが自動発行されずに済むハズなのですが,オシロで見てみると何故かストップコンディションが発行されている…ので,このような形になりました.もしかしたら,ここらで悩んでいた時に設定していたSCLの周波数が遅すぎて不具合があったのかもしれません.


さて,アドレス,データの送信まで終わったので,再びスタートコンディションを発行して,今度は読み取りです.
3バイトが連続で送られてくるので,i2c.read()の連続受信を使用します.アドレスは上の図と同様に,Readの1を当てて0xb5です.
i2c.read()の連続受信ではスタートコンディションが自動発行されるので,i2c.start()は省略しています.

3バイト受信し,最後にi2c.stop()でストップコンディションを発行して終了です.記事書きながら思ったのですが,i2c.read()の連続受信はストップコンディションを自動発行するはずなので,i2c.stop()はやらなくても良い気がします.

最後に,受信したデータを計算することで摂氏温度へ変換します.


…と,このような感じで動かせました.

センサの特性上,物体から離れるほど検知する温度は低くなっていき,対象物の材料によって放射率も変わってくるので,そこらへんは要調整ですね.

大会まで1ヶ月ちょい.この調子で大丈夫なんだろうか…


[追記]
ググったらmbedライブラリが出てきた.なんで調べなかったんだろう… mbed恐るべし
MLX90614 I2C Infrared Thermometer | mbed

unity-socket-ioでアドレスを変えたい

unityでsocket.ioを使うには以下のアセットを使っています.

github.com

使い方は調べれば出てくるので割愛します.

で,prefabをとりあえずsceneにぶちこむとこうなりますね.

f:id:protocol1964:20170204101607p:plain

基本的にはこの SocketIOComponent の url を任意の値にして実行するのですが,実行毎にアドレスが変わるとか,定まってないときのメモ.

GameObject.Findとかでオブジェクトを取得して,SocketIOComponentのurlを直接変えればいいじゃんと思ったんですが,これ初期値をずっと引っ張るっぽいですね.

ということで,prefabのurlをスクリプトから変えてあげて,そいつをInstantiate.

url間違えたー!とかいう場合は,一旦Destroyしてあげて,prefabのurl直してもう一回生成.