通过Arduino端口扩展Raspberry Pi

作为制造者社区的成员,我们一直在寻找创造性的方式来使用硬件和软件。 这次, Patrick Lima和我决定我们想使用Arduino开发板扩展Raspberry Pi的端口,以便我们可以访问更多功能和端口并为设备添加一层保护。 有许多方法可以使用此设置,例如,构建跟随太阳的太阳能电池板,家庭气象站,操纵杆交互等等。

我们决定从构建一个仪表盘开始,该仪表盘允许以下串行端口交互:

  • 控制三个LED以打开和关闭它们
  • 控制三个LED调整其光强度
  • 识别正在使用的端口
  • 在操纵杆上显示输入动作
  • 测量温度

我们还想在一个漂亮的用户界面(UI)中显示端口,硬件和传感器之间的所有交互,如下所示:

UI dashboard

(Bruno Muniz, CC BY-SA 4.0

您可以使用本文中的概念来构建使用许多不同组件的许多不同项目。 您的想象力是极限!

1.开始

Raspberry Pi and Arduino logos

(Bruno Muniz, CC BY-SA 4.0

第一步是将Raspberry Pi的端口扩展为也使用Arduino端口。 使用Linux ARM的本机串行通信实现可以实现这一点,该实现使您能够使用Arduino的数字,模拟和脉宽调制(PWM)端口在Raspberry Pi上运行应用程序。

该项目使用TotalCross (一种用于构建嵌入式设备的UI的开源软件开发套件)来通过终端执行外部应用程序并使用本机串行通信。 您可以使用两个类来实现此目的: Runtime.execPortConnector 。 它们代表执行这些操作的不同方法,因此在本教程中我们将展示如何使用这两种方法,您可以决定哪种方法最适合您。

要启动该项目,您需要:

  • 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,其中一个侧面引脚位于正极,而另一个侧面引脚位于负极。
Connecting the hardware

(Bruno Muniz, CC BY-SA 4.0

2.4测试通讯

将第2.2节中的代码发送到Arduino。 发送以下命令,打开串行监视器并检查通讯协议:


   
   
#2,1*<2,1*>2*
#3,1*+3,10*
#4,0*>4*
#14,0*-14*

这应该是串行监视器中的结果:

Testing communications in Arduino

(Bruno Muniz, CC BY-SA 4.0

设备上的一个LED应该以最大强度点亮,而另一个应以较低强度点亮。

LEDs lit on board

(Bruno Muniz, CC BY-SA 4.0

发送读数命令时,按下按钮并更改电位计的位置将显示不同的值。 例如,将电位计转到正极,然后按按钮。 在仍然按下按钮的情况下,发送命令:


   
   
> 4 *
- 14 *

应该出现两行:

Testing communications in Arduino

(Bruno Muniz, CC BY-SA 4.0

3.设置Raspberry Pi

使用Raspberry Pi通过终端使用cat命令读取条目,并使用echo命令发送消息,通过终端访问串行端口。

3.1进行串行测试

将Arduino连接到Raspberry Pi的USB端口之一,打开终端,然后执行以下命令:

 cat / dev / ttyUSB0 9600 

这将启动与Arduino的连接并显示返回串行的内容。

Testing serial on Arduino

(Bruno Muniz, CC BY-SA 4.0

要测试发送命令,请打开一个新的终端窗口(保持前一个打开),然后发送以下命令:

 echo "command" > / dev / ttyUSB0 9600 

您可以发送与2.4节相同的命令。

您应该在第一个终端中看到反馈以及在2.4节中得到的相同结果:

Testing serial on Arduino

(Bruno Muniz, CC BY-SA 4.0

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 ) ;
    }
}

它看起来应该像这样:

UI

(Bruno Muniz, CC BY-SA 4.0

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上运行应用程序,然后发送命令进行测试。 结果应为:

Testing application running on Raspberry Pi

(Bruno Muniz, CC BY-SA 4.0

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值