软硬件交互

如何通过Arduino用旋钮控制烤箱界面

本教程将教会如何通过ProtoPie Connect连接Arduino连接ProtoPie实现旋钮控制烤箱界面。

ProtoPie

August 24, 2023

如何通过Arduino用旋钮控制烤箱界面

最终效果展示

物料准备

1. ProtoPie专业版或企业版账号
所有涉及软硬件交互的功能,都需要使用ProtoPie Connect。而目前(2023年4月19日)只有付费账号才能登录ProtoPie Connect。前往购买地址
2. 下载ProtoPie Connect和ProtoPie Player
在该页面 注册会员并登录后,可以看到下载按钮。如果已有账号,同样点击注册会员,在注册页面选择“登录账号”后,可在该页面看到如下按钮
1
4. Arduino和旋钮
案例中使用的是Arduino Uno,你可以淘宝、京东等平台购买。以下是推荐的购买地址(无商业推广目的)
5. Arduino代码部分
Arduino本身需要运行代码,才能接收发硬件的信号。如果你还不清楚如何去安装Arduino并运行代码,请先参考该视频
Arduino Sketch文件(代码部分)
/* Originally coded by Yvan / https://www.brainy-bits.com/arduino-rotary-encoder-ky-040/ */
/* Optimized for ProtoPie by Tony KIm / https://www.protopie.cn/ */

// Rotary Encoder Module connections
const int A_CLK=13;    // A CLOCK signal
const int A_DT=12;    // A DATA signal
const int A_SW=11;   // A Switch

const int B_CLK=10;    // B CLOCK signal
const int B_DT=9;    // B DATA signal
const int B_SW=8;   // B Switch

const int C_CLK=7;    // C CLOCK signal
const int C_DT=6;    // C DATA signal
const int C_SW=5;   // C Switch

const int D_CLK=4;    // D CLOCK signal
const int D_DT=3;    // D DATA signal
const int D_SW=2;   // D Switch

// Variables to debounce Rotary Encoder
long TimeOfLastDebounce = 0;
int DelayofDebounce = 0.01;

// Store previous Pins state
int Previous_A_CLK;   
int Previous_A_DT;
int Previous_B_CLK;   
int Previous_B_DT;
int Previous_C_CLK;   
int Previous_C_DT;
int Previous_D_CLK;   
int Previous_D_DT;


void setup() {
  // Put current pins state in variables
  Serial.begin(115200);
  Previous_A_CLK=digitalRead(A_CLK);
  Previous_B_CLK=digitalRead(B_CLK);
  Previous_C_CLK=digitalRead(C_CLK);
  Previous_D_CLK=digitalRead(D_CLK);
  
  Previous_A_DT=digitalRead(A_DT);
  Previous_B_DT=digitalRead(B_DT);
  Previous_C_DT=digitalRead(C_DT);
  Previous_D_DT=digitalRead(D_DT);

  // Set the Switch pin to use Arduino PULLUP resistors
  pinMode(A_SW, INPUT_PULLUP);
  pinMode(B_SW, INPUT_PULLUP);
  pinMode(C_SW, INPUT_PULLUP);
  pinMode(D_SW, INPUT_PULLUP);
}


void loop() {
  // If enough time has passed check the rotary encoder
  if ((millis() - TimeOfLastDebounce) > DelayofDebounce) {
    
    check_rotary();  // Rotary Encoder check routine below
    
    Previous_A_CLK=digitalRead(A_CLK);
    Previous_B_CLK=digitalRead(B_CLK);
    Previous_C_CLK=digitalRead(C_CLK);
    Previous_D_CLK=digitalRead(D_CLK);

    
    Previous_A_DT=digitalRead(A_DT);
    Previous_B_DT=digitalRead(B_DT);
    Previous_C_DT=digitalRead(C_DT);
    Previous_D_DT=digitalRead(D_DT);
    
    TimeOfLastDebounce=millis();  // Set variable to current millis() timer
  }
  
  // Check if Rotary Encoder switch was pressed

  if (digitalRead(A_SW) == LOW) {
    Serial.println("a_btn"); // Send "a_btn" to ProtoPie
  }
  
   if (digitalRead(B_SW) == LOW) {
    Serial.println("b_btn"); // Send "b_btn" to ProtoPie
  }

   if (digitalRead(C_SW) == LOW) {
    Serial.println("c_btn"); // Send "c_btn" to ProtoPie
  }
  
   if (digitalRead(D_SW) == LOW) {
    Serial.println("d_btn"); // Send "d_btn" to ProtoPie
  }
}


// Check if Rotary Encoder was rotated
// Debounce codes - it eliminates bounce effects
void check_rotary() {

// Encoder A
if ((Previous_A_CLK == 0) && (Previous_A_DT == 1)) {
    if ((digitalRead(A_CLK) == 1) && (digitalRead(A_DT) == 0)) {
      Serial.println("a_cw"); // Send "a_cw" to ProtoPie
    }
    if ((digitalRead(A_CLK) == 1) && (digitalRead(A_DT) == 1)) {
      Serial.println("a_ccw"); // Send "a_ccw" to ProtoPie
    }
  }

if ((Previous_A_CLK == 1) && (Previous_A_DT == 0)) {
    if ((digitalRead(A_CLK) == 0) && (digitalRead(A_DT) == 1)) {
      Serial.println("a_cw"); // Send "a_cw" to ProtoPie
    }
    if ((digitalRead(A_CLK) == 0) && (digitalRead(A_DT) == 0)) {
      Serial.println("a_ccw"); // Send "a_ccw" to ProtoPie
    }
  }

if ((Previous_A_CLK == 1) && (Previous_A_DT == 1)) {
    if ((digitalRead(A_CLK) == 0) && (digitalRead(A_DT) == 1)) {
      Serial.println("a_cw"); // Send "a_cw" to ProtoPie
    }
    if ((digitalRead(A_CLK) == 0) && (digitalRead(A_DT) == 0)) {
      Serial.println("a_ccw"); // Send "a_ccw" to ProtoPie
    }
  }  

if ((Previous_A_CLK == 0) && (Previous_A_DT == 0)) {
    if ((digitalRead(A_CLK) == 1) && (digitalRead(A_DT) == 0)) {
      Serial.println("a_cw"); // Send "a_cw" to ProtoPie
    }
    if ((digitalRead(A_CLK) == 1) && (digitalRead(A_DT) == 1)) {
      Serial.println("a_ccw"); // Send "a_ccw" to ProtoPie
    }
  }          


//Encorder B
if ((Previous_B_CLK == 0) && (Previous_B_DT == 1)) {
    if ((digitalRead(B_CLK) == 1) && (digitalRead(B_DT) == 0)) {
      Serial.println("b_cw"); // Send "b_cw" to ProtoPie
    }
    if ((digitalRead(B_CLK) == 1) && (digitalRead(B_DT) == 1)) {
      Serial.println("b_ccw"); // Send "b_ccw" to ProtoPie
    }
  }

if ((Previous_B_CLK == 1) && (Previous_B_DT == 0)) {
    if ((digitalRead(B_CLK) == 0) && (digitalRead(B_DT) == 1)) {
      Serial.println("b_cw"); // Send "b_cw" to ProtoPie
    }
    if ((digitalRead(B_CLK) == 0) && (digitalRead(B_DT) == 0)) {
      Serial.println("b_ccw"); // Send "b_ccw" to ProtoPie
    }
  }

if ((Previous_B_CLK == 1) && (Previous_B_DT == 1)) {
    if ((digitalRead(B_CLK) == 0) && (digitalRead(B_DT) == 1)) {
      Serial.println("b_cw"); // Send "b_cw" to ProtoPie
    }
    if ((digitalRead(B_CLK) == 0) && (digitalRead(B_DT) == 0)) {
      Serial.println("b_ccw"); // Send "b_ccw" to ProtoPie
    }
  }  

if ((Previous_B_CLK == 0) && (Previous_B_DT == 0)) {
    if ((digitalRead(B_CLK) == 1) && (digitalRead(B_DT) == 0)) {
      Serial.println("b_cw"); // Send "b_cw" to ProtoPie
    }
    if ((digitalRead(B_CLK) == 1) && (digitalRead(B_DT) == 1)) {
      Serial.println("b_ccw"); // Send "b_ccw" to ProtoPie
    }
  } 



 //Encorder C
if ((Previous_C_CLK == 0) && (Previous_C_DT == 1)) {
    if ((digitalRead(C_CLK) == 1) && (digitalRead(C_DT) == 0)) {
      Serial.println("c_cw"); // Send "c_cw" to ProtoPie
    }
    if ((digitalRead(C_CLK) == 1) && (digitalRead(C_DT) == 1)) {
      Serial.println("c_ccw"); // Send "c_ccw" to ProtoPie
    }
  }

if ((Previous_C_CLK == 1) && (Previous_C_DT == 0)) {
    if ((digitalRead(C_CLK) == 0) && (digitalRead(C_DT) == 1)) {
      Serial.println("c_cw"); // Send "c_cw" to ProtoPie
    }
    if ((digitalRead(C_CLK) == 0) && (digitalRead(C_DT) == 0)) {
      Serial.println("c_ccw"); // Send "c_ccw" to ProtoPie
    }
  }

if ((Previous_C_CLK == 1) && (Previous_C_DT == 1)) {
    if ((digitalRead(C_CLK) == 0) && (digitalRead(C_DT) == 1)) {
      Serial.println("c_cw"); // Send "c_cw" to ProtoPie
    }
    if ((digitalRead(C_CLK) == 0) && (digitalRead(C_DT) == 0)) {
      Serial.println("c_ccw"); // Send "c_ccw" to ProtoPie
    }
  }  

if ((Previous_C_CLK == 0) && (Previous_C_DT == 0)) {
    if ((digitalRead(C_CLK) == 1) && (digitalRead(C_DT) == 0)) {
      Serial.println("c_cw"); // Send "c_cw" to ProtoPie
    }
    if ((digitalRead(C_CLK) == 1) && (digitalRead(C_DT) == 1)) {
      Serial.println("c_ccw"); // Send "c_ccw" to ProtoPie
    }
  } 


 //Encorder D
if ((Previous_D_CLK == 0) && (Previous_D_DT == 1)) {
    if ((digitalRead(D_CLK) == 1) && (digitalRead(D_DT) == 0)) {
      Serial.println("d_cw"); // Send "d_cw" to ProtoPie
    }
    if ((digitalRead(D_CLK) == 1) && (digitalRead(D_DT) == 1)) {
      Serial.println("d_ccw"); // Send "d_ccw" to ProtoPie
    }
  }

if ((Previous_D_CLK == 1) && (Previous_D_DT == 0)) {
    if ((digitalRead(D_CLK) == 0) && (digitalRead(D_DT) == 1)) {
      Serial.println("d_cw"); // Send "d_cw" to ProtoPie
    }
    if ((digitalRead(D_CLK) == 0) && (digitalRead(D_DT) == 0)) {
      Serial.println("d_ccw"); // Send "d_ccw" to ProtoPie
    }
  }

if ((Previous_D_CLK == 1) && (Previous_D_DT == 1)) {
    if ((digitalRead(D_CLK) == 0) && (digitalRead(D_DT) == 1)) {
      Serial.println("d_cw"); // Send "d_cw" to ProtoPie
    }
    if ((digitalRead(D_CLK) == 0) && (digitalRead(D_DT) == 0)) {
      Serial.println("d_ccw"); // Send "d_ccw" to ProtoPie
    }
  }  

if ((Previous_D_CLK == 0) && (Previous_D_DT == 0)) {
    if ((digitalRead(D_CLK) == 1) && (digitalRead(D_DT) == 0)) {
      Serial.println("d_cw"); // Send "d_cw" to ProtoPie
    }
    if ((digitalRead(D_CLK) == 1) && (digitalRead(D_DT) == 1)) {
      Serial.println("d_ccw"); // Send "d_ccw" to ProtoPie
    }
  } 
}

连接与运行

1. 连接面包版
按照下面的示意图将4个旋钮和Arduino Uno相连
3
2. Arduino同ProtoPie Connect 的连接
  • 通过USB接口将Arduino和装有ProtoPie Connect的电脑相连
  • 运行Arduino代码 ,请注意,此时的接口应该是你的usb接口
4
  • 运行代码
  • 打开serial monitor检测信号是否正常进入,确认后须关闭serial monitor (serial monitor窗口和ProtoPie connect不能同时开启)
  • 打开ProtoPie Connect,选择右上角的Plugin,在点击Run之前先点击Arduino
5
  • 选择在b这步 相同的接口,并将Baud Rate设置为115200.
6
  • 现在当你调节旋钮的时候,应该可以看到数据进入到Connect了。
3. 通过ProtoPie Connect运行Pie文件
  • 点击ProtoPie Connect左上角的加号,选择本地文件,选择要和Arduino连接的Pie文件
  • 导入后,点击预览图标,原型就可在网页端打开
7
  • 此时物理按钮就可以操控屏幕上的视觉效果了
4. 在装有Player的平板上运行交互
  • 你可以用数据线现将平板/手机和电脑相连,这样原型可以在离线环境下从Connect同步到Player。如果希望通过Wifi连接,请一定确保电脑和移动端连在同一个网络环境下。(手机热点可以帮你确保连在同一个网络下)
  • 打开ProtoPie Player,点击USB图标或扫码键。与此同时点击ProtoPie Connect上的Connect键(预览图标旁边的),选择相应的连接方式。如果在连接中遇到问题可参考指导手册 。现在你就可以看到和demo视频中一样的效果啦。
8
9

要点解析

1. 旋钮信息发送的原理
关于Knob旋钮的工作原理,可参考这份中文说明 。在Arduino中,每当旋钮的状态发生变化,都需要发送数据。而发送数据的代码是:Serial.println("message")
2. 发送的信息
Knob旋钮发出3种数据:顺时针旋转(CW)、逆时针旋转(CCW)和按钮(BTN)。因为有4个旋钮,所以分别对4个旋钮的3种数据进行命名:
  • A旋钮:a_cw , a_ccw , a_btn
  • B旋钮:b_cw , b_ccw , b_btn
  • C旋钮:c_cw , c_ccw , c_btn
  • D旋钮:d_cw , d_ccw , d_btn
3. 按钮旋转的信息与交互的对应
每当收到逆时针旋转的信息时,则画面上相应图层逆时针旋转5度
10
每当收到顺时针旋转的信息时,则画面上相应图层顺时针旋转5度
11
每当收到按钮被按下的信息时,则将画面上的旋钮归位。
10
4. 按钮旋转的角度和功能icon的对应
根据画面上旋钮图层(knob-a,knob-b,knob-c,knob-d)的旋转角度范围,让相应的功能icon亮起。角度和功能区的对应关系如下:
12

相关内容