2015/12/26

Retro Pie 3.1 on RaspberryPi A+

ようやく、形になってきたので備忘録としてまとめておきます。

[やりたかった事]
GameBoy colorにRaspberry PI A+を入れて、RetroPieを動かす。

[ハードウェア]
以下のハードウェアを使いました。
  • Raspberry Pi A+
  • GameBoy Color:ハウジングだけでなく十字キー、ABボタン、Start,Selectボタン部分を流用します
  • LCD:aitendoの2.2インチ液晶モジュール(240x320/SPI)[M-TM022-SPI]を使いました
  • 3V -> 5Vモジュール:aitendoの5V昇圧モジュール[DC095-DC5UFA]
  • CY7C63743:Combination Low-Speed USB & PS/2 Peripheral Controller 家にあったゲームパッドから抜き出しました
  • スライドスイッチ:2個
[接続]
・全体
各パーツは図のように接続しています。私の場合、Raspberry PIのテストピンや端子に直付けしました。

・コントローラー -> CY7C63743 -> USB
図のように接続しました。抵抗とコンデンサーは、ゲームパッドについていた部品を流用しています。


[RetroPie設定]
RetroPieはバージョンにより設定ファイルが違うので、参考となるサイトを探す時は注意が必要です。
私の場合、adafruitのサイトを参考にしてバージョン3.1を動作できました。
https://learn.adafruit.com/running-opengl-based-games-and-emulators-on-adafruit-pitft-displays/retropie-setup

adafruitの説明と違う点は
以下になります。

  • netatalkは使わないので、インストールしませんでした
  • fbcpは説明のファイルに書いても失敗したので、別の場所で起動しました
  • /opt/retropie/supplementary/runcommand/runcommand.sh のget_mode()は無くなっていて、修正は要りません

私の場合、fbcpは /etc/profile に設定したらLCDに表示されるようになりました。
追加した文です。
/usr/local/bin/fbcp &
 LCDが表示されたのを確認して、adafruitの /boot/config.txt の解像度を320x240に変更します。
LCDが表示されないまま解像度を変更すると、LCDにもHDMIにも表示されなくなるので注意してください。
私は一回それで失敗しました。(^^;
Linuxがあればsdカードをマウントして、/boot/config.txtを元に戻せばOKです。

苦労した点はゲームボーイのハウジングにRaspberry PIを入れるところでしたが、Raspberry PI Zeroが出たので もっと楽に怪しいゲームボーイを作れると思います。

enjoy!


2015/11/11

Sony MESH

最近、ソニーでkickstarterを使って出資者を募ったプロジェクトのニュースをよく聞くようになりました。
その中で電子ブロック、エレキット世代の私に響くガジェットが製品化!
まずは公式ページ。 http://meshprj.com/
センサーモジュールや、それに反応するモジュール、GPIOなど色々なラインナップがあります。
LEGOのマインドストームみたいに、iPhoneで簡単にプログラミングできるのも面白そうです。

ただ、値段が、、、なぁ〜。(^^;

2015/05/07

MySQL on Raspberry Pi

気圧センサー(MPL115A2)のデータをつぶやかせる材料だけでは、少々もったいないので
データを保存しておこうと考えデータベースサーバを立ち上げ、そこに測定した気圧を
保存することにしました。

1. MySQLサーバのインストール

 $ sudo apt-get install mysql-server

2. このままでは、SDカード上の /var/lib/mysql に保存されてしまうので、NFSサーバに
 データを保存するようにします。
 Raspberry Pi側:/etc/fstab に /var/lib/mysql を追加。
 NFSサーバ側:/var/lib/mysql を接続するDirを作成。/etc/exports に作成したDirを
        追加。
3. MySQLサーバの起動

これでMySQLサーバが起動したので、保存するテーブルやカラムを設定します。
その前にrootユーザのパスワード設定や、その他ユーザの追加などはリンク先を参考に
してください。
http://webkaru.net/mysql/mysql-root-password/

保存するテーブルやカラムは、日にち、時刻、気圧を保存することにしました。
次にRubyからMySQLにアクセスするにはmysqlかmysql2というライブラリを追加する
必要があります。私はmysqlをインストールしてみました。

$ sudo gem install ruby-mysql

ようやく準備が整ったので、プログラムにデータベースにアクセスする記述を追加します。
追加するのは、初期設定部分の

 require "mysql"
 db = Mysql::new('hostname', 'username', 'password', 'dbname')
 db.query("set character set utf8")

と、気圧をstdoutに出していたところに、データベースにインサートする記述を追加
しました。

 store = db.prepare("insert into dbname (date, time, press) values (?,?,?)")
 store.execute(date, time, press)

ここらへんの記述は、現在絶賛使用中のPHPと記述が似ていて親近感があります。(^^)
データベースを使うならば、不要となる配列もあるのですが、とりあえず棚に上げて
おきます。(^^;
とりあえず、気圧データは保存できるようになりました。

2015/03/17

Arduino...

http://makezine.jp/blog/2015/03/arduino-vs-arduino.html
リンクの記事によると、Arduinoのコアメンバー内でゴタゴタが起きているようです。
団体が栄えたり規模が大きくなると、宿命のようにあるのかもしれないけど、沈静化
することを望みます。。。
そして、次なるArduinoにも触れているのですが、CPUはARM の Cortex M0+ の
ようです。
正常進化なのでしょうけれど、AVRじゃなくなるのは少々寂しいですね〜。
Raspberry Piとの違いも分かりずらくなるし。。

2015/02/27

Raspberry Piのによる気圧お知らせ

I2Cで気圧センサ(MPL115A2)と接続できたので、気圧の変動によってtwitterで
つぶやくようにしました。

Rubyによるtwitter接続登録

ここに詳しくプログラムによる接続手続きが書かれているので、そちらを見てください。(^^;
簡単に書くと、twitterの開発者向けサイトにログインして、トークンを得るために
アプリケーション名とかを記入します。すると、トークンが表示されますので、コピー
してプログラム中に挿入します。
サンプルプログラムでHello worldが表示できます。

Arduinoプログラムの移植

Arduino + MPL115A2の組み合わせの時のプログラムがありましたので、それをRuby用に
移植します。
そこにtwitterにつぶやく一文も追加しました。

#!/usr/bin/env ruby
#coding: utf-8

require "date"
require "twitter"

class I2CDevice
  # ioctl command
  # Ref. https://www.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/i2c.h
  I2C_RETRIES = 0x0701
  I2C_TIMEOUT = 0x0702
  I2C_SLAVE = 0x0703
  I2C_SLAVE_FORCE = 0x0706
  I2C_TENBIT = 0x0704
  I2C_FUNCS = 0x0705
  I2C_RDWR = 0x0707
  I2C_SMBUS = 0x0720
  I2C_UDELAY = 0x0705
  I2C_MDELAY = 0x0706

  attr_accessor :address
  def initialize(address)
    @address = address
  end

  def i2cget(address, length=1)
    i2c = File.open("/dev/i2c-1", "r+")
    i2c.ioctl(I2C_SLAVE, @address)
    i2c.write(address.chr)
    ret = i2c.read(length)
    i2c.close
    ret
  end

  def i2cset(*data)
    i2c = File.open("/dev/i2c-1", "r+")
    i2c.ioctl(I2C_SLAVE, @address)
    i2c.write(data.pack("C*"))
    i2c.close
  end
end

class MPL115A2 < I2CDevice
  def initialize
    super(0x60)

    coefficient = i2cget(0x04, 8).unpack("n*")

    @a0 = fixed_point(coefficient[0], 12)
    @b1 = fixed_point(coefficient[1], 2)
    @b2 = fixed_point(coefficient[2], 1)
    @c12 = fixed_point(coefficient[3], 0) / (1<<9)
    p [@a0, @b1, @b2, @c12]
  end

  def fixed_point(fixed, int_bits)
    msb = 15
    deno = (1<<(msb-int_bits)).to_f
    if (fixed & (1<<15)).zero?
      fixed / deno
    else
      -( ( (~fixed & 0xffff) + 1) / deno )
    end
  end

  def calculate_hPa
    i2cset(0x12, 0x01) # CONVERT

    sleep 0.003

    data = i2cget(0x00, 4).unpack("n*")

    p_adc = (data[0]) >> 6
    t_adc = (data[1]) >> 6

    p_comp = @a0 + (@b1 + @c12 * t_adc) * p_adc + @b2 * t_adc
    hPa = p_comp * ( (1150 - 500) / 1023.0) + 500;
  end
end

mpl = MPL115A2.new
loopflag = 0 #周回フラグ
press = [0,0,0,0,0,0,0,0,0,0,0,0,0] #気圧データ保存配列
tcount = 0 #3時間計

# Twitter setup
client = Twitter::REST::Client.new do |config|
  config.consumer_key = "******"
  config.consumer_secret = "******"
  config.access_token        = "******"
  config.access_token_secret = "******"
end

loop do
  sec_total = 0
  min_total = 0

  for min_j in 0..14 do
    for sec_i in 0..14 do
      sec_total = sec_total + mpl.calculate_hPa
      sleep 1
    end
    onemin_ave = sec_total / 15
    min_total = min_total + onemin_ave
    sec_total = 0
    sleep 45
  end
  press[tcount] = min_total / 15
  print("tcount = ", tcount, "pressure = ", min_total/15, "\n")

  if tcount > 0  then
    day = Time.now
    daystr = day.strftime("%Y/%m/%d %H:%M:%S ")

    dpdt = (press[tcount] - press[0]) / (tcount * (loopflag + 0.25) * 0.635)
    printf("pressure = %f, dP/dt = %f ,", press[tcount], dpdt)
    if 2.5 <= dpdt then
      printf("急速に気圧が上昇しています \n")
      mess = "急速に気圧が上昇しています。 @横浜南"
      message = daystr + mess
      client.update(message)
    end
    if 0.5 < dpdt && dpdt < 2.5 then
      printf("気圧が緩やかに上昇しています。 \n")
      mess = "気圧が緩やかに上昇しています。 @横浜南"
      message = daystr + mess
      client.update(message)
    end
    if -0.5 <= dpdt && dpdt <= 0.5 then
      printf("気圧の変化はありません。 \n")
    end
    if -2.5 < dpdt && dpdt < -0.5 then
      printf("気圧が緩やかに下降しています。 \n")
      mess = "気圧が緩やかに下降しています。 @横浜南"
      message = daystr + mess
      client.update(message)
    end
    if dpdt <= -2.5 then
      printf("急速に気圧が落ちています。 \n")
      mess = "急速に気圧が落ちています。突然の雨に気をつけてください。 @横浜南"
      message = daystr + mess
      client.update(message)
    end

    if tcount == 8 then
      press[0] = press[4]
    end

    if tcount >= 12 then
      loopflag = 1
      tcount = 1
    else
      tcount += 1
    end

  else
    tcount += 1
  end
end

で、動作させますと、このようにつぶやきます。

今回はつぶやきまで、ということで終わりにしますが、もう少しネタがあるので
頃合いをみて続きます。

2015/02/13

RaspberryPiでI2Cアクセス(その2)

前回は本題に入らず終わってしまいました。。。(^^;
今回こそはRaspberry PiとセンサとがI2Cで接続できるまでを説明します。

I2C接続の準備

Raspberry PiはデフォルトではI2Cが無効化されていますので、有効にするため設定を変更します。
  • /etc/modulesの編集
      $ sudo vi /etc/modules
      i2c-bcm2708
      i2c-dev
    
  • /etc/modprobe.d/raspi-blacklist.confの編集
      $ sudo vi /etc/moduprobe.d/raspi-blacklist.conf
      #blacklist i2c-bcm2708
    
これらの編集が済んだら、Raspberry Piを再起動します。
その後、i2c-toolsをインストールします。
$ sudo apt-get install i2c-tools
これで準備が完了しました。

センサとのI2C接続

Raspberry PiとMPL115A2を接続し、i2cdetectコマンドを実行するとセンサのアドレスが
表示されます。
raspberrypi ~ $ sudo i2cdetect -y 1

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 
この方のRubyプログラムは完璧なので、そのまま使わせていただきました。(ありがとうございます)
http://lowreal.net/2013/12/26/1
これを実行すると0.5秒おきに気圧がSTDOUTに表示されます。
続きます!

2015/02/03

Raspberry PiでI2Cアクセス

Raspberry Piを買って1年以上放置していたのですが、ようやくI2Cでセンサーとお話し
させて何やらやらせようという気になり、インストールしただけのRaspbianを24時間
動作させても安心な設定に変更しました。
という訳で、その備忘録です。
相変わらずですが、詳しい説明は他の方の説明をお読みください。(^^;)
http://qiita.com/makoto_kw/items/393e098f214f81449c9f

Raspbianがインストールされた状態(ssh接続設定できているものとします)から、
・rootのパスワード変更
・新アカウントの作成
・新アカウントでsudoが使えるように、visudoで新アカウントを追加
・新アカウントでssh, sudoが使えるか確認を行った上で、piユーザの削除
を行いました。

さて次はRaspberry Piの延命処置をおこないました。
・Swapの停止
foo@raspberrypi ~ $ free
             total       used       free     shared    buffers     cached
Mem:        448776      59988     388788          0       9528      28348
-/+ buffers/cache:      22112     426664
Swap:       102396          0     102396
foo@raspberrypi ~ $ sudo swapoff --all
[sudo] password for foo: 
foo@raspberrypi ~ $ free
             total       used       free     shared    buffers     cached
Mem:        448776      59988     388788          0       9528      28348
-/+ buffers/cache:      22112     426664
Swap:            0          0          0
foo@raspberrypi ~ $ sudo apt-get remove dphys-swapfile
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages will be REMOVED:
  dphys-swapfile
0 upgraded, 0 newly installed, 1 to remove and 64 not upgraded.
After this operation, 69.6 kB disk space will be freed.
Do you want to continue [Y/n]? Y
(Reading database ... 62309 files and directories currently installed.)
Removing dphys-swapfile ...
Stopping dphys-swapfile swapfile setup ..., done.
Processing triggers for man-db ...
foo@raspberrypi ~ $ 
・tmp, logディレクトリのNFS化
Swap以外にも/tmpや/var/tmp, /var/logなど、書き込み頻度が高いディレクトリを退避
します。よく見られるのはRAMディスクを作りそこに書き込む方法ですが、今回私は
ファイルサーバにNFS接続しHDDに書き込む事にしました。
まずはサーバ側:
# aptitude install nfs-kernel-server
# mkdir /var/nfs →NFSで接続するディレクトリの作成
# vi /etc/exports
/var/nfs/tmp 192.168.10.xx(rw,sync,fsid=0,no_subtree_check) の追加(Raspberry Piのみの接続にしました)
# /etc/init.d/nfs-kernel-server start
これでサーバ側の準備が整いました。次にRaspberry Pi側:
portmapやidmapdはrpcbindにあり、既にインストールされているようですので、
$ sudo apt-get install nfs-common
$ sudo mount -t nfs 192.168.10.x:/var/nfs /mnt
$ sudo vi /var/fstab
で、何故かnfs4では接続できませんでした。orz
いえ、理由は分かっておりidmapdがRaspberry Pi側で立ち上がらず、それで
接続できないのですが、何故立ち上がらないのかが分かりません。。。
mountコマンドの実験でnfsの接続ができたので、とりあえずそれで良しとします。(^^;)
続きます。

2015/01/17

続・ひねリモコン(プロトタイプ)

まぁ、プロトタイプにケースなんか必要無いのかもしれませんが、ふと玩具屋さんで
nano blockのコレクションケースを見た時に「これだ!」と思い購入しました。

一番気になったのはArduinoの基板の穴とnano blockの凸が同じサイズか?という所
ですが、1000円以下だったので とりあえず購入。
家に帰り、早速穴とブロックの凸を合わせてみたら、その為に作ったかのように嵌った
のでArduinoの穴位置を確認しながら台座となるプレートにブロックをつけます。

ただ、リセットボタンの横にある穴だけはブロックの凸と位置がずれているので
ブロックはつけませんでした。代わりにUSBコネクタ付近がガタつくので、そこに
ブロックをつけてみました。(効果不明)
組み上げるとこんな感じです。このままではUSBケーブルを挿せないので、一面外すか
穴を開けてしまうかですね。

Arduino、nano blockで検索してみると、ブロックでケースを作る人もいましたので、
意外とArduinoとnano blockは相性がいいのかもしれませんね〜。(^^)

2015/01/08

Intel Curie

http://japanese.engadget.com/2015/01/06/curie-quark-se-soc-6-ble/
CESにてIntelがウェアラブルデバイス向けのモジュール「Curie」を発表しました。
GalileoもEdisonも とても興味があったのですが、CPUボードの枠から出なかったので
Raspberry Piを持っている私は買うまでには至りませんでした。
ただし、今度はモジュール内にBluetoothや6軸モーションセンサーがついているので、
とぉ〜っても興味があります!
後は、市販化するのかという点と価格がどうなるのでしょうか?

2015/01/02

ひねリモコン(プロトタイプ)

夏頃、日本のスタートアップが「Ring」というウェアラブルデバイスを開発している
という記事を読んだ時、ひねった動作でコントロールする物を作りたいなぁ、、、と
ふと思いました。
以前、赤外線LEDを100個まとめ買いしていたので、赤外線、ひねる、リモートコント
ロールというキーワードで、安直にテレビのリモコンを作る事にしました。(^^;
理想はボールのようなケースにボタンが一つ、というようなシンプルな物を作りたいの
ですが、とりあえずはArduino+赤外線LED+ジャイロ(L3GD20)という構成で試す
ことにします。
秋月電子で売られているユニバーサル基板に小型のブレッドボードを付けて2階建て
構造にしました。
接続とソフトについては、以下の2つのサイトを参考にさせてもらいました。

http://eikatou.net/blog/2012/07/1796/
http://n.mtng.org/ele/arduino/tutorial012.html

接続はこんな感じです。

ソフトは上の2つのサイトのソースをほぼコピペで、Y軸の傾きに対して紫外線LEDを
CH upするか、CH downするかという簡単なプログラムです。
#include 

#define LED_PIN 2

//CH up
unsigned int chup[] = {432,1664,352,728,324,708,352,704,356,736,348,1716,364,
696,348,732,328,708,348,1724,396,720,348,704,352,720,352,1716,364,696,396,45856,
364,1712,368,684,356,708,348,704,348,736,364,692,356,1720,356,1716,360,1716,368,
688,352,1720,360,1720,356,1728,356,696,352,1728,392};
//CH down
unsigned int chdown[] = {480,1620,352,704,352,708,344,704,356,740,352,700,360,
1716,352,696,360,696,348,1728,364,688,352,704,360,712,356,1716,364,716,388,45792,
372,1704,364,696,352,700,360,696,356,728,348,1724,368,692,356,1720,360,1708,368,
688,356,1716,368,1708,368,1724,352,704,352,1720,408};

const byte L3GD20_ADDR = B1101010;  // SA0 = GND
//const byte L3GD20_ADDR = B1101011;// SA0 = VDD_IO

const byte L3GD20_WHOAMI = 0x0f;
const byte L3GD20_CTRL1 = 0x20;
const byte L3GD20_CTRL2 = 0x21;
const byte L3GD20_CTRL3 = 0x22;
const byte L3GD20_CTRL4 = 0x23;
const byte L3GD20_CTRL5 = 0x24;
const byte L3GD20_X_L = 0x28;
const byte L3GD20_X_H = 0x29;
const byte L3GD20_Y_L = 0x2A;
const byte L3GD20_Y_H = 0x2B;
const byte L3GD20_Z_L = 0x2C;
const byte L3GD20_Z_H = 0x2D;

void L3GD20_write(byte reg, byte val)
{
  Wire.beginTransmission(L3GD20_ADDR);
  Wire.write(reg);
  Wire.write(val);
  Wire.endTransmission();  
}

byte L3GD20_read(byte reg)
{
  byte ret = 0;
  // request the registor
  Wire.beginTransmission(L3GD20_ADDR);
  Wire.write(reg);
  Wire.endTransmission();  
  // read
  Wire.requestFrom((unsigned int)L3GD20_ADDR, 1);
    while (Wire.available()) {
    ret = Wire.read();
  }
  return ret;
}

void SendData(unsigned int *data, int datasize) {
  Serial.println(datasize);
  for (int i = 0; i < datasize; i++) {
    unsigned long start_t = micros();
    unsigned long length = data[i];
    do {
      digitalWrite(LED_PIN, 1 - (i&1));
      delayMicroseconds(9);
      digitalWrite(LED_PIN, 0);
      delayMicroseconds(7);
    } while(long(start_t + length - micros()) > 0);
  }
}

void setup() {
  Serial.begin(9600);
  while (!Serial) {}

  Wire.begin();
  Serial.println(L3GD20_read(L3GD20_WHOAMI), HEX); // should show D4
  L3GD20_write(L3GD20_CTRL1, B00001111);
  pinMode(LED_PIN, OUTPUT); // LED setup
}

void loop() {
  short X, Y, Z;
  float x, y, z;
  int datasize;

  X = L3GD20_read(L3GD20_X_H);
  x = X = (X << 8) | L3GD20_read(L3GD20_X_L);
  Y = L3GD20_read(L3GD20_Y_H);
  y = Y = (Y << 8) | L3GD20_read(L3GD20_Y_L);
  Z = L3GD20_read(L3GD20_Z_H);
  z = Z = (Z << 8) | L3GD20_read(L3GD20_Z_L);
 
  x *= 0.00875; // +-250dps
  y *= 0.00875; // +-250dps
  z *= 0.00875; // +-250dps
  
  if ( y > 10 ) {
    datasize = sizeof(chup) / sizeof(chup[0]);
    digitalWrite(LED_PIN, HIGH);
    SendData(chup, datasize);
    digitalWrite(LED_PIN, LOW);
    Serial.println("CH up");
  }
  
  if ( y < -10 ) {
    datasize = sizeof(chdown) / sizeof(chdown[0]);
    digitalWrite(LED_PIN, HIGH);
    SendData(chdown, datasize);
    digitalWrite(LED_PIN, LOW);
    Serial.println("CH down");
  }
  
  Serial.print(x);    // X axis (deg/sec)
  Serial.print("\t");
  Serial.print(y);    // Y axis (deg/sec)
  Serial.print("\t");
  Serial.println(z);  // Z axis (deg/sec)

  delay(1000);
}

そう、昔のダイアル型のテレビと同じ動作です。(^^;)
X軸でボリュームまでコントロールする予定です。