一、基本原理

在android端写一个小小的控制器来通过局域网来控制树莓派小车的行驶,基本思路是在树莓派上写一个socket服务器,android端写一个socket客户机,两边约定好命令的指令(例如可以简单的把停止约定为“0”,把前进约定为“1”),android负责发送,树莓派负责接受并执行。用这种方法理论上是可以远程控制的,前提是得要知道树莓派被分配的公网IP。

二、知识学习

【android】Socket简单用法

Android自定义View的三种实现方式

三、代码结构

首先看一下最终结果截图:

布局很简单,就布置了几个控件,两个文本输入框,一个按钮,以及一个摇杆。

Android官方没有摇杆控件,为了方便,直接找了网上别人的开源项目。

这是由GcsSloop开发的自定义View。

GcsSloop两个开源项目快速导航:

View辅助工具包,帮助你快速优雅的完成自定义View

https://github.com/GcsSloop/ViewSupport

一个安卓虚拟摇杆程序,可作为游戏控制器或者小车遥控器。

https://github.com/GcsSloop/Rocker

将这两个项目下载下来,后面会用到。

我将遥控器稍微改造了下,添加了能够与树莓派连接并发送数据的功能。

代码结构:

View目录下的类是GcsSloop开发的辅助工具类

RockerView是GcsSloop使用自己的工具类开发的一个摇杆控件

RockerSocketView是我写的继承自RockerView的摇杆控件

MainActivity为控件测试

四、编写过程

AS创建一个新项目

\Rocker-master\library\src\main\java\com\gcssloop\wid目录下的RockerView.java、

\ViewSupport-master\ViewSupport-master\Library\src\main\java\com\gcssloop下的view文件夹

复制到app\src\main\java\cn\luosh\rockertest目录下

导入后会有一些路径错误,根据自身情况修改

将Rocker-master\library\src\main\res\values\attrs.xml复制到values下

此时项目应该已经没有错误了

将Rocker-master\Sample\src\main\res\drawable下的图片资源也复制到我们的drawable目录下(这是摇杆的底盘和按钮图片,自 己可以用其他图片     替换)

接下来就我们自己敲代码啦

新建类RockerSocketView

package cn.luosh.rockertest;
import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
/**
* Created by luo76 on 2017/5/6.
*/
public class RockerSocketView extends RockerView{
//树莓派ip地址
private String carIp;
//端口
private int carPort;
//是否连接标志
private boolean isConnect = false;
private Context context;
Socket socket = null;
BufferedReader reader = null;
BufferedWriter writer = null;
private int rockerEvent = 0;
private int oldrockerEvent = -1;
//定义摇杆事件状态
public static final int Movestop = 0;
public static final int Moveforward = 1;
public static final int Movebackward = 2;
public static final int Turnleft = 3;
public static final int Turnright = 4;
//重新定义了构造方法,目的是为了得到使用该控件的Activity的context
public RockerSocketView(Context context) {
this(context, null);
}
public RockerSocketView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
this.context = context;
}
//初始化函数,由使用该控件的Activity调用
public void init(String carIp,int carPort){
this.carIp = carIp;
this.carPort = carPort;
connectAsyncTask();
//添加摇杆事件监听
this.setListener(new RockerView.RockerListener() {
@Override
public void callback(int eventType, int currentAngle, float currentDistance) {
switch (eventType) {
case RockerView.EVENT_ACTION:
// 触摸事件回调
Log.e("EVENT_ACTION-------->", "angle="+currentAngle+" - distance"+currentDistance);
rockerEvent = judgeEvent(currentAngle);
send();
break;
case RockerView.EVENT_CLOCK:
// 定时回调
Log.e("EVENT_CLOCK", "angle="+currentAngle+" - distance"+currentDistance);
rockerEvent = judgeEvent(currentAngle);
send();
break;
}
}
});
}
//根据角度判断摇杆方向
public int judgeEvent(int currentAngle){
if (currentAngle == -1){
return Movestop;
}else if (currentAngle > 45 && currentAngle <= 135){
return Moveforward;
}else if (currentAngle > 135 && currentAngle <= 225){
return Turnleft;
}else if (currentAngle > 225 && currentAngle <= 315){
return Movebackward;
}else{
return Turnright;
}
}
public void Destroy(){
isConnect = false;
}
//使用异步任务建立socket连接
public void connectAsyncTask(){
AsyncTaskconnect =new AsyncTask() {
@Override
protected Void doInBackground(Void... params) {
try {
socket = new Socket(carIp,carPort);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
publishProgress("连接成功\n");
isConnect = true;
}catch (UnsupportedEncodingException e) {
Toast.makeText(context,"无法建立连接",Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
Toast.makeText(context,"无法建立连接",Toast.LENGTH_SHORT).show();
}
String Line;
try {
while ((Line = reader.readLine()) != null){
publishProgress(Line);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onProgressUpdate(String...values){
if(values[0].equals("连接成功\n")){
Toast.makeText(context,"连接成功",Toast.LENGTH_SHORT).show();
}
else{
System.out.println(values[0]);
}
super.onProgressUpdate(values[0]);
}
};
connect.execute();
}
//不使用线程,事件变化时再发送状态
public void send(){
//先判断是否成功建立socket连接再向树莓派发送数据,避免出现错误
if(isConnect){
if(rockerEvent != oldrockerEvent){
oldrockerEvent = rockerEvent;
try {
//将要发送的int数据转换成String再发送
writer.write(String.valueOf(rockerEvent));
writer.flush();
Log.e("EVENT_ACTION-------->", "rockerEvent=" + rockerEvent);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

给个如何调用的实例

package cn.luosh.rockertest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity {
private RockerSocketView rockersocket;
private EditText carip,carport;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rockersocket = (RockerSocketView) findViewById(R.id.rockersocket);
carip = (EditText) findViewById(R.id.carip);
carport = (EditText) findViewById(R.id.carport);
findViewById(R.id.btn_car_connect).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
rockersocket.init(carip.getText().toString(),Integer.parseInt(carport.getText().toString()));
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
rockersocket.Destroy();
rockersocket = null;
}
}

把activity_main.xml和AndroidManifest.xml的内容也放出来


最后,连上安卓实体机,打开树莓派服务端,运行

附上源代码

安卓端APP遥控树莓派小车相关推荐

  1. Obsidian安卓端app教程

    安卓端 app 现已公开,下载方式可见我们的网站. 同步 对于安卓端来说,你可以选择 [[Obsidian 同步服务]]来同步你的笔记,也可以使用第三方同步服务. Obsidian 同步服务 和桌面端 ...

  2. 基于安卓的视频遥控小车——电脑端开发

    基于安卓的视频遥控小车的电脑端程序采用Java语言编写,Java可以做到一次编译到处运行,因为Java程序是在Java虚拟机中运行的,和平台无关,只要平台上有相应的Java虚拟机. 本设计中安卓手机是 ...

  3. 树莓派小车的4G遥控与视频回传(内网穿透)

    目录 1.项目简介 2.工具 3.步骤 1.手机端 (1)设置ip和端口 (2)遥控界面 2.树莓派 (1)给树莓派配置公网环境 (2)树莓派进行内网穿透 (3)树莓派UDP监听 (4)树莓派视频回传 ...

  4. 树莓派与安卓手机app的WIFI通信(局域网通信)

    参考:树莓派与安卓手机app的WIFI通信(局域网通信) 作者:图触靓 发布时间: 2020-07-29 10:18:12 网址:https://blog.csdn.net/bhbhhyg/artic ...

  5. 自写app与树莓派制作智能小车

    实现的功能有: 1.实现小车的前进,后退,左转,右转. 2.实时视频的传回,查看小车周围的情况. 3.摄像头的上下左右转动,使用舵机云台来实现. 需要的材料: 1.树莓派一个(带有python环境,现 ...

  6. 微信小程序android错误,app安卓端 跳转到微信小程序失败

    详细问题描述 (DCloud产品不会有明显的bug,所以你遇到的问题大都是在特定环境下才能重现的问题,请仔细描述你的环境和重现方式,否则DCloud很难排查解决你的问题) [内容] app安卓端跳转到 ...

  7. 字节跳动推“头条搜索”独立APP 安卓端已上线

    据悉,字节跳动推出"头条搜索"独立APP,安卓端已经上线,苹果应用商店暂时还未搜索到. 天眼查信息显示,"头条搜索"的开发者为北京字节跳动科技有限公司,疑似实际 ...

  8. 安卓端一键自动设置WiFi代理的APP,配合Fiddler、Burp、Charles等抓包工具使用,懒人必备!

    本文为原创文章,转载请注明出处!!! 前言 在安卓逆向.软件测试等工作过程中,使用Fiddler.Burp.Charles等抓包工具,需要经常设置和取消手机的WiFi代理. 因为一个字"懒& ...

  9. H5即时通讯IM聊天APP系统 带原生/安卓苹果端APP源码

    介绍: 带原生/安卓苹果端APP源码+详细视频教程 朋友互站4000+购买来的通讯,全原生,并不是视酷或酷信的二开版本,从底层开始结构就完全不一样, mongodb的库,uniapp混编手端,二开难度 ...

最新文章

  1. linux命令grep如何使用,Linux下如何使用grep搜索文本
  2. 多点优化损失函数地图全局描述
  3. MMM结合Semisync机制实现Mysql Master-Master高可用
  4. 每天干的啥?(2019.3)
  5. 北交计算机学硕培养计划,北京交通大学硕士研究生培养方案——交通信息工程及控制...
  6. python numpy ndarray之basic operations
  7. 第五章:Redis持久化-AOF持久化
  8. 标准模块 threading
  9. Atitit 2018 技术趋势与没落技术总结 目录 1. 2018 技术雷达 1 1.1. HOSTED IDENTITY MANAGEMENT AS A SERVICE (SaaS)身份管理
  10. Atitit.业务系统的新特性 开发平台 新特性的来源总结
  11. Visio 2019中文版软件下载和安装教程
  12. AR VR 将如何深刻影响未来的城市化进程
  13. 微信王者登录太多服务器怎样删除,使用微信登录游戏或其他app时,可以使用不同的个人信息登录 选项已满 怎么删除?如图...
  14. 【编程语言】Swift完全自学手册
  15. 腾讯程序员月薪7万,小编感觉自己像码畜...
  16. 解决git clone提示Permission denied publickey 问题
  17. 《白帽子讲Web安全》安全运营
  18. FPGA入门实验之串口发送
  19. 官网下载eclipse被墙、无法访问解决
  20. 1.4. CFS调度算法

热门文章

  1. outlook邮箱邮件内容乱码_Outlook电子邮件乱码问题巧解
  2. ERP系统对精细化生产管理有何益处?
  3. Java语言之动画的简单入门学习
  4. 微信JS-SDK,助力网页开发
  5. macbook pro外界键盘更换command与ctrl键功能
  6. SpringMVC学习系列(六)------图片的上传
  7. 课题成果,发表省级论文《开展数学阅读 提升数学素养》
  8. opecv 计算曲线斜率
  9. 超牛逼:100 个开箱即用的 Shell 脚本,拿好了~
  10. Unity关于层级细节(LOD)的使用