2014年7月30日 星期三

AMIGO Camera(三)

《AMIGO Camera》是一台由紅米 + Arduino + 伺服馬達組成,比 IP 攝像頭多一點功能的裝置。希望讓使用者在遠端經互聯網控制《AMIGO Camera》,進行移動、拍攝、縮時錄影等功能。早兩天大約設計過《AMIGO Camera》的外觀,今日花點時間編寫實驗程式,看看能否做到遠端控制。

我選擇在 Android 上加入 HTTP 服務器,這樣只要把域名或地址接通,便能在瀏覽器上輸入地址,透過互聯網直接連線。到時在家中的路由器按埠號把數據分發到紅米就行了。利用瀏覽器的好處是不同平台都有,不用安裝特別程式。

我不懂 Android 編程,所以這次試驗遇到不同問題。首先是 NanoHTTPD 有很多版本,網上的教學也有很多版本,通通都不適用最新的版本。public Response serve(IHTTPSession session) 就是新版本的格式。既然不能照抄,那只好參考舊版的編寫方式,加上自己的思考去做。好不容易改好了,畫面也出現了,瀏覽器卻連不上。找了很久,原來是舊版不用 server.start(),新版要。好了,成功連上了,但想連線是在畫面顯示當刻時間,發現那時要更新介面,不如 iOS 般簡單、直接、容易。又找了一會,成功以 Handler 來達成。今回學到不少。下一步就是加入 OTG + Arduino 連線。這樣基本的硬件功能便已齊全。
//========================================================================================
//  AMIGO Camera
//----------------------------------------------------------------------------------------
//  Created by Pacess on 2014-07-30
//  Copyright (c) 2014 Sita Technology Company.  All rights reserved.
//========================================================================================

//----------------------------------------------------------------------------------------
//  0    1         2         3         4         5         6         7         8         9
//  56789012345678901234567890123456789012345678901234567890123456789012345678901234567890

package com.pacess.amigocamera;

import android.widget.TextView;
import android.app.Activity;
import android.app.Fragment;
import android.os.Message;
import android.os.Handler;
import android.os.Bundle;
import android.net.wifi.WifiManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.MenuItem;
import android.view.Menu;
import android.view.View;

import java.text.SimpleDateFormat;
import java.io.IOException;
import java.lang.Runnable;
import java.util.Date;

//========================================================================================
public class MainActivity extends Activity  {

 //----------------------------------------------------------------------------------------
 //  Defines
 private static final int PORT = 8080;

 //----------------------------------------------------------------------------------------
 //  Class variables
 private TextView statusTextView;
 private HTTPServer httpServer;

 //----------------------------------------------------------------------------------------
 Handler handler = new Handler()  {
  @Override public void handleMessage(Message message)  {
   if (statusTextView == null)  {return;}

   String timeStamp = (String)message.obj;

   StringBuilder stringBuilder = new StringBuilder();
   stringBuilder.append("Last request: ");
   stringBuilder.append(timeStamp);

   statusTextView.setText(stringBuilder.toString());
  }
 };

 //----------------------------------------------------------------------------------------
 @Override protected void onCreate(Bundle savedInstanceState)  {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Log.d("MainActivity", "onCreate");

  if (savedInstanceState != null)  {return;}
  getFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit();
 }

 //----------------------------------------------------------------------------------------
 @Override protected void onResume()  {
  super.onResume();
  Log.d("MainActivity", "onResume");

  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  String timeString = dateFormat.format(new Date());
  statusTextView = (TextView)findViewById(R.id.status);
  if (statusTextView == null)  {
   Log.d("MainActivity", "### statusTextView is null...");
  }  else  {
   statusTextView.setText("Time stamp: "+timeString);
  }

  WifiManager wifiManager = (WifiManager)getSystemService(WIFI_SERVICE);
  int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
  final String ipString = String.format("%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff), (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));

  TextView textView = (TextView)findViewById(R.id.ipAddress);
  textView.setText("http://"+ipString+":"+PORT);

  try  {
   httpServer = new HTTPServer();
   httpServer.start();
   Log.d("MainActivity", "HTTP server started...");

  }  catch (IOException e)  {
   e.printStackTrace();
  }
 }

 //----------------------------------------------------------------------------------------
 @Override protected void onPause()  {
  super.onPause();
  Log.d("MainActivity", "onPause");

  if (httpServer == null)  {return;}

  httpServer.stop();
  Log.d("MainActivity", "HTTP server stopped");
 }

 //----------------------------------------------------------------------------------------
 @Override public boolean onCreateOptionsMenu(Menu menu)  {
  //  Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 //----------------------------------------------------------------------------------------
 @Override public boolean onOptionsItemSelected(MenuItem item)  {
  //  Handle action bar item clicks here. The action bar will automatically handle clicks
  //  on the Home/Up button, so long as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_settings)  {return true;}
  return super.onOptionsItemSelected(item);
 }

 //========================================================================================
 //  A placeholder fragment containing a simple view.
 public static class PlaceholderFragment extends Fragment  {

  //----------------------------------------------------------------------------------------
  public PlaceholderFragment()  {
  }

  //----------------------------------------------------------------------------------------
  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)  {
   View rootView = inflater.inflate(R.layout.fragment_main, container, false);
   return rootView;
  }
 }

 //========================================================================================
 public class HTTPServer extends NanoHTTPD  {

  //----------------------------------------------------------------------------------------
  public HTTPServer() throws IOException  {
   super(PORT);
   Log.d("HTTPServer", "Server start "+PORT);
  }

  //----------------------------------------------------------------------------------------
  @Override public Response serve(IHTTPSession session)  {
   Log.d("HTTPServer", "Server response");

   SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   final String timeString = dateFormat.format(new Date());

   //----------------------------------------------------------------------------------------
   //  Use handler to update user interface
   Thread thread = new Thread(new Runnable()  {
    @Override public void run()  {
     Message message = handler.obtainMessage();
     message.obj = timeString;
     message.sendToTarget();
    }
   });
   thread.start();

   //----------------------------------------------------------------------------------------
   //  Create response HTML
   StringBuilder stringBuilder = new StringBuilder();
   stringBuilder.append("<html>");
   stringBuilder.append("<body>");

   stringBuilder.append("<b>AMIGO Camera - ").append(timeString).append("</b><hr>");
   stringBuilder.append("<b>Headers</b> = ").append(String.valueOf(session.getHeaders())).append("<br>");
   stringBuilder.append("<b>Method</b> = ").append(String.valueOf(session.getMethod())).append("<br>");
   stringBuilder.append("<b>URI</b> = ").append(String.valueOf(session.getUri())).append("<br>");
   stringBuilder.append("<b>Parameters</b> = ").append(String.valueOf(session.getParms())).append("<br>");

   stringBuilder.append("</body>");
   stringBuilder.append("</html>");
   return new NanoHTTPD.Response(stringBuilder.toString());
  }
 }
}

沒有留言: