Processing数字互动介面开发软件应用 –结合 Arduino 硬件控制
作者 |
曾吉弘 |
难度 |
普通 |
材料表 |
1. Arduino UNO |
Processing 结合 Arduino
上一篇文章介绍了 Processing 的基础应用,本篇文章要介绍如何让 Processing 透过 USB 传输线来连结 maker 相当喜爱的 Arduino Uno 开发板,另外也会介绍最近相当有人气的 LinkIt 7697 开发板。做法有两种:
- 运用 Arduino 的 StandardFirmata,好处是 Arduino 端不需要再写程序,只要从 Processing 端发送指令即可,但缺点是弹性较低且较难客制化,例如特殊的传感器就无法直接读取数值。
- 自行定义USB发送的讯号,好处是可以做到任何您希望的功能。缺点就是程序代码需要对应,Processing 修改了任何地方,Arduino 端都需要做对应的修改。
现在就让我们看看怎么做吧。
Arduino 端
在此使用 Arduino Uno 开发板。请下载 Arduino 环境,在此使用 Arduino 1.8.5,更新或再旧一点的版本应该都可以用,StandardFirmata 算是相当早期就有的做法了。刻录完成之后,请让 Arduino 接着计算机放着就可以了。
Processing端
请开启 Processing 官方网站的下载页面,根据您所使用的硬件平台下载对应的版本,如果还没下载请参考上一篇。
开启 Processing IDE,点选 Sketch → Import Library → Add Library,开启 Contribution Manager 窗口,并搜寻 arduino。点选 Arduino (Firmata) 再点选 Install,就会开始安装,如下图:
也会带入对应的范例,只要是另外安装的函式库范例都会为在 Contributed Libraries 下。一共有五个范例,今天会说明其中的:
- arduino_input:把 Arduino 脚位状态呈现在 Processing 中
- arduino_output:在 Processing 中点选画面来控制 Arduino 脚位状态 (数字)
- arduino_pwm:在 Processing 中移动鼠标来控制 Arduino 脚位状态 (模拟)
执行 arduino_input 范例
请直接开启 arduino_input 范例, 这个范例属于输位输入与模拟输入。请确认您的 Arduino 板子正在执行 StandardFirmata 程序,您可以重新插拔电源或单击 Arduino 板子上的 Reset 按钮让它重新启动程序。总之,当您按下 Processing 的 Run 按键时,Arduino 必须先执行程序才行,否则会出现 RuntimeException: Error opening serial port COM6: Port not found 这样的错误讯息。
一切顺利的话,会看到下方的画面,上方的方块代表 Arduino Uno 的 D0 - D13 数字输入/输出脚位,您可以在任一个脚位接上按钮,按下按钮就会使对应脚位的方块变成浅色。而下方的圆圈则代表 Arduino Uno 的 A0 - A6 的模拟输入脚位。
重要指令说明如下:
- 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 的话,就会亮起,再点一次则熄灭。执行画面如下:
重要指令说明如下:
- 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() 指令来控制伺服机的角度。
执行画面如下,鼠标愈靠近窗口左边,画面愈黑,反之往右则渐渐变为白色:
重要指令说明如下:
- 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 来依序处理即可,相当简单。
重要的指令说明如下:
- 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
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 这类简易开发板的互动方式,想想看,还有哪些有趣的控制方式呢?
评论