魔改Zippo打火机为番茄计时器

好久没写和开源硬件有关的内容了,绞尽脑汁想了半天,感觉还不如自己动手做个什么。拿出年初在Adafruit网站9.95美刀买的硬币大小的Arduino Gemma单片机琢磨了一会儿,觉得这个大小再加上颗3V纽扣电池,应该可以塞进我珍藏的一个Zippo打火机里,做个有趣的项目。

于是赶紧翻箱倒柜,把那个火机找了出来。说起来包爸也曾是个烟民,九几年白猴君回国探亲,送了我这个经典款的Zippo打火机。结婚后包爸就不再抽烟了,这个火机也束之高阁。一晃八九年过去,今天拿出来再次把玩,感觉机械部件运转灵活,声音清脆,闲放着真是可惜了。又回想了一会儿以前用这个火机打着火苗的情形,灵感有了:决定把火机内部储油的棉花取出,放Gemma和纽扣电池,灯芯换成LED,做成一个番茄工作法的计时器。

所谓番茄工作法,是一种旨在提高工作效率的方法,即每次只选择一个特定的工作项目,专注持续工作25分钟,休息5分钟,然后再工作25分钟,休息5分钟,以此循环……

所需材料

  • Zippo打火机
  • Gemma单片机
  • 发光二极管LED
  • 220欧姆电阻
  • CR2032纽扣电池
  • GPS-14B常闭磁控开关
  • 小块磁铁
  • 5类网线
  • 电烙铁、焊锡、快干胶、剪线器等工具

电路图

代码

代码分为几个部分:

  • 起始部分定义了一些常量和变量,currentTime和previousTime变量存储的是当前的时间(毫秒数),按理说应定义为unsigned long类型,否则一直增长下去(5天左右)会溢出。但是因为需要和常量workTime,restTime做除法,得出小于1的小数做判断,按Processing语言的限制,只能使用float类型。好在这个程序应该不会连续用这么长时间,就算是忘了关,纽扣电池也不见得能撑那么长久,所以倒不至于出问题;
  • setup()定义了硬件配置;
  • loop()是循环执行的主程序,在简短(0.5秒)间隔后,连续调用work()和rest()两个子程序;
  • work()是工作时段,只要在workTime常量规定的时间内,就会执行让LED进行呼吸效果的闪烁;
  • calculateBreathLength()将整个workTime平均分为5份,根据当前时间算出现在所处的位置,然后返回呼吸长度时间(秒数)。越接近(工作时段)结束时,呼吸频率越快(时间短);
  • breath()是LED呼吸灯效果,接受变量l为参数进行调用,在此参数给定的秒数内,分十级亮度,逐渐明亮再逐渐熄灭;
  • rest()是休息时间,在restTime常量规定的时间内,让LED闪亮;
  • blink()让LED以固定亮度闪亮和熄灭。

代码如下:

const float workTime=1500000;  
const float restTime=300000;  
int ledPin=0;  
const int breathInterval[5]={5, 4, 3, 2, 1};  
float previousTime=0;  
float currentTime;

void setup() {  
  pinMode(ledPin, OUTPUT);
}

void loop(){  
  delay(500);  
  work();
  rest();
}

void work() {  
  //flash led for working time  
  currentTime=millis();
  while((currentTime-previousTime)<=workTime) {
    breath(calculateBreathLength());
    currentTime=millis();
  }
  previousTime=currentTime;
}

void breath(int l) {  
  //led breath  
  int breathI=l*1000/2/10;
  for (int i=0;i<=240;i=i+24) {
    analogWrite(ledPin, i);
    delay(breathI);
  }
  for (int i=240;i>=0;i=i-24) {
    analogWrite(ledPin, i);
    delay(breathI);
  }
}

int calculateBreathLength() {  
  //calculate every breath's lasting seconds
  float s=(currentTime-previousTime)/workTime;  
  int stage;
  if (s>=0.8) {
    stage=4;
  } else {
    if (s>=0.6) {
      stage=3;
    } else {
      if (s>=0.4) {
        stage=2;
      } else {
        if (s>=0.2) {
          stage=1;
        } else {
          stage=0;
        }
      }
    }
  }
  return breathInterval[stage];
}

void rest() {  
  //flash led for resting time
  delay(500);
  currentTime=millis();
  while((currentTime-previousTime)<=restTime) {
    blink();
    currentTime=millis();
  }
  previousTime=currentTime;
}

void blink() {  
  //blink led
  analogWrite(ledPin, 0);
  delay(1000);
  analogWrite(ledPin, 120);
  delay(1000);
}

因为Gemma在Arduino IDE中无法使用串口监视器进行输出,程序一旦出问题,都没法输出一下变量进行debug。所以代码调试我是在旧的Arduino Duemilanove板上进行的,没问题后再重新编译传输到Gemma里。

硬件组装

  1. 打开Zippo火机,拆下吸收火机油的棉花、灯芯和底部防漏垫;
  2. 用一根网线截取合适长度,取出4根线穿过灯芯孔;
  3. 发光LED正极串联一个220欧姆的电阻,焊接一根露出来的网线,负极焊接另一根线。正极和电阻焊接的线在火机内部的一头接Gemma的D0口,负极引线的另一头接Gemma的GND口;
  4. 火机外另两根线焊接磁控开关,调整开关位置,贴着火机防风口围壁内侧固定好;
  5. 在火机上盖内侧,用快干胶垂直固定好一块小磁铁,注意上盖盖好时,磁极位置正好能对着刚才固定好的磁控开关;
  6. 磁控开关通向火机内部的线头分别接纽扣电池正极和Gemma的电源口;
  7. 纽扣电池负极引短线焊接在Gemma的GND口;
  8. Gemma和纽扣电池分别用小塑料口袋封装,确保绝缘,装入火机内部。

完成效果

就是这样,火机盖弹开后,常闭磁控开关会连通电源,为Gemma供电,程序开始运行。在25分钟工作时段内,LED会以呼吸灯形式闪亮,越接近工作时间结束,呼吸频率越快。

进入休息时段,LED以固定亮度亮1秒,暗1秒持续到此时段结束,然后再次进入工作时段。

火机盖盖上后,磁控开关断电,程序停止运行。

下一步打算给火机配个木头的底座,摆在桌面上,工作时使用,算是用新技术复活了一个老物件。

注:Zippo图片来自flickr