上篇文章 之后,继续折腾我的小车,又用C#和Android 写了两个控制客户端。

思路比较直接,使用POST请求去发送小车转向参数,然后就动起来了。C#不说了,很熟练写起来也非常简单,使用钩子捕获全局键盘,这样及时程序不在前台也能控制小车。

核心代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WangHeng.Org.PiCar
{
    public partial class MainFrm : Form
    {
        public MainFrm()
        {
            InitializeComponent();
            InitControl();
            KeyHookUtils.Hook_Start();
        }

        private void InitControl()
        {
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Text = "PiCar Control  v" + this.ProductVersion;
            this.MaximizeBox = false;
            this.MinimizeBox = true;
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;

            this.btnDown.Tag = "t_down";
            this.btnLeft.Tag = "t_left";
            this.btnRight.Tag = "t_right";
            this.btnUp.Tag = "t_up";
            this.btnStop.Tag = "t_stop";
            this.statusStrip1.SizingGrip = false;
            this.lblPostResultInfo.Text = "启动成功!";
        }
        protected override bool ProcessDialogKey(Keys keyData)
        {
            if (keyData == Keys.Left)
            {
                lblPostResultInfo.Text = "left";
            }
            return base.ProcessDialogKey(keyData);
        }

        private void ctlButton_Click(object sender, EventArgs e)
        {
            var btn = sender as Button;
            var result = CarCtl.TurnFunc(btn.Tag.ToString());
            this.lblPostResultInfo.Text = "请求成功 - " + result;
        }


    }

    public static class CarCtl
    {
        public enum CarStatus
        {
            Left,
            Right,
            Up,
            Down,
            Stop,
            Unknown
        }

        public static CarStatus CurrentStatus = CarStatus.Unknown;
        public static string TurnFunc(string direcID)
        {
            try
            {
                var requestUrl = "http://192.168.1.171:2000/ctl";
                var requestData = new Dictionary<string, string>() { { "id", direcID } };
                var requestUserAgent = "WangHeng PiCar/v1.0";
                var requestEncoding = Encoding.Default;
                var result = HttpUtility.CreatePostHttpResponse(
                        requestUrl,
                        requestData,
                        5000,
                        requestUserAgent,
                        requestEncoding,
                        null);

                using (var sr = new StreamReader(result.GetResponseStream(), requestEncoding))
                {
                    return sr.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }


    public class KeyHookUtils
    {
        [StructLayout(LayoutKind.Sequential)]
        public class KeyBoardHookStruct
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        //委托 
        public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
        static int hHook = 0;
        public const int WH_KEYBOARD_LL = 13;
        //LowLevel键盘截获,如果是WH_KEYBOARD=2,并不能对系统键盘截取,Acrobat Reader会在你截取之前获得键盘。 
        static HookProc KeyBoardHookProcedure;

        //设置钩子 
        [DllImport("user32.dll")]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //抽掉钩子 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        //调用下一个钩子 
        [DllImport("user32.dll")]
        public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);

        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string name);

        public static void Hook_Start()
        {
            if (hHook == 0)
            {
                KeyBoardHookProcedure = new HookProc(KeyBoardHookProc);
                hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardHookProcedure,
                        GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
                //如果设置钩子失败. 
                if (hHook == 0)
                {
                    Hook_Clear();
                }
            }
        }

        /// <summary>
        /// 取消钩子事件
        /// </summary>
        public static void Hook_Clear()
        {
            bool retKeyboard = true;
            if (hHook != 0)
            {
                retKeyboard = UnhookWindowsHookEx(hHook);
                hHook = 0;
            }
        }

        public static int KeyBoardHookProc(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                KeyBoardHookStruct kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
                Keys k = (Keys)Enum.Parse(typeof(Keys), kbh.vkCode.ToString());
                switch (k)
                {
                    case Keys.Left:
                        if (kbh.flags == 1)
                        {
                            // 这里写按下后做什么事
                            if (CarCtl.CurrentStatus != CarCtl.CarStatus.Up)
                            {
                                CarCtl.TurnFunc("t_left");
                                CarCtl.CurrentStatus = CarCtl.CarStatus.Up;
                            }
                        }
                        else if (kbh.flags == 129)
                        {
                            //放开后做什么事
                            CarCtl.TurnFunc("t_stop");
                            CarCtl.CurrentStatus = CarCtl.CarStatus.Stop;
                        }
                        return 1;
                    case Keys.Up:
                        if (kbh.flags == 1)
                        {
                            // 这里写按下后做什么事
                            if (CarCtl.CurrentStatus != CarCtl.CarStatus.Up)
                            {
                                CarCtl.TurnFunc("t_up");
                                CarCtl.CurrentStatus = CarCtl.CarStatus.Up;
                            }
                        }
                        else if (kbh.flags == 129)
                        {
                            //放开后做什么事
                            CarCtl.TurnFunc("t_stop");
                            CarCtl.CurrentStatus = CarCtl.CarStatus.Stop;
                        }
                        return 1;
                    case Keys.Right:
                        if (kbh.flags == 1)
                        {
                            if (CarCtl.CurrentStatus != CarCtl.CarStatus.Up)
                            {
                                CarCtl.TurnFunc("t_right");
                                CarCtl.CurrentStatus = CarCtl.CarStatus.Up;
                            }
                        }
                        else if (kbh.flags == 129)
                        {
                            //放开后做什么事
                            CarCtl.TurnFunc("t_stop");
                            CarCtl.CurrentStatus = CarCtl.CarStatus.Stop;
                        }
                        return 1;
                    case Keys.Down:
                        if (kbh.flags == 1)
                        {
                            if (CarCtl.CurrentStatus != CarCtl.CarStatus.Up)
                            {
                                CarCtl.TurnFunc("t_down");
                                CarCtl.CurrentStatus = CarCtl.CarStatus.Up;
                            }
                        }
                        else if (kbh.flags == 129)
                        {
                            //放开后做什么事
                            CarCtl.TurnFunc("t_stop");
                            CarCtl.CurrentStatus = CarCtl.CarStatus.Stop;
                        }
                        return 1;
                    default:
                        break;
                }
            }
            return CallNextHookEx(hHook, nCode, wParam, lParam);
        }
    }
}

这里主要说一下Android 的客户端 《阅读全文》

到年末了,最近也有了一些自己的时间。回到了原来的技术处,晚上终于不用疯狂加班了,下班回家也终于有功夫折腾自己以前想玩的玩具了: )

树莓派刚发布的时候就一直想搞一台,但是淘宝上面的价格也是一直居高不下,想想家里已经N多PC,NAS和笔记本了,一直找不到入手的理由。圣诞节附近随便一刷淘宝,发现新版本B+ 已经降到了170块,果断买下。

回家折腾了几天,性能果然弱的跟价格成正比。。。百兆的小网卡实在看不上眼。算了,从小玩意玩起吧,先装一个wifi小车。

有了想法,开始组装! 由于之前折腾了几天Arduino对于各种驱动板已经基本熟悉,所以接线非常简单。底盘随便找了块塑料板,轮子直接双面胶+热熔胶固定,总之是很丑,好吧。。。

《阅读全文》

由于在Wordpress中很多地方用到了google的字体,加上最近我朝又在全面禁用google的服务,直接导致了博客访问慢的无法忍受。没办法只有割爱,先临时禁用google的字体。方法如下:

方法一:在functions.php中注释掉,vim打开functions.php文件,搜索googleapis, 加//注释即可,代码如下:

function wpan_fonts() {
$protocol = is_ssl() ? ‘https’ : ‘http’;
    //</strong>wp_register_style( ‘underthesea-portlligatsans’, “$protocol://fonts.googleapis.com/css?family=Port+Lligat+Sans” );
    //</strong>wp_register_style( ‘underthesea-oswald’, “$protocol://fonts.googleapis.com/css?family=Oswald:400,700,300″ );
    //</strong>wp_register_style( ‘underthesea-pacifico’, “$protocol://fonts.googleapis.com/css?family=Pacifico” );
}

方法二:使用插件

使用插件Remove Open Sans font from WP core,非常给力。使用插件也有一定的好处,就是你下次更新wordpress 的时候不需要再重新更改functions.php代码。

以上两种方式均能明显改善博客打开的速度,有需要的朋友不妨试试。