云天 发表于 2022-2-12 23:26:18

FireBeetle手机可控POV显示器

本帖最后由 云天 于 2022-2-13 09:49 编辑







之前的一个项目,当时没有做成功。用 FireBeetle BLE4.1 低功耗蓝牙控制器 、霍尔传感器、电机和一排 LED 制作的视觉暂留POV显示器。什么是POV?
人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,也被称为是“视觉暂留”。人眼每秒处理的图像不超过 10-12 个。当一系列图像很快的连续性的滚动时,就会让我们产生幻觉,感觉画面在动。这也是制作动画电影的基本原则。项目制作过程:
【电机】
电机是从旧录音机上拆下来的,应该是用来转动磁带的。




根据上面的标识可知:
电机额定电压9V,转速2400rpm,逆时针方向旋转(CCW),可通过改变通电正负极,改变旋转方向。


【控制电机】
利用Arduino+编码器+L289N电机驱动板来控制电机转速。
编码器接在A0引脚,4,5号引脚接LED89N电机驱动板逻辑输入引脚,3号引脚接LED89N电机驱动板通道A使能引脚。

【POV控制器】
FireBeetle BLE4.1 低功耗蓝牙控制器 (Arduino Pro Mini 3.3V),支持锂电池接口:3.7 V锂电池。

D2——D9引脚接LED灯。



【霍尔传感器】
霍尔传感器接FireBeetle控制器的D10引脚。





调整好霍尔传感与磁铁的高度。

【安装 LED】

1、剪取一块洞洞板
2、安装 LED 灯,我使用八个白色的 LED 灯。
3、将所有 LED 灯的负极端口都焊接到洞洞板上。
4、添加电阻,防止 LED 灯因电压过高而烧坏。我使用 220ohm 的电阻,将每个电阻焊接到每个 LED 灯的正极。
5、将跳线电缆连接到电阻的另一个脚上。
电阻常识:
五色环电阻
五个色环电阻的识别:第一、二、三环分别代表三位有效数的阻值;第四环代表倍率;第五环代表误差。 如果第五条色环为黑色,一般用来表示为绕线电阻器,第五条色环如为白色,一般用来表示为保险丝电阻器。如果电阻体只有中间一条黑色的色环,则代表此电阻为零欧姆电阻。
例:红 红 黑 棕 金
其电阻为 220×10^1=2.2KΩ 误差为±5%
第一色环是百位数,第二色环是十位数,
第三色环是个位数,第四色环是应乘颜色次幂,
第五色环是误差率。
首先,从电阻的底端,找出代表公差精度的色环,金色的代表5%,银色的代表10%。上例中,最末端色环为金色,故误差率为5%。再从电阻的另一端,找出第一条、第二条色环,读取其相对应的数字,上例中,前三条色环都为红红黑,故其对应数字为红2、红2、黑0,其有效数是220。再读取第四条倍数色环,棕1。所以,我们得到的阻值是220×10^1=2.2KΩ。即阻值在2090-2310之间都是好的电阻。 如果第四条倍数色环为金色,则将有效数乘以0.1。如果第四条倍数色环为银色,则乘以0.01。

【测试程序】

程序代码中进行了详细注释




显示“HELLO WORLD”

int asc;
int letterSpace;
int delayTime;

void setup() {
// initialize digital pinoutput.
Serial.begin(9600);
for(unsigned char i=2;i<10;i++){//定义2-9号,共8个引脚为输出
    pinMode(i, OUTPUT);
}
   pinMode(10, INPUT);//10号引脚为输入,接霍尔传感器
for(unsigned char i=2;i<10;i++){//初始化8个引脚为低电平
   digitalWrite(LED_BUILTIN, LOW);
}
asc=toascii('A');//获取大写字母A的Ascci值
letterSpace=4;//定义字母之间的间距(毫秒)
delayTime=500;//定义点出现的时间(微秒)
}
//字母十六进制点阵数组
int Mychar={
{0xff,0xe7,0xe7,0xdb,0x81,0xbd,0x7e,0xff},//A
{0xff,0xc1,0xbd,0xc1,0xbd,0x7d,0x81,0xff},//B
{0xff,0xc3,0xbd,0xfd,0xfd,0x79,0x83,0xff},//C
{0xff,0xc1,0x9d,0xbd,0xbd,0x9d,0xc1,0xff},//D
{0xff,0x81,0xfd,0x81,0xfd,0xfd,0x81,0xff},//E
{0xff,0x81,0xfd,0x81,0xfd,0xfd,0xfd,0xff},//F
{0xff,0xc3,0xb9,0xfd,0x8d,0xb9,0x83,0xff},//G
{0xff,0xbd,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//H
{0xff,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//I
{0xff,0xbf,0xbf,0xbf,0xbf,0xbd,0xc3,0xff},//J
{0xff,0x9d,0xed,0xf5,0xe9,0xdd,0xbd,0xff},//k
{0xff,0xfd,0xfd,0xfd,0xfd,0xfd,0x81,0xff},//L
{0xff,0x99,0x99,0x99,0xa5,0xa5,0xa5,0xff},//M
{0xff,0xb9,0xb9,0xb5,0xad,0x9d,0x9d,0xff},//N
{0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff},//O
{0xff,0xc1,0xbd,0xbd,0xc1,0xfd,0xfd,0xff},//P
{0xff,0xc3,0x99,0xbd,0xbd,0xa9,0xc3,0xbf},//Q
{0xff,0xc1,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//R
{0xff,0xc3,0xbd,0xc3,0x3f,0x7d,0x83,0xff},//S
{0xff,0x80,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//T
{0xff,0xbd,0xbd,0xbd,0xbd,0xbd,0xc3,0xff},//U
{0xff,0x7e,0xbd,0xdd,0xdb,0xe3,0xf7,0xff},//V
{0xff,0xff,0xff,0x66,0xa6,0x99,0x99,0xff},//W
{0xff,0xdd,0xeb,0xf7,0xe3,0xd9,0xbc,0xff},//X
{0xff,0xbc,0xd9,0xe3,0xf7,0xf7,0xf7,0xff},//Y
{0xff,0x80,0xdf,0xe7,0xfb,0xfd,0x80,0xff} //Z
};
int pinState=0;
void printLetter(String s){//显示字符串函数

for(int m=0;m<s.length();m++){
    char s1=s.charAt(m);    //取字符
    int xh=toascii(s1)-asc;//与字符'A'的Ascii码取差,获取在数组中的位置
   
    for(int k=0;k<8;k++){//8*8点阵
   for(int b=0;b<8;b++){
       pinState=!bitRead(Mychar,b);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
       digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
   }
    delayMicroseconds(delayTime);//点间隔时间
   }
   delay(letterSpace);//字母间隔时间
}
}

void loop() {
   boolean d=digitalRead(10);//获取霍尔传感器状态
   
   if(!d){//有磁时,为低电平
   printLetter("HELLOWORLD");

   }
   

}
【App Inventor2与FireBeetle BLE4.1低功耗蓝牙连接】
为获取POV旋转一周所需时间,花了一天时间,搞定了“App Inventor2与FireBeetle BLE4.1低功耗蓝牙连接”。

1、使用APP Inventor2 WxBit中自带的“低功耗蓝牙模块”。

2、获取蓝牙地址
扫描蓝牙设备,获取蓝牙地址



名称为:Bluno2
3、获取监听服务及特征UUID
UUID是“Universally Unique Identifier”的简称,通用唯一识别码的意思。对于蓝牙设备,每个服务都有通用、独立、唯一的UUID与之对应。也就是说,在同一时间、同一地点,不可能有两个相同的UUID标识的不同服务。


服务UUID有四个,FireBeetle BLE4.1低功耗蓝牙,监听服务使用“dfb0”。



特征UUID有16个,与服务“dfb0”对应的有3个,第一个为监听特征UUID(所有中第14个)。

收到信息后,在调试窗口显示。

4、FireBeetle BLE4.1低功耗蓝牙测试程序
Serial.begin(115200); 波特率一定要使用115200,我试了9600,APP Inventor2接收不到信息。
char num_str;
void setup() {

Serial.begin(115200); //initial the Serial
}

void loop(){

//if(Serial.available()){
//Serial.write(Serial.read()); //send what has been received
//Serial.println(); //print line feed character
//}
itoa(564,num_str,10);
Serial.write(num_str);

delay(2000);
}【获取旋转一周时间】



当前程序使用micros();获取系统运行时间,为微秒,如电机旋转过慢,时间数据可能会溢出。后期改成millis();为毫秒。

int asc;
int letterSpace;
int delayTime;
char num_str;//保存转换后时间差的数字字符串
int time_pre=0;
void setup() {
// initialize digital pinoutput.
Serial.begin(115200);
for(unsigned char i=2;i<10;i++){//定义2-9号,共8个引脚为输出
    pinMode(i, OUTPUT);
}
   pinMode(10, INPUT);//10号引脚为输入,接霍尔传感器
for(unsigned char i=2;i<10;i++){//初始化8个引脚为低电平
   digitalWrite(LED_BUILTIN, LOW);
}
asc=toascii('A');//获取大写字母A的Ascci值
letterSpace=4;//定义字母之间的间距(毫秒)
delayTime=500;//定义点出现的时间(微秒)
time_pre=micros();
}
//字母十六进制点阵数组
int Mychar={
{0xff,0xe7,0xe7,0xdb,0x81,0xbd,0x7e,0xff},//A
{0xff,0xc1,0xbd,0xc1,0xbd,0x7d,0x81,0xff},//B
{0xff,0xc3,0xbd,0xfd,0xfd,0x79,0x83,0xff},//C
{0xff,0xc1,0x9d,0xbd,0xbd,0x9d,0xc1,0xff},//D
{0xff,0x81,0xfd,0x81,0xfd,0xfd,0x81,0xff},//E
{0xff,0x81,0xfd,0x81,0xfd,0xfd,0xfd,0xff},//F
{0xff,0xc3,0xb9,0xfd,0x8d,0xb9,0x83,0xff},//G
{0xff,0xbd,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//H
{0xff,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//I
{0xff,0xbf,0xbf,0xbf,0xbf,0xbd,0xc3,0xff},//J
{0xff,0x9d,0xed,0xf5,0xe9,0xdd,0xbd,0xff},//k
{0xff,0xfd,0xfd,0xfd,0xfd,0xfd,0x81,0xff},//L
{0xff,0x99,0x99,0x99,0xa5,0xa5,0xa5,0xff},//M
{0xff,0xb9,0xb9,0xb5,0xad,0x9d,0x9d,0xff},//N
{0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff},//O
{0xff,0xc1,0xbd,0xbd,0xc1,0xfd,0xfd,0xff},//P
{0xff,0xc3,0x99,0xbd,0xbd,0xa9,0xc3,0xbf},//Q
{0xff,0xc1,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//R
{0xff,0xc3,0xbd,0xc3,0x3f,0x7d,0x83,0xff},//S
{0xff,0x80,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//T
{0xff,0xbd,0xbd,0xbd,0xbd,0xbd,0xc3,0xff},//U
{0xff,0x7e,0xbd,0xdd,0xdb,0xe3,0xf7,0xff},//V
{0xff,0xff,0xff,0x66,0xa6,0x99,0x99,0xff},//W
{0xff,0xdd,0xeb,0xf7,0xe3,0xd9,0xbc,0xff},//X
{0xff,0xbc,0xd9,0xe3,0xf7,0xf7,0xf7,0xff},//Y
{0xff,0x80,0xdf,0xe7,0xfb,0xfd,0x80,0xff} //Z
};
int pinState=0;
void printLetter(String s){//显示字符串函数

for(int m=0;m<s.length();m++){
    char s1=s.charAt(m);    //取字符
    int xh=toascii(s1)-asc;//与字符'A'的Ascii码取差,获取在数组中的位置
   
    for(int k=0;k<8;k++){//8*8点阵
   for(int b=0;b<8;b++){
       pinState=!bitRead(Mychar,b);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
       digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
   }
    delayMicroseconds(delayTime);//点间隔时间
   }
   delay(letterSpace);//字母间隔时间
}
}

void loop() {
   boolean d=digitalRead(10);//获取霍尔传感器状态
   
   if(!d){//有磁时,为低电平
   itoa(micros()-time_pre,num_str,10);
   time_pre=micros();
   Serial.write(num_str);
   printLetter("HELLOWORLD");

   }
   

}

【发送显示内容】
通过APP发送显示内容

String show_str;
String comdata;void loop() {
   boolean d=digitalRead(10);//获取霍尔传感器状态
   while(Serial.available()>0)
   {
    comdata+=char(Serial.read());
    delay(2);
   }
   if(comdata.length()>0)
   {
    show_str=comdata;
    comdata="";
   }
   if(!d){//有磁时,为低电平
   itoa(micros()-time_pre,num_str,10);
   time_pre=millis();//micros();为微秒,millis();为毫秒
   //Serial.write(num_str);
   printLetter(show_str);

   }
   

}



因字母编码和程序读取的顺序,显示的内容看着不太习惯。调整一下程序如下:
void printLetter(String s){//显示字符串函数

for(int m=s.length()-1;m>=0;m--){
    char s1=s.charAt(m);    //取字符
    int xh=toascii(s1)-asc;//与字符'A'的Ascii码取差,获取在数组中的位置
   
    for(int k=7;k>=0;k--){//8*8点阵
   for(int b=0;b<8;b++){
       pinState=!bitRead(Mychar,k);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
       digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
   }
    delayMicroseconds(delayTime);//点间隔时间
   }
   delay(letterSpace);//字母间隔时间
}
}读取顺序调整。




【显示时间】1、用电子表格画点阵图,再转换成16进制形式。

对应:0,1,2,3,4,5,6,7,8,9,:字符共11个
{{0xE7,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xE7},//0
{0xE7,0xF7,0xF7,0xF7,0xF7,0xF7,0xF7,0xE3},//1
{0xE7,0xDB,0xFB,0xF7,0xEF,0xDF,0xDB,0xC3},//2
{0xC7,0xFB,0xFB,0xC7,0xC7,0xFB,0xFB,0xC7},//3
{0xF7,0xEF,0xDF,0xD7,0xC3,0xF7,0xF7,0xF7},//4
{0xC3,0xDF,0xDF,0xC7,0xFB,0xFB,0xF7,0xCF},//5
{0xF3,0xEF,0xDF,0xC7,0xDB,0xDB,0xDB,0xE7},//6
{0xC3,0xFB,0xFB,0xFB,0xFB,0xFB,0xFB,0xFB},//7
{0xE7,0xDB,0xDB,0xE7,0xE7,0xDB,0xDB,0xE7},//8
{0xE7,0xDB,0xDB,0xDB,0xE3,0xFB,0xF7,0xCF},//9
{0xC7,0xC7,0xC7,0xFF,0xFF,0xC7,0xC7,0xC7}};//:
void printTime(String s){//显示字符串函数

for(int m=s.length()-1;m>=0;m--){
    char s1=s.charAt(m);    //取字符
    int xh=0;
    if(s1==':'){
      xh=10;
    }
    else{
      xh=toascii(s1)-toascii('0');//与字符'0'的Ascii码取差,获取在数组中的位置
    }

   
    for(int k=0;k<8;k++){//8*8点阵
   for(int b=0;b<8;b++){
       pinState=!bitRead(Mychar,k);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
       digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
   }
    delayMicroseconds(delayTime);//点间隔时间
   }
   delay(letterSpace);//字母间隔时间
}
}
【显示视频】
https://www.bilibili.com/video/BV1zY411L71J?share_source=copy_web
【知识扩展】
测试小功率的LED灯珠工作电压:1、需要使用的万用表,万用表选择二极管档。(其测试电流约1mA)2、观测时,长脚为正。用表测时如图:若表有读数,则此时红表笔所测端为二极管的正极,同时发光二极管会发光。3、若没有读数,则将表笔反过来再测一次;如果两次测量都没有示数,表示此发光二极管已经损坏。

汤果 发表于 2022-2-13 06:48:58

好高级的样子

云天 发表于 2022-3-6 17:25:59

https://tanqin.blog.csdn.net/article/details/108151033?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2.pc_relevant_default&utm_relevant_index=5
全面且简单明了的蓝牙服务及UUID介绍

水晶 发表于 2022-3-10 11:59:35

666666666666666666

QQQQQQQ 发表于 2022-3-11 20:42:14

厉害厉害

QQQQQQQ 发表于 2022-3-11 20:45:56

厉害厉害

QQQQQQQ 发表于 2022-3-11 20:51:51

厉害厉害

QQQQQQQ 发表于 2022-3-11 20:52:52

厉害厉害

QQQQQQQ 发表于 2022-3-12 11:05:58

厉害厉害

发表于 2022-4-13 11:18:12

汤果 发表于 2022-2-13 06:48
好高级的样子

嗯,奇异博士的那玩意儿用这个同样也可以实现
页: [1]
查看完整版本: FireBeetle手机可控POV显示器