作为制造者社区的成员,我们一直在寻找创造性的方式来使用硬件和软件。 这次, Patrick Lima和我决定我们想使用Arduino开发板扩展Raspberry Pi的端口,以便我们可以访问更多功能和端口并为设备添加一层保护。 有许多方法可以使用此设置,例如,构建跟随太阳的太阳能电池板,家庭气象站,操纵杆交互等等。
我们决定从构建一个仪表盘开始,该仪表盘允许以下串行端口交互:
- 控制三个LED以打开和关闭它们
- 控制三个LED调整其光强度
- 识别正在使用的端口
- 在操纵杆上显示输入动作
- 测量温度
我们还想在一个漂亮的用户界面(UI)中显示端口,硬件和传感器之间的所有交互,如下所示:
您可以使用本文中的概念来构建使用许多不同组件的许多不同项目。 您的想象力是极限!
1.开始
第一步是将Raspberry Pi的端口扩展为也使用Arduino端口。 使用Linux ARM的本机串行通信实现可以实现这一点,该实现使您能够使用Arduino的数字,模拟和脉宽调制(PWM)端口在Raspberry Pi上运行应用程序。
该项目使用TotalCross (一种用于构建嵌入式设备的UI的开源软件开发套件)来通过终端执行外部应用程序并使用本机串行通信。 您可以使用两个类来实现此目的: Runtime.exec和PortConnector 。 它们代表执行这些操作的不同方法,因此在本教程中我们将展示如何使用这两种方法,您可以决定哪种方法最适合您。
要启动该项目,您需要:
- 1树莓派3
- 1个Arduino Uno
- 3个LED
- 2个电阻介于1K和2.2K欧姆之间
- 1个按钮
- 1个电位计,介于1K和50K欧姆之间
- 1个原型板(又名面包板)
- 跳线
2.设置Arduino
创建通信协议以接收消息,处理消息,执行请求并在Raspberry Pi和Arduino之间发送响应。 这是在Arduino上完成的。
2.1定义消息格式
每条收到的消息将具有以下格式:
- 指示的功能称为
- 使用的端口
- 字符分隔符(如果需要)
- 需要发送的值
- 消息结束的指示
下表列出了具有相应功能,示例值和示例说明的字符列表。 本示例中使用的字符选择是任意的,可以随时更改。
性格 | 功能 | 例 | 示例说明 |
---|---|---|---|
* | 指令结束 | -- | -- |
, | 分隔器 | -- | -- |
# | 设置模式 | #8,0 * | 引脚8输入模式 |
< | 设定数字值 | <1,0 * | 将引脚1设置为低电平 |
> | 获得数字价值 | > 13 * | 获取值针13 |
+ | 获取PWM值 | + 6,250 * | 设置引脚6的值250 |
- | 获得类比价值 | -14 * | 获取值引脚A0 |
2.2源代码
以下源代码实现了上面指定的通信协议。 必须将其发送到Arduino,以便它可以解释和执行消息的命令:
void setup
(
)
{
Serial.
begin
(
9600
)
;
Serial.
println
(
"Connected"
)
;
Serial.
println
(
"Waiting command..."
)
;
}
void loop
(
)
{
String text
=
""
;
char character
;
String pin
=
""
;
String value
=
"0"
;
char separator
=
'.'
;
char inst
=
'.'
;
while
( Serial.
available
(
)
)
{
// verify RX is getting data
delay
(
10
)
;
character
= Serial.
read
(
)
;
if
( character
==
'*'
)
{
action
( inst
, pin
, value
)
;
break
;
}
else
{
text.
concat
( character
)
;
}
if
( character
==
','
)
{
separator
= character
;
if
( inst
==
'.'
)
{
inst
= character
;
}
else
if
( separator
!=
','
&& character
!= inst
)
{
pin.
concat
( character
)
;
}
else
if
( character
!= separator
&& character
!= inst
)
{
value.
concat
( character
)
;
}
}
}
void action
(
char instruction
, String pin
, String value
)
{
if
( instruction
==
'#'
)
{
//pinMode
pinMode
( pin.
toInt
(
)
, value.
toInt
(
)
)
;
}
if
( instruction
==
'<'
)
{
//digitalWrite
digitalWrite
( pin.
toInt
(
)
, value.
toInt
(
)
)
;
}
if
( instruction
==
'>'
)
{
//digitalRead
String aux
= pin
+
':'
+ String
( digitalRead
( pin.
toInt
(
)
)
)
;
Serial.
println
( aux
)
;
}
if
( instruction
==
'+'
)
{
// analogWrite = PWM
analogWrite
( pin.
toInt
(
)
, value.
toInt
(
)
)
;
}
if
( instruction
==
'-'
)
{
// analogRead
String aux
= pin
+
':'
+ String
( analogRead
( pin.
toInt
(
)
)
)
;
Serial.
println
( aux
)
;
}
}
2.3制作电子产品
定义您需要测试以检查与Arduino的通信并确保输入和输出按预期响应的内容:
- LED以正逻辑连接。 通过电阻连接到GND引脚,并通过数字端口I / O 2和PWM 3激活它。
- 该按钮具有连接到数字端口I / O 4的下拉电阻,如果未按下则发送信号0,如果按下则发送信号1。
- 电位计的中心引脚连接到模拟输入A0,其中一个侧面引脚位于正极,而另一个侧面引脚位于负极。
2.4测试通讯
将第2.2节中的代码发送到Arduino。 发送以下命令,打开串行监视器并检查通讯协议:
#2,1*<2,1*>2*
#3,1*+3,10*
#4,0*>4*
#14,0*-14*
这应该是串行监视器中的结果:
设备上的一个LED应该以最大强度点亮,而另一个应以较低强度点亮。
发送读数命令时,按下按钮并更改电位计的位置将显示不同的值。 例如,将电位计转到正极,然后按按钮。 在仍然按下按钮的情况下,发送命令:
>
4
*
-
14
*
应该出现两行:
3.设置Raspberry Pi
使用Raspberry Pi通过终端使用cat
命令读取条目,并使用echo
命令发送消息,通过终端访问串行端口。
3.1进行串行测试
将Arduino连接到Raspberry Pi的USB端口之一,打开终端,然后执行以下命令:
cat / dev / ttyUSB0 9600
这将启动与Arduino的连接并显示返回串行的内容。
要测试发送命令,请打开一个新的终端窗口(保持前一个打开),然后发送以下命令:
echo "command" > / dev / ttyUSB0 9600
您可以发送与2.4节相同的命令。
您应该在第一个终端中看到反馈以及在2.4节中得到的相同结果:
4.创建图形用户界面
第一部分使用两个UI组件:一个列表框和一个编辑。 它们在Raspberry Pi和Arduino之间建立连接,并测试一切是否按预期工作。
模拟您在其中放置命令的终端并观察答案:
- 编辑用于发送消息。 将其放置在底部,其填充宽度可将组件扩展到屏幕的整个宽度。
- 列表框用于显示结果,例如在终端中。 将其添加到TOP位置(从左端开始),其宽度等于Edit,而FIT高度则垂直占据未被Edit填充的所有空间。
package
com.totalcross.sample.serial
;
import
totalcross.sys.Settings
;
import
totalcross.ui.Edit
;
import
totalcross.ui.ListBox
;
import
totalcross.ui.MainWindow
;
import
totalcross.ui.gfx.Color
;
public
class SerialSample
extends MainWindow
{
ListBox Output
;
Edit Input
;
public SerialSample
(
)
{
setUIStyle
( Settings.
MATERIAL_UI
)
;
}
@Override
public
void initUI
(
)
{
Input
=
new Edit
(
)
;
add
( Input, LEFT, BOTTOM, FILL, PREFERRED
)
;
Output
=
new ListBox
(
)
;
Output.
setBackForeColors
(
Color .
BLACK ,
Color .
WHITE
)
;
add
( Output, LEFT, TOP, FILL, FIT
)
;
}
}
它看起来应该像这样:
5.设置串行通讯
如上所述,有两种设置串行通信的方法:Runtime.exec和PortConnector。
5.1选项1:使用Runtime.exec
java.lang.Runtime
类允许应用程序与其运行环境一起创建连接接口。 它允许程序使用Raspberry Pi的本机串行通信。
使用与第3.1节中使用的命令相同的命令,但现在使用UI上的“编辑”组件将命令发送到设备。
阅读序列
应用程序必须不断读取序列号,如果返回值,请使用线程将其添加到列表框中。 线程是在后台处理进程而不阻止用户交互的一种好方法。
以下代码在此线程上创建一个新进程,该进程执行cat
命令,测试串行,并启动无限循环以检查是否接收到新消息。 如果收到内容,则该值将添加到“列表框”组件的下一行。 只要应用程序正在运行,此过程就将继续运行:
new
Thread
(
)
{
@Override
public
void run
(
)
{
try
{
Process Runexec2
=
Runtime .
getRuntime
(
) .
exec
(
"cat /dev/ttyUSB0 9600 \n "
)
;
LineReader lineReader
=
new LineReader
( Stream.
asStream
( Runexec2.
getInputStream
(
)
)
)
;
String input
;
while
(
true
)
{
if
(
( input
= lineReader.
readLine
(
)
)
!=
null
)
{
Output.
add
( input
)
;
Output.
selectLast
(
)
;
Output.
repaintNow
(
)
;
}
}
}
catch
(
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
}
} .
start
(
)
;
}
发送命令
发送命令是一个简单的过程。 每当您在“编辑”组件上按Enter时 ,它就会发生。
要将命令转发到设备,如3.1节所示,您必须实例化一个新终端。 为此,Runtime类必须在Linux上执行sh
命令:
try
{
Runexec
=
Runtime .
getRuntime
(
) .
exec
(
"sh"
) .
getOutputStream
(
)
}
catch
(
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
用户在“编辑”中编写命令并按Enter后 ,应用程序将触发一个事件,该事件以“编辑”中指示的值执行echo
命令:
Input.
addKeyListener
(
new
KeyListener
(
)
{
@Override
public
void specialkeyPressed
(
KeyEvent e
)
{
if
( e.
key
== SpecialKeys.
ENTER
)
{
String s
= Input.
getText
(
)
;
Input.
clear
(
)
;
try
{
Runexec.
write
(
(
"echo \" "
+ s
+
" \" > /dev/ttyUSB0 9600 \n "
) .
getBytes
(
)
)
;
}
catch
(
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
}
}
@Override
public
void keyPressed
(
KeyEvent e
)
{
}
//auto-generate code
@Override
public
void actionkeyPressed
(
KeyEvent e
)
{
}
//auto-generate code
}
)
;
在连接了Arduino的Raspberry Pi上运行应用程序,然后发送命令进行测试。 结果应为:
Runtime.exec源代码
以下是解释所有部分的源代码。 它包括将在第31行读取串行的线程和将在第55行发送命令的KeyListener
:
package
com.totalcross.sample.serial
;
import
totalcross.ui.MainWindow
;
import
totalcross.ui.event.KeyEvent
;
import
totalcross.ui.event.KeyListener
;
import
totalcross.ui.gfx.Color
;
import
totalcross.ui.Edit
;
import
totalcross.ui.ListBox
;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
totalcross.io.LineReader
;
import
totalcross.io.Stream
;
import
totalcross.sys.Settings
;
import
totalcross.sys.SpecialKeys
;
public
class SerialSample
extends MainWindow
{
OutputStream Runexec
;
ListBox Output
;
public SerialSample
(
)
{
setUIStyle
( Settings.
MATERIAL_UI
)
;
}
@Override
public
void initUI
(
)
{
Edit Input
=
new Edit
(
)
;
add
( Input, LEFT, BOTTOM, FILL, PREFERRED
)
;
Output
=
new ListBox
(
)
;
Output.
setBackForeColors
(
Color .
BLACK ,
Color .
WHITE
)
;
add
( Output, LEFT, TOP, FILL, FIT
)
;
new
Thread
(
)
{
@Override
public
void run
(
)
{
try
{
Process Runexec2
=
Runtime .
getRuntime
(
) .
exec
(
"cat /dev/ttyUSB0 9600 \n "
)
;
LineReader lineReader
=
new
LineReader
( Stream.
asStream
( Runexec2.
getInputStream
(
)
)
)
;
String input
;
while
(
true
)
{
if
(
( input
= lineReader.
readLine
(
)
)
!=
null
)
{
Output.
add
( input
)
;
Output.
selectLast
(
)
;
Output.
repaintNow
(
)
;
}
}
}
catch
(
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
}
} .
start
(
)
;
try
{
Runexec
=
Runtime .
getRuntime
(
) .
exec
(
"sh"
) .
getOutputStream
(
)
;
}
catch
(
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
Input.
addKeyListener
(
new
KeyListener
(
)
{
@Override
public
void specialkeyPressed
(
KeyEvent e
)
{
if
( e.
key
== SpecialKeys.
ENTER
)
{
String s
= Input.
getText
(
)
;
Input.
clear
(
)
;
try
{
Runexec.
write
(
(
"echo \" "
+ s
+
" \" > /dev/ttyUSB0 9600 \n "
) .
getBytes
(
)
)
;
}
catch
(
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
}
}
@Override
public
void keyPressed
(
KeyEvent e
)
{
}
@Override
public
void actionkeyPressed
(
KeyEvent e
)
{
}
}
)
;
}
}
5.2选项2:使用PortConnector
PortConnector专门用于串行通信。 如果要遵循原始示例,则可以跳过本节,因为此处的目的是显示另一种更简单的串行操作方法。
更改原始源代码以与PortConnector一起使用:
package
com.totalcross.sample.serial
;
import
totalcross.io.LineReader
;
import
totalcross.io.device.PortConnector
;
import
totalcross.sys.Settings
;
import
totalcross.sys.SpecialKeys
;
import
totalcross.ui.Edit
;
import
totalcross.ui.ListBox
;
import
totalcross.ui.MainWindow
;
import
totalcross.ui.event.KeyEvent
;
import
totalcross.ui.event.KeyListener
;
import
totalcross.ui.gfx.Color
;
public
class SerialSample
extends MainWindow
{
PortConnector pc
;
ListBox Output
;
public SerialSample
(
)
{
setUIStyle
( Settings.
MATERIAL_UI
)
;
}
@Override
public
void initUI
(
)
{
Edit Input
=
new Edit
(
)
;
add
( Input, LEFT, BOTTOM, FILL, PREFERRED
)
;
Output
=
new ListBox
(
)
;
Output.
setBackForeColors
(
Color .
BLACK ,
Color .
WHITE
)
;
add
( Output, LEFT, TOP, FILL, FIT
)
;
new
Thread
(
)
{
@Override
public
void run
(
)
{
try
{
pc
=
new PortConnector
( PortConnector.
USB ,
9600
)
;
LineReader lineReader
=
new LineReader
( pc
)
;
String input
;
while
(
true
)
{
if
(
( input
= lineReader.
readLine
(
)
)
!=
null
)
{
Output.
add
( input
)
;
Output.
selectLast
(
)
;
Output.
repaintNow
(
)
;
}
}
}
catch
( totalcross.
io .
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
}
} .
start
(
)
;
Input.
addKeyListener
(
new
KeyListener
(
)
{
@Override
public
void specialkeyPressed
(
KeyEvent e
)
{
if
( e.
key
== SpecialKeys.
ENTER
)
{
String s
= Input.
getText
(
)
;
Input.
clear
(
)
;
try
{
pc.
writeBytes
( s
)
;
}
catch
( totalcross.
io .
IOException ioe
)
{
ioe.
printStackTrace
(
)
;
}
}
}
@Override
public
void keyPressed
(
KeyEvent e
)
{
}
@Override
public
void actionkeyPressed
(
KeyEvent e
)
{
}
}
)
;
}
}
您可以在项目存储库中找到所有代码。
6.后续步骤
本文介绍如何通过使用Runtime或PortConnector类将Raspberry Pi串行端口与Java一起使用。 您还可以用其他语言调用外部文件,并创建无数其他项目,例如用于水族箱的水质监测系统,该水族箱可以通过模拟输入进行温度测量,或者可以控制温度和湿度的鸡笼和伺服电机来旋转卵。
以后的文章将使用PortConnector实现(因为它专注于串行连接)来完成与所有传感器的通信。 它还将添加一个数字输入并完成UI。
以下是一些参考资料,供您阅读:
连接Arduino和Raspberry Pi后,请在下面留下您的评论。 我们希望阅读它们!
翻译自: https://opensource.com/article/20/7/arduino-raspberry-pi