Create a connected Fish Tank with Fishino

By on May 27, 2016
Pin It



Thanks to the Fishino board, we will make an existing fish tank smart, and we will place it in the IoT world.


By now, technology has made way in our daily life as well, and the home automation becomes every day more familiar: it is easy to think, for example, to a heating system that warns us about a window that has been left opened, so to avoid the heat dispersal; but it is also evocative to see it used in less usual situation, such as the management of a fish tank (as the proposal in these pages). The project that we present here allows in fact to manage a fish tank, without having to modify the structure or the electrical aspect. The idea is the one to go from a traditional fish tank to a smart one, without fears for not being able to reverse the decision.

During the project development we found a valuable ally in Fishino UNO; the choice was dictated by the specific functions that the board has: a WiFi connection so to enable the remote monitoring of the water temperature, by using any device that has an Internet connection and an internal clock (RTC, Real Time Clock), with a cluster battery backup so to manage  the operating stages (in the place of the usual analog timers, that in the case of a blackout should be put back in line). Finally, by means of a microSD card, it is possible to define with ease the various aspects that are connected to the fish tank’s life, such as the hours of light, the carbon dioxide quantity supplied, and if to activate an oxygen diffuser. In order to implement the same functions in a classic Arduino UNO, we would have had to supply it of RTC, SD and WiFi shields, thus increasing costs and encumbrance.

The choice of a Fishino UNO is then a natural one, since the name… is suitable for the designated purpose!


The system

Let’s see what it is about, by referring ourselves to figure; in it you will see that the system’s beating heart is Fishino UNO, thanks to which it is possible to supply the fish tank with the features that make it “smart” and that thus distinguish it from a traditional fish tank. The device’s functioning is a simple one: Fishino UNO “learns” the times for the ceiling lights’ turning on and off, for the carbon dioxide supplier and for the aerator, by means of the file contained in the microSD card. After this operation, it will be able to activate or deactivate the devices, depending on the time. The time is set by the RTC (Real Time Clock) module that is inserted in Fishino UNO and, even in the case of power supply lacking, the backup battery allows not to skip the programmed time. Thanks to the waterproof probe (containing a Maxim’s DS18B20), it is possible to detect the water temperature and, after every half hour, this data will be automatically sent via WiFi, so to be memorized on a MySQL database (we will see how). At a later time it will be possible, by means of any device that is supplied with an Internet connection, to view the temperature on a handy diagram so to know, for example, the minimum or maximum temperature reached by the water. The detected temperature, besides being saved on the database, is also used in order to send, in the case of critical temperatures (too hot or cold) an e-mail to a determined address, to to be warned and to be able to act in time.





In the magazine number 198 we explained Fishino UNO’s hardware, therefore here we will limit ourselves to explain the additional circuit part that is required by the project, whose wiring diagram is shown in figure . We may see that the water temperature is acquired by Fishino by means of a stainless steel waterproof probe, designed with a Maxim DS18B20 temperature digital sensor (U1). The U1 allows to accurately measure the temperature in wet environments – not only if immersed in fluids – with a range going from -55°C to +125°C; of course such limits are much more than what is needed for a fish tank. Each sensor is supplied with a 64-bit serial code, memorized inside a ROM; this fact, coupled with the fact that only a communication pin (1-Wire) is required, allows to connect more than a sensor in a bus, so to be able to measure the temperature in several points in the fish tank, and thus to obtain more accurate indications as for the environmental conditions. The temperature is measured with an accuracy of ± 0,5°C (in the field between -10°C and +85 °C). The probe is powered by means of a voltage going from 3V to 5,5V.




The connection of the probe to Fishino UNO is carried out like this:

  • the black wire goes to the GND pin;
  • the red wire goes to the 5V pin;
  • the yellow wire (data channel) goes to the digital pin 2, to which we must connect a 4,7 k resistor (R1) towards the 5V (pull-up).

As for RL1 and RL2, we used two boards that are supplied with 240V/10A monostable relays, driven by TTL signals; each relay module requires a 5 Vcc power supply, drawn by Fishino UNO (to which even the ground is connected, via the black wire), and makes the N.O. (Normally Open – NO), the COM (Common) and the N.C. (Normally Closed) contacts accessible.

The modules having RL1 and RL2 are driven by means of Fishino UNO by means of the digital pins 8 and 9: the high logical level corresponds to an excited relay and the zero to the idle relay. The relays’ exchange must be used as a switch, thus C and NO will interrupt a load’s power supply wire and will then close it when the relay is activated. We have thought to connect the aerator to the RL1 and to manage the ceiling lights and the CO2 supplier by means of the RL2. As for the share of carbon dioxide and oxygen, you are free to use the method you are used to: as an example, you may connect a normally closed solenoid valve to the relay, it will open and close the pipe in which the CO2 passes through, after having crossed the pressure reducer placed above the tank. The loads for the ceiling lights and the carbon dioxide supplier may be separately managed by using an additional relay but, in our opinion, it is much easier to use just one since, as you will know, the CO2 share is mostly needed during the light periods, because it contributes to the photosynthesis, during which the fish tank’s flora produces oxygen, by absorbing carbon dioxide (thanks to the light).



After having dealt with the hardware, let’s move on to the software part, that is essentially divided in two parts: the first one concerns Fishino UNO’s programming, while the second one is the one that allows to save the data on a MySQL relational database, that then allows the recovery, so to build a diagram on the basis of the temperature that is detected every half an hour. We could see the two parts by following the logical path that will follow the temperature data from the acquisition moment to the save, and up to the reaching of the diagram’s output.

It is therefore opportune to start from the bottom, that is to say from how the data saving on a database occurs, since it will help you to understand how the sketch works.


Web page and MySQL relational database

In order to keep the water temperature monitored (by using any device that is provided with an Internet connection), it is enough to make what is detected by Fishino UNO available for the external world; the easiest way to make that is to use a personal website by means of which it will be possible to have a sort of shared window between you and Fishino.

In figure  you may see the online path carried out by the temperature data: a first webpage deals with receiving the data and saving it on the database, while a second one will be needed in order to draw the data from the database and to show it in the diagram.




As a first thing the website must be created, if you do not have one already.

In order to do so, you may confide in the many hosting services that are available for free or for a price; what is important is that the possibility to have a MySQL database is included in the service. Once the website has been created and the database has been activated, please go to the section in which it will be possible to manage your database (if you have more than one, just choose any one of them).

Once inside, please create a table (it is handy to use the creation tool that is surely supplied) with the fish tank’s name and the following fields by respecting uppercase and lowercase letters:

  • Temperature

Type: FLOAT;

  • Timestamp







This way you will have created a table with two parameters in which Timestamp (that, if not differently defined, is automatically set to the date and time of the server on which the database is hosted) is the primary key, that is to say there will never be two lines in the table that have the same Timestamp. If you do not want to use the table creation tool, you may use the console (it is usually found under the SQL entry) and insert the Listing 1’s code.


CREATE TABLE fish tank (
Temperature FLOAT NOT NULL,


Please then click on Execute or save ( Esegui or Salva), depending on the cases. The result will be as the one shown in figure .




As regards the webpage that will be used by Fishino in order to load the temperature on the database, a PHP script may be used. The choice fell on PHP since it is a server-side scripting language, it is perfectly suited for the task to be performed: Fishino operates as a client, that connects itself to the server, sends the temperature that is processed and saved on the database. In order to write the page it is enough to use a text editor such as Windows’ Notepad. The file will have to be saved, for example as in in.php (we advise to change name but please do not pick a too long one), and later loaded on your web space (preferably in the root directory) by using, for example, the hosting service’s user panel (or file manager). Let us see the parts the script in the in.php file is composed of. Please start by opening the <?php tag (please remember to close it with ?> in the end); your script will follow from here.

You will then report (Listing 2) the connection parameters to your database (they are usually found in the control panel): $username, $password, $db, $tablename, $host. The variables’ names (we would like to remind that in the PHP language they are indicated with the $ in front) may also be changed, but we have to remember to keep the chosen name for the whole script. Some hosting services advise to keep the username, password and host fields blank. This is because of two reasons: first of all, they are automatically recovered, and then the PHP’s disadvantage is that often, in the case of syntax errors (or of any other kind of error) the whole code is printed to screen, including all these personal parameters. In any case, if you don’t make any mistake, there is no risk. The $tablename variable represents the table’s name; we named it “fish tank” but if you chose a different name you will then have to substitute it with the one you set.


// ------------- TODO ----------------------
$tablename = “fish tank”;


In order to make the server interpret the data transmitted by Fishino UNO, we will use the GET method (we could have also used the POST method) with which it is possible to make requests to a Web Server. The GET method consists in queuing the data we’re interested in, at the requested page address. In order to make it, you will have to add – after the page address – a question mark followed by the couples name = value for the data you’re interested in. If there is more than one couple, they must be divided by the & symbol. The result will be a $_GET global variable, containing the array of all the field=value couples that we will be able to use in the script.

In our case we will only have a couple made by the temperature and the related value.

For example, we might want to transmit the temperature=25.6 couple to the Web Server, so to have it interpret it and, at a later time, to load it on the database. In order to do so, you will have to assign to the $temperature_fish tank variable the value in the temperature field, as transmitted by the $_GET array.

For example, if you name the temperature’s field with the t character, you will then be able to select this specific array element, represented by the $_GET variable, by writing $temperature_fish tank = $_GET[‘t’] (please remember the apostrophes before and after the t). This way, by typing in the browser’s address bar, you will have as a result that the $temperature_fish tank variable will be equal to 25.6.


$temperature_fish tank = $_GET[‘t’];
echo “Temperature fish tank = “ . $temperature_fish tank;


Additionally, if you wanted to view the data you just inserted as shown by the browser, it will be enough to show it as an output with the echo function (or print, or any other function with the same task).

It must be noticed that if there were any syntax or typing errors (or any other kind of error) in the MySQL query, those would not be notified but, simply, no data would be loaded on the database. In the case of errors you would simply see the temperature as an output but, by controlling in the data contained in the database, you would see an empty table. In order to verify that no mistake has been done, after having done the test we just described (to insert the temperature of 25.6), please check that the data has been correctly saved in the database.

If everything happened without mistakes, then the table will not be empty but it will contain the data that has just been inserted. We advise to try to insert more than one piece of data, so to populate the table, as shown in figure.




This will prove useful when you will have to test the proper functioning of the script showing the diagram (we will talk about it later). The time has come to save the data in the database (Listing 4).


if ($con = mysqli_connect($host, $username, $password))
mysqli_select_db($con,$db) or die (“Unable to select database”);
$query = “INSERT INTO $tablename VALUES ($temperature_fish tank, now())”;
$result = mysqli_query($con,$query);
echo(‘Unable to connect to database.’);

For this purpose, as a first thing you will have to use the mysqli function, in order to make your Web application to connect to the MySQL Database Manager and, later, to select the database you’re interested in. By means of if ($con = mysqli_connect($host, $username, $password)) a connection is attempted and, in the case of failure, the error Unable to connect to database is shown.

On the other hand, if it reaches a successful ending, a connection to the database is attempted, by means of mysqli_select_db($con,$db); even here an error message is shown in the case of failure.


//Sending mail to critical temperatures
if ($temperature_fish tank < 23)
$message = “The aquarium temperature is too LOW! Detected temperature of “ .
$temperature_fish tank . “°C in data: “ . date(“Y-m-d H:i:s”);
mail(‘’,’Temperature fish tank CRITICS ', $ message);
if ($temperature_fish tank > 29)
$message = “The aquarium temperature is too HIGH! Detected temperature of “ .
$temperature_fish tank . “°C in data: “ . date(“Y-m-d H:i:s”);
Temperature fish tank CRITICS ', $ message
); } ?>


The mysqli function also accepts the database’s name as a fourth parameter; therefore you might even use a single function in order to make the application to connect to the MySQL Database Manager and to select a database: mysqli_connect($host, $username, $password, $db).

The $con variable is the identification value for the connection, that will be used in order to communicate with the database.

Once the connection occurs, a query (a database interrogation) to the database will be sent, by means of the mysqli_query(identification, query) function, in which the identification value for the connection is $con (if missing, the last identification value will be chosen) and the query is indicated as a string in $query. With this query we specify to insert the temperature and time values in the database fields in the $tablename table (in our case, fish tank). By means of the now() function, the Web Server’s current time is returned, in the YYYY-MM-DD HH:MM:SS format.

Since while defining the database table you set the CURRENT_TIMESTAMP Timestamp field as a default value, even if moving NULL in the place of now() no problem would arise.

You might want to add the possibility to receive an e-mail in the case of critical temperatures (an example is found in figure). This may be achieved by means of the e-mail function, whose syntax is: bool mail ( string $a , string $item , string $message [, string $header_addizional [, string $additional_headers ]] ). When omitting the optional parameters (between square brackets) it is obvious how this function enables the automatic sending of the $message, having $item as e-mail subject, to the $a address. The function may be recalled when the temperature is outside of a certain range (in the example, under 23°C and above 29°C).




Temperature diagram

As for the diagram on which to view the last detected temperatures, the simplest solution is the one to take advantage of the many libraries found on the Web; the one we used is the solution offered by Google, that makes a great number of optimized diagrams (for various devices, easy-to-use, and that may be customized as for many aspects) freely available. The Google charts – whose documentation may be found at the following web address: – may be freely used and are supplied under Creative Commons Attribution 3.0 license, while the coding examples are supplied under Apache 2.0 license.

The diagram we are showing in our example has been chosen from the “Line Charts” category, with a white background, a blue diagram line and curvilinear progress. On the X axis, we will represent the time progress, while on the Y axis there will be the temperature. Each single piece of data will then be labelled, with a date and the temperature in text format, that will appear when hovering the mouse cursor (or when touching the screen, as regards touch screen devices).

Even for the diagram visualization you will have to create a PHP script, by naming the file as fish tank_graphic.php. Please start (Listing 6) from the opening tags of a common HTML page (html, head, title, body) and then open the PHP tag by means of <?php. With the $max_value variable you will define how much data to show in the diagram: please choose the value and consider that a piece of data will be sent and loaded every 30 minutes, thus in order to see the diagram of a whole day, the variable’s value will have to be at least equal to 48. In our example we set this value to be equal to 100, which is enough to cover a bit more than 2 days.


<title>fish tank</title>
<h1 align=”center”>fish tank </h1>
<p align=”center”>- based on Fishino UNO - </p>
$max_value = 100; //How many shows maximum values in the graph
// ------------- TODO ----------------------
$tablename = “fish tank”;

Finally, please type in the parameters for the connection to the database, as you already did with the in.php file.

In the same way, we will have the web application to connect to the database: this may be achieved as for the in.php file or, in a shortened way, as shown in Listing 7.


$con = mysqli_connect($host, $username, $password);
mysqli_select_db($con, $db) or die(‘Error connection...’);


In order to get the temperatures from the database we should set the $q6 query; this time we will specify that we want to retrieve the temperatures with the connected dates from the fish tank table, and that they are to be ordered by starting from the most recent one.

After having queried the database by means of the mysqli_query function we will have the answer to the query in $r6. Let’s save then the number of rows extracted from the database in the $n_record variable, by means of the mysqli_num_rows function, this way we will obtain the total number of pieces of data in the database.

Once we have received the reply, we will have to consider the first 100 rows only of what has been returned by the database. In order to do so, please use the mysqli_fetch_row(function) function, that as a parameter has the result of the database query (in our case, the $r6 variable).

This function loads a row of the result associated with the specified identification value in an array, or returns NULL if there are no more rows.

Each element of this array will be formed by the couple temperature-data. Please use the list() construct in order to assign the value of the two fields to two variables, each represented by an array’s cell ($temp_water[$i] and $temp_time[$i], with $i being a variable initialized at 0 and increased by 1 at each cycle). This construct is used in order to assign values to a variables’ list in a single operation.

With the while cycle in the Listing 8 it is specified that, for each row returned by the database, the temperature and the date must be saved in the two arrays indicated, that respectively represent the temperature vector and the date vector. If the number of returned rows is greater than the maximum of the wanted elements, the cycle will end when the elements have run out; if on the other hand the number of rows is greater, then the cycle will end when the $i counter variable reaches the maximum value.


$q6 = “SELECT Temperature, Timestamp FROM $tablename ORDER BY Timestamp DESC”;
$i = 0;
$r6 = mysqli_query($con,$q6);
$n_rilevazioni = mysqli_num_rows($r6);
while((list($temp_water[$i], $temp_time[$i]) = mysqli_fetch_row($r6)) and $i<$max_value)
$i = $i+1;
$i = $i - 1;


Once out of the while cycle, it will be needed to decrease the $i variable by 1, so to have the $temp_water array index referred to the last inserted value (if the returned rows are more than 100, the value of $i will obviously be 100). This way you will have two arrays: one containing all the temperatures to be represented in the diagram, the other one with the dates concerning the acquisition. Now it’s the moment to insert these values in a diagram; in order to take advantage from the one offered by Google, please add the necessary libraries (Listing 9). At a later time, it will be enough to set some parameters related to the graphical aspect and to supply the data to be represented.


<script type=”text/javascript” src=””></script>


The most interesting aspect is the one concerning the data transmission, that usually occurs under the form of a table in which the columns represent the fields to be represented in this order: x coordinates and y coordinates, while the rows are the veritable pieces of data.

In Listing 10 we may in fact see the data variable, that represents the data, that allows to add columns by means of the addColumn(tipe, name) method and to add rows by means of the addRows method.


<script type=”text/javascript”>
google.load(‘visualization’, ‘1’, {packages: [‘corechart’, ‘line’]});
function drawBackgroundColor() {
var data = new google.visualization.DataTable();
data.addColumn(‘date’, ‘Time’);
data.addColumn(‘number’, ‘Temp’);
$j = 0;
while($i >= 0 and $temp_water[$i] != 0)
echo “[“;
echo “new Date(“;
$temp_date = date(“Y,m-1,d,H,i,s”, strtotime($temp_time[$i]));
echo $temp_date;
echo “)”;
echo “,”;
echo $temp_water[$i];
echo “]”;
if ($i > 0)
echo “,”;
$j = $j + 1;
$i = $i - 1;
var options = {
hAxis: {
title: ‘t’
vAxis: {
title: ‘gradi’
focusTarget: ‘category’,
curveType: ‘function’,
lineWidth: 3,
title: ‘Temperature fish tank of recent day’,
backgroundColor: ‘white’
var chart = new google.visualization.LineChart(document.getElementById(‘chart_div’));
chart.draw(data, options);
<!-- Portions of this code are modifications based on work created and shared by Google and used according to
terms described in the Creative Commons 3.0 Attribution License. Code samples are licensed under the Apache
2.0 License. -->


In order to avoid to have to manually add a row for each element, it is possible to take advantage of a cycle that iteratively adds rows that are separated by a comma, until the penultimate element is reached, after which the comma is no longer needed. In the end of the cycle there will be a result of the following kind: data.addRows([[data, temperature], [data, temperature], [data, temperature]]);. By associating the diagram to the chart_div container, it will be possible to show it as an output:

<span style="font-weight: 400;">&lt;div id=”chart_div”&gt;&lt;/div&gt;</span>

Before seeing the final result we will show you how it is possible to view the total amount of data in the database and the last detected temperature with the related time, as an output (Listing 11).


<p align=”right”><?php echo “Dati rilevati: #” . $n_rilevazioni; ?> </p>
echo “<br /><br /><strong>last temperature: “ . $temp_water[0] . “&deg;C in data: “ .
$temp_time[0] . “</strong>”;

Once the file has been saved (please remember to close all the opened tags), please load it on your website by means of the file manager.




The diagram will then be visible  by typing in, for example tank_temperature.php.




The Sketch

Let’s move on now to schematise the sketch’s logic in a few steps. We will do it with the help of Fig. 10. Initially the necessary libraries are included:

#include <Wire.h>

#include <RTClib.h>

#include <Flash.h>

#include <FishinoUdp.h>

#include <FishinoSockBuf.h>

#include <Fishino.h>

#include <SPI.h>

#include <SD.h>

#include <OneWire.h>

#include <avr/io.h>

#include <avr/wdt.h>




Many of them will already be there if you downloaded Arduino IDE, while other ones, such as Fishino’s ones (that may be downloaded from the fishino_websites, RTClib.h, needed in order to use the Real Time Clock-module, and OneWire,  they should be added to Arduino’s Libraries directory.

At a later time some variables will have to be defined (Listing 12): for example, it must be declared the pin to which the DS18B20 probe is connected (pin 2, in our case), Fishino client library must be initialized and the WiFi network’s name must be defined, as well as the related password (MY_SSID and MY_PASS). You will then have to define the pins with which the two relays will be activated and deactivated. Thanks to MAX_VAL you will be able to specify how many time slots you want to consider as a maximum, it is not advisable to insert a high value only to add a few times.


File myFile;
//Pin DATA di DS18B20: pin 2
OneWire ds(2);
// Initialize the Fishino client library
FishinoClient client;
//Credenziali rete WiFi
#define MY_SSID “your_network”
#define MY_PASS “your_password”
/* I define the relay activation pin.
* BUBBLES (Aeratore) = Pin 8
* PLAFO (ceiling+ CO2) = Pin 9
#define BUBBLES 8
#define PLAFO 9
//maximum number of time slots in the file in the microSD
#define MAX_VAL 4
//Matrix used to store read times by micro SD

For example, if you want to turn on the ceiling lights only once and, maybe, the aerator twice, you might want to set MAX_VAL as a value slightly greater than 3 (for example, 4 or 5) so not to waste memory and to be able to add some more time slots in the future – without having to recompile the sketch – and simply by means of a modification of the file found on the microSD.

Finally, a times_bubbles_cl[MAX_VAL][7] array will be used, in order to be able to save the times contained in the microSD, so to avoid having to open it at every cycle, but only once.

This array will have as many rows as the time slots (the remaining ones will be set to zero) and the columns will respectively indicate:

  1. switching on hour;
  2. switching on minute;
  3. switching on second;
  4. switching off hour;
  5. switching off minute;
  6. switching off second;
  7. a character to distinguish between lighting and carbon dioxide supplier or aerator.



In Listing 13 we may see Fishino’s initialization in the Setup: it may be noticed how Fishino is in station mode. The time array is initially filled with zeroes but, by means of the read_SD() function, it is progressively populated by the times defined by the microSD.



The microSD is read by means of the read_SD() function, that does not need any parameter and only opens the plaf.txt file (figure), that is contained in the microSD. Inside this file the times related to the two relays’ switching on and off are found, in the HHMMSSHHMMSSn format, with n being a character 1 or 2, depending if it is a time related to the lighting and to the carbon dioxide, or to the aerator. If, for example, we wanted to turn on the aerator between 10 and 11 o’clock in the morning, we would have to write: 1000001100001.




Please add a new text row only if you want to add a new time, otherwise do not leave empty rows.


void setup() {
//Initialize array elements to 0
for (int t = 0; t < MAX_VAL; t++)
for (int j = 0; j < 7; j++)
times_bubbles_cl[t][j] = 0;
//I read the file and insert the microSD times in the matrix
//Relé non eccitati
digitalWrite(PLAFO, LOW);
digitalWrite(BUBBLES, LOW);
// only for Leonardo needed
while (!Serial)
// initialize SPI
// reset and test WiFi module
// resetta e testa il modulo WiFi
while (!Fishino.reset())
Serial << F(“Fishino RESET FAILED, RETRYING....\n”);
Serial << F(“Fishino WiFi RESET OK\n”);
// go into station mode
// Initialize the Wifi connection.
Serial << F(“Setting up the Wifi connection...\n”);
// try forever to connect to AP
// tenta la connessione finchè non riesce
while (!Fishino.begin(MY_SSID, MY_PASS) == STATION_GOT_IP)
#ifdef IPADDR


Once all the characters of the plaf.txt file have been read and they have been inserted in the times string, it is analysed and, on the basis of the position of the character considered, a value (calculated by making the difference between tens and units) is inserted in an array’s cell (Listing 14). For example, the string “34” is transformed in the number 34 by multiplying 3 by 10, and by adding it to 4; all of this after having converted from char to integer.


/ * Function that reads the files in the microSD plaf.txt
* And extracts times by inserting them in the matrix
* Schedules.
* CS - pin 4 * /
read_SD void ()
String times = "";
if (! SD.begin (4)) {
int i;
char c;
myFile = ( "plaf.txt");
if (myFile) {
i = 0; // Number of rows found in the file
while (myFile.available ()) {
c = ();
times + = c;
if (c == '\ n') // There is one more line
++ I;
myFile.close ();
int x = 0;
int count = 0;
for (int j = 0; j <= i; j ++)
while (times [cont]! = '\ n')
switch (x)
case 0:
times_bubbles_plaf [j] [0] + = (times [cont] - 48) * 10; // HOURS_ON
case 1:
times_bubbles_plaf [j] [0] + = (times [cont] - 48);
case 2:
times_bubbles_plaf [j] [1] + = (times [cont] - 48) * 10; // MINUTES_ON
case 3:
times_bubbles_plaf [j] [1] + = (times [cont] - 48);
case 4:
times_bubbles_plaf [j] [2] + = (times [cont] - 48) * 10; // SECONDS_ON
case 5:
times_bubbles_plaf [j] [2] + = (times [cont] - 48);
case 6:
times_bubbles_plaf [j] [3] + = (times [cont] - 48) * 10; // HOURS_OFF
houses 7:
times_bubbles_plaf [j] [3] + = (times [cont] - 48);
houses 8:
times_bubbles_plaf [j] [4] + = (times [cont] - 48) * 10; // MINUTI_OFF
houses 9:
times_bubbles_plaf [j] [4] + = (times [cont] - 48);
10 houses:
times_bubbles_plaf [j] [5] + = (times [cont] - 48) * 10; // SECONDI_OFF
11 houses:
times_bubbles_plaf [j] [5] + = (times [cont] - 48);
12 houses:
times_bubbles_plaf [j] [6] + = (times [cont] - 48); // 1 (ceiling) or 2 (bubbles)
X ++;
++ Count;
++ Count;
x = 0;
times = "";



The getTemp() function, in Listing 15, has not been modified by the function that may also be found in the OneWire library. Its purpose is the one to calculate the temperature on the basis of what has been measured by the DS18B20 sensor. The function returns a float representing the temperature, expressed in Celsius degrees.


// Function to calculate the temperature in degrees Celsius through the DS18B20
float getTemp() {
byte data[12];
byte addr[8];
if ( ! {
//no more sensors on chain, reset search
return -1000;
if ( OneWire::crc8( addr, 7) != addr[7]) {
return -1000;
if ( addr[0] != 0x10 && addr[0] != 0x28) {
return -1000;
ds.write(0x44, 1); // start conversion, with parasite power on at the end
byte present = ds.reset();;
ds.write(0xBE); // Read Scratchpad
for (int i = 0; i < 9; i++) { // we need 9 bytes
data[i] =;
byte MSB = data[1];
byte LSB = data[0];
float tempRead = ((MSB << 8) | LSB); //using two’s compliment
float TemperatureSum = tempRead / 16;
return TemperatureSum;



The hour_right (Listing 16) function accepts three times (formed by hours and minutes) as parameters and it checks if the third time is located between the first two.

The function does not check if the two times are inverted, but it is possible to add such an option. In any case it is enough to respect the order with which they are inserted.

Initially, the reply variable is set to true, in the function. At a later time, it is verified if the examined time is found outside of the analyzed interval; in the case this turns out to be true, the reply variable is set to false.

At the end of the comparisons, the reply value is returned. If such a value has remained set to true, it thus means that the analyzed time is found between the other two.


/ * Function to decide whether the current time is in a range or less.
* Accept 6 parameters:
* - First Hour
* - First minute
* - Second hour
* - According to minutes
* - Current time
* - Current Minute
* Returns true or false if the calculation result is affirmative or not * /
boolean right now (Now1 int, int minute1, Now2 int, int minute2, ora_adesso int, int minut_adesso)
// I consider now an affirmative answer
boolean answer = true;
// I'm going to look for if you are out of range
if (now_hour <Now1 || ora_adesso> Now2)
answer = false;
if (Now1 Now2 && == == minute1 minute2)
answer = false;
if (hour_now == Now1 && minute_now <minute1)
answer = false;
if (hour_now == Now2 && minute_now> = minute2)
answer = false;
if (Now1> Now2 || (Now1 == Now2 && minute1> minute2))
answer = false;
return response;


Let’s move on now to analyze the main body of the sketch, that is the Loop() function (Listing 17).

At the beginning of the cycle, the WatchDog is set at 8 seconds, so to reset Fishino in the case of blocks or infinite cycles, that may be caused by unexpected errors.

The server to which to connect is then defined, it is enough to indicate the address of your Website (on which you will have loaded the in.php file) in server[]. Hereafter, the temperature value is read by the probe, measurement errors are avoided thanks to a while cycle.

Every 30 minutes the connection to the server must occur, in order to send the temperature, therefore possible connections that are still opened are closed, and the string to be sent is readied.

The string will be composed of the GET, with which we dealt already, and of the path for the in.php file on the website (if you put it in the root directory, it is enough to type in /in.php) followed by the t field (the one that will be used by the PHP script in order to recover the temperature value) with the related value. Finally, a connection to the Web Server is carried out and and a temperature is sent by means of the GET method.

Please remember also to change the host address found in client.println(“Host:”) by typing in, once again, the server address.

At a later time, the time array is scrolled, so to decide if the relays have to be switched on or off and, in order to do so, two variables are used: bubbles_on and plaf_on that are initially set to false and if the ora_giusta function returns true, they are then set to true.

At the end of the cycle, only the relays whose related boolean variable is set to true are excited, otherwise they are deactivated.

For safety reasons, once per day (at 23:59) Fishino is reset, so to zero all the counters and to avoid useless blocks. The reset is obtained by making it wait for 15 seconds, so to make the WatchDog expire.

This part may obviously be avoided.

The WatchDog counter is then reset, after having waited for a second. Finally the loop() cycle is executed again.

If you never set the time of Fishino’s RTC, you may easily do it by loading the Softrtc sketch that you may find among the samples in the RTClib library.


void loop () {
// Set the WatchDog to 8 seconds
wdt_enable (WDTO_8S);
// Server address:
char server [] = "<a href=";sa=D&amp;ust=1464260862236000&amp;usg=AFQjCNFd5-YUR1G1Bgsm-4alaDzGtOTs7g" target="_blank"></a>";
float temperature = 0.0;
while (temperature &lt;1 || temperatures&gt; 84) // Avoid impossible to print data
getTemp temperatures = ();
// Initialize string to send to the server
Temperatures String = "";
// Current date
DateTime now = ();
// These variables are not needed, are used for convenience only
int hours = now.hour ();
int minute = now.minute ();
int seconds = now.second ();
int year = now.year ();
// At the beginning of each hour and every half ...
if ((minutes == 0 || minutes == 30) &amp;&amp; (seconds == 0 || seconds == 1))
// Close any connection before send a new request.
// This will free the socket on the WiFi module
// Closes every ventuale connection before sending a new request
// This free socket on WiFi module
client.stop ();
delay (1000);
// I prepare the string to be sent to the server
temperatures = "GET /in.php?t=";
temperature + = temperature;
temperature + = "HTTP / 1.1";
// If you get a connection, reporting back via serial:
// If you get a connection, print the data on the serial
if (client.connect (server 80))
//Serial.println("connected To server ");
// Make a HTTP request:
client.println (temperatures);
client.println ( "Host: <a href=";sa=D&amp;ust=1464260862236000&amp;usg=AFQjCNFd5-YUR1G1Bgsm-4alaDzGtOTs7g" target="_blank"></a>");
client.println ( "User-Agent: ArduinoWiFi / 1.1");
client.println ( "Connection: close");
client.println ();
/ * Variables to decide whether to turn on or off the relays:
* False = off
* True = on
* Initially I consider them off * /
bubbles_on boolean = false, plaf_on = false;
/ * I scan the array of times to check if something has to be turned on
* (If you can find something to turn pose to true the bubbles_on or plaf_on variables) * /
for (int k = 0; k &lt;MAX_VAL; k ++)
if (times_bubbles_plaf [k] [0] + times_bubbles_plaf [k] [1] + times_bubbles_plaf [k] [3] + times_bubbles_plaf [k] [4]! = 0)
if (hour_exactly (times_bubbles_plaf [k] [0], times_bubbles_plaf [k] [1], times_bubbles_plaf [k] [3], times_bubbles_plaf [k] [4], hours, minutes) == true)
switch (times_bubbles_plaf [k] [6])
case 1:
plaf_on = true;
case 2:
bubbles_on = true;
// I turned on (true) or off (false)?
if (plaf_on == false)
digitalWrite (PLAFO, LOW);
digitalWrite (PLAFO, HIGH);
if (bubbles_on == false)
digitalWrite (BUBBLES, LOW);
digitalWrite (BUBBLES, HIGH);
// I reset the Fishino once a day
if (hours == 23 &amp;&amp; minutes == 59 &amp;&amp; (seconds&gt; = 0 || seconds &lt;= 5))
delay (15000); // I leave the expiration 8/2 of WatchDog
// I wait a second
delay (1000);
// Reset the counter of WatchDog
wdt_reset ();



The system we presented here carries out only some of the functions that may be executed in the field of fishkeeping; with a bit more sensors and some modifications to the software, you might use the temperature value so to have Fishino manage the cooling and heating aspect, for example. On the basis of the plaf.txt file, you might insert a further file in the microSD and use it in order to define the colour of the RGB LED strips, so to give a merry spirit to the environment.


From openstore

Fishino UNO

Waterproof DS18B20 Digital temperature sensor

Module 1 relays (mounted)


About Boris Landoni

Boris Landoni is the technical manager of Skilled in the GSM field, embraces the Open Source philosophy and its projects are available to the community.

Leave a Reply

Your email address will not be published. Required fields are marked *