Управляем светодиодом через интернет с использованием RaspberryPi

в 15:33, , рубрики: diy или сделай сам, gpio, node.js, nodejs, Raspberry Pi, socket.io

В наше время обычная вещь, подключённая к интернету, начинает становиться обыденностью. Даже появилось понятие — «интернет вещей» (Internet of Things, IoT). Но как подступиться к этому своеобразному интернету новичку — не всегда понятно, потому что хотя статей по данной теме много, но каждому хочется, чтобы статья была простой для воспроизведения и чтобы в ней разбиралось что-то очень близкое и приятное для читателя.

Поэтому попробуем подключить к интернету самое простое — светодиод, взятый из сломанной оптической мыши. Будем через страницу в интернете включать и выключать светодиод, управлять частотой его мерцаний.

Для нашего эксперимента потребуется

  • Интернет через ethernet кабель
  • Raspberry Pi
  • Сломанная оптическая мышка, провода, паяльник
  • Доступ к сайту на vps с правами рута

Собираем схему

Схема подключения светодиода к интернету будет следующая:

Управляем светодиодом через интернет с использованием RaspberryPi

Итак, соберём все детали на столе и спаяем нашу простую схемку. Из мышки достаём светодиод и резистор в 220 Ом. Резистор нужен для ограничения тока, он мелкий и его еле можно разглядеть на конце провода. Для соединения проводов с GPIO использую коннекторы, которые выпросил в сервисе, ремонтирующем компьютеры.

Управляем светодиодом через интернет с использованием RaspberryPi

Подготавливаем софт

На сервере и «малинке» должен стоять node.js и npm (node package manager). Устанавливаем по данной инструкции.

Для того, чтобы node.js смог работать с GPIO, требуется установить модуль rpi-gpio. А для соединения с сервером RPi потребуется socket.io-client. Устанавливаются пакеты командой sudo npm install rpi-gpio и sudo npm install socket.io-client.

Скрипт для node.js на Raspberry Pi:

var socket = require('socket.io-client')('vpssite.domain:3141');
var gpio = require('rpi-gpio');
var fs = require('fs');

// hack due to error
fs.exists = require('path').exists;
var async = require('async');

// pin GPIO4
var pin = 7;

// current fps
var piFps = 0;
var currentValue = false;
var timemanager;


var set0 = function(err, results) {
    if (err)
        console.log(err);
    console.log('Pin ' + pin + ' closed');
    directWrite(pin, false, function() {
        clearTimeout(timemanager);
    });
};

var blinkexec = function() {
    delayedWrite(7, true, function() {
        delayedWrite(7, false, blinkexec)
    });
};

var blink = function(err, results) {
    if (err)
        console.log(err);
    console.log('Pin ' + pin + ' blinking');
    blinkexec();
};


function directWrite(pin, value, callback) {
    return gpio.write(pin, value, callback);

}
function delayedWrite(pin, value, callback) {
    var delay = Math.round(1000 / piFps / 2);
    
    clearTimeout(timemanager);
    timemanager = setTimeout(function() {
        directWrite(pin, value, callback);
    }, delay);
}


var release = function() {
    console.log('Writes complete, pause then unexport pins');
    setTimeout(function() {
        gpio.destroy(function() {
            console.log('Closed pins, now exit');
            return process.exit(0);
        });
    }, 500);
};


socket.on('connect', function() {
    console.log('connected');
    
    socket.on('setfps', function(data) {
        console.log(data);
        
        if (data.fps > 0) {
            piFps = data.fps;
            gpio.setup(pin, gpio.DIR_OUT, blink);
        } else {
            gpio.setup(pin, gpio.DIR_OUT, set0);
        }
    });
    
    socket.on('disconnect', function() {
        console.log('disconnect');
        release();
    });
});

Следующий код на сервере создаёт сервер на порту 3141 и принимает команду setfps и рассылает её дальше всем браузерам и Raspberry Pi.

Скрипт для node.js на vps

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

var allClients = [];
var count = 0;
var fpsPi = 0;

server.listen(3141);

server.on('error', function(e) {
    if (e.code == 'EADDRINUSE') {
        console.log('Address in use, exit...');
        process.exit();
    }
});

app.get('/', function (req, res) {
  res.send('Fps is ' + fpsPi);
  console.log('requested / - show ' + fpsPi);
});

function getDate() {
    var datas = new Date();
    return datas.getHours() + ':' + datas.getMinutes() + ':' + datas.getSeconds() + '.' + datas.getMilliseconds()
}

function consolelog(msg) {
    console.log(getDate() + ' ' + msg);
}

io.on('connection', function (socket) {
    io.emit('setfps', {fps: fpsPi});
    
    // browser subscribes to listen to the station 
    socket.on('subscribe', function(data) {
        socket.json.emit('subscribed', {fps: fpsPi});
        io.emit('setfps', {fps: fpsPi});
    });
    
    // disconnect on error. Browser will reconnect
    socket.on('error', function() {
        socket.disconnect();
    });
    
    // client disconnects
    socket.on('disconnect', function() {
        consolelog('Client disconnected.');
    });
    
    // save to RPi and browsers new fps
    socket.on('setfps', function(fps){
        fpsPi = fps;
        consolelog('setfps ' + fpsPi);
        io.emit('setfps', {fps: fpsPi});
    });
});

Запуск работы

Запускаем скрипты следующим образом. На RaspberryPi — с правами рута: sudo nodejs led.js, а на сервере — просто добавляем в кронтаб * * * * * cd /var/www/apps; node server.js >> cron_rpi.log, это не так красиво, зато всегда сервер будет запущен и можем о нём забыть.

На странице своего сайта включаем код для слайдера jquery-ui и socket.io с нашего сервера. При получении сигнала от node.js-сервера слайдер выставляет текущее значение fps и наоборот — при передвижении слайдера мы на сервер отсылаем новое значение fps, которое сервер затем рассылает всем клиентам в браузеры и на Raspberry Pi.

Код, размещаемый на странице сайта

<script src="http://vpssite.domain:3141/socket.io/socket.io.js"></script>

<script>
    function setSlided(val) {
        if (val == 0) {
            $('#freq').html('Выключен');
            $('#freq2').hide();
        }
        else {
            $('#freq').html(val);
            $('#freq2').show();
        }
    }

    $(function() {
        // соединяемся с nodejs на сервере
        var socket = io.connect('http://bk-it.ru:3141');
        socket.emit('subscribe');

        // при ответе сервера выставляем текущую fps
        socket.on('subscribed', function(data) {
            if (!data.error) {
                setSlided(data.fps);
                $("#loading").hide();
                
                $("#slider").slider({
                    min: 0,
                    max: 20,
                    value: data.fps,
                    slide: function(event, ui) {
                        $("#slider").slider({ disabled: true });
                        socket.emit('setfps', ui.value);
                        //setSlided(ui.value);
                    }
                });
            }
        });
        socket.on('setfps', function(data) {
            if (!data.error) {
                setSlided(data.fps);
                $("#slider").slider({value: data.fps});
                $("#slider").slider({ disabled: false});
            }
        });
    });
</script>

<h1>Светодиод, подключённый к интернету</h1>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>

<div class="panel panel-info">
    <div class="panel-heading">Управляйте светодиодом через сайт</div>
    <div class="panel-body">
        <div class="row">
            <div class="col-md-6">
                <div  style="height:40px;position:relative;top:12px;">
                    <div id="slider"><div id="loading" style="position:relative;top:-4px;">Соединяемся <img src="/img/loading.gif" alt=""></div></div>
                </div>
            </div>
            <div class="col-md-6">
                <div class="well-sm">Частота мерцания <b id="freq">Выключен</b> <span id="freq2" style="display:none;">раз/сек</span></div>
            </div>
        </div>
    </div>
    <div class="panel-footer">
        Не так часто встречаете светодиод, подключённый к интернету?
    </div>
</div>

Результаты работы

А вот и видео, описывающее, что получилось:

Вот, собственно, и всё, светодиод подключен к интернету. Теперь с любого устройства с интернетом мы можем управлять его мерцанием. Простор для дальнейшего творчества большой. Можно к выводам подключить модуль-реле и кнопкой на своём сайте включать и выключать любое оборудование. Модулей для Raspberry Pi продаётся очень много, поэтому полёт фантазии почти не ограничен, особенно если в наличии восторженный восьмилетний почитатель электроники.

Полезные ссылки, помогшие эксперименту

Документация Socket.io;
Документация Node.js;
Пакет rpi-gpio;
Документация на jquery-ui слайдер;
Распиновка GPIO на Raspberry Pi;
Модуль respawn для автоматического запуска nodejs-приложения (но я выбрал crontab).

Код можете взять из rpi-led на гитхабе.

Автор: shukshinivan

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js