Posts Tagged ‘手机’

悲剧的一周

上周夸下海口,说准备做一个两轮的平衡小车。当时的想法实在是非常肤浅:把手机捆在NXT身上,不断的向NXT发送当前的倾斜角度,NXT根据角度进行调整。当小车向前倾的时候,轮子就向前滚,小车向后倾时,轮子就向后滚。同时根据角度倾斜的大小来设置轮子的滚动速度。

按照这个思路开始搭建,因为手头的乐高颗粒严重不够,只好从萝卜头身上割了点肉下来,即使这样也只能用小件拼大件,最后搭了个像怪物似的“两轮平衡小车”:

未成功的两轮平衡小车

未成功的两轮平衡小车

细心的同学可能发现小车的背后有个亮度传感器,这个是后话,等会儿解释。最初我的想法是手机通过蓝牙和NXT连接并控制电机运动。HTC手机控制NXT的代码如下(其中Timer设置的时间间隔是50ms):

private Nxt nxt;
private void ConnectButton_Click(object sender, EventArgs e)
{
    try
    {
        nxt = new Nxt();
        nxt.Connect("COM9");
    }
    catch (Exception)
    {
        nxt.Dispose();
        nxt = null;
    }
}

private void nxtTimer_Tick(object sender, EventArgs e)
{
    if (nxt != null)
    {
        try
        {
            GVector gvector = mySensor.GetGVector();
            //1.75 is the fix number for center line
            double gz = gvector.Z - 1.75;

            sbyte power = Convert.ToSByte(-8 * gz);

            nxt.SetOutputState(MotorPort.PortB, power, MotorModes.Regulated,
                MotorRegulationMode.Speed, 0, MotorRunState.Running, 0);
        }
        catch (Exception) { }
    }
}

冒着被丈母娘训斥的危险折腾到半夜,终于完工了。一般来说,工作的完成意味着悲剧的开始:这个小车反应非常迟钝,先躺到地上,然后轮子才开始动,就像个耍赖的小P孩。我试过把50ms的Timer调的更小,但是这时候手机已经处理不过来,反而更慢了。

本来想把这个失败的作品马上拆了(萝卜头的胳膊还处于脱臼状态),但是有点儿不甘心。于是第二天又想了个“好办法”:也许是蓝牙传输太慢,我能不能根据倾斜角度来改变屏幕的颜色,然后NXT用亮度传感器通过读屏幕颜色来控制电机呢?于是脱臼的萝卜头又被拆掉了一个传感器,凑成了这样一个小车:

背着手机的小车

背着手机的小车

用来改变屏幕颜色的代码如下:

private void UpdateSpeed()
{
    GVector gvector = mySensor.GetGVector();
    int gray = Convert.ToInt32(256 * (gvector.Z + 9.8) / 19.6);
    if (gray < 0) gray = 0;
    if (gray > 255) gray = 255;

    this.BackColor = Color.FromArgb(gray, gray, gray);
}

再次折腾到半夜。事实证明,变色的反应速度比蓝牙要快一点,但还是不足以让小车保持平衡。小车吱吱嘎嘎前后晃动几下,就倒在了地上。这个平衡小车的尝试到此宣告失败。失败的原因总结如下:
1,小车重心太高,以至于倒下的速度太快,这个因为零件太少实在没办法
2,数据反应速度太慢,经我测试,WM手机+C#的最快反应速度只能到50ms,对于平衡这样的工作来说实在是太漫长了

搭车再提供一项悲剧,我用了两年的笔记本坏掉了,虽然还能凑合用,但是显卡貌似没有绿色了。没错,这个就是315曝光的HP笔记本,从症状判断应该是显卡损坏,坏掉的时候正好过了保修期几天…..

昨晚拆性大发,把这个烂笔记本拆了个七零八落:

臭名昭著的HP Presario笔记本

臭名昭著的HP Presario笔记本

拆完发现主板的显卡是集成的,没啥好玩的,只好又装回去。还好,重新装好后只剩下两颗螺丝,开机居然还可以使用,创下我破坏史上的最低纪录。

这就是一周总结了,奋战了三个晚上,没做出啥成果,只能供大家娱乐一下了。后来在网上搜了一下,还真有NXT的两轮平衡小车,用亮度传感器+两个电机,有兴趣的同学自己去研究一下吧:

http://www.nxtprograms.com/segway/index.html

NXT两轮平衡小车

NXT两轮平衡小车

蓝牙手机变身小车遥控器 (3)

发现已经一星期没有更新博客了,真是非常抱歉。上周做完手机遥控器以后,一直没有其他进展,也没来得及发攻略。接下来的时间可能会更忙,只能先跟大家说声抱歉了。在此严重羡慕一下大苹果同学,这个家伙周末带mm飞去武汉大学看樱花了,眼红啊。。。

今天跟大家分享一下在Windows Mobile环境里,如何配置和调用Web Service(就是上次说的四个程序中的第二个)。

一、首先配置开发环境,需要安装下面的几个东东:
1,VS2008 及其 SP1
2,Windows Mobile 6 Professional SDK Refresh.msi
3,Windows Mobile 6.5 Professional Developer Tool Kit (CHS).msi

安装之后,在“新建工程”菜单里,就可以看到智能手机的选项:

新建智能手机项目

新建智能手机项目

新建智能手机项目只有一个选项,输入项目名称后,单击进入下一步,选择平台版本:

选择手机版本

选择手机版本

单击确定,一个新的手机项目就建好了。

新建的工程

新建的工程

二、控件,编译和部署
试着拖两个按钮到手机界面上去,你可以发现跟普通的windows应用程序开发是类似的。我添加了三个按钮,分别是“启动”,“停止”和“退出”。双击“退出”按钮,对按钮添加相应的事件,其他两个按钮稍后再说:

private void ExitButton_Click(object sender, EventArgs e)
{
    this.Dispose();
    this.Close();
}

选择菜单中的“编译->编译项目”,然后到这个项目的文件夹目录里面翻一翻,在 \PhoneRemoteControl\bin\Debug目录下,可以发现多了个exe文件。

先别激动,这个文件在电脑上双击是不能运行的。必须要复制到手机上,用你的手指头单击它,然后发现……还是不能运行。

嗯,这是因为你的人品不够好,弥补办法是去微软网站上下载一个叫“NET Compact Framework”的东西并安装上。

好了,再用一阳指点一下,这次可以看到软件的界面了,再点一下“退出”按钮,程序就退出了(好像是废话)

三、调用Web Service
首先要在工程中引用WebService:在工程名字上用右键,弹出的菜单中选择“add web reference”。我这个是英文版,不知道中文版里叫什么,参考一下吧:

add reference

add reference

在接下来的设置窗口里,输入上一步中做好的web service地址,另外给它起个名字叫“PingService”:

输入service地址

输入service地址

添加了service之后,咱们看看怎么调用,非常简单,用一个函数就可以了:

private void WCFRequestCall(string data)
{
    PingService.Ping client = new PhoneRemoteControl.PingService.Ping();
    client.Url = "http://chenwu03/service/ping.asmx";
    client.SetData(data);
}

这里有一句 client.Url=”…”; 这个为部署修改用的,例如你开发的时候用chenwu03,将来可能换到另一台电脑上,那么在这里改成chenwu04就可以了。建议把这个参数放到配置文件里,这样如果需要修改的话,不需要改代码重新编译,直接用写字板改一下配置文件即可。

四、在手机上画方向盘
其实想要遥控小车,用前进后退,左转右转几个按钮就可以了。但是为了捍卫Dev的尊严,我决定做的花哨一点,于是很骚包的画了一个方向盘。在Form中绘图,可以在一个叫Form1_Paint的函数中添加程序,不说废话,看代码吧:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    int windowWidth = 480;
    int borderOffset = 40;
    int topStart = 160;
    int lineOffset = 25;
    int r = windowWidth / 2 - borderOffset;

    Graphics g = this.CreateGraphics();

    Pen pen = new Pen(Color.Red);
    pen.Width = 3;
    pen.DashStyle = DashStyle.Dash;
    g.DrawLine(pen, borderOffset-lineOffset, topStart + r, windowWidth - borderOffset+lineOffset, topStart + r);
    g.DrawLine(pen, windowWidth / 2, topStart - lineOffset, windowWidth / 2, topStart + 2 * r + lineOffset);

    pen.Color = Color.Blue;
    pen.DashStyle = DashStyle.Solid;
    pen.Width = 8;
    g.DrawEllipse(pen, borderOffset, topStart, 2 * r, 2 * r);

    steeringWheel.CenterX = windowWidth / 2;
    steeringWheel.CenterY = topStart + r;
    steeringWheel.R = r;
}

画出来的效果如下:

在手机上的效果

在手机上的效果

五,计算小车的速度
因为Arduino中,直流电机的控制是通过PWM管脚输出的,值范围是0~1023;所以我让手机发出的数据就是简单的“VLeft,VRight”,值的范围都是从0到1023,用正负号表示前进还是后退。

设计的思路是,点击圆圈的正上方时,两个轮子都向前走,离圆心越近速度越慢,最远的地方发送的数据是“1023,1023”;
类似的,点击最下面时,发送的数据是“-1023,-1023”;
当点击圆圈的左边时,我会把右侧的速度变大,左侧的速度变小,这样小车就向左转了;
当点击到圆圈外面时,直接忽略掉。

需要注意的是,在实际操作中,当输出速度低于300时,电机其实已经带不动了,所以我把低于300的值全都重置成0。这部分代码就不贴了,反正大家可以按照自己的喜好编写控制代码。需要的同学可以下载代码回去参考下。
至于上面提到的“启动”和“停止”按钮,我们就发送“600,600”,“0,0”两组数据即可

六、发送数据
当你的咸猪手在手机上乱摸时,我是用Form1_MouseMove来响应事件的。事实证明,这个数据发送的频率太高了,每秒钟会发送几百组速度。为了回避这个问题,我们设置一个定时器,每隔150毫秒执行一次,中间MouseMove的事件全部扔掉。

private string currentCommand = "";
private string lastCommand = "";

private void SendTimer_Tick(object sender, EventArgs e)
{
    if (!string.IsNullOrEmpty(lastCommand))
    {
        currentCommand = lastCommand;
        lastCommand = "";
    }
    if (!string.IsNullOrEmpty(currentCommand))
    {
        WCFRequestCall(currentCommand);
        currentCommand = "";
    }
}

好了,接下来是检查工作成果。用手指头在屏幕上一阵乱摸,可以从端口监视器里看到不断跳出来的数组:

端口监视器

端口监视器

有经验的同学可能有两个问题:
1,为什么不直接用WebRequest发起请求呢?
这个我试过,手机容易死机,原因还不清楚
2,为什么不直接用TCP/IP发送数据到PC端口呢,绕这么大一圈?
这个我也试过,程序不会出错,但是电脑上也没有接到数据,原因依然不清楚

不管怎么说,反正试出来一个可行的方法。这篇博客中提到的源代码在这里下载,其他部分下回继续:
http://www.diy-robots.com/Resources/XiaoI/PhoneRemoteControl.zip

蓝牙手机变身小车遥控器 (2)

过生日这几天一直在腐败,今天抽时间把小车遥控器的攻略补上。这个小车遥控器一共需要四个程序,首先看看运行在笔记本上,用来接收手机指令的Web Service。

先创建一个Web Service工程,命名为 WCF_ReceivePhoneMessage:

创建 WebService 工程

创建 WebService 工程

在工程根目录里新建一个文件Web.config,并添加以下的设置:

  <appSettings>
    <add key="PortNumber" value="31718" />
  </appSettings>

这个是将来用来转发指令的Socket端口号。接下来再创建一个ping.asmx文件,这个就是传说中的Web Service的接口,代码如下:

	[WebService(Namespace = "http://www.diy-robots.com/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    public class Ping : System.Web.Services.WebService
    {
        static private int PortNumber = Convert.ToInt32(ConfigurationManager.AppSettings["PortNumber"]);

        [WebMethod]
        public void SetData(string data)
        {
            if (!string.IsNullOrEmpty(data))
            {
                try
                {
					//Here are codes to get the IP address of local computer
					//You can also set it directly
                    string ipAddress = "";
                    IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
                    foreach (IPAddress ipAddr in ipHost.AddressList)
                    {
                        ipAddress = ipAddr.ToString();
                        if (ipAddress.IndexOf(":") < 0) break;
                    }

					//Send TCP/IP data
                    TcpClient client = new TcpClient();
                    client.Connect(ipAddress, PortNumber);
                    NetworkStream stream = client.GetStream();
                    byte[] bytes = Encoding.ASCII.GetBytes(data);
                    lock (stream)
                    {
                        stream.Write(bytes, 0, bytes.Length);
                    }
                    client.Close();
                }
                catch (Exception) { }
            }
        }
    }

段代码其实很简单,就是创建一个SetData函数,把传进来的string类型的参数转成byte数组,然后转发到制定的Socket端口。为了让这个Web Service生效,还需要在IIS里面生成一个应用程序。看下面这个图:

设置IIS

设置IIS

IIS设置设置好了,咱们在浏览器里简单测试一下程序是不是部署成功,输入下面的地址:其中ComputerNames是你自己电脑的名字
http://ComputerName/service/ping.asmx?op=SetData

这个页面只要不出错就算成功了,正确的显示应该是这样:

Web Service创建成功

Web Service创建成功

本来想把手机上的程序也一起发完,不过又累又困,下回再说吧 :)

上面这个web service的源代码可以在这里下载:
http://www.diy-robots.com/Resources/XiaoI/WCF_ReceivePhoneMessage.zip

蓝牙手机变身小车遥控器 (1)

上篇博客提到我企图把手机改造成小爱的遥控器,这几天终于有点眉目了。这个小车刚组装好的时候,我就接上电池试运行了一下,结果它在屋里一溜小跑,紧赶慢赶才没让它撞到东西。这就是决定做一个遥控器的原因。本着“变废为宝,物尽其能”的DIY精神,我打算利用现有的手机来改造。

说起变废为宝,老婆大人一定心情很惆怅。因为大多数情况下,我喜欢拆东西,在家里干的都是些“变宝为废”的事情。但这次绝对是废物利用。我的手机是HTC钻石2,这款手机的系统是Windows Mobile;优点是CPU强大,屏幕细腻;缺点是经常丢短信,漏电话。也就是说,这款手机作为一个手机,基本可以认为是废物点心。好,接下来开始我们的变废为宝之旅吧。

第一次做手机上的开发,这段时间真是颇费周折。下面是我尝试的几个方案,供大家参考:

最初我想用类似控制萝卜头的办法,使用蓝牙串口向PC发送数据,结果发现并不是所有蓝牙连接都具有虚拟端口的功能。可以查看蓝牙支持的功能,那个Serial Port就是乐高机器人用来通讯的。

HTC手机的蓝牙,不支持Serial Port通信

HTC手机的蓝牙,不支持Serial Port通信

第二个方案是改用手机的WIFI功能,在PC上搭建一个网页,通过URL Request的方式向PC发送数据,非常杯具的是WIFI可以向公网IP发数据,但是不能向局域网内的笔记本发数据。经验证,我放在博客主机上的测试页面可以收到数据,虽然最终也没有成功,不过也算是进了一步。

第三次尝试又用回蓝牙,发现在ActiveSync连接模式下,手机共享的是笔记本的网络资源,这样就可以向自身发送URL Request了。经测试,数据流可以走通。正在欢欣鼓舞的时候,发现一件更杯具的事情:不知道为什么,每发送五六条数据之后,手机就像死机了一样,必须休息几分钟之后才能继续发送。这要是用来当遥控器,小爱怕早就把电视撞烂了。

接下来的几天没有什么进展,非常郁闷,以至于我经常萌生一些邪念(例如把手机扔进马桶)。事实证明最痛苦的时候往往已经接近成功了。有一天我把URL Request换成Web Service做了一次尝试,神奇的发现手机调用Web Service居然又快又不会死机。接下来的事情只能用峰回路转来形容,Web Service再通过TCP/IP协议把数据转发到一个Socket端口,一个端口监听程序再把数据通过USB发给Arduino开发板。经过这么多热心的同志,终于把鸡毛信从手机送到电机了,看看数据流图(其中WebService服务器和笔记本逻辑上是独立的,物理上是同一台电脑):

手机遥控器的数据流图

手机遥控器的数据流图

不知道会不会被手机开发的业内人士们鄙视,不过我至少摸索了一条没人走过的曲折道路,整个流程一共需要写四个程序:
1,在手机上运行的小程序,我画了一个方向盘,用手指触摸的时候,会换算成电机速度发送给Web Service。
   开发环境是Windows Mobile 6.5,使用的语言是C#
2,Web Service程序,接受指令之后,通过TCP/IP的方式转发。使用的语言还是C#,服务器是IIS。
3,端口监听以及Arduino开发板通信的程序。一边通过监听Socket端口接受2发来的指令,一边把指令通过模拟的串口发送给Arduino开发板。使用的开发语言还是C#。
4,Aruino开发板程序,这部分是通过模拟的串口接受指令,转换成电机对应的占空比电流脉冲,从而控制小车的两个直流电机。开发环境是Arduino 0017,开发的语言怀疑是类Java(软件上面有一个咖啡杯的图标)

回头一看,发现自己真够啰嗦的,写了半天还没见到代码,赶紧给标题后面加了个“1”。先贴两个程序的截图吧,改天再把代码发上来。

手机上的程序截图

手机上的程序截图

端口监听及转发程序截图

端口监听及转发程序截图