アーカイブ

‘HTTP’ タグのついている投稿

HTML+JavaScriptをArduinoに直結できるシリアルポートサーバーを作った

2012 年 5 月 19 日 コメントはありません

SerialPort Serverを使うと、HTMLとJSをArduinoに直結できる。JavaScriptを少し書くだけでArduinoに「カーテン開けろ」とか「部屋の明るさよこせ」とか命令を送れるわけだ。
Web系の技術とハードウェアの技術を同時に使うには、間に「つなぎ」が必要なので、必要な機能を全部入れたサーバーを作ってみた感じです。
(github pagesでプロジェクトページ作ってみたんだけど楽でいいですね)


こういうこと。


シリアルポートサーバーはrubygemsでインストールできる。

gem install serialport-server
which serialport-server
serialport-server --help
serialport-server /dev/tty.デバイス名
–helpでヘルプが出る。デバイス名を引数にして起動できる。
Macならsudo gem installで一発でインストールできるはず。

起動するとHTTPサーバー(http://localhost:8783)、WebSocketサーバー(ws://localhost:8784)、TCP Socketサーバー(localhost:8785)の3つが同時に起動する。

HTTPのサーバーはajaxのクロスドメイン制限を超えてデータのやりとりができるようにしてある。シリアルポートサーバーを動かして、HTMLとJavaScriptを書けばArduinoとJavaScriptが直接通信できるというわけだ。
(response headerにAccess-Control-Allow-Originを付ければ良いとyuisekiのはてブを見ていて知ったのでやってみた。)


■デモ
SerialPort Serverのページにも英語で書いたが、簡単なサンプルプロジェクトを日本語でも解説しておく。


1. Arduinoにプログラムを書き込む

2. ArduinoのDigital13番ピンにLEDを、Analog0番ピンにCdSと10kΩの抵抗を接続する
SerialPort Server sample
SerialPort Server sample

3. SerialPort Serverを起動する

4. Ajax版サンプルを開いて試してみる

5. WebSocket版サンプルを開いて試してみる


WebブラウザからLEDを点灯消灯したり、CdSの明るさの値がリアルタイムにスライダーに反映されているはずだ。
実際にAjaxサンプルのJavaScriptのコードを見てみると簡単さがわかると思う。17行でLEDとCdSを制御できている。



もちろんWebサーバーなので、複数のWebブラウザがSerialPort Serverから同時にデータを読み出そうとしても大丈夫。
増井研で1年以上、遠隔操縦ロボットの制御に使っていたプログラムを元に使っているのでプロセスを起動してたぶん3ヶ月ぐらい放置していても動き続けるぐらいに安定していると思う。

Rubyでcometサーバー作る

2011 年 11 月 22 日 コメントはありません
カテゴリー: 未分類 タグ: , , , ,

最近cometとかいう最新技術が流行っているらしいので、eventmachine_httpserverで作ってみた。


ここにサーバーとクライアントの例がある。どっちも50行ぐらいで実装できた。
comet at master from shokai/eventmachine-study – GitHub

サーバー起動して、タイムアウトを10秒に指定。
このサーバーは、POSTされた値を保持して、GETされたら返す。GETに対してはレスポンスを遅らせて返す。

ruby server.rb 8080 10


クライアントを起動。GETしてから25秒後にPOST
ruby client.rb 25
GET(comet) -> wait 25 sec -> POST
* GET
sleep 25 sec
404 ## 10秒経過、切断された

* GET ## 再接続
404

* GET ## 3回目
* POST kazusuke
POST success
200
kazusuke
200 ## 5秒待ってようやく値が返ってきた
kazusuke
* GET

サーバー側のログはこんなんなってた
http server start, port:8080, comet_timeout:10(sec)
load: 2.20 cmd: ruby 11481 waiting 0.45u 0.32s
request_method : GET
path_info : /message
query_str :
post_content :
request_method : GET
path_info : /message
query_str :
post_content :
request_method : POST
path_info : /message
query_str :
post_content : kazusuke
kazusuke
request_method : GET
path_info : /message
query_str :
post_content :

cometサーバー、接続が不安定なクライアントにpush通知するのに便利。

AndroidでHTTP POST

2011 年 5 月 13 日 コメントはありません
カテゴリー: 未分類 タグ: , , , , ,

targetはAndroid2.3.4、API 10向けにビルドして試した。

参考:


<uses-permission android:name="android.permission.INTERNET" />

import java.io.*;
import java.util.*;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

HttpClient client = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://localhost:8080");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("message", "ほむ"));
try{
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
HttpResponse res = client.execute(httppost);
ByteArrayOutputStream os = new ByteArrayOutputStream();
res.getEntity().writeTo(os);
Log.v("result", os.toString());
Log.v("status", res.getStatusLine().getStatusCode());
}
catch(Exception e){
e.printStackTrace();
}


リクエストができているかの確認は、EM::HttpServerで見た
gem install eventmachine_httpserver
#!/usr/bin/env ruby
require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'

class Handler < EM::Connection
include EM::HttpServer

def process_http_request
res = EM::DelegatedHttpResponse.new(self)
puts "request_method : #{@http_request_method}"
puts "path_info : #{@http_path_info}"
puts "query_str : #{@http_query_string}"
puts "post_content : #{@http_post_content}"
res.status = 200
res.content = "こんにちは"
res.send_response
end
end

EM::run do
EM::start_server("0.0.0.0", 8080, Handler)
puts "http server start, prot 8080"
end

serial-http-gateway作った

2011 年 2 月 4 日 コメントはありません

シリアルポートをhttpで使えるツールを作った。
ブラウザでhttp://localhost:8783を開くとデータが読める。POSTでデータを送ると書き込める。

webブラウザでロボットを操作する部分がある、OB降臨システムというのを作っているのでその部品として作った。

githubに全部置いた。



■インストールと起動
git clone git://github.com/shokai/serial-http-gateway.git
cd serial-http-gateway
gem install serialport eventmachine eventmachine_httpserver json ArgsParser


Arduino等をMacに接続すると/dev/tty.usb〜〜という名前になる。引数に渡して起動する。
./serial-http-gateway --help
./serial-http-gateway /dev/tty.usbserial-A7006Rqn
port 8783で起動する。引数-portで変更できる。



gemが全て入っていれば実行ファイル単体で動くので、適当なパスが通っている場所にコピーして置くと便利。
sudo cp serial-http-gateway /usr/local/sbin/



■使う
HTTP POSTでシリアルポートに書き込める。
% curl -d 'testtest' 'http://localhost:8783'


HTTP-GETでシリアルポートからのデータが読める。
最近100件の受信データが保存してあって配列で返ってくる。timeに時間が入っている。
curl 'http://localhost:8783'


時間はミリ秒でunixtimeなので、1000で割れば普通のunixtimeになる
[{"data":287,"time":1296767483756},{"data":288,"time":1296767483253},{"data":291,"time":1296767482751},{"data":293,"time":1296767482246},{"data":292,"time":1296767481743},{"data":293,"time":1296767481238},{"data":294,"time":1296767480736},{"data":299,"time":1296767480233},{"data":303,"time":1296767479729},{"data":305,"time":1296767479226},{"data":307,"time":1296767478721},{"data":312,"time":1296767478219},{"data":321,"time":1296767477714},{"data":332,"time":1296767477211},{"data":344,"time":1296767476709},{"data":359,"time":1296767476204},{"data":"\u0000390","time":1296767475701}]


eventmachine_httpserver便利だなー

Androidのギャラリーの「共有」メニューから呼び出せるアプリを作る

2010 年 11 月 5 日 コメントはありません
カテゴリー: 未分類 タグ: , , , ,

Androidのカメラで撮影した画像はギャラリーに保存される。そこで画像を選んで共有またはshareなどを選択すると、暗黙的Intent呼び出しで画像を別アプリに渡せる。
Flickrアップローダとか、EvernoteとかDropboxのアプリに写真を渡しているのはこのしくみを使っている。


まずManifest.xmlにintent-filterを追加する。
image/*でもいいが、それだと動画も渡せてしまうので手当たり次第mimeTypeを指定してみる。

<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/jpeg"/>
<data android:mimeType="image/jpg"/>
<data android:mimeType="image/png"/>
<data android:mimeType="image/bmp"/>
<data android:mimeType="image/bitmap"/>
</intent-filter>
ようするに、アプリに「私はimageをSENDするというアクションを受け取って適当に処理できますよ」と宣言させておくと、
他のアプリが「誰でもいいからSENDしたimageを受け取ってくれる人いませんか」と呼び出したときに連携できるようになる。


で、このintent-filterと関連付けられているクラス(普通なら一番最初に生成したjavaクラス)の
onCreate()内でintentを受け取る。
あらかじめ、受け取った画像を表示するためのImageViewを配置しておく。
private ImageView imageView;
this.imageView = (ImageView)this.findViewById(R.id.ImageView01);


暗黙的Intentで呼び出されたのか、普通のIntent呼び出しで起動したのか(つまりホーム画面からアプリのアイコンを押して起動したのか)を判別して処理する。
呼び出し元の名前だけで判別してる
Uri imageUri = null;
try{
imageUri = Uri.parse(getIntent().getExtras().get("android.intent.extra.STREAM").toString());
}
catch(Exception e){
e.printStackTrace();
}
if (imageUri != null) {
Log.v("ImageIntent", "暗黙的intentから起動");
Bitmap bmp = null;
try {
bmp = Media.getBitmap(getContentResolver(), imageUri);
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
if (bmp != null) {
this.imageView.setImageBitmap(bmp);
}
}
else{
Log.v("ImageIntent", "普通に起動");
}
imageUriにはcontent://media〜〜というパスが入る。これはAndroid内のファイルシステムのパスではないが、Media.getBitmapに渡すとデータを読み出せる。
とりあえずImageViewに表示だけしておく。


あとEclipseだと日本語が化けるのでterminalからadb logcatしてる。


Bitmapを受け取ったら、後は適当に処理すれば良い。
リサイズとか
public Bitmap resize(Bitmap bmp, float scale){
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
}



form/multipart-dataでgyazo.comにアップロードするとか、色々できる。
Manifest.xmlにpermissionを追加
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
public void upload(Bitmap bmp) throws ParseException, IOException{
File dataDir = new File(Environment.getExternalStorageDirectory(), this.getPackageName());
dataDir.mkdirs();
FileOutputStream tmp = new FileOutputStream(new File(dataDir, "tmp.png"));
bmp.compress(Bitmap.CompressFormat.PNG, 80, tmp);
tmp.close();

HttpClient httpClient = new DefaultHttpClient();
HttpPost post = new HttpPost("http://gyazo.com/upload.cgi");
MultipartEntity entity = new MultipartEntity();

entity.addPart("id", new StringBody("your-gyazo-id"));
entity.addPart("imagedata", new FileBody(new File(dataDir, "tmp.png"), "image/png"));
post.setEntity(entity);
post.setHeader("User-Agent", "TestAndroidApp/0.1");

httpClient.execute(post);
}
httpでform/multipart-dataでアップロードするには
の2つが必要。


■参考
このへんが参考になった

track feed