【BOULKOO】Leonardo&Mpr121纸片钢琴-Arduino中文社区 - Powered by Discuz! Archiver

ForeverGeeker 发表于 2019-6-19 09:16

【BOULKOO】Leonardo&Mpr121纸片钢琴

本帖最后由 ForeverGeeker 于 2019-6-21 07:19 编辑

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

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

<相关教程>Mpr121模块www.arduino.cn/thread-23148-1-1.htmlhttps://learn.sparkfun.com/tutorials/mpr121-hookup-guidehttp://www.arduino.cn/thread-23378-1-1.htmlhttp://bildr.org/2011/05/mpr121_arduino/Arduino模拟键盘www.arduino.cn/thread-21472-1-1.htmlhttp://www.ncnynl.com/archives/201607/387.html

<设想与推论>主要实现的功能是触摸纸片后,反馈到Arduino是哪个键触摸,然后Arduino模拟usb键盘,输出到PC上,PC上键盘钢琴软件开启。之后就可以合成音了。
主要问题也是模拟的usb键盘的,需要用到Leonardo板的Keyboard库来模拟HID键盘。MPR121的驱动也完成了,一个I2C口可以带4个MPR121,(地址分别是0X5A,0X5B,0X5C,0X5D。然后对应轮询,检测按下和弹起一个对应一个键盘按键输出。实现效果。
项目中的36键我们使用了PC端的Everyonepiano软件自定义了按键文件,通过按键盘来实现对应的钢琴键,然后通过arduino Leonard的键盘模拟输出实现触摸导电油墨按键发出钢琴音的功能。PS:提示下,有些MPR121模块的I2C地址是固定的,需要自行跳线更换地址,当时制作的就遇到一直没法多个MPR121同时工作,后面才发现模块跳线被固定了。
<开发日志>目前实现了Arduino Nano一个带三个,并且读取36键的触摸信号。2018/2/13实现Arduino Nano对三个mpr121的引脚编号,对应打印数组内数据。等待购买Leonardo板,实现了函数的集成和编写。 2018/2/14制作导电油墨钢琴键完成2018/2/16工程结束2018/2/28
程序介绍(工程文件于文件夹中MPR121_Paper_Paino)注意:不要直接复制以下代码进入软件,将可能出现未知错误!

/*********************BOULKOO******************************

编写:BOULKOO Michael_B 2018.2.13

纸片钢琴 arduino端代码

硬件支持:

arduino lenaedo/ micro , MPR121

硬件连接:

arduino Leonardo/micro I2C 连接1~4个MPR121模块,ABCD的MPR121硬件分别对应1~48号引脚

arduino Leonardo/micro usb连接电脑,模拟键盘输出。
ToKeyboard() //将记录数组输出变成模拟键盘按键

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

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

用户需要修改的:

#define amount 模块数量

uint16_t KeyBoard 引脚对应的键盘按键

/*********************BOULKOO******************************/

#include <Wire.h> //I2C库 (通讯函数)

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

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

#define MPRA 0x5A

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

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

#define MPRD 0x5D

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

uint16_t KeyBoard = {‘&’,’2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′,’1′,’2′,’1′,’3′,0,0};

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

//第一个是没有用是,从第二位开始。即从1号位置开始。

//创建对象

Adafruit_MPR121 capA = Adafruit_MPR121();

Adafruit_MPR121 capB =Adafruit_MPR121();

Adafruit_MPR121 capC =Adafruit_MPR121();

Adafruit_MPR121 capD =Adafruit_MPR121();

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

uint16_t lasttouchedA = 0;

uint16_t currtouchedA = 0;

uint16_t lasttouchedB = 0;

uint16_t currtouchedB = 0;

uint16_t lasttouchedC = 0;

uint16_t currtouchedC = 0;

uint16_t lasttouchedD = 0;

uint16_t currtouchedD = 0;

uint16_t Touched;//变更为按下引脚的记录数组

uint16_t Released; //变更为松开引脚的记录数组

uint8_t rom1;//缓存数组

uint8_t rom2;

void setup() {   //启示代码

while (!Serial);      // 等待电脑打开串口

   Serial.begin(9600);

   Serial.println(” MPR121 纸片钢琴测试 “);

   MPR121Check();//检测I2C上的设备是否在线

   Keyboard.begin(); //Keyboard库初始化

   Keyboard.releaseAll(); //所有键盘按键松开

}

void MPR121Check() //MPR121初始化函数,检测是否在线

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

{

if(amount >= 1)

{

   if (!capA.begin(MPRA))

   {

    Serial.println(“MPRA not found, check wiring?”);

    delay(100);

   }

   else

   {

   Serial.println(“MPRA found!”);

   }

}

if(amount >= 2)

{

       if (!capB.begin(MPRB))

   {

    Serial.println(“MPRB not found, check wiring?”);

    delay(100);

   }

   else

   {

   Serial.println(“MPRB found!”);

   }

}

if(amount >=3)

{

   if (!capC.begin(MPRC))

   {

    Serial.println(“MPRC not found, check wiring?”);

    delay(100);

   }

   else

   {

   Serial.println(“MPRC found!”);

   }

}

if(amount >=4)

   {

   if (!capD.begin(MPRD))

   {

    Serial.println(“MPRD not found, check wiring?”);

    delay(100);

   }

   else

   {

   Serial.println(“MPRD found!”);

   }

   }

}

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

{

uint8_t n =0;    //标志符

uint8_t n2 =0;

uint8_t i=0;

if(amount >=1)

{

   currtouchedA = capA.touched();                                                                              

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

    // it if *is* touched and *wasnt* touched before, alert!

    if ((currtouchedA & _BV(i)) && !(lasttouchedA & _BV(i)) ) {                           

       Serial.print(i+1); Serial.println(” touched”);

       Touched = i+1;

       n++;

    }

    // if it *was* touched and now *isnt*, alert!

    if (!(currtouchedA & _BV(i)) && (lasttouchedA & _BV(i)) ) {

   Serial.print(i+1); Serial.println(” released”);

      Released = i+1;

      n2++;

    }

}

// reset our state

    lasttouchedA = currtouchedA;

}

if(amount >=2)

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

currtouchedB = capB.touched();

for ( i=0; i<12; i++) {

    // it if *is* touched and *wasnt* touched before, alert!

    if ((currtouchedB & _BV(i)) && !(lasttouchedB & _BV(i)) ) {

      Serial.print(i+12+1); Serial.println(” touched”);

      Touched = i+12+1;

      n++;

    }

   if (!(currtouchedB & _BV(i)) && (lasttouchedB & _BV(i)) ) {

       Serial.print(i+12+1); Serial.println(” released”);

       Released = i+12+1;

       n2++;

    }

}

// reset our state

lasttouchedB = currtouchedB;

}

if(amount >=3)

{

currtouchedC = capC.touched();

for ( i=0; i<12; i++) {

    // it if *is* touched and *wasnt* touched before, alert!

    if ((currtouchedC & _BV(i)) && !(lasttouchedC & _BV(i)) ) {

       Serial.print(i+12+12+1); Serial.println(” touched”);

      Touched = i+12+12+1;

      n++;

    }

   if (!(currtouchedC & _BV(i)) && (lasttouchedC & _BV(i)) ) {

      Serial.print(i+12+12+1); Serial.println(” released”);

      Released = i+12+12+1;

      n2++;

    }

}

// reset our state

    lasttouchedC = currtouchedC;

}

if(amount >=4)

{

currtouchedD = capD.touched();

for (i=0; i<12; i++) {

    // it if *is* touched and *wasnt* touched before, alert!

    if ((currtouchedD & _BV(i)) && !(lasttouchedD & _BV(i)) ) {

      Serial.print(i+12+12+12+1); Serial.println(” touched”);

      Touched = i+12+12+12+1;

      n++;

      }

    if (!(currtouchedC & _BV(i)) && (lasttouchedC & _BV(i)) ) {

      Serial.print(i+12+12+12+1); Serial.println(” released”);

      Released = i+12+12+12+1;

      n2++;

    }

}

// reset our state

lasttouchedD = currtouchedD;

}

}

void ToKeyboard() //将记录数组输出变成模拟键盘按键

{

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

uint8_t i2=0;

while(Released!= 0 || Touched!= 0)

{

   if(Touched!= 0)

   {

    rom1 = Touched;

    Keyboard.press(KeyBoard);

    i++;

   }

    delayMicroseconds(100);

   if(Released!= 0)

   {

    rom2 = Released;

   Keyboard.release(KeyBoard);

   i2++;

   }

}

rom1 = 0;

rom2 = 0;

Released=0;

Touched=0;

}

void loop() {                                                      //主函数直接调用相关函数进行循环

TandRcheck(); //检测引脚变更

ToKeyboard();//输出到键盘

}

程序逻辑:初始化 –> 循环体{ 1检测引脚是否变动 2数据转换输出到键盘 }
<纸片制作>
我们可以在某宝买到廉价的石墨导电涂料,一般二三十块钱一小罐。如果买导电笔和专业导电涂料价格会比较高。但是廉价的石墨导电涂料溶剂味道比较大,类似于油漆味,使用时请注意通风。我们尝试用酒精替换原有溶剂,但是效果并不理想。酒精不能乳化原有涂料,反而能起到类似于萃取的效果把原有溶剂析出。我们也会在此基础上继续进行深入研究,接下来就是画钢琴了,这里有个比较方便的方法就是先打印一个模板,裁剪完成后铺在纸上用刮玻璃的工具刷涂料,再起模板就好了。
源码和相关技术资料下载地址:https://office.boulkoo.com/desk/index.php?share/folder&user=1&sid=GQFhHC6T
<版权声明>如需转载到其他平台请联系BOULKOO官方。 Mail: mail@boulkoo.com

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

ForeverGeeker 发表于 2019-6-20 08:50

坐沙发,居然没有人来。。。。。。。。。。

whatmood 发表于 2019-7-14 16:36

曲高和寡,我来了。最近正在做类似的项目,参考一下此文,感谢

ForeverGeeker 发表于 2019-11-23 07:45

提示一点:如果要连接多个mpr121到arduino上,市面上大多数买得到的mpr121模块是固定了i2c的地址的,需要更改电路PCB上面的连线,通过自己飞线将ADD引脚按照我教程代码中的设置方式连接到VCC或SDA或SCL上才能改变地址。(如有问题可以加 BOULKOO公众号了解更多,或邮件至我们的官方邮箱寻求帮助)

E_200 发表于 2019-12-25 10:01

学习到了,另外请问楼主有了解MPR121的非接触检测吗?这款产品是可以在不触摸的情况下检测到手指的,但是要输值进寄存器,我尝试过但是失败了。不知楼主有无了解过

HereXianggeGe 发表于 2021-1-28 21:56

您好,请问能放出电路图吗?想知道mpr121连在arduino的哪个口上,谢谢楼主
页: [1]
查看完整版本: 【BOULKOO】Leonardo&Mpr121纸片钢琴