查看: 439|回复: 3

[项目分享] 【BOULKOO】Mpr121纸片钢琴

[复制链接]
-13eb2a714d23f566.jpg

<关于>
梗概: 由导电胶制作的纸片钢琴接触纸,通过传感器连接到Arduino,并模拟键盘输入到电脑。
出生地: BK-QZGK-LAB BK-CAN-YX-LAB

视频链接[CHINA]:


视频链接[NATIONAL]:
https://www.youtube.com/watch?v=KguJgxtnhYw

<相关教程>
Mpr121模块
Arduino模拟键盘
<设想与推论>
主要实现的功能是触摸纸片后,反馈到Arduino是哪个键触摸,然后Arduino模拟usb键盘,
输出到PC上,PC上键盘钢琴软件开启。之后就可以合成音了。
QQ截图20190619081604.jpg

主要问题也是模拟的usb键盘的,需要用到Leonard板的Keyboard库来模拟HID键盘。
MPR121的驱动也完成了,一个I2C口可以带4个MPR121,(地址分别是0X5A,0X5B,0X5C,0X5D。
然后对应轮询,检测按下和弹起一个对应一个键盘按键输出。实现效果。
FrdC1cW5sEvn-xSWlHq3jTRZK1Za.png 145235yezraasyhsuazlia.jpg

项目中的36键我们使用了PC端的Everyonepiano软件
自定义了按键文件,通过按键盘来实现对应的钢琴键,然后通过arduino Leonard的键盘模拟输出
实现触摸导电油墨按键发出钢琴音的功能。
钢琴键.png
PS:提示下,有些MPR121模块的I2C地址是固定的,需要自行跳线更换地址,
当时制作的就遇到一直没法多个MPR121同时工作,后面才发现模块跳线被固定了。

<开发日志>
目前实现了Arduino Nano一个带三个,并且读取36键的触摸信号。2018/2/13
实现Arduino Nano对三个mpr121的引脚编号,对应打印数组内数据。
等待购买Leonardo板,实现了函数的集成和编写。 2018/2/14
制作导电油墨钢琴键完成2018/2/16
  工程结束2018/2/28

-1df1bd66875ceca8.jpg
程序介绍(工程文件于文件夹中MPR121_Paper_Paino
注意:不要直接复制以下代码进入软件,将可能出现未知错误!
[AppleScript] 纯文本查看 复制代码
/*********************BOULKOO******************************[/size][/color][/font][/backcolor][/align][/align][font=微软雅黑][align=left][color=#000000]
[/color][/align]
[color=#000000][align=left]编写:BOULKOO Michael_B 2018.2.13[/align]

[align=left]纸片钢琴 arduino端代码[/align]

[align=left]硬件支持:[/align]

[align=left]arduino lenaedo/ micro , MPR121[/align]

[align=left]硬件连接:[/align]

[align=left]arduino lenaedo/micro I2C 连接1~4个MPR121模块,ABCD的MPR121硬件分别对应1~48号引脚[/align]

[align=left]arduino lenaedo/micro usb连接电脑,模拟键盘输出。[/align]
[align=left]ToKeyboard() //将记录数组输出变成模拟键盘按键[/align]

[align=left]MPR121Check() //MPR121初始化函数,检测模块地址是否在线[/align]

[align=left]TandRcheck()  //检测MPR121上的引脚变动情况,采用混合扫描[/align]

[align=left]用户需要修改的:[/align]

[align=left]#define amount 模块数量[/align]

[align=left]uint16_t KeyBoard[50] 引脚对应的键盘按键[/align]

[align=left]/*********************BOULKOO******************************/[/align]

[align=left]#include <Wire.h> //I2C库 (通讯函数)[/align]

[align=left]#include <Keyboard.h> //keyboard库  (Lenaedo模拟PC键盘库函数)[/align]

[align=left]#include “Adafruit_MPR121.h” //MPR121库 (读取驱动模块数据函数)[/align]

[align=left]#define MPRA 0x5A[/align]

[align=left]#define MPRB 0x5B     //MPR121模块ADD脚默认地址/接地为 0x5A, 连接到 3.3V 为 0x5B      [/align]

[align=left]#define MPRC 0x5C     // 连接到 SDA 为 0x5C , 连接 SCL 为 0x5D (模块编号)[/align]

[align=left]#define MPRD 0x5D[/align]

[align=left]#define amount 3 //I2C总线上的数量 用户输入1~4(根据硬件来决定)[/align]

[align=left]uint16_t KeyBoard[50] = {‘&’,’2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′,’1′,’2′,’1′,’3′,0,0};[/align]

[align=left]//在此用户输入每个引脚号码对应模拟键盘的键[/align]

[align=left]//第一个是没有用是,从第二位开始。即从1号位置开始。[/align]

[align=left]//创建对象[/align]

[align=left]Adafruit_MPR121 capA = Adafruit_MPR121(); [/align]

[align=left]Adafruit_MPR121 capB =Adafruit_MPR121();[/align]

[align=left]Adafruit_MPR121 capC =Adafruit_MPR121();[/align]

[align=left]Adafruit_MPR121 capD =Adafruit_MPR121();[/align]

[align=left]//创建相应的状态变量  lasttouchedA是上次的状态,currtouchedA是当前状态,两者用于检测引脚状态//是否又变动。B,C,D与此相同。[/align]

[align=left]uint16_t lasttouchedA = 0;[/align]

[align=left]uint16_t currtouchedA = 0;[/align]

[align=left]uint16_t lasttouchedB = 0;[/align]

[align=left]uint16_t currtouchedB = 0;[/align]

[align=left]uint16_t lasttouchedC = 0;[/align]

[align=left]uint16_t currtouchedC = 0;[/align]

[align=left]uint16_t lasttouchedD = 0;[/align]

[align=left]uint16_t currtouchedD = 0;[/align]

[align=left]uint16_t Touched[50];  //变更为按下引脚的记录数组[/align]

[align=left]uint16_t Released[50]; //变更为松开引脚的记录数组[/align]

[align=left]uint8_t rom1;//缓存数组[/align]

[align=left]uint8_t rom2;[/align]

[align=left]void setup() {   //启示代码[/align]

[align=left]  while (!Serial);        // 等待电脑打开串口[/align]

[align=left]   Serial.begin(9600);[/align]

[align=left]   Serial.println(” MPR121 纸片钢琴测试 “);[/align]

[align=left]   MPR121Check();//检测I2C上的设备是否在线[/align]

[align=left]   Keyboard.begin(); //Keyboard库初始化[/align]

[align=left]   Keyboard.releaseAll(); //所有键盘按键松开[/align]

[align=left]}[/align]

[align=left]void MPR121Check() //MPR121初始化函数,检测是否在线[/align]

[align=left]//(自检函数,可以打开串口助手查看设备信息)[/align]

[align=left]{[/align]

[align=left]  if(amount >= 1)[/align]

[align=left]  {[/align]

[align=left]     if (!capA.begin(MPRA))[/align]

[align=left]     {[/align]

[align=left]    Serial.println(“MPRA not found, check wiring?”);[/align]

[align=left]    delay(100);[/align]

[align=left]     }[/align]

[align=left]     else[/align]

[align=left]     {[/align]

[align=left]     Serial.println(“MPRA found!”);[/align]

[align=left]     }[/align]

[align=left]  }[/align]

[align=left]  if(amount >= 2)[/align]

[align=left]  {[/align]

[align=left]       if (!capB.begin(MPRB))[/align]

[align=left]     {[/align]

[align=left]    Serial.println(“MPRB not found, check wiring?”);[/align]

[align=left]    delay(100);[/align]

[align=left]     }[/align]

[align=left]     else[/align]

[align=left]     {[/align]

[align=left]     Serial.println(“MPRB found!”);[/align]

[align=left]     }[/align]

[align=left]  }[/align]

[align=left]  if(amount >=3)[/align]

[align=left]  {[/align]

[align=left]   if (!capC.begin(MPRC))[/align]

[align=left]     {[/align]

[align=left]    Serial.println(“MPRC not found, check wiring?”);[/align]

[align=left]    delay(100);[/align]

[align=left]     }[/align]

[align=left]     else[/align]

[align=left]     {[/align]

[align=left]     Serial.println(“MPRC found!”);[/align]

[align=left]     }[/align]

[align=left]  }[/align]

[align=left]  if(amount >=4)[/align]

[align=left]   {[/align]

[align=left]     if (!capD.begin(MPRD))[/align]

[align=left]     {[/align]

[align=left]    Serial.println(“MPRD not found, check wiring?”);[/align]

[align=left]    delay(100);[/align]

[align=left]     }[/align]

[align=left]     else[/align]

[align=left]     {[/align]

[align=left]     Serial.println(“MPRD found!”);[/align]

[align=left]     }[/align]

[align=left]   }[/align]

[align=left]}[/align]

[align=left]void TandRcheck()  //检测MPR121上的引脚变动情况,采用混合扫描[/align]

[align=left]{[/align]

[align=left]uint8_t n =0;    //标志符[/align]

[align=left]uint8_t n2 =0;[/align]

[align=left]uint8_t i=0;[/align]

[align=left]if(amount >=1)[/align]

[align=left]  {[/align]

[align=left]     currtouchedA = capA.touched();                                                                                [/align]

[align=left]混合扫描: 在一个函数周期里面同时扫描释放的键和被按下的键,提高识别按键事件的速度和准确度。 曾使用分别扫描的,分开按别按下和识别松开的函数,效果不佳。  [/align]
[align=left]  for ( i=0; i<12; i++) {[/align]

[align=left]    // it if *is* touched and *wasnt* touched before, alert![/align]

[align=left]    if ((currtouchedA & _BV(i)) && !(lasttouchedA & _BV(i)) ) {                             [/align]

[align=left]       Serial.print(i+1); Serial.println(” touched”);[/align]

[align=left]       Touched[n] = i+1;[/align]

[align=left]       n++;[/align]

[align=left]    }[/align]

[align=left]    // if it *was* touched and now *isnt*, alert![/align]

[align=left]    if (!(currtouchedA & _BV(i)) && (lasttouchedA & _BV(i)) ) {[/align]

[align=left]     Serial.print(i+1); Serial.println(” released”);[/align]

[align=left]      Released[n2] = i+1;[/align]

[align=left]      n2++;[/align]

[align=left]    }[/align]

[align=left]  }[/align]

[align=left]  // reset our state[/align]

[align=left]    lasttouchedA = currtouchedA;[/align]

[align=left]}[/align]

[align=left]if(amount >=2)[/align]

[align=left]混合扫描: 此处的amount为设备个数,根据设备数量调整扫描设备个数,提高适用性。 若设备数多,识别速度可能相应降低。  [/align]
[align=left]  {[/align]

[align=left]  currtouchedB = capB.touched();[/align]

[align=left]  for ( i=0; i<12; i++) {[/align]

[align=left]    // it if *is* touched and *wasnt* touched before, alert![/align]

[align=left]    if ((currtouchedB & _BV(i)) && !(lasttouchedB & _BV(i)) ) {[/align]

[align=left]      Serial.print(i+12+1); Serial.println(” touched”);[/align]

[align=left]        Touched[n] = i+12+1;[/align]

[align=left]        n++;[/align]

[align=left]    }[/align]

[align=left]     if (!(currtouchedB & _BV(i)) && (lasttouchedB & _BV(i)) ) {[/align]

[align=left]       Serial.print(i+12+1); Serial.println(” released”);[/align]

[align=left]       Released[n2] = i+12+1;[/align]

[align=left]       n2++;[/align]

[align=left]    }[/align]

[align=left]  }[/align]

[align=left]  // reset our state[/align]

[align=left]  lasttouchedB = currtouchedB;[/align]

[align=left]  }[/align]

[align=left]if(amount >=3)[/align]

[align=left]{[/align]

[align=left]  currtouchedC = capC.touched();[/align]

[align=left]  for ( i=0; i<12; i++) {[/align]

[align=left]    // it if *is* touched and *wasnt* touched before, alert![/align]

[align=left]    if ((currtouchedC & _BV(i)) && !(lasttouchedC & _BV(i)) ) {[/align]

[align=left]       Serial.print(i+12+12+1); Serial.println(” touched”);[/align]

[align=left]        Touched[n] = i+12+12+1;[/align]

[align=left]        n++;[/align]

[align=left]    }[/align]

[align=left]     if (!(currtouchedC & _BV(i)) && (lasttouchedC & _BV(i)) ) {[/align]

[align=left]      Serial.print(i+12+12+1); Serial.println(” released”);[/align]

[align=left]      Released[n2] = i+12+12+1;[/align]

[align=left]      n2++;[/align]

[align=left]    }[/align]

[align=left]  }[/align]

[align=left]  // reset our state[/align]

[align=left]    lasttouchedC = currtouchedC; [/align]

[align=left]}[/align]

[align=left]if(amount >=4)[/align]

[align=left]{[/align]

[align=left]  currtouchedD = capD.touched();[/align]

[align=left]  for (i=0; i<12; i++) {[/align]

[align=left]    // it if *is* touched and *wasnt* touched before, alert![/align]

[align=left]    if ((currtouchedD & _BV(i)) && !(lasttouchedD & _BV(i)) ) {[/align]

[align=left]        Serial.print(i+12+12+12+1); Serial.println(” touched”);[/align]

[align=left]        Touched[n] = i+12+12+12+1;[/align]

[align=left]        n++;[/align]

[align=left]      }[/align]

[align=left]    if (!(currtouchedC & _BV(i)) && (lasttouchedC & _BV(i)) ) {[/align]

[align=left]      Serial.print(i+12+12+12+1); Serial.println(” released”);[/align]

[align=left]      Released[n2] = i+12+12+12+1;[/align]

[align=left]      n2++;[/align]

[align=left]    } [/align]

[align=left]  }[/align]

[align=left]  // reset our state[/align]

[align=left]  lasttouchedD = currtouchedD; [/align]

[align=left]  }[/align]

[align=left]}[/align]

[align=left]void ToKeyboard() //将记录数组输出变成模拟键盘按键[/align]

[align=left]{[/align]

[align=left]转换函数: 根据用户设置的对应按键,将对应的按键编号转化为对应键盘按键,并输出到PC 串口模拟HID设备  [/align]
[align=left]  uint8_t i=0;[/align]

[align=left]  uint8_t i2=0;[/align]

[align=left]  while(Released[i2]!= 0 || Touched!= 0)[/align]

[align=left]  {[/align]

[align=left]   if(Touched!= 0)[/align]

[align=left]   {[/align]

[align=left]    rom1 = Touched;[/align]

[align=left]    Keyboard.press(KeyBoard[rom1]);[/align]

[align=left]    i++;[/align]

[align=left]   }[/align]

[align=left]    delayMicroseconds(100);[/align]

[align=left]   if(Released[i2]!= 0)[/align]

[align=left]   {[/align]

[align=left]    rom2 = Released;[/align]

[align=left]     Keyboard.release(KeyBoard[rom2]);[/align]

[align=left]     i2++;[/align]

[align=left]   }[/align]

[align=left]  }[/align]

[align=left]  rom1 = 0;[/align]

[align=left]  rom2 = 0;[/align]

[align=left]  Released[0]=0;[/align]

[align=left]  Touched[0]=0;[/align]

[align=left]}[/align]

[align=left]void loop() {                                                        //主函数直接调用相关函数进行循环[/align]

[align=left]TandRcheck(); //检测引脚变更[/align]

[align=left]ToKeyboard();  //输出到键盘[/align]

[align=left]}[/align]

[align=left]


程序逻辑:初始化 –> 循环体  { 1检测引脚是否变动 2数据转换输出到键盘 }

<纸片制作>
我们可以在某宝买到廉价的石墨导电涂料,一般二三十块钱一小罐。如果买导电笔和专业导电涂料价格会比较高。
但是廉价的石墨导电涂料溶剂味道比较大,类似于油漆味,使用时请注意通风。
我们尝试用酒精替换原有溶剂,但是效果并不理想。酒精不能乳化原有涂料,反而能起到类似于萃取的效果把原有溶剂析出。
我们也会在此基础上继续进行深入研究,接下来就是画钢琴了,
这里有个比较方便的方法就是先打印一个模板,裁剪完成后铺在纸上用刮玻璃的工具刷涂料,再起模板就好了。

QQ图片20180602000315.jpg QQ图片20180602000321.jpg QQ图片20180602000326.jpg
QQ图片20180602000301.jpg QQ图片20180602000331.jpg




源码和相关技术资料下载地址:
https://office.boulkoo.com/desk/index.php?share/folder&user=1&sid=GQFhHC6T
<版权声明>
如需转载到其他平台请联系BOULKOO官方。 Mail: mail@boulkoo.com

欢迎关注我们的微信公众号
qrcode_for_gh_77e808f6c858_258.jpg

gada888  版主

发表于 2019-6-20 07:21:25

有营养的帖子
回复 支持 1 反对 0

使用道具 举报

Michael_B  见习技师
 楼主|

发表于 2019-6-19 10:45:23

沙发!!!
回复 支持 反对

使用道具 举报

Michael_B  见习技师
 楼主|

发表于 2019-6-20 08:49:37


感谢支持!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
wifi气象站

硬件清单

btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2019 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail