Story

Introduction

With this project I wanted to keep things as simple as as they can be. The instructions that you will find below are usable for any newcomer that joins the IOT world and want to build something. Is not necessarily to build a IOT Aquarium. For example a cat or a dog feeder can be build taking in consideration the same steps but at a different scale.

I wanted to build this project since some time now but due to various reasons I didn't do it. I was looking also on thingverse.com for something already built ones but none that I found was ok for my needs. Finally, winning a free Smart 7688 DUO board at "The Future of Smart Home and Office is Here! " contest has given me the needed motivation to start building this project.

For the moment is complying with the following specifications:

Scheduled feeding time

Keep track of temperature inside my aquarium, wich between me and you is very small and during time I had several casualties among the fishes due to temperature issues

Schedule fish tank light

Alert if food is empty

Smart 7688 DUO

LinkSmart 7588 DUO is a really nice board. It has an almost complete arduino linked with a linux computer. So what could you ask for more? I compare it with a Arduino Micro and with a home made clone of Adafruit HUZZAH. You can see that in terms of shape there is not much difference, but 7688 DUO is the clear winner here because of it's capabilities.

;

;

1 / 2 • 7688DUO vs Arduino Micor

I know that this is not the best comparison that I could make and I should do a comparison between 7688 DUO, onion omega2, CHIP and so on...but my point is that you can buy the 7688 DUO with 16$ when a arduino micro cost 18$ and adafruit huzzah cost 10$. Ok Omega 2 cost 5$ but you don't have the arduino inside and you are missing a USB power port.

Only thing that I don't like about this board is that I expected it to be a little bit faster, but for a project like the one I'm describing here is more than enough.

Ok enough trying to convince you that 7688 is a really cool board for development and let's move on to our project.

Step 1: Configuring Smart 7688 DUO

To configure your board, you will need to follow the below steps in the mentioned order:

4.Assure that you are able to connect remotely with SSH. For windows I recommend Putty and WinSCP. In case you are running Linux this should be already integrated in Bash. In windows, if you would like to run ssh from Visual Studio Code terminal you can install OpenSSH

Step 2: 3D printing needed parts

Complete design of the IOT Aquarium controler was done by me in Fusion 360. I won't try to convince you again that Fusion 360 is a really great app for makers. ;-)

Being a completely new design I'm aware that it my have some flaws that I had miss, but if you share them with me I'll be more than happy to apply the needed corrections.

Assembled IOT Aquarium Controller

The IOT Aquarium controller is made of 3 3D printed parts:

main body(with red in the above picture)

feeding mechanism (brown and yellow)

neopixel strip holder (gray)

I print everything with a FlashForge Finder 3d printer. Infill of 50%/Layer height 0.18mm. If you want the body to have a better quality than mine I suggest using supports for the wire guiders.

;

;

1 / 2

You can find all 3d printed files here http://www.thingiverse.com/thing:2137857 or in the download section of this project. If you don't have a 3D printer you can always use a 3D printing service or ask a friend that have a 3D printer.

Step 3: Schematics & Soldering

Schematic is not that difficult to understand. We have the stepper motor driver board IN1-IN4 connected to D9-D13 from 7688 DUO. Then we have a light barrier, made from a red 5mm LED and a photoresistor. The LDR is connected to A5 on our development board. DS18b20 is connected to pin D3 and neopixel strip is connected to D8 pin.

Everything is powered from the 5v pin. Why I chose this is because when looking at the Smart 7688 DUO schematic I see a direct link to the power micro usb. This means that if you power everything from a microusb phone charger it should work ok.

Schematic of IOT Aquarium controller

When soldering please take in consideration the best layout that suits you. My approach you can see in the picture below.

Prefboard view

Also what I will suggest is to use a colored pref-board with metallic vias. Soldering is much easier on this types of pref-boards.

I would suggest to use this kind of pref-board for better soldering especially if you are a beginner.

Be careful to leave the wires for the LED, LDR and neopixel strip a little bit longer to reach from the board to the fixation holes on the 3dprinted parts.

;

;

1 / 2 • I put the 220ohm resistor on the wire for the led

Step 4: Put everything together

Feeder mechanism

Assemble the feeder mechanism according to below photos

;

;

1 / 8 • Take the main body and put inside the feeder

In the end if everything went ok you should be able to do this:

Feeder should turn without retention

Assembly the stepper motor

Next you need to fix the stepper motor with 2pcs 15x3mm screws and nuts. You will need to use the long plier because one area is a little bit difficult to reach for fixing the nut.

;

;

1 / 5 • Place the motor in the corresponding location

Mounting the brain

Slide in the electronics and fix the LDR together with the red LED in the main body with hot glue.

;

;

1 / 3 • Fix LED with hit glue to the main body

After this is done the only thing remaining is fixing the loose wires with some cable ties like in the below pictures.

;

;

1 / 3 • More cable ties in the predefined locations

Step 4: Programming the brain

Programming this board is not that hard. We will start with the programing of ATMEGA32U4 done with ArduinoIDE.

Next is time to write our node.js code. I build everything with Visual Studio Code. You can do the same because is a cross platform editor. Use SCP or WinSCP for transferring files over SSH if you don't want to edit with nano.

Create a new file called IOTAquarium.js with your prefered editor. Here is the code you should write inside:

- here you should put your authentication key given by your blynk project

var AUTH = '98fd3b118cc84d8e9e54f90ed085449b';//you will get this key from your blynk project in your phone app

-in case you are not from romania, here you should put your timezone

var time = moment.tz(now, "Europe/Bucharest");//change here if you are not living in ROMANIA

Next, save everything and you can run a test with the following command:

node --harmony IOTAquarium.js

Remaining now is to make sure that our code is launching every time when our Smart 7688 DUO board starts and that it continues to run in case of unexpected stoppages. For this we will prepare a script that will run every minute. To make this happen we will use cron.

Create a new file with your prefered editor called IOTAquarium. This file will contain below code:

Feeding mechanism P2

Feeding mechanism P3

Feeding mechanism small P3

neopixel_strip

Schematics

iotaquarium_sckxOcBI84.fzz

Code

IOTAquarium.inoC/C++

/*************** IOTAquarium *********************** This is a project to help you manage your fish tank. Folowing functions are currently implemented: -fish feed -empty food tank allert -water temperature monitoring -light controler(using 5v neopixel) Used pins: 28BYJ-48 motor/ULN2003A (IN1,IN2,IN3,IN4) <-> Smart7688 DUO(13,12,11,10) DS18B20 (red,yellow,gray) <-> Smart7688 DUO(5V,D3,GND)+4,7k betwen 5V and DATA LDR (VCC,data) <-> Smart7688 DUO(5V,A5)+10k betwen GND and DATA ******************************************************/#include<stdlib.h>//****** Steper library#include<Stepper.h>//include stepper library for moving the feeder// change this to fit the number of steps per revolution for your mottor//configure the steper on the connected pinsconstints1=13;constints2=12;constints3=11;constints4=10;intdel=2000;//delay betwen steps//*******************************//****** LDRintldrPin=A5;//pin on wich is connected the LDRintldrValue=0;//initial value for the LDR value//*******************************//****** Temperature#include<OneWire.h>#include<DallasTemperature.h>#define ONE_WIRE_BUS 3// Data wire is plugged into pin 3OneWireoneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devicesDallasTemperaturesensors(&oneWire);// Pass our oneWire reference to Dallas Temperature.//*******************************//****** Neopixel#include<Adafruit_NeoPixel.h>#define PIN 8// We use pin * for comunicating with 5v neopixel strip#define NUMPIXELS 16//curently I had only 4 neopixel. I need here a improvementAdafruit_NeoPixellight=Adafruit_NeoPixel(NUMPIXELS,PIN,NEO_GRB+NEO_KHZ800);// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.//colorsbyteR=0;byteG=0;byteB=0;//intensitybyteI=255;//*******************************//****** Serial port comunication#include<SerialCommand.h>SerialCommandsCmd;//This SerialCommand object will be used to comunicate betwen Atmega32U4 and MT7688//*******************************//****** Keeping up with the time#include<TimeLib.h>#include<TimeAlarms.h>AlarmIda1,a2,a3;//needed alarms objectsStringdays_to_feed="";//variable needed for understanding in wich days feeding needs to happenvoidsetup(){Serial.begin(9600);//sets serial port for communicationSerial1.begin(57600);// open internal serial connection to MT7688ANsensors.begin();// start up the librarylight.setBrightness(255);// start with full brighnesslight.begin();// this initializes the NeoPixel library.// Define recognized commands callbacks for SerialCommandsCmd.addCommand("FEED",feed);// function to feed the fishsCmd.addCommand("L",lights);// function to start/stop the lights and establish color with intensitysCmd.addCommand("T",times);// time sincronizationsCmd.addCommand("A",alarms);// alarm setupsCmd.setDefaultHandler(unrecognized);// handler for command that isn't matched (says "What?")//allocate functions to each alarma1=Alarm.alarmRepeat(1,1,1,light_on);a2=Alarm.alarmRepeat(1,1,1,light_off);a3=Alarm.alarmRepeat(1,1,1,feed_me);//configure stepper pins as outputpinMode(s1,OUTPUT);pinMode(s2,OUTPUT);pinMode(s3,OUTPUT);pinMode(s4,OUTPUT);}voidloop(){ldrValue=analogRead(ldrPin);// read the value from the sensorsensors.requestTemperatures();// send the command to get temperaturesStringtmp="";//temporary variable to make the message that will be sent over serialtmp=tmp+sensors.getTempCByIndex(0);tmp=tmp+";";tmp=tmp+ldrValue;Serial1.println(tmp);//send the values to the MT7688sCmd.readSerial();// see if MT7688 is telling us something//digitalClockDisplay();//just for debugging if you need to see what time it isAlarm.delay(5);// wait a little for the alarms to work}//function to set color and brightness for the neopixelvoidbecuri(byteR,byteG,byteB,byteJ){for(inti=0;i<16;i++){light.setPixelColor(i,light.Color(0,0,0));//establish neopixel colors}for(inti=0;i<map(J,0,100,0,16);i++){// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255light.setPixelColor(i,light.Color(R,G,B));//establish neopixel colors}light.show();// this sends the updated pixel color to the hardware.}//function to move the motor and feed the fishvoidfeed(){for(inti=0;i<=80;i++){forwardsFull();};for(inti=0;i<=80;i++){backwardsFull();};motorOff();}//function for lights with 4 parameters that reads the message from MT7688 and makes the Atmega32U4 to act acordinglyvoidlights(){intaNumber,bNumber,cNumber,intens;char*arg;arg=sCmd.next();if(arg!=NULL){aNumber=atoi(arg);// converts a char string to an integer//Serial.print("First argument was: "); //only debuging//Serial.println(aNumber); //only debuging}else{aNumber=0;}arg=sCmd.next();if(arg!=NULL){bNumber=atol(arg);// Serial.print("Second argument was: ");// Serial.println(bNumber);}else{bNumber=0;}arg=sCmd.next();if(arg!=NULL){cNumber=atol(arg);// Serial.print("3 argument was: ");// Serial.println(cNumber);}else{cNumber=0;}arg=sCmd.next();if(arg!=NULL){intens=atol(arg);// Serial.print("4 argument was: ");// Serial.println(intens);}else{intens=0;}R=aNumber;G=bNumber;B=cNumber;I=intens;becuri(R,G,B,I);}//function for arduino time sincronizationvoidtimes(){time_tbNumber;char*arg;arg=sCmd.next();if(arg!=NULL){inttmp;bNumber=0;for(inti=0;i<10;i++){tmp=(int)arg[i]-48;bNumber=(10*bNumber)+tmp;}}else{bNumber=0;}setTime(bNumber);//Serial.print(bNumber);}//Get the defined alarms from MT7688 and set them in our arduino time alarm libraryvoidalarms(){intaNumber;longbNumber,cNumber;StringdNumber;char*arg;arg=sCmd.next();if(arg!=NULL){aNumber=atoi(arg);}else{aNumber=0;}arg=sCmd.next();if(arg!=NULL){bNumber=atol(arg);}else{bNumber=0;}arg=sCmd.next();if(arg!=NULL){cNumber=atol(arg);}else{cNumber=0;}arg=sCmd.next();if(arg!=NULL){dNumber=arg;}else{dNumber="";}byteh,m,s;if(aNumber==1){Alarm.free(a1);Alarm.free(a2);h=bNumber/(60*60);m=(bNumber%(60*60))/60;s=((bNumber%(60*60))%60);Alarm.disable(a1);a1=Alarm.alarmRepeat(h,m,s,light_on);Alarm.enable(a1);//Serial.println(Alarm.read(a1));//Serial.print(h);//Serial.print(" ");//Serial.print(m);//Serial.print(" ");//Serial.println(s);h=cNumber/(60*60);m=(cNumber%(60*60))/60;s=((cNumber%(60*60))%60);//Serial.print(h);//Serial.print(" ");//Serial.print(m);//Serial.print(" ");//Serial.println(s);Alarm.disable(a2);a2=Alarm.alarmRepeat(h,m,s,light_off);Alarm.enable(a2);//Serial.println(Alarm.read(a2));}elseif(aNumber==2){h=bNumber/(60*60);m=(bNumber%(60*60))/60;s=((bNumber%(60*60))%60);days_to_feed=dNumber;//Serial.print(h);//Serial.print(" ");//Serial.print(m);//Serial.print(" ");//Serial.println(s);Alarm.disable(a3);a3=Alarm.alarmRepeat(h,m,s,feed_me);Alarm.enable(a3);}}//ligt ON at alarm triggervoidlight_on(){if(((R==0)&(G==0)&(B==0))||(I==0)){becuri(255,255,255,100);}else{becuri(R,G,B,I);}//Serial.println(R);//Serial.println(G);//Serial.println(B);//Serial.println(I);//Serial.println("ON");}//ligt OFF at alarm triggervoidlight_off(){becuri(0,0,0,0);//Serial.println("OFF");}//feed at alarm triggervoidfeed_me(){Stringtmp="";if(weekday()==1){tmp="7";}else{tmp=String(weekday()-1);}if(find_text(tmp,days_to_feed)!=-1){feed();}//Serial.println("FEED");}// This gets set as the default handler, and gets called when no other command matches.voidunrecognized(constchar*command){Serial.println("What?");}//3 helping aid functionsvoiddigitalClockDisplay(){// digital clock display of the timeSerial.print(hour());printDigits(minute());printDigits(second());Serial.println();}voidprintDigits(intdigits){Serial.print(":");if(digits<10)Serial.print('0');Serial.print(digits);}intfind_text(Stringneedle,Stringhaystack){intfoundpos=-1;for(inti=0;i<=haystack.length()-needle.length();i++){if(haystack.substring(i,needle.length()+i)==needle){foundpos=i;}}returnfoundpos;}//stop motorvoidmotorOff(){digitalWrite(s1,LOW);digitalWrite(s2,LOW);digitalWrite(s3,LOW);digitalWrite(s4,LOW);}//move backwardvoidbackwardsFull(){digitalWrite(s1,HIGH);digitalWrite(s2,HIGH);digitalWrite(s3,LOW);digitalWrite(s4,LOW);delayMicroseconds(del*2);digitalWrite(s1,LOW);digitalWrite(s2,HIGH);digitalWrite(s3,HIGH);digitalWrite(s4,LOW);delayMicroseconds(del*2);digitalWrite(s1,LOW);digitalWrite(s2,LOW);digitalWrite(s3,HIGH);digitalWrite(s4,HIGH);delayMicroseconds(del*2);digitalWrite(s1,HIGH);digitalWrite(s2,LOW);digitalWrite(s3,LOW);digitalWrite(s4,HIGH);delayMicroseconds(del*2);}//move forwardvoidforwardsFull(){digitalWrite(s1,LOW);digitalWrite(s2,LOW);digitalWrite(s3,LOW);digitalWrite(s4,HIGH);delayMicroseconds(del*2);digitalWrite(s1,LOW);digitalWrite(s2,LOW);digitalWrite(s3,HIGH);digitalWrite(s4,LOW);delayMicroseconds(del*2);digitalWrite(s1,LOW);digitalWrite(s2,HIGH);digitalWrite(s3,LOW);digitalWrite(s4,LOW);delayMicroseconds(del*2);digitalWrite(s1,HIGH);digitalWrite(s2,LOW);digitalWrite(s3,LOW);digitalWrite(s4,LOW);delayMicroseconds(del*2);}

IOTAquarium.jsJava

//include moment module for working with time varmoment=require('moment-timezone');varq=require('moment');//include blynk module to be able to comunicate with our app varBlynk=require('blynk-library');varAUTH='98fd3b118cc84d8e9e54f90ed085449b';//you will get this key from your blynk project in your phone app varblynk=newBlynk.Blynk(AUTH);//create the link betwen your app and the IOT device //include serial module to be able to facilitate the comunication betwen MT7688 and ATMEGA32U4 varcom=require("serialport");//default serial port for this comunication with the configuration parameters varserialPort=newcom.SerialPort("/dev/ttyS0",{baudrate:57600,parser:com.parsers.readline('\r\n')});//If everything is ok write on the console that port is open serialPort.on('open',function(){console.log('Portopen...');});//else write a error message serialPort.on('error',function(){console.log('Error...');});vart="";//variable to keep time varf=0;//variable to know when to feed varR=0,B=0,G=0,I=255;//RGB and Intensity values varziua;//day to feed varv0=newblynk.VirtualPin(0);//light varv1=newblynk.VirtualPin(1);//feeding schedlue varv2=newblynk.VirtualPin(2);//feed buttom varv3=newblynk.VirtualPin(3);//display temperature varv4=newblynk.VirtualPin(4);//brigness varv5=newblynk.VirtualPin(5);//lighting color varv6=newblynk.VirtualPin(6);//status of the food //include sntp module to be sync our clock and know exact time from the internet varSntp=require('sntp');// Request server time varoptions={host:'pool.ntp.org',// Defaults to pool.ntp.org port:123,// Defaults to 123 (NTP) resolveReference:true,// Default to false (not resolving) timeout:1000// Defaults to zero (no timeout) };Sntp.time(options,function(err,time){if(err){console.log('Failed:'+err.message);}});functiontime_su(){//**********************************/ now=Sntp.now();vartime=moment.tz(now,"Europe/Bucharest");//change here if you are not living in ROMANIA vartime_string=time.format();varta=now+q.parseZone(time_string).utcOffset()*60*1000;serialPort.write("T "+ta+"\r\n");//**convert time to local timezone and send it to ATMEGA32U4 so that we can use it in our arduino schetc */ vardate=newDate();//************make an email alert once per day in case food container is empty */ if((f>=1000)&&(ziua!=date.getDate())){blynk.email("IOTAquarium","It's time to replace your fish food container!");//here is the email alert message ziua=date.getDate();}setTimeout(time_su,60000);//repeate this every 5 minute }time_su();//below is the code that decide what to do when interacting with various widgets from our blynk app v0.on('write',function(param){//console.log(param); serialPort.write("A 1 "+param[0]+" "+param[1]+" "+param[3]+"\r\n");});v1.on('write',function(param){serialPort.write("A 2 "+param[0]+" "+"12"+" "+param[3]+"\r\n");});v2.on('write',function(param){serialPort.write("FEED\r\n");});v3.on('read',function(){v3.write(t);});v6.on('read',function(){if(f>1000){v6.write("EMPTY");}else{v6.write("OK");}});v4.on('write',function(param){I=param[0];serialPort.write("L "+R+" "+G+" "+B+" "+I+"\r\n");//console.log(R, G, B, I); });v5.on('write',function(param){R=param[0];G=param[1];B=param[2];serialPort.write("L "+R+" "+G+" "+B+" "+I+"\r\n");//console.log(R, G, B, I); });serialPort.on('data',function(data){vararr=data.split(";");t=arr[0];f=arr[1];parseInt(f,10);});