【IoT入門】小型Arduino互換機で温度を取得する

最初に

IoT機器を作りたいと思い、温度取得まで出来たのでここにメモを残します。

用意するもの

・ESP01
(Amazonだと1個500円くらいかな…)
image.png

・BMP280
(Amazonだと安くて1個200円くらい、ただ湿度がとれるBME280のほうが少し高いけど良いかもしれません。)
image.png

代用は効くけど、あると便利なもの
・ESP01書き込み機材
image.png

※Amazonにもありますが、改造が必要です。

・ブレッドボード
・ブレッドボード用の線材
image.png

・USB to Serial変換
image.png

#実施

配線

image.png

1.準備

モジュールを利用するために、下記のURLをボードマネージャに追加します。

http://arduino.esp8266.com/stable/package_esp8266com_index.json  
image.png image.png

ボードはボードマネージャからespと探すとesp8266というのが出るので、この設定を利用します。
image.png

image.png

私の設定値はこんな感じです。

image.png

2.Code

モジュールインストール

まず、利用するモジュールをダウンロードします。
ライブラリをインクルードからライブラリの管理を選択します。
image.png

ライブラリマネージャBME280と検索します。
image.png
BME280というモジュールをインストールします。

ソースコード編集

モジュールにはコーディング例が入っているのでこれを利用します。
ファイルスケッチ例と選択して、下の方に行きます
image.png

BME280の中にBME_280_I2C_Testという項目があるのでこれを選択します。
image.png

ソースコードを下記のように変更します。
Wire.begin();を削除し、2行追加します。
これはi2cで利用するポートの初期化と指定です。

  Wire.pins(0, 2);  //追加  
  Wire.begin(0, 2); //追加  
 //Wire.begin();      削除  
  
  while(!bme.begin())  
  {  
    Serial.println("Could not find BME280 sensor!");  
....  

3.書き込みとテスト

これを、書き込み用の環境に接続し、書き込みを行います。

書き込みボタン
image.png

下記のような表示が出れば正常に終了です。

Executable segment sizes:  
IROM   : 238256          - code in flash         (default or ICACHE_FLASH_ATTR)   
IRAM   : 27680   / 32768 - code in IRAM          (ICACHE_RAM_ATTR, ISRs...)   
DATA   : 1260  )         - initialized variables (global, static) in RAM/HEAP   
RODATA : 888   ) / 81920 - constants             (global, static) in RAM/HEAP   
BSS    : 25456 )         - zeroed variables      (global, static) in RAM/HEAP   
最大958448バイトのフラッシュメモリのうち、スケッチが268084バイト(27%)を使っています。  
最大81920バイトのRAMのうち、グローバル変数が27604バイト(33%)を使っていて、ローカル変数で54316バイト使うことができます。  
esptool.py v2.8  
Serial port /dev/cu.wchusbserial1410  
Connecting....  
Chip is ESP8266EX  
Features: WiFi  
Crystal is 26MHz  
MAC: b4:e6:2d:26:d9:43  
Uploading stub...  
Running stub...  
Stub running...  
Configuring flash size...  
Auto-detected Flash size: 1MB  
Compressed 272240 bytes to 198387...  
  
Writing at 0x00000000... (7 %)  
Writing at 0x00004000... (15 %)  
Writing at 0x00008000... (23 %)  
Writing at 0x0000c000... (30 %)  
Writing at 0x00010000... (38 %)  
Writing at 0x00014000... (46 %)  
Writing at 0x00018000... (53 %)  
Writing at 0x0001c000... (61 %)  
Writing at 0x00020000... (69 %)  
Writing at 0x00024000... (76 %)  
Writing at 0x00028000... (84 %)  
Writing at 0x0002c000... (92 %)  
Writing at 0x00030000... (100 %)  
Wrote 272240 bytes (198387 compressed) at 0x00000000 in 22.6 seconds (effective 96.3 kbit/s)...  
Hash of data verified.  
  
Leaving...  
Hard resetting via RTS pin...  

実行環境に接続して、電源をいれれば温度が取得できます。
image.png

感想

実際に、ハードウェアがからんでくるため、いろいろなところで悩んでしまいました。
電気系の勉強もサボっているため、引き続きしていきたいと思います。
参考になったら良いねください!

詰まったところ、詰まりそうなところ

USB to Serial はMACではドライバが必要

組み込み系のケーブルは基本的にはドライバが必要です。
購入した機器により、異なるのですが基本的には「型番」で検索をすると出てきます。
「型番」が不明な場合は、基板上の英数字で検索すればいいと思います。

恐ろしく読みづらいので、私はスマホで撮影して拡大して見ています。

image.png ![image.png](/image/b66528f3-a5f9-46a4-b1be-1bc8b55298d2.png)

GNDレベルを同一にしないとSerial通信ができない

シリアル通信を行う場合、該当の機器と電位差が発生してしまう事があります。
そうすると、うまく通信が行えない場合があるので、各グランド線は同一にしておくと良いです。
※私もこれで通信できず悩みました…

電源が安定化しないと動かない場合がある(書き込み失敗も含む)

今回は3.3Vでしたが、電源がこの電圧を出力できていてもうまく行かない場合があります。
理由として、電気を消費し始めると電圧が下がってしまい安定動作しなくなります。
対策としてキャパシタなどをグランドと3.3Vの間に挟んでおくと、電流が不足した場合にそこから
取得するようなイメージになるので安定化します。
私も電源回路には10μF0.1μFのキャパシタを挟んでいます。

こんな感じです。
image.png

シリアル通信で変な文字が出る

Serialの通信速度が異なる場合になります。
ソースコードで定義されている通信速度に合わせる、または何度か変更してみるのもいいと思います。

Serialが複数ポートあり、選択に失敗している。

私もよく間違えるのですが、2台以上のSerialポートが有る場合に選択を間違えているとうまく動きません。
ツールシリアルポートから選択が可能です。

image.png

書き込みモードにならない

Amazonなどに売っている、書き込み用の機材は一部配線を改造しなければ書き込みできません。
私もこのようにGPIO0GNDショートさせています。
image.png
image.png

cssのみで立体な球体を表現

背景

娘と公園に行って、木になっていた赤い実がキレイなのでCSSで表現してみました。

結論

このようになりました。

See the Pen akai mi by hashito (@hashito) on CodePen.

説明

CSSのmi1クラスにて実態の赤い実を表現して、疑似要素beforeで光の反射を白い丸とFilterで表現、afterで影部分をbox-shadowとFilterで表現しました。
Filter使いやすいですね。

ただ、ヒナまつりのイクラ丼とかをコレで表現するとクソ重そう…

10分で作って注文する名刺

最初に

WEB系のプログラマの皆さんはかなりの割合で自分のホームページとか持っていると思います。
これを飲み会のときに「こんなページ作っているんですよ!」とか言うのあまりスマートではないな…
と思い立ち、自分で名刺を作ったので作り方を共有します。

作り方

用意するもの

・Googleのアカウント
・名刺の内容(自由)
・830円捻出可能なクレジットカード

1.名刺作成

ここにテンプレートをおいておきます。
これを好きなようにコピーしてください。
サイズも規定のサイズ、アイコンなどもはみ出さない様になっています…

QRコードは下記のようなサイトで作成し、更新してください。

QRコード[二次元バーコード]作成 【無料】
https://www.cman.jp/QRcode/

私のサイトにリンクされてしまいます…

作成が終わったら、「ファイル」→「ダウンロード」→「PDF」でダウンロードしておいてください。
image.png

枠ギリギリだとはみ出してしまって、修正を繰り返す事になります。  
こちらで私は問題なかったのですが…問題ありましたらコメントいただければ助かります…  

2.注文する

今回、私が利用したのは下記です。
<東京カラー印刷>
https://www.tcpc.co.jp

他のサイトだと色々とサイズが違うかもしれないので、
別途調べてみてください。

ここから下記のように選びました。

用紙サイズ:91x55mm(初期値)
用紙種類:特白アートポスト(初期値)
用紙の厚さ:180kg(初期値)
色数:片面フルカラー
条件:100枚 4営業日

400枚でもあまり変わらないのですが…  
ミスが有ることをを考えて最初は少なく試したほうがオススメです…  
私もTwitter アカウント名をミスりました…  

この内容で注文します。

3.アップロードする

少し分かりづらいのですが、
右上のマイページをクリックします。
image.png

その後、「購入履歴・ご入稿」をクリックします。

image.png

次のページから入稿を選択すればアップロードする画面にたどり着くかと思います…
(印刷済みであるため…画像がないので申し訳ないです)
先程、ダウンロードしたPDFをアップロードして完了です。

大変迷惑な話ですが、私はこのあと色々とはみ出していたため数回アップロードし直しました。  
問題がある場合メールで連絡が来ます。  

結論

こんな感じで到着します。
image.png

Riot.jsでブロックを落とすゲーム

背景

前回、蛇のゲームつくったので他のも作ってみようと思った。
そして完成したものは下記
ブロック落とすゲーム
http://hasito.com/block-down/

※蛇のゲーム
https://qiita.com/hashito/items/a38a25665ad7befb5483
http://hasito.com/snake/

仕様

  • ブロックは2個1組で落下で回転可能
  • 色はランダムで決定
  • 同一色4つがつながった場合に削除
  • 下のブロックが消えた場合は落下

コード

ブロックオブジェクト

  • 位置情報、色情報、ポーズ情報を保持
  • 実態としてマップは参照しない
//色は3種類として、poseは全4種類と定義  
    var colors = [0xff0000,0xffff00,0x0000ff];  
    var poses  = [[[0,0],[-1,0]],[[0,0],[0,-1]],[[-1,0],[0,0]],[[0,-1],[0,0]]];  
    var Blok=(point,pose,cols)=>{  
//初期化、とりあえず指定ない場合はランダム  
      var _point  = point;  
      var _pose   = (pose!=undefined)?pose:Math.floor(Math.random()*poses.length);  
      var _colors = (cols!=undefined)?cols:[colors[Math.floor(Math.random()*colors.length)],colors[Math.floor(Math.random()*colors.length)]];  
      return {  
//Point取得関数  
        point   :()=>{return _point; },  
//pose取得関数  
        pose    :()=>{return poses[_pose];  },  
//次のposeへ変更  
        poseNext:()=>{  
          _pose = (++_pose<poses.length)?_pose:0;  
          },  
//color取得関数  
        colors  :()=>{return _colors;},  
//ブロック位置情報取得  
        get     :function(){return this._get([0,0]);},  
//特定の場所分移動させた場合のPointの取得  
        _get    :(p)=>{  
          return  [  
            [poses[_pose][0][0]+_point[0]+p[0],poses[_pose][0][1]+_point[1]+p[1]],  
            [poses[_pose][1][0]+_point[0]+p[0],poses[_pose][1][1]+_point[1]+p[1]]  
          ];  
        },  
//ポイント移動用関数  
        set     :(p)=>{_point = [_point[0]+p[0],_point[1]+p[1]];}  
      };  
    };  

ゲームオブジェクト

  • メインのオブジェクト
  • 描画先マップ、仮想のマップ、ブロック、ゲーム状態などを保持
    var Game=(view)=>{//view=描画用2次元配列  
      var _top_buff = 2;//高さの余分な部分(不要?)  
      var _w        = view.length;//幅情報(描画範囲を参照)  
      var _h        = view[0].length+_top_buff;//縦幅  
      var _map      = false;//マップの実態 初期化されるまではfalse  
      var _blok     = false;//ブロックの実態 初期化されるまではfalse  
      var _view     = view;//表示用マップ  
      var _status   = 0;//ゲームステータス 0:停止,1:ゲーム中,2:ゲーム終了  
      var _his      = [];//削除履歴 cnt:連鎖情報,delblock:削除ブロック数,step:何step目情報  
      var _step_cnt = 0;//stepカウント  
      return {  
//削除履歴取得  
        get_his:function(){  
          return _his;  
        },  
//表示処理  
        drawn:function(){  
          _map.forEach((w,wi)=>{  
              w.forEach((h,hi)=>{  
                if(hi<_top_buff){  
                  return;  
                }else{  
                  _view[wi][hi-_top_buff]=h;  
                }  
              });  
          });  
          /*blockの表示 */  
          var bp  = _blok._get([0,-_top_buff]);  
          var bpc = this.map2block_chk(bp);  
          if(bpc[0]===0)_view[bp[0][0]][bp[0][1]]=_blok.colors()[0];  
          if(bpc[1]===0)_view[bp[1][0]][bp[1][1]]=_blok.colors()[1];  
        },  
//ブロック初期化関数   
//戻り値として出現場所にブロックがあるかを返す(呼び出し元で参照しゲーム終了か判断するのに使っています…)  
        block_init:function(){  
          var p=[Math.floor(_w/2),1];  
          _blok=Blok(p);  
          return (this.map2block_chk([p])[0]===0);  
        },  
//マップ初期化関数  
        map_init:()=>{  
          _map=new Array(_w);  
          for(i=0;i<_w;i++){  
            _map[i] = new Array(_h);  
            _map[i].fill(0);  
          }  
        },  
//マップ情報確認関数  
//ポイントの配列で対象箇所にブロック(数値)があるか枠外(false)かを返します  
        map2block_chk:function(points,f){  
          var ret=[];  
          points.forEach(v=>{  
            var f1 = _w >  v[0];  
            var f2 = 0  <= v[0];  
            var f3 = _h >  v[1];  
            var f4 = 0  <= v[1];  
            ret.push(f1&&f2&&f3&&f4&&(_map[v[0]][v[1]]));  
          });  
          return ret;  
        },  
// 削除対象ブロック検索処理  
// 戻り値は削除件数  
        map2dels:function (){  
          var _search=(p,chain,c,zumi)=>{  
            var cc = zumi.filter((v)=>{return (v[0]===p[0])&&(v[1]===p[1]);})  
            zumi.push(p);  
            if((cc.length==0)&& this.map2block_chk([p])[0]&&(_map[p[0]][p[1]]==c)){  
              chain.push(p);  
              _search([p[0]+1,p[1]  ],chain,c,zumi);  
              _search([p[0]-1,p[1]  ],chain,c,zumi);  
              _search([p[0]  ,p[1]+1],chain,c,zumi);  
              _search([p[0]  ,p[1]-1],chain,c,zumi);  
            }  
          };  
          // 削除処理  
          var dels=[];  
          _map.forEach((w,wi)=>{  
            w.forEach((h,hi)=>{  
              var _chain = [];  
              var _zumi  = dels.slice(0, dels.length);;  
              (h)&&_search([wi,hi],_chain,h,_zumi);  
              if(_chain.length>=4){  
                dels = _chain.concat(dels);  
              }  
            });  
          });  
          return dels;    
        },  
// 降下処理  
        map2down:function(){  
          var cnt=0;  
          _map.forEach((w,wi)=>{  
            w.forEach((h,hi)=>{  
              var f={};  
              var np    = [wi,hi+1];  
              var npchk = this.map2block_chk([np],f)[0];  
              var f1 = h;  
              var f2 = (npchk===0);  
              if(f1 && f2){  
                cnt++;  
                _map[np[0]][np[1]]=_map[wi][hi];  
                _map[wi][hi]=0;  
              }  
            });  
          });  
          return cnt;  
        },  
//ブロック移動イベント  
        left:function(){  
          var c=this.map2block_chk(_blok._get([-1,0]));  
          if((c[0]===0) && (c[1]===0)){_blok.set([-1,0])}  
        },  
//ブロック移動イベント  
        right:function(){  
          var c=this.map2block_chk(_blok._get([1,0]));  
          if((c[0]===0) && (c[1]===0)){_blok.set([1,0])}  
        },  
//ブロック降下イベント  
        down:function(){  
            var p=_blok.get();  
            _map[p[0][0]][p[0][1]]=_blok.colors()[0];  
            _map[p[1][0]][p[1][1]]=_blok.colors()[1];  
            this.block_init();  
        },  
//ブロック回転イベント  
        space:()=>{  
          _blok.poseNext();  
        },  
//ブロック/マップ初期化関数  
        init:function(){  
          this.map_init();  
          this.block_init();  
        },  
//ステータスの変更  
        statusChange:function(){  
          if(++_status>=3){  
            _status=0;  
            this.init();  
          }  
        },  
//step処理(main)  
        step:function(){  
          _step_cnt++;  
          if(_status!=1) return;  
          // 降下処理  
          var f = {};  
          var c = this.map2block_chk(_blok._get([0,1]),f);  
          if((c[0]===0) && (c[1]===0)){  
            _blok.set([0,1]);  
          }else{  
            // 移動できない場合は固定ブロックに変換  
            var p=_blok.get();  
            _map[p[0][0]][p[0][1]]=_blok.colors()[0];  
            _map[p[1][0]][p[1][1]]=_blok.colors()[1];  
            if(!this.block_init()){  
              _status=2;  
            }  
          }  
          var _delcnt=0;  
          do{  
            var downs= this.map2down();// 全体降下処理  
            var dels = this.map2dels();// 削除対象の検索  
            dels.forEach(v=>{  
              _map[v[0]][v[1]]=0;  
            });  
            if(dels.length>0){  
              _delcnt++;  
              _his.push({step:_step_cnt,cnt:_delcnt,delblock:dels.length});  
            }  
          }while((dels.length>0)||(downs!=0))// 落下移動がなく削除もない場合は終了  
        }  
      }  
    }  
  

ソース全体

<html>  
  <head>  
    <title>Hello Riot.</title>  
    <meta charset="UTF-8"/>  
  </head>  
  <body>  
    <block-down></block-down>  
    <script type="riot/tag" src="block-down.tag"></script>  
   <script src="riot/riot+compiler.min.js"></script>  
    <script>riot.mount('block-down')</script>  
  
  
  </body>  
</html>  
  
<block-down>  
    <h1>ブロックおとすやつ</h1>  
    <h3>ESCキーで開始してください</h3>  
    <svg riot-width={ w*15 } riot-height={ h*15 } >  
      <g each={d,i in view} transform={('translate('+(i*15)+',0)')}>  
        <rect each={d2,i2 in d} x="0" riot-y={i2*15} data-point='{(JSON.stringify({x:i,y:i2}))}' width="15" height="15" title=""  fill="#{('000000'+d2.toString(16)).slice(-6)}"></rect>  
      </g>  
    </svg>  
    <div style="float:right;">  
      <h3 each={his}>{step}step目:{cnt}連鎖成功:{delblock}個</h3>  
    </div>  
  <script>  
    var self   = this;  
    self.tme   = false;  
    self.tme2  = false;  
    self.view  = [];  
    self.his   = [];  
  
    var colors = [0xff0000,0xffff00,0x0000ff];  
    var poses  = [[[0,0],[-1,0]],[[0,0],[0,-1]],[[-1,0],[0,0]],[[0,-1],[0,0]]];  
    var Blok=(point,pose,cols)=>{  
      var _point  = point;  
      var _pose   = (pose!=undefined)?pose:Math.floor(Math.random()*poses.length);  
      var _colors = (cols!=undefined)?cols:[colors[Math.floor(Math.random()*colors.length)],colors[Math.floor(Math.random()*colors.length)]];  
      return {  
        point   :()=>{return _point; },  
        pose    :()=>{return poses[_pose];  },  
        poseNext:()=>{  
          _pose = (++_pose<poses.length)?_pose:0;  
          },  
        colors  :()=>{return _colors;},  
        get     :function(){return this._get([0,0]);},  
        _get    :(p)=>{  
          return  [  
            [poses[_pose][0][0]+_point[0]+p[0],poses[_pose][0][1]+_point[1]+p[1]],  
            [poses[_pose][1][0]+_point[0]+p[0],poses[_pose][1][1]+_point[1]+p[1]]  
          ];  
        },  
        set     :(p)=>{_point = [_point[0]+p[0],_point[1]+p[1]];}  
      };  
    };  
    var Game=(view)=>{  
      var _top_buff = 2;  
      var _w        = view.length;  
      var _h        = view[0].length+_top_buff;  
      var _map      = false;  
      var _blok     = false;  
      var _view     = view;  
      var _status   = 0;  
      var _his      = [];  
      var _step_cnt = 0;  
      return {  
        get_his:function(){  
          return _his;  
        },  
        drawn:function(){  
          _map.forEach((w,wi)=>{  
              w.forEach((h,hi)=>{  
                if(hi<_top_buff){  
                  return;  
                }else{  
                  _view[wi][hi-_top_buff]=h;  
                }  
              });  
          });  
          /*blockの表示 */  
          var bp  = _blok._get([0,-_top_buff]);  
          var bpc = this.map2block_chk(bp);  
          if(bpc[0]===0)_view[bp[0][0]][bp[0][1]]=_blok.colors()[0];  
          if(bpc[1]===0)_view[bp[1][0]][bp[1][1]]=_blok.colors()[1];  
        },  
        block_init:function(){  
          var p=[Math.floor(_w/2),1];  
          _blok=Blok(p);  
          return (this.map2block_chk([p])[0]===0);  
        },  
        map_init:()=>{  
          _map=new Array(_w);  
          for(i=0;i<_w;i++){  
            _map[i] = new Array(_h);  
            _map[i].fill(0);  
          }  
        },  
        map2block_chk:function(points,f){  
          var ret=[];  
          points.forEach(v=>{  
            var f1 = _w >  v[0];  
            var f2 = 0  <= v[0];  
            var f3 = _h >  v[1];  
            var f4 = 0  <= v[1];  
            ret.push(f1&&f2&&f3&&f4&&(_map[v[0]][v[1]]));  
          });  
          return ret;  
        },  
        // 削除対象ブロック検索処理  
        map2dels:function (){  
          var _search=(p,chain,c,zumi)=>{  
            var cc = zumi.filter((v)=>{return (v[0]===p[0])&&(v[1]===p[1]);})  
            zumi.push(p);  
            if((cc.length==0)&& this.map2block_chk([p])[0]&&(_map[p[0]][p[1]]==c)){  
              chain.push(p);  
              _search([p[0]+1,p[1]  ],chain,c,zumi);  
              _search([p[0]-1,p[1]  ],chain,c,zumi);  
              _search([p[0]  ,p[1]+1],chain,c,zumi);  
              _search([p[0]  ,p[1]-1],chain,c,zumi);  
            }  
          };  
          // 削除処理  
          var dels=[];  
          _map.forEach((w,wi)=>{  
            w.forEach((h,hi)=>{  
              var _chain = [];  
              var _zumi  = dels.slice(0, dels.length);;  
              (h)&&_search([wi,hi],_chain,h,_zumi);  
              if(_chain.length>=4){  
                dels = _chain.concat(dels);  
              }  
            });  
          });  
          return dels;    
        },  
        // 降下処理  
        map2down:function(){  
          var cnt=0;  
          _map.forEach((w,wi)=>{  
            w.forEach((h,hi)=>{  
              var f={};  
              var np    = [wi,hi+1];  
              var npchk = this.map2block_chk([np],f)[0];  
              var f1 = h;  
              var f2 = (npchk===0);  
              if(f1 && f2){  
                cnt++;  
                _map[np[0]][np[1]]=_map[wi][hi];  
                _map[wi][hi]=0;  
              }  
            });  
          });  
          return cnt;  
        },  
        left:function(){  
          var c=this.map2block_chk(_blok._get([-1,0]));  
          if((c[0]===0) && (c[1]===0)){_blok.set([-1,0])}  
        },  
        right:function(){  
          var c=this.map2block_chk(_blok._get([1,0]));  
          if((c[0]===0) && (c[1]===0)){_blok.set([1,0])}  
        },  
        down:function(){  
            var p=_blok.get();  
            _map[p[0][0]][p[0][1]]=_blok.colors()[0];  
            _map[p[1][0]][p[1][1]]=_blok.colors()[1];  
            this.block_init();  
        },  
        space:()=>{  
          _blok.poseNext();  
        },  
        init:function(){  
          this.map_init();  
          this.block_init();  
        },  
        statusChange:function(){  
          if(++_status>=3){  
            _status=0;  
            this.init();  
          }  
        },  
        step:function(){  
          _step_cnt++;  
          if(_status!=1) return;  
          // 降下処理  
          var f = {};  
          var c = this.map2block_chk(_blok._get([0,1]),f);  
          if((c[0]===0) && (c[1]===0)){  
            _blok.set([0,1]);  
          }else{  
            // 移動できない場合は固定ブロックに変換  
            var p=_blok.get();  
            _map[p[0][0]][p[0][1]]=_blok.colors()[0];  
            _map[p[1][0]][p[1][1]]=_blok.colors()[1];  
            if(!this.block_init()){  
              _status=2;  
            }  
          }  
          var _delcnt=0;  
          do{  
            var downs= this.map2down();// 全体降下処理  
            var dels = this.map2dels();// 削除対象の検索  
            dels.forEach(v=>{  
              _map[v[0]][v[1]]=0;  
            });  
            if(dels.length>0){  
              _delcnt++;  
              _his.push({step:_step_cnt,cnt:_delcnt,delblock:dels.length});  
            }  
          }while((dels.length>0)||(downs!=0))// 落下移動がなく削除もない場合は終了  
        }  
      }  
    }  
  
    /* タイマーセット関数(一定ごとに時を進める用)  
    */  
    var tm_set=()=>{  
      if(self.tme)clearInterval(self.tme);  
      self.tme=setInterval(()=>{  
        self.tm-=0.1;  
        tm_set2(self.tm);  
      },1000);  
    };  
    /* タイマーセット関数(メイン処理用)  
    */  
    var tm_set2=(v)=>{  
      if(self.tme2)clearInterval(self.tme2);  
      self.tme2=setInterval(()=>{  
        self.game.step();  
        self.game.drawn();  
        self.his=self.game.get_his();  
        self.update();  
      },v);  
    };  
    /* マウント前イベント  
    */  
    self.on("before-mount",(v)=>{  
      self.h=40;  
      self.w=20;  
      self.view=new Array(self.w);  
      for(i=0;i<self.w;i++){  
        self.view[i] = new Array(self.h);  
        self.view[i].fill(0);  
      }  
      self.game=Game(self.view);  
      self.game.init();  
      self.tm=300;  
      document.onkeydown = function (e){  
        var k = e.keyCode;  
        var g = self.game;  
        if(k==37){g.left();};//左  
        if(k==40){g.down();};//落下  
        if(k==39){g.right();};//右  
        if(k==32){g.space();}//回転  
        if(k==27){g.statusChange();}//game開始  
      };  
      tm_set();  
    });  
  
  
  </script>  
</block-down>  
  

残課題

  • 2度目にいろいろな情報が残ってしまう(雑)
  • 細かすぎてどこに落ちて言っているかがわかりにくい
  • 横移動時の反応が悪い気がする
  • 消えた時に一括で消えるのでどう連鎖しているか不明

Cloud StorageへPythonを使ってファイルをアップロードする方法【RaspberryPIで定点カメラを作る

最初に

RaspberryPIで定点カメラを作ったので備忘録 第一弾です。
動画も作成したのでよろしく!
IMAGE ALT TEXT HERE

分かること

  • google cloud storageのアップロード方法

作成

用意するもの

  • GoogleCloudPlatformのアカウント

手順

1.GoogleCloudPlatform関連

GoogleCloudPlatform(GoogleCloudStorage)って?

基本的には下記のようなサービスです。

Googleが提供するクラウドにある巨大なフォルダのようなもので、ファイルを書き込んだり 読み込んだりが簡単に出来るサービスです。
価格は無料分もありますが、読み込み・書き込み・存在するデータサイズで課金されていきます。

Cloud側設定
GoogleCloudPlatformにログインしていただき、横のStorage>ブラウザをクリックします。
image.png

表示されたページでバケットを作成をクリックします。
image.png

こんな感じでバケットを作成します。
バケット名は自由でいいです。
image.png
image.png
image.png

作成が完了したら、アクセスするサービスアカウントを作成します。
左のメニューからIAMと管理>サービスアカウントをクリックします。
image.png

画面上部のサービスアカウントを作成をクリックします
image.png

名前は適当でいいです。わかりやすいものにしてください。
image.png

サービスアカウントの権限ではCloud Storageの権限を付与します。
ロールではCloud Storage>ストレージの管理者としておいてください
image.png

最後にキーの作成をクリック
image.png

JSONタイプを選択した状態で作成をクリックするとダウンロードできます。
このファイルは後で利用するので大切に保管しておいてください。
また、この情報が公になると大量の請求が来たりするのでご注意ください。
image.png

ローカル側で確認
Pythonの環境を構築していきます。
今回、python3を利用するため、下記のコマンドで環境を入れます。

sudo apt install python3 python3-pip  

インストールが完了したら、Cloud Storageにアクセスするため、google-cloud-storageというpythonのモジュールをインストールします。

pip3 install google-cloud-storage  

そして、テスト用のコードを作成します。
下記のコードをnanoなどで作成してください。

#nano で作る場合  
nano file2gs.py  

※バケット名の部分は自分のバケット名を入れてください。

# /home/pi/image.jpgをアップロードするお試しコード  
from google.cloud import storage  
import os  
os.environ["GOOGLE_APPLICATION_CREDENTIALS"]="/home/pi/gkey.json"  
client = storage.Client()  
bucket = client.get_bucket('{バケット名}')  
blob2 = bucket.blob('test.txt')  
blob2.upload_from_filename(filename='/home/pi/test.txt')  
  

実行する前に先程ダウンロードしたjsonファイルを開いてコピーしてgkey.jsonというファイルに書き込みます。

#nano で作る場合  
nano gkey.json  

開いたファイルに内容をコピーしてください。
また、アップロードするファイルも作成しておきます。

echo "hi. this is text file">test.txt  

この状態で、下記のコマンドを入力し、実行します。

python3 file2gs.py  
  
image.png

下のリンクをクリックすれば内容も確認できます。
デフォルトでは世界には公開されないのでご安心を…
image.png

これで、クラウドにデータをアップロードすることができました。

[電子工作]137円の8x8 LEDを光らせてみた。

概要

aliexpressで137円の8x8マトリックスを見つけたので光らせてみました。

IMAGE ALT TEXT HERE

利用した機器

Arduino UNO互換機
699円
img.png

MAX7219 8*8 ドットマトリックスモジュール
137円
img.png

手順

1.Libraryのインストール

MAX72で検索して出てくるMatrizLedというモジュールを利用します。
スクリーンショット 2020-03-18 22.25.51.png

2.スケッチ例の取得

スケッチ例からscrollという例を取得します
スクリーンショット 2020-03-18 22.15.54.png

3.スケッチ例確認

スケッチを確認しpin番号を確認します。
スクリーンショット 2020-03-18 22.25.59.png

4.配線

下記の配線図を参考に配線します。
スクリーンショット 2020-03-18 22.34.30.png

スクリーンショット 2020-03-18 22.34.30.png

5.書き込み動作確認

書き込みを行うとこのように動作します。
download.gif

66円のドアセンサを試してみる

最初に

66円のドアセンサを入手したのでArduinoで動かしてみました。

用意するもの

Arduino UNO互換機
image.png
https://www.amazon.co.jp/gp/product/B013QV28CW/ref=ppx_yo_dt_b_asin_title_o08_s00?ie=UTF8&psc=1

ドアセンサ
66円のドアセンサです。
image.png

AliExpress
https://ja.aliexpress.com/item/4000138461602.html?spm=a2g0s.9042311.0.0.27424c4daU7DDx

配線
image.png
https://www.amazon.co.jp/gp/product/B00P9BVKOK/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1

ブレッドボード
image.png

https://www.amazon.co.jp/gp/product/B010SHL6RK/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1

抵抗
10kオームです
image.png

手順

1.動作確認

基本的に片方に磁気センサが入っており、時期が近づくとONになる機構です。
ですので、一度テスタなどで確認するとわかりやすいと思います。
image.png

2.スケッチ例を選択

スケッチは、単純なデジタル読み込みのスケッチですので標準で入っています。
「ファイル」>「スケッチ例」>「01.BASIC」>「DigitalReadSerial」を選択します
image.png
この中で、利用するIOポートとシリアル通信の通信速度が規定されています。
今回は初期値で実施するのでIOは2番でシリアル通信の通信速度は9600bpsになります。
image.png

3.配線図確認

配線はこんな感じです。
image.png

ソースコード上でIO2番になっているので、入力は2番です。
ドアセンサーは極性がないため、どちらに挿しても動きます。

4.書き込み And 動作確認

この状態で書き込めば、シリアルコンソールで動きを確認する事ができます。
download.gif

備考

今回の記事も動画にしてあります。
もし、ご興味がある方はご確認を。

IMAGE ALT TEXT HERE

IoTを15分で簡単構築!【Balena x Raspberry PI】

#背景
balenaというサービスを見つけたので使ってみました。
動画にもしてみました。

IMAGE ALT TEXT HERE

#概要
balenaはIoT機器を管理するCloudサービスです。
基本的にRaspberryPIなどのデバイスに専用のOSを書き込んで実行すると、Cloudから様々な制御が可能です。
ローカルデバイスの再起動から、デバイスの更新…ターミナル制御までできてしまう便利なやつです。

動画でもお話したとおり、今回は下記のチュートリアルを参考に進めさせていただきました。
https://www.balena.io/docs/learn/getting-started/raspberry-pi2/python/

価格

2020/04/26現在、は下記のとおりです。
image.png

free プラン

10端末までは無料とのこと
image.png

個人利用レベルであれば10端末を超えることはあまりないと思います。
それではやっていきましょう。

環境構築

ユーザ登録

まずは、balenaに登録を行います。

image.png

私はgithubに連携して行いました

image.png image.png

アプリケーション作成

管理するプロジェクトのようなものらしいです。

image.png image.png

デバイス追加

管理を行うデバイスを追加します。

image.png

RaspberryPI2しかないため、これを追加します。

image.png image.png

最後のDownload balenaOSをクリックし、イメージを取得します。

img書き込み

取得したイメージをSDカードへ書き込みます。
私はEtcherというソフトウエアが推奨のようだったので、そちらで書き込みを行いました。

image.png

書き込んだSDカードをRaspberryPIに挿入し、外部に出られるLANを接続し起動します。

動作確認

起動して、正常にネットワークに繋がると デバイスのページにRaspberryPI2が追加されます。

image.png

このデバイスをクリックすると、再起動やログの確認、ターミナルへの接続などが可能です。
image.png

ソフトウェア開発企業におけるシステム統合の技術的アプローチ

ソフトウェア開発企業におけるシステム統合の技術的アプローチ

※ この記事はAIによって自動生成されています

目次

  1. はじめに
  2. M&A後のシステム統合における技術的課題
  3. データベース統合のベストプラクティス
  4. マイクロサービスアーキテクチャを活用した段階的統合
  5. CI/CDパイプラインの統合方法
  6. まとめ

はじめに

最近、長野県のソフトウェア開発企業のM&A事例が報告されました。このような企業統合において、技術面での統合は非常に重要な課題となります。本記事では、M&A後のシステム統合における技術的なアプローチについて、具体的な実装方法を交えて解説します。

M&A後のシステム統合における技術的課題

主な課題

  • 異なる技術スタックの統合
  • レガシーシステムの移行
  • セキュリティポリシーの統一
  • 開発プロセスの標準化

解決アプローチ例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# システム互換性チェックツールの例
def check_system_compatibility(system_a, system_b):
compatibility_score = 0
checks = {
'database_type': 20,
'programming_language': 30,
'api_compatibility': 25,
'authentication_method': 25
}

for key, weight in checks.items():
if system_a[key] == system_b[key]:
compatibility_score += weight

return compatibility_score

データベース統合のベストプラクティス

段階的マイグレーション手法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 統合用の中間テーブル作成
CREATE TABLE integrated_customer_data (
id SERIAL PRIMARY KEY,
legacy_id VARCHAR(50),
system_origin VARCHAR(20),
customer_name VARCHAR(100),
-- その他の必要なフィールド
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- データマッピングビューの作成
CREATE VIEW customer_mapping AS
SELECT
new_id,
old_id,
system_name,
migration_status
FROM migration_tracking_table;

マイクロサービスアーキテクチャを活用した段階的統合

Docker Composeを使用した統合環境の構築

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: '3'
services:
legacy-system:
build: ./legacy
ports:
- "8080:8080"
environment:
- DB_HOST=legacy-db

new-system:
build: ./new
ports:
- "8081:8081"
environment:
- DB_HOST=new-db

api-gateway:
build: ./gateway
ports:
- "80:80"
depends_on:
- legacy-system
- new-system

CI/CDパイプラインの統合方法

GitHub Actionsワークフロー例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
name: Integrated CI/CD Pipeline

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run integrated tests
run: |
npm install
npm run test:integration

deploy:
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to production
run: |
echo "Deploying to production environment"
# デプロイメントスクリプト

まとめ

M&A後のシステム統合では、技術的な課題を段階的に解決していくことが重要です。本記事で紹介した実装例やベストプラクティスを参考に、自社の状況に合わせた統合計画を立てることをお勧めします。

キーポイント:

  • 段階的なシステム統合アプローチ
  • データベースマイグレーションの慎重な実施
  • マイクロサービスアーキテクチャの活用
  • 自動化されたCI/CDパイプラインの構築

参考

Googleの新AIコーディングエージェント「Jules」を最大限活用する実装テクニック

Googleの新AIコーディングエージェント「Jules」を最大限活用する実装テクニック

※ この記事はAIによって自動生成されています

目次

  1. Jules概要と特徴
  2. 開発環境のセットアップ
  3. Julesを活用したコード生成の実践
  4. Gemini 2.5 Proとの連携テクニック
  5. ベストプラクティスとTips
  6. 注意点と制限事項

はじめに

GoogleがAIコーディングエージェント「Jules」を一般公開し、最新のGemini 2.5 Proを搭載したことで、開発者の生産性向上に新たな可能性が開かれました。本記事では、Julesを実際の開発現場で効果的に活用するための具体的な実装方法と実践的なテクニックを解説します。

Jules概要と特徴

Julesは、Gemini 2.5 Proを基盤とした最新のAIコーディングアシスタントです。主な特徴として:

  • マルチ言語対応
  • コンテキスト理解能力の向上
  • リアルタイムコード提案
  • セマンティック解析機能

が挙げられます。

開発環境のセットアップ

1
2
3
4
5
6
# Julesのインストール(例)
npm install @google/jules-sdk

# 環境変数の設定
export JULES_API_KEY="your_api_key"
export GEMINI_MODEL="gemini-2.5-pro"

Julesを活用したコード生成の実践

基本的な使用例

1
2
3
4
5
6
7
8
9
10
11
12
13
from jules import JulesClient

# クライアントの初期化
jules = JulesClient()

# コード生成の例
response = jules.generate_code(
prompt="データベースに接続して、ユーザー情報を取得するAPIエンドポイントを作成",
language="python",
framework="flask"
)

print(response.code)

高度な使用例

1
2
3
4
5
6
7
8
9
10
11
12
# コンテキストを考慮したコード生成
context = {
"project_type": "web_application",
"database": "postgresql",
"authentication": "oauth2"
}

response = jules.generate_code_with_context(
prompt="セキュアなユーザー認証システムの実装",
context=context,
best_practices=True
)

Gemini 2.5 Proとの連携テクニック

Gemini 2.5 Proの高度な機能を活用するためのテクニック:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from jules.gemini import GeminiEnhancer

# Gemini機能の活用
enhancer = GeminiEnhancer()

# コードの最適化
optimized_code = enhancer.optimize(
code=original_code,
optimization_targets=[
"performance",
"security",
"readability"
]
)

ベストプラクティスとTips

  1. プロンプトの最適化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 良い例
    prompt = """
    Create a RESTful API endpoint that:
    - Handles user authentication
    - Validates input data
    - Returns JSON response
    - Includes error handling
    """

    # 避けるべき例
    prompt = "Make an API"
  2. エラーハンドリング

    1
    2
    3
    4
    5
    6
    7
    try:
    response = jules.generate_code(prompt)
    if response.quality_score < 0.8:
    # 品質が低い場合は再生成
    response = jules.regenerate_code(prompt)
    except JulesException as e:
    logger.error(f"Code generation failed: {e}")

注意点と制限事項

  • APIの利用制限(1時間あたりのリクエスト数)
  • 生成されたコードの検証必要性
  • セキュリティ考慮事項
  • ライセンスの確認

まとめ

Julesは強力なAIコーディングアシスタントとして、開発効率を大幅に向上させる可能性を秘めています。適切な使用方法と実装テクニックを理解することで、より効果的な開発が可能になります。

参考