Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2017:pool-ctrl

Toto je starší verze dokumentu!


Zadání

Navrhněte modul pro ovládání činnosti filtračního a vyhřívacího systému bazénu. Použijte vývojovou desku s ethernetovým rozhraním (např. FRDM-K64F). Vytvořte jednoduchou webovou aplikaci, která umožní konfiguraci a řízení systému.


Úvod

Cieľom tohoto projektu je umožniť vzdialené ovládanie zariadení bazéna bez potreby návštevy technickej miestnosti. Taktiež je požadované automatické riadenie činnosti čerpadla a filtrácie na základe nastavených časov. Keďže je použitý modul K64F, ktorý obsahuje veľké množstvo vstupne-výstupných portov, do budúcnosti je možné tento projekt ľahko upraviť na využitie riadenia iných častí domácnosti, prípadne je možné pridať ďalšie senzory a možnosti pripojenia a ovládania (WiFi, displej, atď…).


Vývojový hardware

Ako vývojový HW boli použité nasledujúce moduly. FRDM-K64F ako hlavná riadiaca doska, CY7C68013A na debugovanie I2C zbernice a vlastný modul obsahujúci spínanie zariadení (čerpadlo, generátor ozónu) pomocou relé a zároveň integrujúci obvod reálneho času (RTC).

  • NXP Freedom Development Board FRDM-K64F
  • LCSOFT CY7C68013A Mini Board
  • Prídavná doska s relé a RTC


Obvodové zapojenie

K modulu FRDM-K64F bol navrhnutá externá doska obsahujúca relé a obvod reálneho času (RTC), ktorý komunikuje s procesorom pomocou I2C zbernice. Zapojenie a plošný spoj je vyobrazený nižšie. Pull-up rezistory na I2C dátovej zbernici nie sú použité z dôvodu, že ich obsahuje priamo doska FRDM-K64F, na ktorej je na túto zbernicu pripojený akcelerometer. Bolo preto potrebné vyvarovať sa použitiu rovnakých adries zariadení. Obvod reálneho času používa 5V úrovne, zatiaľ čo K64F 3.3V úrovne, a preto by ani nemohli byť pull-up rezistory použité a zapojené medzi 5V napájaciu vetvu a I2C zbernice. Obvod reálneho času DS1307 avšak bezproblémovo funguje aj v takomto zapojení.

Schéma zapojenia

Obraz plošného spoja

Zoznam súčiastok

Part     Value          Device       Package  
BATT1    CR2032V        CR2032V      CR2032V  
C1       100n           C-KER_0805   0805     
D1       1N4148.        1N4148.      SOD-80   
D2       1N4148.        1N4148.      SOD-80   
D3       1N4148.        1N4148.      SOD-80   
IO1      DS1337+        DS1337+      DIL8     
JP2                     PINHD-2X10   2X10     
K1       CZM_5/3        CZM_5/3      CZM_5/3  
K2       CZM_5/3        CZM_5/3      CZM_5/3  
K3       CZM_5/3        CZM_5/3      CZM_5/3  
PAD_+5V  +5V            PAD_1---     PAD_1--- 
R3       1k             R_0805       0805     
R4       1k             R_0805       0805     
R5       1k             R_0805       0805     
RE1      E3206S         E3206S       E3206S   
RE2      E3206S         E3206S       E3206S   
RE3      E3206S         E3206S       E3206S   
T1       BC847          BC847        SOT23    
T2       BC847          BC847        SOT23    
T3       BC847          BC847        SOT23    
XTAL1    32k768         CRYSTALTC26V TC26V
  

Software

Softwarové riešenie je možné rozdeliť na 2 časti - klientskú a serverovú. Klientská časť obsahuje samotnú web stránku v HTML a javascripte. Serverová časť sa skladá z jednoduchého webového servera schopného obsluhovať požiadavky GET a POST. Pomocou požiadavok POST sa následne vykonávajú všetky dostupné príkazy, ktoré museli byť ručne vytvorené a následne sa v kóde spracovávajú. Stránka sa načítava z microSD karty na ktorú sú umiestnené súbory index.html a api.js. Taktiež je tu možné umiestniť jQuery a Bootstrap javascript knižnice, avšak načítavanie stránky by kvôli nízkemu výkonu serveru zabralo značne dlhú dobu, preto sú tieto nalinkované na externé servery.

Web stránka - HTML kód

<!DOCTYPE html>
<html lang="en">
    <head>
		<title>Swimmingpool controller</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
        <script src="api.js"></script>
    </head>
 
    <body>
        <div class="container">
            <div class="row" id="last_update_time">
            </div>
			<button type="button" class="btn btn-primary"><h1>Swimmingpool controller</h1></button>			
            <div class="row" id="light_control">
                <div class="col-xs-12">
                    <h1><span name="state" class="label label-default">Light:</span></h1>
                    <ul class="nav nav-pills">
                        <li name="on" class="active"><a data-toggle="pill" onclick="trigger('light', 'on'); return false;">ON</a></li>
                        <li name="off"><a data-toggle="pill" onclick="trigger('light', 'off'); return false;"=>OFF</a></li>
                    </ul>
                </div>
            </div>
 
            <div class="row" id="pump_control">
                <div class="col-xs-12">
                    <h1><span name="state" class="label label-default">Pump:</span></h1>
                    <ul class="nav nav-pills">
                        <li name="on"><a data-toggle="pill" onclick="trigger('pump', 'on'); return false;">ON</a></li>
                        <li name="off"><a data-toggle="pill" onclick="trigger('pump', 'off'); return false;"=>OFF</a></li>
                        <li name="auto" class="active"><a data-toggle="pill" onclick="trigger('pump', 'auto'); return false;">AUTO</a></li>
                    </ul>
                </div>
            </div>
 
            <div class="row" id="ozone_control">
                <div class="col-xs-12">
                    <h1><span name="state" class="label label-default">Ozone:</span></h1>
                    <ul class="nav nav-pills">
                        <li name="on"><a data-toggle="pill" onclick="trigger('ozone', 'on'); return false;">ON</a></li>
                        <li name="off"><a data-toggle="pill" onclick="trigger('ozone', 'off'); return false;"=>OFF</a></li>
                        <li name="auto" class="active"><a data-toggle="pill" onclick="trigger('ozone', 'auto'); return false;">AUTO</a></li>
                    </ul>
                </div>
            </div>
			<p>&nbsp;</p>
            <div class="row">
                <div class="col-xs-12 col-sm-6" id="pump_intervals">
                    <h1><span class="label label-default">Pump ON intervals:</span></h1>
                    <div class="table-responsive">
                        <table class="table table-striped">
                            <thead>
                                <tr>
                                    <th>Start Hour</th>
                                    <th>Start Minute</th>
                                    <th>End Hour</th>
                                    <th>End Minute</th>
                                    <th></th>
                                </tr>
                            </thead>
                            <tbody>
                            </tbody>
                        </table>
                    </div>
                    <button type="button" class="btn btn-success" onclick="add_interval('pump');">Add <span class="glyphicon glyphicon-plus"></span></button>
                    <button type="button" class="btn btn-default" onclick="update_intervals('pump', 'set');">Update</button>
                </div>
 
                <div class="col-xs-12 col-sm-6" id="ozone_intervals">
                    <h1><span class="label label-default">Ozone ON intervals:</span></h1>
                    <div class="table-responsive">
                        <table class="table table-striped">
                            <thead>
                                <tr>
                                    <th>Start Hour</th>
                                    <th>Start Minute</th>
                                    <th>End Hour</th>
                                    <th>End Minute</th>
                                    <th></th>
                                </tr>
                            </thead>
                            <tbody>
                            </tbody>
                        </table>
                    </div>
                    <button type="button" class="btn btn-success" onclick="add_interval('ozone');">Add <span class="glyphicon glyphicon-plus"></span></button>
                    <button type="button" class="btn btn-default" onclick="update_intervals('ozone', 'set');">Update</button>
                </div>
            </div>
			<p>&nbsp;</p>
			<p>&nbsp;</p>
            <div class="row">
                <div class="form-group">
                    <div class="col-xs-6 col-md-2">
                        <label for="sel_year">Year:</label>
                        <select class="form-control" id="sel_year">
                            <option value="18">2018</option>
                            <option value="19">2019</option>
                            .
                            .
                            .
                        </select>
                    </div> 
                    <div class="col-xs-6 col-md-2">
                        <label for="sel_month">Month:</label>
                        <select class="form-control" id="sel_month">
                            <option value="1">1</option>
                            <option value="2">2</option>
                            <option value="3">3</option>
                            <option value="4">4</option>
                            .
                            .
                            .
                        </select>
                    </div> 
                    <div class="col-xs-6 col-md-1">
                        <label for="sel_date">Day:</label>
                        <select class="form-control" id="sel_date">
                            <option value="1">1</option>
                            <option value="2">2</option>
                            <option value="3">3</option>
                            .
                            .
                            .
                        </select>
                    </div> 
                    <div class="col-xs-6 col-md-2">
                        <label for="sel_day">Day of week:</label>
                        <select class="form-control" id="sel_day">
                            <option value="1">Monday</option>
                            <option value="2">Tuesday</option>
                            <option value="3">Wednesday</option>
                            <option value="4">Thursday</option>
                            <option value="5">Friday</option>
                            <option value="6">Saturday</option>
                            <option value="7">Sunday</option>
                        </select>
                    </div> 
                    <div class="col-xs-6 col-md-1">
                        <label for="sel_hour">Hours:</label>
                        <select class="form-control" id="sel_hour">
                            <option value="0">0</option>
                            <option value="1">1</option>
                            <option value="2">2</option>
                            .
                            .
                            .
                        </select>
                    </div> 
                    <div class="col-xs-6 col-md-1">
                        <label for="sel_min">Minutes:</label>
                        <select class="form-control" id="sel_min">
                            <option value="0">0</option>
                            <option value="1">1</option>
                            <option value="2">2</option>
                            .
                            .
                            .
                        </select>
                    </div> 
                    <div class="col-xs-6 col-md-1">
                        <label for="sel_sec">Seconds:</label>
                        <select class="form-control" id="sel_sec">
                            <option value="0">0</option>
                            <option value="1">1</option>
                            <option value="2">2</option>
                            .
                            .
                            .
                        </select>
                    </div>
				</div>
				<div class="row">
					&nbsp;<button type="button" class="btn btn-primary" onclick="set_clock();">Set clock</button>
				</div>					
            </div> 
        </div>
 
    </body>
</html>

Web stránka - Javascript kód

var update_interval = 20;
var error_resend_timeout = 2000;
 
function set_status(controller, new_status) {
    if (new_status == 0) {
        $("#" + controller + "_control li[name='off']").addClass('active').siblings().removeClass('active');
        console.log("changing " + controller + " to off");
    } else if (new_status == 1) {
        $("#" + controller + "_control li[name='on']").addClass('active').siblings().removeClass('active');
        console.log("changing " + controller + " to on");
    } else if (new_status == 2) {
        $("#" + controller + "_control li[name='auto']").addClass('active').siblings().removeClass('active');
        console.log("changing " + controller + " to auto");
    }
}
 
function set_state(controller, new_state) {
    if (new_state == 0) {
        $("#" + controller + "_control span[name='state']").addClass('label-danger').removeClass('label-default label-success');
        console.log("changing " + controller + " state to stop");
    } else if (new_state == 1) {
        $("#" + controller + "_control span[name='state']").addClass('label-success').removeClass('label-default label-danger');
        console.log("changing " + controller + " state to play");
    }
}
 
function update_time(tmv) {
    var d = new Date();
    // ds1307 uses year in range 0 - 99
    d.setFullYear(tmv.year + 2000);
    // ds1307 uses month in range 1 - 12
    d.setMonth(tmv.month - 1);
    d.setDate(tmv.date);
    d.setHours(tmv.hour);
    d.setMinutes(tmv.min);
    d.setSeconds(tmv.sec);
    $("#last_update_time").html("Last update: " + d.toLocaleString());
 
    $("#sel_year").val(tmv.year);
    $("#sel_month").val(tmv.month);
    $("#sel_date").val(tmv.date);
    $("#sel_day").val(tmv.day);
    $("#sel_hour").val(tmv.hour);
    $("#sel_min").val(tmv.min);
    $("#sel_sec").val(tmv.sec);
}
 
function set_clock_ajax(data_string) {
    $.ajax({
        url: "/set_clock",
        type: "POST",
        data: data_string,
        contentType: "text/plain",
        dataType: "json",
        success: function(result) {
            update_time(result.time);
        },
        error: function (request, status, error) {
            console.log(request);
            console.log(status);
            console.log(error);
            setTimeout(function() {set_clock_ajax(data_string);}, error_resend_timeout);
        },
    });
}
 
function set_clock() {
    var year = $("#sel_year").val().toString();
    var month = $("#sel_month").val().toString();
    var date = $("#sel_date").val().toString();
    var day = $("#sel_day").val().toString();
    var hour = $("#sel_hour").val().toString();
    var min = $("#sel_min").val().toString();
    var sec = $("#sel_sec").val().toString();
    var data_string = year + " " + month + " " + date + " " + day + " " + hour + " " + min + " " + sec;
    set_clock_ajax(data_string);
}
 
function trigger_ajax(controller, action) {
    $.ajax({
        url: "/" + controller + "_" + action,
        type: "POST",
        dataType: "json",
        success: function(result) {
            set_status(controller, result.status);
            set_state(controller, result.state);
            update_time(result.time);
        },
        error: function (request, status, error) {
            console.log(request);
            console.log(status);
            console.log(error);
            setTimeout(function() {trigger_ajax(controller, action);}, error_resend_timeout);
        },
    });
}
 
function trigger(controller, action) {
    console.log(controller + "_" + action);
    trigger_ajax(controller, action);
}
 
function update_all() {
    trigger("light", "status");
    trigger("pump", "status");
    trigger("ozone", "status");
}
 
var max_intervals = {
    "pump": 10,
    "ozone": 10,
};
var intervals_count = {
    "pump": 0,
    "ozone": 0,
};
 
function add_interval(ctrl) {
    console.log("adding " + ctrl + " interval");
    var hour_spinbox = '<input type="number" min="0" max="23" value="0" style="max-width: 50px;" required>';
    var min_spinbox = '<input type="number" min="0" max="59" value="0" style="max-width: 50px;" required>';
 
    if (intervals_count[ctrl] < max_intervals[ctrl]) { //max input box allowed
        $("#" + ctrl + "_intervals tbody").append(
                '<tr id="' + ctrl + '_interval' + intervals_count[ctrl] + '"><th name="start_hour">' + hour_spinbox + '</th><th name="start_minute">' + min_spinbox +
                '</th><th name="end_hour">' + hour_spinbox + '</th><th name="end_minute">' + min_spinbox +
                '</th><th><button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-minus"></span></button></th></tr>');
        intervals_count[ctrl]++;
        var tr_to_remove = $("#" + ctrl + "_intervals tbody tr:last");
        $(tr_to_remove).find("button").click(function () {
            console.log("removing " + ctrl + " interval");
            $(tr_to_remove).remove();
            intervals_count[ctrl]--;
        });
    }
    return tr_to_remove;
}
 
function set_interval(interval, values) {
    $(interval).find("[name=start_hour] input").val(values["start_hour"]);
    $(interval).find("[name=start_minute] input").val(values["start_minute"]);
    $(interval).find("[name=end_hour] input").val(values["end_hour"]);
    $(interval).find("[name=end_minute] input").val(values["end_minute"]);
}
 
function set_intervals(ctrl, intervals, tmv) {
    for (var i = 0; i < intervals.length; i++) {
        var interval = add_interval(ctrl);
        set_interval(interval, intervals[i]);
    }
    update_time(tmv);
}
 
function clear_intervals(ctrl) {
    $("#" + ctrl + "_intervals tbody").empty();
    intervals_count[ctrl] = 0;
}
 
function update_intervals_ajax(ctrl, act, data_values) {
    $.ajax({
        url: "/" + ctrl + "_" + act + "_intervals",
        type: "POST",
        data: data_values,
        contentType: "text/plain",
        dataType: "json",
        success: function(result) {
            console.log("set new values for " + ctrl + " intervals");
            clear_intervals(ctrl);
            set_intervals(ctrl, result["intervals"], result["time"]);
            update_all();
        },
        error: function (request, status, error) {
            console.log(request);
            console.log(status);
            console.log(error);
            setTimeout(function() {update_intervals_ajax(ctrl, act, data_values);}, error_resend_timeout);
        },
    });
}
 
function update_intervals(ctrl, act) {
    console.log("update " + ctrl + " intervals");
    var data_values = "";
    $("#" + ctrl + "_intervals tbody tr").each(function(index) {
        var start_hour = $(this).find("[name=start_hour] input").val().toString();
        var start_minute = $(this).find("[name=start_minute] input").val().toString();
        var end_hour = $(this).find("[name=end_hour] input").val().toString();
        var end_minute = $(this).find("[name=end_minute] input").val().toString();
        data_values += start_hour + " ";
        data_values += start_minute + " ";
        data_values += end_hour + " ";
        data_values += end_minute;
        data_values += "\n";
    });
 
    console.log(data_values);
 
    update_intervals_ajax(ctrl, act, data_values);
}
 
update_all();
setInterval(update_all, update_interval * 1000);
update_intervals("pump", "get");
update_intervals("ozone", "get");

Demonštračné video


Záver


Odkazy na použité zdroje

2017/pool-ctrl.1515896512.txt.gz · Poslední úprava: 2018/01/14 03:21 autor: Adam Bartoš