用毫米波雷达(人体存在检测)为辉光管时钟续命
开篇废话:
最近看了一本有趣的书,叫作《知鱼之乐》,是中国的一个独立学者——王东岳写的,作者现在还活着,七十多岁了,发型属于聪明绝顶大佬型,很有辨识度。《知鱼之乐》这本书视野很开阔,信息密度大,由许多篇小文章组成,每一篇短小精悍,提出的问题又引人深思。在中国这片务实的土地上,有这样一位务虚的人,实在难得,所以推荐给诸君。王东岳老师还写了一本《物演通论》,说是站在137亿年的尺度思考人类走向的问题,该书的核心理论是“递弱代偿”原理——越原始的物质、物种、文化,越具有奠基性、稳定性,这话什么意思呢?就把人和单细胞生物二者拎出来说,如果以“存在”“存活”的标准来看的话,人和单细胞生物的地位是同等的。人是从单细胞生物一路演化过来的,这就是“递”;单细胞生物在地球上生活了30多亿年,至今不灭,恐龙称霸地球1亿8千万年,灭绝了,哺乳动物至今有八、九千万年,人类至今三百到五百万年.....演化造成了越后演的生物,结构越复杂,对于外界的依赖越大,生存所需的条件越多也越苛刻,比如单细胞中的蓝绿藻,光能自养型(有光就能活、就能增殖),可忍受高温,冰冻,缺氧,干涸及高盐度,强辐射,再对比看看人类存活和增殖所需的条件,这是“弱”字;人类生存依赖的东西太多,相比于其他低等生物,存活是一件很困难的事情,所以,人类就用各种各样的能力和工具来“代为补偿”自身存在度的”弱“。人类这么努力的结果是什么呢,是跟单细胞生物站在同一根及格线上,这根线就是”存活“;此前众多的物种,要是它们自身存在度很”弱“,且”代偿“的能力不够强,二者之和,就够不到”存活“这根线,结果就是灭绝。以上我对于《物演通论》的一些浅薄的解读,里边有很多值得思考、、推敲、反驳的地方,如果这些浅薄看法能引出诸君的些许思考,也算是达到目的了。
书中的原图(看完后,心中不骂人的,是真的猛士):
我所理解的图:
此前我发过一次这个图,搭配食用,效果佳:
进入正题:
某一天,我手上拿到一个大杀器——df出的毫米波雷达(SEN0395)
资料打开一看
?终于有传感器可以检测人体存在,牛逼!再一看
WOC!!!这不就是我梦里的那个模块吗。
曾几何时,我被那个人体红外热释电(人体运动检测,人体不运动就撂挑子)给逼疯的时候,没有人来管管,没想到我也能熬到拨云见日的这一天。亲测这个模块很稳定,感应角度为正面的160度范围,长度上为9米,只要你站在这个范围内,就算一动不动,毫米波雷达模块也帮你抓得死死的,毕竟它可以检测睡眠状态下的人,静站着的人当然也不在话下。我为什么说这个毫米波雷达模块是大杀器?
1.有了这个模块,就不用再担心电池供电的装置电量不够了;有人的时候装置才工作,无人的时候装置就睡眠,大大降低了耗电,狠狠地拉长了续航时间。我之前做的那个《光年》时钟窗花就是挂在电池上,这个制作算是有救命稻草了。
2.有了这个模块,很多使用寿命不长的器件,就被疯狂地续上了命。比如辉光管、荧光管等老一代的数码显示器件,跟它简直是绝配。辉光管和荧光管被淘汰有很多原因,其中一个便是使用寿命问题,国产辉光管寿命是1000h(仅41天),苏联辉光管寿命是5000h(仅208天),荧光管寿命稍长,但也不过是1万小时左右,它俩现在还被人们念着,是因为它们点亮后很好看。
动漫《命运石之门》中,用辉光管做的世界线变动率探测仪(我只能感慨剧组真有钱啊,这个尺寸的辉光管,一根至少500RMB+啊,好家伙,直接用了8根来做这个道具)
说到这里,我不得不吐槽一下辉光管现在卖这么贵,都没人出来管管吗?一个鸽子蛋大小的管子,卖60RMB啊。受不了辉光管的信仰光辉啊,手还是得剁啊。咬牙买了4根回来,型号是苏联产的IN12A,做个时钟吧,穷屌丝就不要想着时钟上还能读秒了,用4根管子来表达小时、分钟已经是福报了
好了,这次的两个主角到齐了,开始搞事情了
制作思路:
1.利用毫米波雷达的检测人体功能。当有人的时候,辉光管才会被点亮,显示时钟的读数;当没有人的时候,辉光管就不必亮起(IN12A辉光管里充有水银蒸汽,所以在点亮时,数字周边会有白蓝色光晕);
2.使用esp32-e(DFR0654)的作为控制器,使用其上的wifi通信功能,获取网络时间,这里就不必额外使用时钟模块了;
3.由于需要用到的引脚较多(每个辉光管需要4个管脚控制,仅辉光管部分就需要16个),而esp32-e上的io口数量有限,这里用到一个使用IIC协议扩展出16位io口的模块(DFR0626);
4.辉光管的工作电压为直流170v左右(电流不大),这里直接使用现成的电源模块(MC34063方案),输入12v,可提供170v&5v的两种电压输出;
5.辉光管的驱动芯片使用的是俄罗斯的K155ID1,用4个管脚控制数字0-9这10个状态,1个芯片控制1个辉光管;
6.小时和分钟之间使用一种叫做INS-1的管子,作为冒号;
准备材料:
1.毫米波雷达(人体存在检测)
2.IN12A辉光管 x4
3.ESP32-E控制器(焊好排母)
4.ESP32-E扩展板
5.IIC 16位数字IO扩展模块
6.12v电源转170v&5v输出模块
7.俄罗斯K155ID1辉光管专用驱动 x4
8.INS-1点状辉光管 x2
9.IN12A辉光管管座 x4
10.迷你面包板 x3
11.公母头跳线 21cm*30根 x3
12.20k电阻 x4 100k电阻 x2
13.A42高压NPN三极管(TO-92封装) x2
14.12v1A以上的适配器(DC2.1公头)
补充描述:20k电阻需要串联到辉光管的阳极管脚(也就是接170v电压的那个脚);100k电阻需要串联到INS-1的阳极管脚(也是170v那个管脚);A42三极管的作用为可用ESP32-E的PWM管脚控制INS-1的渐明渐暗;
连线图:
补充:
INS-1的正极为连接小圆柱(亮)的这个脚,负极为连接大圆柱(暗)的这个脚
制作:
手边居然有个超级合适的纸盒子,我去,这是冥冥中......?
整起来,这次就不用焊接了,直接杜邦线指哪儿插哪儿,舒适极了
其他器件通过打胶固定
程序烧录:
先安装如下几个库文件
毫米波雷达
IIC 16位数字IO扩展模块
Time
Timezone
要知道ESP32-E烧录程序的方法,然后将下边的程序打开,修改为自己的wifi名称和密码后,烧录到控制器里
#include <DFRobot_MCP23017.h>
#include <Arduino.h>
#include <HardwareSerial.h>
#include<stdlib.h>
#include <TimeLib.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiUdp.h>
#include "DFRobot_mmWave_Radar.h"
#ifdef ARDUINO_AVR_UNO
SoftwareSerial Serial1( 2, 5 ); //RX, TX
#endif
static const char ntpServerName[] = "ntp.aliyun.com";
const int timeZone = 8;
DFRobot_mmWave_Radar sensor(&Serial2);
WiFiUDP Udp;
unsigned int localPort = 8888;
time_t getNtpTime();
WiFiMulti WiFiMulti;
DFRobot_MCP23017 mcp(Wire, /*addr =*/0x27);//constructor, change the Level of A2, A1, A0 via DIP switch to revise I2C address within 0x20~0x27
// the number of the LED pin
const int ledPin0 = 4;
const int ledPin1 = 12;
// setting PWM properties
const int freq = 5000;
const int ledChannel0 = 0;
const int ledChannel1 = 1;
const int resolution = 8;
time_t prevDisplay = 0;
int A_value = 0x00;
int B_value = 0x00;
int count = 60;
boolean state = 0;
int val = 0;
void setup(void)
{
Serial.begin(115200);
Serial2.begin(115200);
delay(10);
// configure LED PWM functionalitites
ledcSetup(ledChannel0, freq, resolution);
ledcSetup(ledChannel1, freq, resolution);
// attach the channel to the GPIO to be controlled
ledcAttachPin(ledPin0, ledChannel0);
ledcAttachPin(ledPin1, ledChannel1);
pinMode(4,OUTPUT);
pinMode(12,OUTPUT);
pinMode(2, OUTPUT);//D9 ledpin
sensor.factoryReset(); //恢复出厂设置
sensor.DetRangeCfg(0, 9); //设置感应距离,最远为9m
sensor.OutputLatency(0, 0); //设置输出延时
WiFiMulti.addAP("wif名称", "密码");
while(WiFiMulti.run() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
delay(2000);
Serial.print( "IP number assigned by DHCP is " );
Serial.println( WiFi.localIP() );
Serial.println( "Starting UDP" );
Udp.begin( localPort );
Serial.print( "Local port: " );
//Serial.println( Udp.localPort() );
Serial.println( "waiting for sync" );
setSyncProvider( getNtpTime );
setSyncInterval( 300 );
while(mcp.begin() != 0){
Serial.println("Initialization of the chip failed, please confirm that the chip connection is correct!");
delay(1000);
}
/*pinMode function is used to set the pin mode of the module
Parameter pin, the available parameter is shown below:
eGPA0 eGPA1 eGPA2 eGPA3 eGPA4 eGPA5 eGPA6 eGPA7 eGPA
0 1 2 3 4 5 6 7
eGPB0 eGPB1 eGPB2 eGPB3 eGPB4 eGPB5 eGPB6 eGPB7 eGPB
8 9 10 11 12 13 14 15
Parameter mode, can be set to: INPUT, OUTPUT, INPUT_PULLUP mode (internal 100KΩ pull-up resistor)
*/
//mcp.pinMode(/*pin = */mcp.eGPA7, /*mode = */OUTPUT);
/*Set all Group GPIOA pins to output*/
mcp.pinMode(/*pin = */mcp.eGPA, /*mode = */OUTPUT);
mcp.pinMode(/*pin = */mcp.eGPB, /*mode = */OUTPUT);
Serial.println("Pin output high level!");
/*digitalWrite function is used to make the pin output HIGH or LOW. The pin needs to be set to output mode before using this function.
Designate a pin on the IO expansion board; parameter pin, the available parameter is shown below:
eGPA0 eGPA1 eGPA2 eGPA3 eGPA4 eGPA5 eGPA6 eGPA7 eGPA
0 1 2 3 4 5 6 7
eGPB0 eGPB1 eGPB2 eGPB3 eGPB4 eGPB5 eGPB6 eGPB7 eGPB
8 9 10 11 12 13 14 15
*/
//mcp.digitalWrite(/*pin = */mcp.eGPA7, /*level = */HIGH);
/*Set GPIOIA0-GPIOIA7 to high*/
//mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0xFF);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0xff);
Serial.println("Pin output low level!");
//mcp.digitalWrite(/*pin = */mcp.eGPA7, /*level = */LOW);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0xff);
for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){
// changing the LED brightness with PWM
ledcWrite(ledChannel0, dutyCycle);
ledcWrite(ledChannel1, dutyCycle);
delay(5);
}
for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
// changing the LED brightness with PWM
ledcWrite(ledChannel0, dutyCycle);
ledcWrite(ledChannel1, dutyCycle);
delay(5);
}
for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){
// changing the LED brightness with PWM
ledcWrite(ledChannel0, dutyCycle);
ledcWrite(ledChannel1, dutyCycle);
delay(5);
}
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x00);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x00);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x11);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x11);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x22);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x22);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x33);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x33);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x44);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x44);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x55);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x55);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x66);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x66);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x77);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x77);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x88);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x88);
delay(100);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0x99);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0x99);
delay(100);
Serial.println("nixie init on!");
}
void loop(void)
{
val = sensor.readPresenceDetection();
Serial.print("val:");
Serial.println(val);
if(val == 1){
showtime();
if(state == 0){
ledcWrite(ledChannel0, 255);
ledcWrite(ledChannel1, 255);
state = 1;
delay(500);
}
if(state == 1){
ledcWrite(ledChannel0, 0);
ledcWrite(ledChannel1, 0);
state = 0;
delay(500);
}
}
else{
ledcWrite(ledChannel0, 0);
ledcWrite(ledChannel1, 0);
state = 0;
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */0xff);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */0xff);
}
if( timeStatus() != timeNotSet ) {
if( now() != prevDisplay ) {
Serial.println("time update!");
prevDisplay = now();
}
}
// for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){
// // changing the LED brightness with PWM
// ledcWrite(ledChannel0, dutyCycle);
// ledcWrite(ledChannel1, dutyCycle);
// delay(3);
// }
// for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
// // changing the LED brightness with PWM
// ledcWrite(ledChannel0, dutyCycle);
// ledcWrite(ledChannel1, dutyCycle);
// delay(3);
// }
}
void nixie_WriteOneChar(int pos,int num){
if(pos == 3){
switch(num){
case 0:B_value = B_value&0xf0;B_value = B_value|0x00;break;
case 1:B_value = B_value&0xf0;B_value = B_value|0x01;break;
case 2:B_value = B_value&0xf0;B_value = B_value|0x02;break;
case 3:B_value = B_value&0xf0;B_value = B_value|0x03;break;
case 4:B_value = B_value&0xf0;B_value = B_value|0x04;break;
case 5:B_value = B_value&0xf0;B_value = B_value|0x05;break;
case 6:B_value = B_value&0xf0;B_value = B_value|0x06;break;
case 7:B_value = B_value&0xf0;B_value = B_value|0x07;break;
case 8:B_value = B_value&0xf0;B_value = B_value|0xf8;break;
case 9:B_value = B_value&0xf0;B_value = B_value|0x09;break;
case 10:B_value = B_value&0xf0;B_value = B_value|0x0f;break;
default:break;
}
}
if(pos == 4){
switch(num){
case 0:B_value = B_value&0x0f;B_value = B_value|0x00;break;
case 1:B_value = B_value&0x0f;B_value = B_value|0x10;break;
case 2:B_value = B_value&0x0f;B_value = B_value|0x20;break;
case 3:B_value = B_value&0x0f;B_value = B_value|0x30;break;
case 4:B_value = B_value&0x0f;B_value = B_value|0x40;break;
case 5:B_value = B_value&0x0f;B_value = B_value|0x50;break;
case 6:B_value = B_value&0x0f;B_value = B_value|0x60;break;
case 7:B_value = B_value&0x0f;B_value = B_value|0x70;break;
case 8:B_value = B_value&0x0f;B_value = B_value|0x80;break;
case 9:B_value = B_value&0x0f;B_value = B_value|0x90;break;
case 10:B_value = B_value&0x0f;B_value = B_value|0xf0;break;
default:break;
}
}
if(pos == 1){
switch(num){
case 0:A_value = A_value&0xf0;A_value = A_value|0x00;break;
case 1:A_value = A_value&0xf0;A_value = A_value|0x01;break;
case 2:A_value = A_value&0xf0;A_value = A_value|0x02;break;
case 3:A_value = A_value&0xf0;A_value = A_value|0x03;break;
case 4:A_value = A_value&0xf0;A_value = A_value|0x04;break;
case 5:A_value = A_value&0xf0;A_value = A_value|0x05;break;
case 6:A_value = A_value&0xf0;A_value = A_value|0x06;break;
case 7:A_value = A_value&0xf0;A_value = A_value|0x07;break;
case 8:A_value = A_value&0xf0;A_value = A_value|0x08;break;
case 9:A_value = A_value&0xf0;A_value = A_value|0x09;break;
case 10:A_value = A_value&0xf0;A_value = A_value|0x0f;break;
default:break;
}
}
if(pos == 2){
switch(num){
case 0:A_value = A_value&0x0f;A_value = A_value|0x00;break;
case 1:A_value = A_value&0x0f;A_value = A_value|0x10;break;
case 2:A_value = A_value&0x0f;A_value = A_value|0x20;break;
case 3:A_value = A_value&0x0f;A_value = A_value|0x30;break;
case 4:A_value = A_value&0x0f;A_value = A_value|0x40;break;
case 5:A_value = A_value&0x0f;A_value = A_value|0x50;break;
case 6:A_value = A_value&0x0f;A_value = A_value|0x60;break;
case 7:A_value = A_value&0x0f;A_value = A_value|0x70;break;
case 8:A_value = A_value&0x0f;A_value = A_value|0x80;break;
case 9:A_value = A_value&0x0f;A_value = A_value|0x90;break;
case 10:A_value = A_value&0x0f;A_value = A_value|0xf0;break;
default:break;
}
}
}
void showtime() {
int Hour = hour();
int Min = minute();
int Sec = second();
int HourHigh,HourLow,MinHigh,MinLow,SecHigh,SecLow;
Serial.print(Hour);
Serial.print(":");
Serial.print(Min);
Serial.print(":");
Serial.println(Sec);
HourHigh=Hour/10;
if(HourHigh){
nixie_WriteOneChar(4, HourHigh);
}
else{
nixie_WriteOneChar(4,10);
}
HourLow=Hour%10;
nixie_WriteOneChar(3, HourLow);
MinHigh=Min/10;
nixie_WriteOneChar(2, MinHigh);
MinLow=Min%10;
nixie_WriteOneChar(1, MinLow);
mcp.digitalWrite(/*pin = */mcp.eGPA, /*Port Value = */A_value);
mcp.digitalWrite(/*pin = */mcp.eGPB, /*Port Value = */B_value);
}
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while( Udp.parsePacket() > 0 ) ; // discard any previously received packets
Serial.println( "Transmit NTP Request" );
// get a random server from the pool
WiFi.hostByName( ntpServerName, ntpServerIP );
Serial.print( ntpServerName );
Serial.print( ": " );
Serial.println( ntpServerIP );
sendNTPpacket( ntpServerIP );
uint32_t beginWait = millis();
while( millis() - beginWait < 1500 ) {
int size = Udp.parsePacket();
if( size >= NTP_PACKET_SIZE ) {
Serial.println( "Receive NTP Response" );
Udp.read( packetBuffer, NTP_PACKET_SIZE ); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = ( unsigned long )packetBuffer[40] << 24;
secsSince1900 |= ( unsigned long )packetBuffer[41] << 16;
secsSince1900 |= ( unsigned long )packetBuffer[42] << 8;
secsSince1900 |= ( unsigned long )packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println( "No NTP Response :-(" );
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket( IPAddress &address )
{
// set all bytes in the buffer to 0
memset( packetBuffer, 0, NTP_PACKET_SIZE );
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket( address, 123 ); //NTP requests are to port 123
Udp.write( packetBuffer, NTP_PACKET_SIZE );
Udp.endPacket();
}
完成:
插上12v的适配器