visionsl 发表于 2016-1-11 10:07:11

懒人养鱼之-自动鱼缸

起因    一年前玩过一个小鱼缸,不过因为太小, 而且懒于换水, 没多久就放弃了; 后来一直想做点什么在家里可以玩儿的, 恰巧在DFRobot社区见到有人做了个懒人养鱼的换水系统(原文<爱小鱼又养不活?——懒人必试自动换水系统>), 于是下决心也整一个, 但不同的是这次准备玩儿大的, 整个大一点的鱼缸.
   鱼缸要大代价就高, 就不能随便整, 所以去泡了几天养鱼的坛子, 发现大鱼缸比小鱼缸复杂多了. 先是分淡水缸和海缸, 然后按过滤系统分: 上滤/侧滤/底滤.
   因好奇看了一下海缸, 不看不知道, 一看吓一跳! 海缸固然观赏价值极高(可以养珊瑚, 养海鱼, 想想都爽), 但其过程不但开缸极其复杂, 而且开缸成功后, 整个海水循环系统十分昂贵和复杂, 于是放弃.
   既然做不了海缸, 就淡水缸吧. 然后考虑过滤系统, 简单说一下其中两种过滤系统的优劣:

上部过滤:
    上部循环过滤是放在水族箱上面,整个过滤槽露在水族箱的外面,通过潜水泵将水抽出进入过滤槽,再由过滤槽底部的出水管流回到水族箱内,在过滤槽内可以放置过滤棉、活性炭、生化石等。上部过滤器的最大优点是清洗方便,在过滤材料污染时,可以很方便地取出进行清洗。最大的缺点是体积比较大,占的空间也相对要大。停电时滤材也不会因泡在水中而缺氧使菌群大量死亡.滤材内的硝化菌在停电后还可以存活很长的时间.
缺点:
    噪音 - 流水声更是必须忍受的
    过滤槽清洁较麻烦
    扬水马达及滴水区与下水口会有声音,有些马达比较吵,滴水声对于浅眠的人来说是一大困扰
    上部过滤所佔面积大,影响其他设备的摆放
    破坏美观

底部过滤 :


(因本次采用的是底滤, 所以配上图说明, 借用养鱼坛子的一张图)
    适合大缸子,水草缸除外
    由沉/卧水马达在底下将水吸起利用水压力把水抽上去主缸,主缸水满了之后溢流回去底缸就这样一直循环一直循环,这种溢流设计可以将缸底下的排泄物给清除.通常都用木柜/铁架包门 - 底部看起来较为美观
    如设计上无误,换水也方便,拥有极为可观的滤材容量, 培养硝化菌极多,主缸整体看起来会很清爽
缺点:
    因需多个底滤缸,滤材,价格挺昂贵
    不适合铺底沙,如设计不当,会有流水噪音
    过滤槽难清洁整理


综合考虑, 既然搞大了, 那就直接整个美观大方的, 而且底滤系统因底部有柜子, 方便布置咱的智能设备, 只要把线和设备整理好, 鱼缸外观看起来观赏价值高.

先上两张鱼缸图






思路
    既然是懒人鱼缸, 所以需要的功能设计如下:
- 自动换水 (本设计的宗旨就是这货)
- 自动浇花 (排出的水不用就浪费了, 拿来浇花杠杠滴)
- 自动打氧 (全天24X7开启本人觉得没必要, 所以要改成定时开一阵子即可)
- 观赏LED灯,人来自动开, 人走演示自动关
    以上功能原本用一个普通的Arduino UNO就能做到, 不过后来考虑到联网及易于随时控制, 随时改程序, 随时在电脑上观察各种配件工作情况, 以及远程控制等等拓展功能, 于是采用了功能更为强大的树莓派.
(先上一张控制树莓派的装X图)

准备材料
- 1.2米大鱼缸一枚(带底滤系统)
- 树莓派 B+版
- 四路带光耦继电器
- 人体红外热释电运动传感器
- 鱼缸抽水泵X2 (1只注水, 1只排水)
- 鱼缸打氧机
- 鱼缸加热棒
- 家庭滴灌套装滴灌设备2100
- 注水用的储水罐(20升以上)一只
- 2-3孔插线板1只
- 小型开关电源(输入220V, 输出12V3A)
- 20P接线端子, 5孔电线连接器, 3孔电线连接器等
- PowerCube带USB魔方插座
- 多种Size的牛筋管若干
- 2芯0.5平线, 4芯0.3平线若干; 包线管若干
- 4分走线管及弯头/三通头若干
- 3D打印

设计图
- 电图设计



制作过程
    过程中用到3D打印的有几个地方:
   1. 人体红外热释电运动传感器, 因是唯一要放置在鱼缸外部的元件, 所以需要制作一个盒子来放它. 刚开始是用黑色材料打印,效果如下



后来发现厚度设计有误, 修改了一下, 换了绿色打印, 效果如下,



装好后:



    2. 树莓派和继电器放在底柜中, 需要有个盒子或支架放置, 初步设计为挂在底缸旁边



继电器底座


固定支架(螺丝位设计了一些凹槽, 用于预防水从缸里渗出时, 从凹槽流走, 防止流到电路板上造成短路)



安装好两层的效果图


安装效果图



安装到底缸实际效果.


    3. 设备接线(见前面的电路设计图)
      3.1 首先全部设备需经过20P的接线端子进入主线路, 使用接线端子是考虑到以后设备更换方便, 接线设计:
   1 - 220V(零线) 1号线(蓝) 220V共用零线(不知有没问题,请高人指点)
K3 2 - 220V(火线) 1号线(灰) 注水
K2 3 - 220V(火线) 2号线(蓝) 氧气
   4 - 220V(火线) 2号线(蓝) 热棒
K4 5 - 12V(负极) 3号线(红) LED
K4 6 - 12V(正极) 3号线(绿) LED
K1 7 - 220V(零线) 3号线(黄) 排水
K1 8 - 220V(火线) 3号线(黑) 排水
   9 - 5V    (负极) 4号线(黑)
   10 - 5V(正极) 4号线(红)
   11 - Data 4号线(绿) 人体
   12 - Data 4号线(绿) 预留
   13 - Data 7号线(红) 预留
   14 - Data 7号线(绿) 预留
   15 - Data 7号线(黄) 预留
   16 - Data 7号线(黑) 预留
   17 - 220V(零线) 5号线(蓝) 预留
   18 - 220V(火线) 5号线(灰) 预留
   19 - 220V(火线) 6号线(蓝) 预留
   20 - 220V(火线) 6号线(灰) 预留



(先接主线路, 主线路经过走线管, 这样看起来整洁些,也在一定程度上防水)



再接外部设备, 从上到下按顺序依次是注水水泵,氧气,热棒,LED,排水水泵;再下来是5V(主线引到树莓派上)和人体红外传感器信号线), 12-20号脚预留(以后备用)



20P接线完成后, 在底滤中部找个位置固定开关电源





设置开关电源的背面还有空间, 用来固定220V电源(输入)插座, 并配了一个PowerCube魔方插座(4面有3脚插座,而且省空间),这里需要供电给:底滤水泵(24X7常开), 开关电源, 树莓派(USB)




回过头继续接主线路的另一端, 220V的全部接上, 并从开关电源引出12V接给LED灯; 接在继电器上的设备主要是(注水水泵/排水水泵/打氧机/LED观赏灯), 这些通过4PIN的公母接头与继电器相连(不直接焊接是为了方便维修)



与树莓派下方的继电器接上, 线上标识好继电器方便识别


排水->浇花(见后面的视频)

注水(从储水罐中抽水注入鱼缸中) (见后面的视频)








内部处理好后, 外部由排水泵引出水管接到滴灌设备上,这是滴灌时的效果. 还不错


    4.继电器与树莓派接线
继电器                树莓派
5V                        5V
GND                        GND
K1                        GPIO_03                排水浇花水泵
K2                        GPIO_04                氧气
K3                        GPIO_05                LED观赏灯
K4                        GPIO_06                注水水泵



    5.树莓派准备工作
      - 准备系统(本例中采用的是原生的树莓派Linux系统)
      > 下载系统安装包>>>>GO
      > 具体安装方法
      > 第一次启动系统, 详细配置过程
      - 安装配置支持库
      > 安装Wiring Pi支持库
      > 同步时钟(用于定时器)
      > 安装pi4j java支持环境(让Java可以访问IO口)
      - 无线网络配置及准备
      > 此处如果希望树莓派能通过Wifi方式联网, 需要购置USB无线网卡,并配置; 否则用网线即可


程序(Java版本)/**
* 智能鱼缸主程序
* 功能设计要点:
*               - 人体红外传感器.                              GPIO_02
*               - 自动排水.水泵.1号继电器.                GPIO_03
*               - 氧气控制.3号继电器.                        GPIO_04
*               - LED控制.4号继电器(人来开灯).      GPIO_05
*               - 自动注水.水泵.2号继电器.                GPIO_06
* @author Penny
*
*/
public class Fish {

    private static boolean isAct = false;
    private static long pin05_on_time = 0;
   
      public static void main(String[] args) throws InterruptedException{
                System.out.println("Fish program is running...");

      final GpioController gpio = GpioFactory.getInstance();
      final GpioPinDigitalInput hasPerson = gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, PinPullResistance.PULL_DOWN);
      final GpioPinDigitalOutput pin03 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_03, "MyRELAY_injection", PinState.HIGH);//排水,此继电器高电平表示OFF
      pin03.setShutdownOptions(true, PinState.LOW);
      final GpioPinDigitalOutput pin04 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_04, "MyRELAY_oxygen", PinState.HIGH);      //氧气
      pin04.setShutdownOptions(true, PinState.LOW);
      final GpioPinDigitalOutput pin05 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_05, "MyRELAY_led", PinState.HIGH);                //LED灯
      pin05.setShutdownOptions(true, PinState.LOW);
      final GpioPinDigitalOutput pin06 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_06, "MyRELAY_drain", PinState.HIGH);      //注水
      pin06.setShutdownOptions(true, PinState.LOW);
               
      long pin06_on_time = 0;                //注水
      long pin03_on_time = 0;
      long pin04_on_time = 0;
      String[] oxygenTimes = FishConfig._TRIGGE_G04_TIME.split(",");                //每日打氧气可设多个时间段,以","分隔
      
      hasPerson.addListener(new GpioPinListenerDigital() {
            public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
                  if(FishConfig._DEBUG)System.out.println(" --> GPIO PIN STATE CHANGE: " + event.getPin() + " = " + event.getState());
                if(event.getState().isHigh()){
                        isAct=true;
                        pin05_on_time=(new Date()).getTime();                //更新时间点
                        if(FishConfig._DEBUG)System.out.println("update pin05_on_time.");
                }else{ isAct=false;}
            }
      });
      
                while(true){
                        String nowTime = new SimpleDateFormat("HH:mm").format(new Date());
                int t06 = DateCompare(nowTime, FishConfig._TRIGGE_G06_TIME, "HH:mm");
                int t03 = DateCompare(nowTime, FishConfig._TRIGGE_G03_TIME, "HH:mm");
               
                        //注水
                        if(t06==0){
                              if(pin06.getState().isHigh()){
                                        pin06.low();      //低电平接通
                                        pin06_on_time=(new Date()).getTime();                //开启时间节点
                                        System.out.println("--> in water: ON..."+pin06_on_time);
                                        Common.recordLogs(new StringBuffer(new Date()+"--> in water: ON..."+pin06_on_time+"\r\n"), FishConfig.LOGFILE);
                              }System.out.print(".");
                        }
                        if((((new Date()).getTime()-pin06_on_time)/1000/60)>=FishConfig._TRIGGE_G06_TIME_DELAY){
                              if(pin06.getState().isLow()){
                                        pin06.high();
                                        System.out.println("--> in water: OFF...");
                                        Common.recordLogs(new StringBuffer(new Date()+"--> in water: OFF..."+pin06_on_time+"\r\n"), FishConfig.LOGFILE);
                              }
                }

                        //排水/浇花
                        if(t03==0){
                              if(pin03.getState().isHigh()){
                                        pin03.low();      //低电平接通
                                        pin03_on_time=(new Date()).getTime();                //开启时间节点
                                        System.out.println("--> out water: ON..."+pin03_on_time);
                                        Common.recordLogs(new StringBuffer(new Date()+"--> out water: ON..."+pin03_on_time+"\r\n"), FishConfig.LOGFILE);
                              }if(FishConfig._DEBUG)System.out.print(".");
                        }
                        if((((new Date()).getTime()-pin03_on_time)/1000/60)>=FishConfig._TRIGGE_G03_TIME_DELAY){
                              if(pin03.getState().isLow()){
                                        pin03.high();
                                        System.out.println("--> out water: OFF...");
                                        Common.recordLogs(new StringBuffer(new Date()+"--> out water: OFF..."+"\r\n"), FishConfig.LOGFILE);
                              }
                }
                        
                        //打氧气,维持1分钟
                        boolean doOxygen = false;                //是否需要加氧气
                        for(String g04time : oxygenTimes){
                              int t04 = DateCompare(nowTime, g04time, "HH:mm");
                              if(t04==0){doOxygen=true;break;}      //需要加氧气
                        }
                        if(doOxygen){
                              if(pin04.getState().isHigh()){
                                        pin04.low();      //低电平接通
                                        pin04_on_time=(new Date()).getTime();                //开启时间节点
                                        System.out.println("--> oxygen: ON..."+pin04_on_time);
                                        Common.recordLogs(new StringBuffer(new Date()+"--> oxygen: ON..."+pin04_on_time+"\r\n"), FishConfig.LOGFILE);
                              }if(FishConfig._DEBUG)System.out.print(".");
                        }
                        if((((new Date()).getTime()-pin04_on_time)/1000/60)>=FishConfig._TRIGGE_G04_TIME_DELAY){
                              if(pin04.getState().isLow()){
                                        pin04.high();
                                        System.out.println("--> oxygen: OFF...");
                                        Common.recordLogs(new StringBuffer(new Date()+"--> oxygen: OFF..."+"\r\n"), FishConfig.LOGFILE);
                              }
                }

                        //开LED,维持X分钟
                        if(isAct){
                              if(pin05.getState().isHigh()){
                                        pin05.low();      //低电平接通
                                        pin05_on_time=(new Date()).getTime();                //开启时间节点
                                        System.out.println("--> LED: ON..."+pin05_on_time);
                                        Common.recordLogs(new StringBuffer(new Date()+"--> LED: ON..."+pin05_on_time+"\r\n"), FishConfig.LOGFILE);
                              }if(FishConfig._DEBUG)System.out.print(".");
                        }
                        if((((new Date()).getTime()-pin05_on_time)/1000/60)>=FishConfig._TRIGGE_G05_TIME_DELAY){
                              if(pin05.getState().isLow()){
                                        pin05.high();
                                        System.out.println("--> LED: OFF...");
                                        Common.recordLogs(new StringBuffer(new Date()+"--> LED: OFF..."+"\r\n"), FishConfig.LOGFILE);
                              }
                }
                        
                Thread.sleep(1000);
                }
               
                //System.out.println("Fish program is end...");
      }
      
      /**
   * 根据时间类型比较时间大小
   * @param source
   * @param traget
   * @param type "YYYY-MM-DD" "yyyyMMdd HH:mm:ss"类型可自定义
   * @param 传递时间的对比格式
   * @return
   *0 :source和target时间相同   
   *1 :source比target时间大
   *-1:source比target时间小
   * @throws Exception
   */
    public static int DateCompare(String source, String target, String type) {
            int ret = 2;
            try{
                  SimpleDateFormat format = new SimpleDateFormat(type);
                  Date sourcedate = format.parse(source);
                  Date tragetdate = format.parse(target);
                  ret = sourcedate.compareTo(tragetdate);
            }catch(Exception e){e.printStackTrace();}
            return ret;
    }
      
}


下面是视频片段
https://player.youku.com/player.php/sid/XMTQ0Mjc3NzU0NA==/v.swf
感应观赏灯

https://player.youku.com/player.php/sid/XMTQ0ODE3MDY3Mg==/v.swf
命令行控制注水演示

https://player.youku.com/player.php/sid/XMTQ0ODE2NDgyOA==/v.swf
命令行控制排水演示

https://player.youku.com/player.php/sid/XMTQ0MjgxNjI1Mg==/v.swf
滴灌效果特写

最后上几张鱼图
   






..........>>>>完











hnyzcj 发表于 2016-1-11 10:10:05

今年流行占坑

dsweiliang 发表于 2016-1-11 10:13:00

今年流行占坑

visionsl 发表于 2016-1-11 10:14:33

今年流行占坑{:5_144:}

Juice 发表于 2016-1-11 11:32:53

这个可以远程控制么?单身狗出差半个月回来家里的花花草草可能都做天使去了~

virtualwiz 发表于 2016-1-11 11:35:07

今年流行占坑
看起来,全数控鱼缸是大势所趋啊:lol

visionsl 发表于 2016-1-11 11:36:10

Juice 发表于 2016-1-11 11:32
这个可以远程控制么?单身狗出差半个月回来家里的花花草草可能都做天使去了~ ...

绝对,必须可以远程!
出差半个月的话, 储水罐需要备个40升的(有自来水管的可以无视)
然后你的花花草草会比你在家还长得好:lol

visionsl 发表于 2016-1-11 11:36:59

因为用的是滴灌方式浇花, 所以40升水应该可以用超过半个月

heinau 发表于 2016-1-11 14:35:24

visionsl 发表于 2016-1-11 11:36
绝对,必须可以远程!
出差半个月的话, 储水罐需要备个40升的(有自来水管的可以无视)
然后你的花花草草会比 ...

好棒哦!
然而比我在家还要长得好。。感觉自己的存在好没用的样子{:5_137:}

大连林海 发表于 2016-1-11 19:57:54

太牛掰的项目了 模

visionsl 发表于 2016-1-11 22:09:40

大连林海 发表于 2016-1-11 19:57
太牛掰的项目了 模

谢谢大牛支持

wangsh 发表于 2016-1-12 17:02:07

这个必须顶!

大连林海 发表于 2016-1-12 19:54:11

visionsl 发表于 2016-1-11 22:09
谢谢大牛支持

我是小白 来学习

visionsl 发表于 2016-1-18 11:55:19

补: 实际使用效果中, 发现人体红外有时会误报, 后来增加了些功能, 在半夜时段, 不管是否有人都不开LED灯, 以免鱼儿受到惊吓

MoonShine 发表于 2016-1-27 17:23:29

楼主真是好人哪!连链接都有,先谢谢了,我再想想优化优化

visionsl 发表于 2016-1-29 11:54:42

MoonShine 发表于 2016-1-27 17:23
楼主真是好人哪!连链接都有,先谢谢了,我再想想优化优化

有好想法也Share一下, 共同进步!:handshake

guoguode 发表于 2021-11-15 21:25:54

厉害!下来慢慢研究、

派大星ym 发表于 2022-8-11 08:27:02

酷酷酷酷

派大星ym 发表于 2022-8-11 08:29:09

{:6_213:},,,,,
页: [1]
查看完整版本: 懒人养鱼之-自动鱼缸