嘿!您似乎在 United States,您想使用我们的 English 网站吗?
Switch to English site
Skip to main content

Processing数字互动介面开发软件应用 –结合 Arduino 硬件控制

cover2_48921eaa4039e63e7c86b640a0c6c21dcfa0dba1.jpg

 

作者

曾吉弘

难度

普通

材料表

1.    Arduino UNO

 

Processing 结合 Arduino

       上一篇文章介绍了 Processing 的基础应用,本篇文章要介绍如何让 Processing 透过 USB 传输线来连结 maker 相当喜爱的 Arduino Uno 开发板,另外也会介绍最近相当有人气的 LinkIt 7697 开发板。做法有两种:

  1. 运用 Arduino StandardFirmata,好处是 Arduino 端不需要再写程序,只要从 Processing 端发送指令即可,但缺点是弹性较低且较难客制化,例如特殊的传感器就无法直接读取数值。
  2. 自行定义USB发送的讯号,好处是可以做到任何您希望的功能。缺点就是程序代码需要对应,Processing 修改了任何地方,Arduino 端都需要做对应的修改。

现在就让我们看看怎么做吧。

 

Arduino 端

   在此使用 Arduino Uno 开发板。请下载 Arduino 环境,在此使用 Arduino 1.8.5,更新或再旧一点的版本应该都可以用,StandardFirmata 算是相当早期就有的做法了。刻录完成之后,请让 Arduino 接着计算机放着就可以了。

b014_cd2eeda33a5f5b0cfa700741d966b87a7021f740.png

 

Processing端

    请开启 Processing 官方网站的下载页面,根据您所使用的硬件平台下载对应的版本,如果还没下载请参考上一篇。

    开启 Processing IDE,点选 Sketch → Import Library → Add Library,开启 Contribution Manager 窗口,并搜寻 arduino。点选 Arduino (Firmata) 再点选 Install,就会开始安装,如下图:

b023_9906d4ff144a9e4e503d51dd49c057435a4f9c64.png

 

   也会带入对应的范例,只要是另外安装的函式库范例都会为在 Contributed Libraries 下。一共有五个范例,今天会说明其中的:

  • arduino_input:把 Arduino 脚位状态呈现在 Processing 中
  • arduino_output:在 Processing 中点选画面来控制 Arduino 脚位状态 (数字)
  • arduino_pwm:在 Processing 中移动鼠标来控制 Arduino 脚位状态 (模拟)

b031_d25577860d26d753cb9779b788fa81a101a23f82.png

 

执行 arduino_input 范例

    请直接开启 arduino_input 范例,   这个范例属于输位输入模拟输入。请确认您的 Arduino 板子正在执行 StandardFirmata 程序,您可以重新插拔电源或单击 Arduino 板子上的 Reset 按钮让它重新启动程序。总之,当您按下 Processing 的 Run 按键时,Arduino 必须先执行程序才行,否则会出现 RuntimeException: Error opening serial port COM6: Port not found 这样的错误讯息。

b041_00036c0630469b00bbe81ab18ff78ddfa2ec8a56.png

 

一切顺利的话,会看到下方的画面,上方的方块代表 Arduino Uno 的 D0 - D13 数字输入/输出脚位,您可以在任一个脚位接上按钮,按下按钮就会使对应脚位的方块变成浅色。而下方的圆圈则代表 Arduino Uno 的 A0 - A6 的模拟输入脚位。

b051_d7686679cdee8c16ba7803aff1d8e882f2bce839.png

b061_99c8b27dfab3a0a63f74bc347e7f8ca50f52881d.png

 

重要指令说明如下:

  • import cc.arduino.*; --> 汇入 arduino 函式库
  • Arduino arduino; --> 宣告一个 arduino 对象,如果您的专题需要连接多片 arduino 的话,就需要宣告多个 arduino 对象
  • println(Arduino.list()); -->列出可用的 COM port  装置
  • arduino = new Arduino(this, Arduino.list()[0], 57600);--> 设定这个 arduino 对象的 COM port 位置与传输速率。list()[0] 代表第一个 arduino,或者您可以改用 “COMX” 来指定位置
  • pinMode(i, Arduino.INPUT); → 设定指定脚位为输入模式
  • digitalRead(i) -> 读取数字脚位状态,也就是 arduino UNO 的 D0 - D13 脚位
  • analogRead(i) → 读取模拟脚位状态,也就是 arduino UNO 的 A0 - A5 脚位

 

arduino_input 完整程序代码如下,为避免占版面已删除批注,请自行开启 Processing 来看批注:

import processing.serial.*;

import cc.arduino.*;

Arduino arduino;

color off = color(4, 79, 111);
color on = color(84, 145, 158);

void setup() {
  size(470, 280);
  println(Arduino.list());
  arduino = new Arduino(this, Arduino.list()[0], 57600);
  
  for (int i = 0; i <= 13; i++)
    arduino.pinMode(i, Arduino.INPUT);
}

void draw() {
  background(off);
  stroke(on);
  
  for (int i = 0; i <= 13; i++) {
    if (arduino.digitalRead(i) == Arduino.HIGH)
      fill(on);
    else
      fill(off);
      
    rect(420 - i * 30, 30, 20, 20);
  }

  noFill();
  for (int i = 0; i <= 5; i++) {
    ellipse(280 + i * 30, 240, arduino.analogRead(i) / 16, arduino.analogRead(i) / 16);
  }
}

 

执行 arduino_out 范例

   这个范例属于数字输出,点选画面上端的方块就可以改变该方块的颜色,并让 Arduino 对应的脚位为高电位,如果该脚位接一个 LED 的话,就会亮起,再点一次则熄灭。执行画面如下:

b071_9b9e39de934466a84d2bba6b10abf467bda17f60.png

 

重要指令说明如下:

  • pinMode(i, Arduino.OUTPUT);; → 设定指定脚位为输出模式
  • for (int i = 0; i <= 13; i++) { if (values[i] == Arduino.HIGH)... →  根据 value[] 数组的内容来决定画面上端方块的颜色
  • digitalWrite(pin, Arduino.HIGH); → 写入数字脚位状态,HIGH 为高电位,如果该脚位接一个 LED 的话,就会亮起。反之 LOW 则是低电位(LED熄灭)

 

arduino_output 完整程序代码如下,为避免占版面已删除批注,请自行开启 Processing 来看批注:

import processing.serial.*;
import cc.arduino.*;
Arduino arduino;

color off = color(4, 79, 111);
color on = color(84, 145, 158);

int[] values = { Arduino.LOW, Arduino.LOW, Arduino.LOW, Arduino.LOW,
 Arduino.LOW, Arduino.LOW, Arduino.LOW, Arduino.LOW, Arduino.LOW,
 Arduino.LOW, Arduino.LOW, Arduino.LOW, Arduino.LOW, Arduino.LOW };

void setup() {
  size(470, 200);
  println(Arduino.list());
  arduino = new Arduino(this, Arduino.list()[0], 57600);
  for (int i = 0; i <= 13; i++)
    arduino.pinMode(i, Arduino.OUTPUT);
}

void draw() {
  background(off);
  stroke(on);
  
  for (int i = 0; i <= 13; i++) {
    if (values[i] == Arduino.HIGH)
      fill(on);
    else
      fill(off);
      
    rect(420 - i * 30, 30, 20, 20);
  }
}

void mousePressed()
{
  int pin = (450 - mouseX) / 30;
  
  if (values[pin] == Arduino.LOW) {
    arduino.digitalWrite(pin, Arduino.HIGH);
    values[pin] = Arduino.HIGH;
  } else {
    arduino.digitalWrite(pin, Arduino.LOW);
    values[pin] = Arduino.LOW;
  }
}

 

执行 arduino_pwm 范例

本范例为模拟写入,把 Processing 窗口上鼠标的 X 坐标转换为 Arduino PWM 可接受的数值范围 (0-255)。如果您根据程序说明,在 D9 与 D11 各接一个 LED 的话,当鼠标慢慢往右移动时,9号LED会慢慢变亮,11号则是慢慢变亮。但当鼠标往左时,两个 LED 的亮暗情况就相反了。

其实这个范例与 arduino_servo 可说是完全相同,差别只在于前者是用 arduino.analogWrite() 指令,后者则是用 arduino.servoWrite() 指令来控制伺服机的角度。

执行画面如下,鼠标愈靠近窗口左边,画面愈黑,反之往右则渐渐变为白色:

b081_a930ca3df2823e9d83b8d8254bb8ed0cbc344ae5.pngb091_01570a0084494e677d7ef3b0caac4807049ed886.png

 

重要指令说明如下:

  • analogWrite(9, constrain(mouseX / 2, 0, 255)); → 取得鼠标的 X 坐标 (mouseX) 并限制范围在 0 -255 之间,用来控制 9 号 LED 的亮度
  • analogWrite(11, constrain(255 - mouseX / 2, 0, 255)); → 与上一步差不多,但 255 - mouseX 是用来反向控制 LED 亮度。

 

arduino_pwm 完整程序代码如下,为避免占版面已删除批注,请自行开启 Processing 来看批注:

import processing.serial.*;

import cc.arduino.*;

Arduino arduino;

void setup() {
  size(512, 200);

  println(Arduino.list());
  arduino = new Arduino(this, "COM6", 57600);
}

void draw() {
  background(constrain(mouseX / 2, 0, 255));

  arduino.analogWrite(9, constrain(mouseX / 2, 0, 255));
  arduino.analogWrite(11, constrain(255 - mouseX / 2, 0, 255));
}

 

透过 USB 传讯号

    如果您要接收数字传感器(例如 DHT 11 温湿度传感器或加速度计),Processing 的 Arduino 函式库就做不到啦,因为它只支持基本的数字模拟 I/O  而已,这时候应该怎么办呢?这时候就需要自己定义要收发哪些数据。请看以下说明:

Processing 端

 

    基本的概念就是一个萝卜一个坑,Processing 发送什么,Arduino 端就会根据收到的东西来做事情。这边的重点就是两边一定要对应,否则就会邮差总是按错铃啦!影片连结:https://www.youtube.com/watch?v=_EgFOG1MyEk

    这里会用到 Processing 的 Serial 函式库,请根据先前步骤安装完成即可。执行画面如下,按下画面的方形与圆形区域(请把他们当成按钮),画面会变色,且会送出字符 ‘A’ (方形)与 ‘B’ ()给 7697,7697 则根据接受到的指令来做事。请注意 serial 串行传输基本的概念就是先发送的数据会先抵达(不考虑掉封包的情况),就好像排队一样,同时间只会接收到一笔数据,所以 Arduino 端只要用 if 或 switch 来依序处理即可,相当简单。

b101_564c5a1f8df1fabe5bf699abf429a95d25281941.png

b111_e0506e251126b0b92701caf1a29a82aaa271a889.png

 

重要的指令说明如下:

  • myPort = new Serial(this, Serial.list()[0], 9600); 建立一个新的序列通讯对象,用法与之前的 arduino 对象相当类似
  • while (myPort.available() > 0) → 如果 myPort 有接收到数据,代表 Arduino 端有数据传过来
  • int inByte = myPort.read(); 读取接收到的数据,并存入 inByte 整数变数中。您可能会好奇 Arduino 不是送一个字符 'C' 吗?为什么要用整数呢?原因在于序列通讯最基础的型态是 ASCII 编码,https://www.ascii-code.com/
  • if (inByte == 'C') → 如果收到内容的 ASCII 编码为 C 也就是 67,对应编码如下图:
  • write('A'); → 送出字符A,别忘了它的 ASCII 编码实际上是 65

b121_f56979d9dfc425f01d60062a3f4a8e46d8b6bbdd.png

Processing 端完整程序代码如下:

import processing.serial.*;

int rectX, rectY;      // Position of square button
int circleX, circleY;  // Position of circle button
int rectSize = 90;     // Diameter of rect
int circleSize = 93;   // Diameter of circle
color rectColor, circleColor, baseColor;
color rectHighlight, circleHighlight;
color currentColor;
boolean rectOver = false;
boolean circleOver = false;

int bg=0;

Serial myPort;

void setup() {

  myPort = new Serial(this, Serial.list()[0], 9600);
  size(640, 360);
  rectColor = color(0);
  rectHighlight = color(51);
  circleColor = color(255);
  circleHighlight = color(204);
  baseColor = color(102);
  currentColor = baseColor;
  circleX = width/2+circleSize/2+10;
  circleY = height/2;
  rectX = width/2-rectSize-10;
  rectY = height/2-rectSize/2;
  ellipseMode(CENTER);
}

void draw() {
  update(mouseX, mouseY);
  while (myPort.available() > 0) {
    int inByte = myPort.read();
    if (inByte == 'C') {
      bg++;
      if (bg>1) bg=0;
      if (bg==1) currentColor=color(0,255,0);
      if (bg==0) currentColor=color(255,0,0);
    }
  }
  
  background(currentColor);
  
  if (rectOver) {
    fill(rectHighlight);
  } else {
    fill(rectColor);
  }
  stroke(255);
  rect(rectX, rectY, rectSize, rectSize);
  
  if (circleOver) {
    fill(circleHighlight);
  } else {
    fill(circleColor);
  }
  stroke(0);
  ellipse(circleX, circleY, circleSize, circleSize);
}

void update(int x, int y) {
  if ( overCircle(circleX, circleY, circleSize) ) {
    circleOver = true;
    rectOver = false;
  } else if ( overRect(rectX, rectY, rectSize, rectSize) ) {
    rectOver = true;
    circleOver = false;
  } else {
    circleOver = rectOver = false;
  }
}

void mousePressed() {
  if (circleOver) {
    currentColor = circleColor;
    myPort.write('A');  //送出字符A
  }
  if (rectOver) {
    currentColor = rectColor;
    myPort.write('B');  //送出字符B
  }
}

boolean overRect(int x, int y, int width, int height)  {
  if (mouseX >= x && mouseX <= x+width && 
      mouseY >= y && mouseY <= y+height) {
    return true;
  } else {
    return false;
  }
}

boolean overCircle(int x, int y, int diameter) {
  float disX = x - mouseX;
  float disY = y - mouseY;
  if (sqrt(sq(disX) + sq(disY)) < diameter/2 ) {
    return true;
  } else {
    return false;
  }
}

 

Arduino端

 

    Arduino 端程序较为简单,重要指令说明如下:

  • begin(9600); → 启动序列通讯,通讯速率为 9600,请注意这时不可开启 Arduino IDE 的 Serial Monitor,否则 Processing 会因为 COM port 被占用而无法联机
  • if (Serial.available()) { → 如果 Serial 有接收到数据,代表 Processing 端有数据传过来
  • char code = Serial.read(); → 将串行端口接收到的数据存在字符变量 code 中
  • if (code == 'A') → 判断 code 值并执行对应的动作(控制两颗LED)
  • print('C'); → 透过串行端口送出字符 C
void setup()
{
  Serial.begin(9600);
  pinMode(3,INPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  digitalWrite(4,LOW);
  digitalWrite(5,LOW);
}

void loop()
{
  if (Serial.available()) {
    char code = Serial.read();
    if (code == 'A') {
      digitalWrite(4,HIGH);
      digitalWrite(5,LOW);
    }
    if (code == 'B') {
      digitalWrite(4,LOW);
      digitalWrite(5,HIGH);
    }
  }

  if (digitalRead(3)==LOW) {  //如果#3按钮被按下,送字符 C 给 Processing
    Serial.print('C');
    delay(200);
  }
}

 

想想看

   本篇文章介绍了两种 Processing 与 Arduino Uno 这类简易开发板的互动方式,想想看,还有哪些有趣的控制方式呢?

CAVEDU Education is devoted into robotics education and maker movement since 2008, and is intensively active in teaching fundamental knowledge and skills. We had published many books for readers in all ages, topics including Deep Learning, edge computing, App Inventor, IoT and robotics. Please check CAVEDU's website for more information: http://www.cavedu.com, http://www.appinventor.tw
DesignSpark Electrical Logolinkedin