2014年4月27日日曜日

Processingで2Dと3Dの混在

ProcessingではsizeでP3Dオプションをつけることで3次元を描くことができます
しかしそうした場合2Dで線や文字を書こうとすると3Dの奥に書かれてしまい、手前に表示させることができません

これを解決するにはhint(DISABLE_DEPTH_TEST);と関数を呼べばいいようです

本当にこの方法でいいのかは不明ですが
一応狙い通りの前後関係で表示できるようになりました



奥の四角を3Dで 手前の線と数字を2Dとして描いています

なお、2Dで描くべきモノをすべて書いたらhint(ENABLE_DEPTH_TEST);で3Dに戻す必要があります


2Dと3Dの位置関係はもっとうまいやり方が有るようなきがするのですが。。。

2014年4月24日木曜日

Processingで3D空間

Processingで3Dを使うときのサンプルというか 雛形みたいなもの

final float BoxSize = 120;
final float AxisLineLen = BoxSize / 1.5;
final float AutoRotateDegree = 0.5;

boolean IsCameraRotate = true;
float CameraXrot = radians( 45);
float CameraYrot = radians(-45);
float CameraZoom = 160;
boolean pmousePressed = false, pkeyPressed = false;

void setup() { 
  textFont(createFont("GulimChe", 20));
  textAlign(LEFT, TOP);
  size(1024, 768, P3D);
  noFill();
}

void draw() {
  keyboardInput();
  
  pushMatrix();
  background(127);
  MoveCamera();
  DrawBox();
  popMatrix();
  
  text("camR:" + (int)degrees(CameraXrot), 0, 20 * 0);
  text("camP:" + (int)degrees(CameraYrot), 0, 20 * 1);
  text("zoom:" + (int)CameraZoom, 0, 20 * 2);
  text("musX:" + mouseX, 0, 20 * 3);
  text("musY:" + mouseY, 0, 20 * 4);
  
  if (!pkeyPressed && keyPressed && key == 's')
  { save("./Processing_ss.png"); } 
  
  pmousePressed = mousePressed;
  pkeyPressed   = keyPressed;
}

void keyboardInput() {
  if (!pkeyPressed && keyPressed) {
    if (key == '1') { IsCameraRotate = false; CameraXrot = radians( 0); CameraYrot = radians( 0); }
    if (key == '2') { IsCameraRotate = false; CameraXrot = radians(90); CameraYrot = radians( 0); }
    if (key == '3') { IsCameraRotate = false; CameraXrot = radians(90); CameraYrot = radians(89.71); }
    if (key == '4') { IsCameraRotate =  true; CameraXrot = radians(45); CameraYrot = radians(-45); }
  }
}

void DrawBox() {
  strokeWeight(2);
  stroke(255);     box(BoxSize / 2, BoxSize / 4, BoxSize);
  
  strokeWeight(1);
  stroke(#FF0000); line(0, 0, 0, 0, 0,  AxisLineLen); sphere(1, 0, 0, AxisLineLen);
  stroke(#7F0000); line(0, 0, 0, 0, 0, -AxisLineLen);
  stroke(#00FF00); line(0, 0, 0,  AxisLineLen, 0, 0); sphere(1, AxisLineLen, 0, 0);
  stroke(#007F00); line(0, 0, 0, -AxisLineLen, 0, 0);
  stroke(#0000FF); line(0, 0, 0, 0,  AxisLineLen, 0); sphere(1, 0, AxisLineLen, 0);
  stroke(#00007F); line(0, 0, 0, 0, -AxisLineLen, 0);
}

boolean MvCm_IsCameraMove = false;
void MoveCamera() {
  if (mousePressed) {
    CameraXrot += radians(pmouseX - mouseX) / 2;
    CameraYrot += radians(pmouseY - mouseY) / 2;
    MvCm_IsCameraMove = MvCm_IsCameraMove || pmouseX != mouseX || pmouseY != mouseY;
  } else {
    if (pmousePressed) 
    { IsCameraRotate = IsCameraRotate ? false : !MvCm_IsCameraMove; }
    if (IsCameraRotate) { CameraXrot += radians(AutoRotateDegree); }
    MvCm_IsCameraMove = false;
  }
  
  if (keyPressed && key == 'z') { CameraZoom++; }
  if (keyPressed && key == 'x') { CameraZoom--; }
  
  CameraXrot = (CameraXrot + TWO_PI) % TWO_PI;
  if (CameraYrot < -HALF_PI) { CameraYrot += PI; }
  if (CameraYrot >  HALF_PI) { CameraYrot -= PI; }
  
  camera(CameraXrot, CameraYrot, CameraZoom);
}

void camera(float X, float Y, float zoom) 
{ camera(cos(Y) * zoom * sin(X), sin(Y) * zoom, cos(Y) * zoom * cos(X), 0, 0, 0, 0, 1, 0); }

void sphere(float r, float x, float y, float z) 
{ pushMatrix(); translate(x, y, z); sphere(r); popMatrix(); }

やってることは
1カメラを移動
2箱と棒を表示
だけです
他にマウスのドラッグでカメラの位置を変えたりだとか、キーの入力に応じて視点を変更したりスクリーンショットを保存できます


カメラの位置変更はキーボードで行っています
一旦はマウスホイールでの動作も確認したのですが、あまりマウスホイールはどうさがよろしくなかったので

スクリーンショットの撮影をキー入力関数で行わないのは
キー入力:できるだけ早い段階でキーを読んでおきたい
スクリーンショット:すべての表示が終わった最後の段階で撮影したい
という違いによるものです

Processingは基本的に静的変数が使えないので
複数フレーム間で共有したい値はすべてグローバル変数にする必要があります
この辺りがちょっと面倒です

ちょっとした3Dの表示とかはProcessingだと簡単に作れるので
C#とかで作ったデータを何らかの方法でProcessingに渡すことができれば いろいろ応用が広がりそうです

2014年4月23日水曜日

Processingの3D空間で円弧

Processingにはarc関数があり、平面上で円弧を描きたい場合にはこれを使えばいいのですが、3Dで円弧を描くための関数は作られていないようです
ということで、間に合わせで作ってみました


void arc(float x, float y, float z, String vec, float start, float stop, float size) {
  final float lineDeg = radians(360 / 32);
  float px0, py0, pz0, px1, py1, pz1;
  boolean IsBreak = false;
  start = (start);
  stop  = (stop);
  
  do {
    float startRot = start;
    float stopRot  = startRot + lineDeg;
    if (stopRot > stop) { stopRot = stop; IsBreak = true; }
    
    px0 = px1 = py0 = py1 = pz0 = pz1 = 0;
    
    if (vec == "X") {
      pz0 = -sin(start)   * size;
      py0 = -cos(start)   * size;
      pz1 = -sin(stopRot) * size;
      py1 = -cos(stopRot) * size;
    }
    if (vec == "Y") {
      px0 =  sin(start)   * size;
      pz0 =  cos(start)   * size;
      px1 =  sin(stopRot) * size;
      pz1 =  cos(stopRot) * size;
    }
    if (vec == "Z") {
      px0 =  sin(start)   * size;
      py0 = -cos(start)   * size;
      px1 =  sin(stopRot) * size;
      py1 = -cos(stopRot) * size;
    }
    
    line(px0 + x, py0 + y, pz0 + z, px1 + x, py1 + y, pz1 + z);
    
    start = stopRot;
  } while (!IsBreak);
}

使い方は
  arc(0, 0, 0, "Z", radians(-45), radians(45), 30);
のような感じ
引数1-3に円の中心の位置を指定 
引数4に表示する面を文字列で指定 
引数5に開始点 引数6に終了点 をそれぞれラジアンで
引数6に円の半径を
与えます

そうすると
このように表示できます

基本的にlineのラッパー関数なので、線の色や太さ等はlineと同等に変更できます
言い換えればこの関数の前で色を変更した場合、lineなどに影響を与えるので注意してください

現在制限されていることですが
・任意の面で表示できない(X,Y,Zの3種類だけ)
・軸の正面から見て時計方向の回転だけ(start<=stopは許されない)
という点があります

1つ目については、個人的に使いたいのが3面で表示するだけで十分だったというのがあります
また、任意面で表示できるようにするのは面倒というのもあります

2つ目については、例えばstart:90 stop:0の90°の円弧を描くということはできません
例えばジャイロセンサの入力から回転を描く場合、おそらくdps等に応じて円弧の長さを変えると思いますが
startを0で固定し、ジャイロが負の方向に回転した場合はstopが負の値になる というような処理はできません
そのように使いたい場合は まず終了点を変数に保存し、終了点が負なら終了点をstartで与え、stopを0に それ以外の場合はstartを0にして終了点をstopとして与える といった判定処理が必要です

円の滑らかさは関数内lineDegを変更することで行います
Degと書きながら中身はRadianです 上記では360°を32分割しているので、正確には円ではなく32角形です
これが360角形とかになるとよほど拡大しない限りは問題なく滑らかになると思いますが、実際には32でも十分に滑らかです


また円を1周ぐるりと書く場合は、上記"start<=stop"が不可という制限から、start0 stop0ではなく、start0 stop6.283を与える必要があります(6.28は2piです)


(これくらいの関数 普通に実装されてるので実現できそうだなぁ…)

2014年4月22日火曜日

Processing

ふとしたことで、Processingを始めてみました(昔にちょっと触ってたけど)

バージョンが2になっていて、フォントが「Ubuntuっぽい感じの」ジワッとした感じになっていました

ProcessingはたぶんNexus7を買った時に少し触った程度だったと思います
なのでMacに入ってたProcessingはAndoroidoの設定のままでした(Winは新規で導入)

ProcessingはArduino以上にややこしい感じがしてあまり使っていませんでした
しかし3Dオブジェクトの表示などは簡単にできるので、便利かなーと思って始めた次第です



とりあえず、マイコンと接続するためにシリアルポートを使ってみました

import processing.serial.*;
Serial myPort = null;

void setup() {
  textFont(createFont("GulimChe", 20));
  size(1024, 768);
}

void draw() {
  if (myPort == null) { OpenSerialPortSelect(); }
}

void serialEvent(Serial myPort) {
  background(0);
  text(myPort.readStringUntil('\n'), 10, 20);
}

void OpenSerialPortSelect() {
  String[] SerialList = Serial.list();
  background(0);
  
  for (int i = 0; i < SerialList.length && i < 10; i++)
  { text((i + 1) + " : " + SerialList[i], 10, 20 + (i * 20)); }
  
  int SerialSelect = -1;
  if (keyPressed) { SerialSelect = key - '1'; }
  if (SerialSelect >= 0 && SerialSelect < SerialList.length) {
    myPort = new Serial(this, SerialList[SerialSelect], 115200);
    myPort.bufferUntil('\n');
  }
}

こんな感じです
まず接続するポートを選択する画面が表示されます
これはSerial.list()で得られた配列の最初9個を表示します
そして1-9のキーで選択したポートを115.2kbaudで開きます
あとは改行コードを受信した時点で割り込みが発生し、その文字列を表示します

ここまでできれば受信した文字列を配列に展開し、実数などに変換して棒グラフの長さを変えるなどすればぐらふぃかるに表示したり、いろいろできます

以前に缶サット用にPFD(Primary Flight Display)をC#で作りましたがProcessingを使えばこういうのももっと簡単に作れそうです

2014年4月20日日曜日

水中の圧力

水中では「10m潜る毎に1気圧増える」とか言われています
ということで計算してみました

まず、1気圧についてですが
atmという単位が有り、1気圧=1atmです
1atmは101325Paです
ということで101.325kPaを基準とします

まずPaという単位ですが、これはパスカル(Pascale)の略で、由来はブレーズ・パスカルさんです そのため頭文字は大文字です
次にPaの定義ですが、"N/m^2"で、1m四方に1Nの圧力がかかっている状態です

Nという単位もおなじみだと思いますが、由来はアイザック・ニュートンです
Nの定義は"kg・m/s^2"で、1kgの物体を1秒間で1m毎秒まで加速させるエネルギーです

さて、これらを組み合わせて圧力を計算してみましょう

とりあえず水深10mの地点の圧力を計算するので、水の重さは"1000kg/m^3"掛ける10で10000kgです
次にm/s^2を用意します これは地球の重力加速度で、およそ9.8m/s^2です
10000kg・9.8m/s^2で98000N=98kNとなり N/1m^2はPaになるので、98kPaとなります
これで圧力がわかりました 次にこの値を気圧にしてみます
98kPa / 101.325kPaはおよそ0.967です
ということで水深10m地点の圧力は0.967気圧となりました

水面が標準大気圧で押されている場合、水表面は1気圧の圧力がかかっているので、1が加算され、10m地点では1.97気圧 くらいになります

ちなみに、純粋な水の圧力で1気圧になる地点は
101.325kN / 9.8m/s^2で10339.2857kgとなり、水深10mと34cmくらいです

以上 水中の圧力の計算でした
間違っていたら申し訳ないです

2014年4月16日水曜日

ペリフェラルの絡み合い

シューティングタイマーとは別のプロジェクトで、超音波を扱う装置を作っています

超音波距離計と似ていますが、アナログはもっと簡単で、タイミングはシビアです

40kHzのパルス作成とADCによる1Mspsでの読み込みを並列して行いたかったので、ハードウェアで処理してみました


イメージとしては上のような感じです
TIM3で40kHzのパルス作成(GPIO2本から正負の出力)
TIM4で1MHzのADCトリガ作成
TIM5で1Hzのソフトウェア割り込み作成
ADC1で波形のキャプチャ
DMA1でADCのデータを転送

TIM5の割り込みでTIM3とTIM4を有効化
しばらく放置するとDMA転送終了割り込みがかかるので、それでTIM3および4を無効化
その後TIM3とTIM4のカウンタをクリア
そしてフラグをセットし次の転送に備えてDMAを初期化

という動作です
タイマ3と4のカウンタをクリアしているのは、次回も確実に同じタイミングで動作するように という目的です  これは1uSecのズレも問題となるためです(ならソフトウェアでタイマ有効化とかするな という話ですが…)


40kHzのパルスを出しつつ、1MspsでAD変換しつつ そのデータをメモリにコピーしつつ
という処理ですが、ソフトウェアで行っているのはタイマ2本の有効化/無効化と細々とした設定だけです
STM32のペリフェラルはCPUを介さずに連動させることができるので、複雑なことでも簡単に処理できます(初期化が面倒ですが。。)


この方法で読みだしたデータですが
このようにタイミングのズレも無く綺麗に読み取ることができます

ただ、40kHzを1MHzでサンプリングすると1周期25サンプルなので、波形としてはある程度形が出ますが、タイミング精度はあまり良くありません
できれば1ケタほどサンプリング周波数を上げたいですが、さすがにマイコン内蔵ADCだと難しいですね

次はこの波形をどうやって処理しようか を考えてみようと思います


各ペリフェラルの初期化は(気が向いたら)近いうちに書きます

2014年4月10日木曜日

シューティングタイマー

シューティングタイマーですが、かなり動作確認ができてきました

まずストッププレートですが
このような感じです
手持ちでちょうどいいLEDがなかったので、上部に出しています

次に裏側ですが
このように、基板1枚が貼り付けてあります
それとHITを検出するための圧電素子が1個にLEDが2個ついています

その基板がこちら

分かる人はわかると思いますが、ごちゃごちゃと部品がついていますが、やっていることは信号の補強だけです
圧電素子の信号はデジタル的には非常に扱いづらいので、信号を適切な範囲に囲ってからノイズに負けないようにオペアンプでインピーダンスを下げています
試作段階ではすべての処理を表示デバイスで行っていましたが、将来的な面を考えるとこの方式が最適だと思ったからです

表示デバイスとの接続はLANケーブルを使用します
もちろんEthernetではないので、一般のLAN機器に接続すると壊れ/壊します


表示デバイスはまだ試作ですが
このような表示になると思います

液晶ではなく、有機ELディスプレイを使うため、明るい場所でもはっきりと読むことができます
表示内容はスタートから着弾までの時間と、前回の着弾から今回の着弾までの時間です

早撃ちの場合は多分ダブルタップとかやらないとおもうので、スタートからの時間だけでいいかな と思っています

将来的には5枚のターゲットにセンサを貼り付けて、すべてのプレートのHITを表示 というのもやりたいので、その場合は初弾の時間と最終弾の時間を表示 という機能が追加されると思います


夏までにはちゃんとケースに入れて簡単に扱えるようにしたいですが、意外とケースに仕込むのが大変です
なんせ厚さが15mm程度しか無いので、ちょっと背が高い部品は注意しないと簡単にぶつかってしまいます
この辺りは勘だけで設計するのは無理なので、ゆっくりとやろうと思います

2014年4月8日火曜日

atan2の変換テーブル

atan2を変換する整数テーブルを作ってみました

      1 #include <stdio.h>
      2 #include <math.h>
      3 #include <stdint.h>
      4
      5 #define PI (3.14159265358979323846264338327950288)
      6
      7 /*             Row  Col  */
      8 int atan2table[100][100];
      9 const int tableSize   = (int) (sizeof(atan2table   ) / sizeof(atan2table[0][0]));
     10 const int tableColumn = (int) (sizeof(atan2table[0]) / sizeof(atan2table[0][0]));
     11 const int tableRow    = (int)((sizeof(atan2table   ) / sizeof(atan2table[0][0])) /
     12                               (sizeof(atan2table[0]) / sizeof(atan2table[0][0])));
     13
     14 void createTable(void) {
     15     int x, y;
     16
     17     for (x = 0; x < tableRow; x++) {
     18         for (y = 0; y < tableColumn; y++)
     19         { atan2table[x][y] = (int)((atan2(y + 1, x + 1) * (180.0 / PI)) * 1000); }
     20     }
     21 }
     22
     23 int ATAN2(y, x) {
     24     int xp = 1;
     25     int yp = 1;
     26
     27     if (!x && !y)  { return(0); }
     28
     29     if (!x) { if (y > 0) { return(90000); } else { return(-90000); } }
     30     if (!y) { if (x > 0) { return( 0); }    else { return(180000); } }
     31
     32     if (x < 0) { x = -x; xp = -1; }
     33     if (y < 0) { y = -y; yp = -1; }
     34
     35     x--; y--;
     36
     37     if (x >= tableRow)    { return(0); }
     38     if (y >= tableColumn) { return(0); }
     39
     40     int deg = atan2table[x][y];
     41
     42     if (xp == -1) { deg = 180000 - deg; }
     43     if (yp == -1) { deg = -deg; }
     44
     45     return(deg);
     46 }
     47
     48 int main(void) {
     49     int i;
     50
     51     createTable();
     52
     53     for (i = 0; i < 360; i += 5) {
     54         int x = sin(i * (PI / 180.0)) * 95;
     55         int y = cos(i * (PI / 180.0)) * 95;
     56         double _atan2 = atan2(y, x) * (180.0 / PI);
     57         double _ATAN2 = ATAN2(y, x) * 0.001;
     58
     59         printf("deg:%3d, ", i);
     60         printf("x:%3d, y:%3d, ", x, y);
     61         printf("%8.3lf, ", _atan2);
     62         printf("%8.3lf, ", _ATAN2);
     63         printf("%6.3lf, ", _atan2 - _ATAN2);
     64         printf("\n");
     65     }
     66
     67     return(0);
     68 }
     69

入力範囲はx,y共に-100 - +100の間です(デフォルトのテーブルサイズでは)
このコードでは予めmatn.hのatan2でテーブルを作成していますが、組み込み用途向けの場合は予めテーブルをCSVとして作成しておき、配列に読み込んでROMに焼く という方法が使えます
また、入力がテーブルを超えた場合はテーブルに収まるように除算してから処理する というアルゴリズムにすれば精度は落ちますが広い範囲の入力を受けることができます

このコードではハードコードされた値があるため、intの代わりにshortにしようと思うとちょっと面倒ですが、uint16_tでテーブルを作った場合、100x100だと20kByteくらいのROMが必要になります

精度は悪く、ifの連続なので処理速度も期待できませんが、ソフトウェアで浮動小数点を計算するとか、そもそも浮動小数点が使えない環境では使えるかも

2014年4月4日金曜日

基板

P板に注文していた基板が届きました


シルクズレもカスレもなく、さすがとしか言いようがありません
シルク切れてるのは設計の問題ですね
とりあえず試作ということで。。。

今回の基板は1枚がかなり小型で、結局最終的にはすべての部品が表面実装となりました
いちおうコネクタ部分は穴が開けてあり、いままで使っていた挿入型のコネクタも使えるようにしてありますが、基本的にはコネクタを使わず 被覆線をそのままハンダ付けするような感じにしようと思っています

ひと通りの部品は手元に有るのですが、78L05の表面実装を買い忘れていて、これはリード部品を実装して対応しようと思います
また、本来はフルカラーLEDを接続するためのコネクタも、手持ちのフルカラーLEDがないので 単色LEDを載せる感じになりそうです

まぁ試作品なので、ちゃんと動きそうならちゃんと実装します

今回は4面付で5枚注文しました なのでターゲット20枚分の基板があります
が、部品はそこまで買ってないので、何枚かはそのまま眠らせることになりそうです

ターゲットプレートがちゃんと動けば、コントローラ側の仕様を固めて、また基板発注です


シューティングタイマーの仕様もいくつか考えています

モードは
・ファーストショット/FS(ダブルタップ等で最初の1発で停止)
・ラストショット/LS(最後に撃った時点の時間を表示)
・2TEAM/TT(ターゲットにヒットする毎にLEDの色が反転)
・MORE TEAM/MT(ターゲットにヒットする毎にプリセットの色をランダムに選択)
というのを実装しようと思っています

とりあえずFSモードは必須ですし、できればTTモードも作りたいです

MTモードはどうやって遊ぶかというと
予め4チームほど作っておき、色を決めてプリセットしておきます
ブザーが鳴るとゲームスタートで、終了のブザーが鳴るまで自分以外の色のターゲットを撃ちます
ターゲットを撃ってもランダムで色が変更されるので、自分のチームの色になるまで撃ち続け、また自分の色になったら撃つのをやめる という状況認識が必要になります
終了のブザーの後、一番色が多いチームの勝ち
という感じです

距離15mに置いてハンドガン縛りや、25mに置いてボルトアクション縛り などでやると楽しいと思います

とりあえず、ターゲットプレートの試作に部品を実装して、ちゃんと使えることが確認できてから ですね


追記 2014/04/05 0:39
とりあえずオペアンプ周りの動作確認ができました
LED周りは4個中2個は動作確認が済んでいます
ということで、手直しせずにこのまま使えそうです
一旦この基板は放置で、表示側の回路を考えようと思います

2014年4月2日水曜日

球状シリコン太陽電池

近所のスーパーに行った際、工作コーナーに新しく球状シリコン太陽電池を入荷していたので思わず買ってしまいました


スペックは0.45V/1.67A(0.75W) 開放0.57V 短絡1.80A という感じです
短冊状で。いい感じの大きさの比較が思いつきませんが、KindlePWを縦半分にしたくらい という感じでしょうか

球状シリコン太陽電池についてですが
一般的な太陽電池は切手のような受光素子が大量に並んでいる感じですが、球状シリコン太陽電池は米粒のような1個1個が受光素子です
そして最大の違いが製造工程にあり、通常のシリコンを切削して作るのと違い、球状シリコン太陽電池は液状のシリコンを滴下し、涙滴状に固まった粒を加工して使用します
そのためシリコンの使用効率が高く、シリコンの使用量は一般的な太陽電池の5分の1程度にできるようです
欠点としては、面積効率が良くない 受光できても角度によって(球の周囲)は変換効率が低い などが挙げられます
しかしアルミニウムで凸レンズ状の板を作り、そこに受光素子を埋め込むことにより上記の欠点を解決しているようです
また、1個の受光素子が小型で、ある程度曲げることができるため、多少の曲面であればそのまま貼り付けることができます

で、とりあえず使う予定はないのですが 曲面に曲げることができるなら缶サットに使えるかな とか思っています
去年作った缶サットも太陽電池を載せていますが、0.3Wのパネルを4枚使用し、電気はLEDを光らせるだけにしか使っていませんでした(LEDで可視光通信も実装したのですが、動画記録が無いので確認はできていません)

缶の直径は66mmで、半径33mmのRに曲げなければいけませんが、今回買ったパネルはそこまで曲げることはできなさそうな感じです

また、全体をパネルで覆うと協賛企業のロゴが隠れてしまうため、レギュレーション違反になるという問題もあります(そもそも僕は缶サット甲子園の参加資格は無いので気にしなければ問題無いという話でもありますが)

また、3枚を120°毎に3枚貼り付けた場合は1.35V/1.67Aの電源になりますが、直列で接続すると圧倒的に効率が落ちてしまうため、1枚1枚にMPPC(もしくはMPPT)に対応したDCDCコンバータを接続する必要があります

MPPCなDCDCはLTC3105が便利ですが、このパネルとは微妙にミスマッチです
電流が3分の1で電圧が4倍くらいあればちょうどいいですが


なお、この球状シリコン太陽電池はAmazonでも購入できます
http://www.amazon.co.jp/dp/B00FIXNAX8/