Open top menu
sexta-feira, 14 de novembro de 2014


Continuando com a série de postagens do projeto de automação com RaspBerry Pi (primeiro post aqui e segundo post de montagem da placa aqui), hoje vamos desenvolver o WebServer com Flask para comandar as saídas da placa de automação.



Conexões com GPIO
O projeto é baseado na versão B do RaspBerry. Vamos relembrar que os pinos GPIOs não tem nenhuma proteção e usam nível lógico de 3V3, isso significa que ligar 5V em alguma entrada inutilizará a porta e provavelmente todo o RaspBerry. Faça uma dupla checagem, revisando todas as conexões antes de qualquer teste e alimentação da placa.




Flask
Flask é um microframework WEB escrito em Python baseado nas bibliotecas WSGI Werkzeug e Jinja2. Ele é flexível e simplifica o desenvolvimento WEB com todo o poder do Python.



Instalando o Flask no RaspBerry

Após instalação e configuração do Linux no Raspberry, podemos conectar remotamente no terminal via SSH, dispensando o uso de teclado, mouse e monitor. Usuários do Windows podem baixar o programa PuTTY e conectar conforme passos abaixo:
O putty não requer instalação, execute o arquivo putty.exe e digite o IP do RaspBerry.


Digite o login (pi) e password (raspberry) para abertura do terminal.



  
O primeiro passo é atualizar todos os pacotes instalados no linux.
$ sudo apt-get update  
E em seguida instalar o pip e o Flask

$ sudo apt-get install python-pip
$ sudo pip install flask
RPi.GPIO
Vamos usar o módulo RPi.GPIO para controlar as portas, ele abstrai o acesso as GPIOs do RaspBerry, disponibilizando funções prontas em Python para leitura e acionamento das portas.
Ele já vem instalado na distribuição Raspbian, mas nas primeiras versões ele não permitia ler o estado de uma porta configurada como OUT,  gerando a exceção :
GPIO pins are not duplex (they can read or write but not both) ... you must explicitly change it to write mode to write and read mode to read ...
Como essa funcionalidade será usada em nosso projeto, será necessária sua atualização.

$ sudo apt-get update
$ sudo apt-get remove python-rpi.gpio python3-rpi.gpio
$ sudo apt-get install python-pip python3-pip
$ sudo pip-2.7 uninstall RPi.GPIO
$ sudo pip-3.2 uninstall RPi.GPIO
$ sudo apt-get install python-rpi.gpio python3-rpi.gpio

Criando o WebServer.

A estrutura do nosso projeto está dividida em 2 arquivos. O arquivo webraspio.py é o código fonte do nosso programa e o arquivo template/webraspio.html contém o html que será usado como template para resposta das requisições http.
A principal característica de um projeto de automação é permitir visualizar os valores de seus sensores e estado das saídas de sua placa de comando,  os pinos que estão sendo usados para comandar a placa devem permitem ler e alterar seu estado, o programa usa essa informação para gerar o html de resposta para o usuário.
webraspio.py




from flask import Flask, render_template

import datetime
import RPi.GPIO as GPIO

app = Flask(__name__)

GPIO.setmode(GPIO.BOARD)

tempdata = {
      'title' : 'GPIO pins are not duplex (they can read or write but not both) ... you must explicitly change it to write mode to write and read mode to read ...'
      }
statuspin = {'statusSaida1' : False,
'statusSaida2' : False,
'statusSaida3' : False,
'statusSaida4' : False
}  
pinSaida1 = 15;
pinSaida2 = 16;
pinSaida3 = 18;
pinSaida4 = 22;

GPIO.setup(pinSaida1, GPIO.OUT)
GPIO.setup(pinSaida2, GPIO.OUT)
GPIO.setup(pinSaida3, GPIO.OUT)
GPIO.setup(pinSaida4, GPIO.OUT)

GPIO.output(pinSaida1, False)
GPIO.output(pinSaida2, False)
GPIO.output(pinSaida3, False)
GPIO.output(pinSaida4, False)

@app.route("/")
def home():

   tempdata = {
      'title' : 'RPi GPIO Control'
      }
     
   return retornaestatus()

   
@app.route("/saida1/on")
def actionSaida1On():
GPIO.output(pinSaida1, True)  
return retornaestatus()
@app.route("/saida1/off")
def actionSaida1Off():
GPIO.output(pinSaida1, False)  
return retornaestatus()
@app.route("/saida2/on")
def actionSaida2On():
GPIO.output(pinSaida2, True)  
return retornaestatus()
@app.route("/saida2/off")
def actionSaida2Off():
GPIO.output(pinSaida2, False)  
return retornaestatus()

@app.route("/saida3/on")
def actionSaida3On():
GPIO.output(pinSaida3, True)  
return retornaestatus()
@app.route("/saida3/off")
def actionSaida3Off():
GPIO.output(pinSaida3, False)  
return retornaestatus()


@app.route("/saida4/on")
def actionSaida4On():
GPIO.output(pinSaida4, True)  
return retornaestatus()
@app.route("/saida4/off")
def actionSaida4Off():
GPIO.output(pinSaida4, False)  
return retornaestatus()


def retornaestatus():

statusSaida1 = GPIO.input(pinSaida1);
statusSaida2 = GPIO.input(pinSaida2);
statusSaida3 = GPIO.input(pinSaida3);
statusSaida4 = GPIO.input(pinSaida4);
statuspin = {'statusSaida1' : statusSaida1,
'statusSaida2' : statusSaida2,
'statusSaida3' : statusSaida3,
'statusSaida4' : statusSaida4
}  
return render_template('webraspio.html', **statuspin)

if __name__ == "__main__":
   app.run(host='0.0.0.0', port=80, debug=True)
   
       
arquivo template/webraspio.html


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="automacaolivre">
    <link rel="icon" href="../../favicon.ico">

    <title>WebServer</title>

<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">

<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">

<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="../../assets/js/ie-emulation-modes-warning.js"></script>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>

  <body role="document">

    <!-- Fixed navbar -->
    <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="/">WebServer</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/">Home</a></li>            
            <li><a href="http://www.automacaolivre.com.br">Instruções</a></li>                       
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>

    <div class="container theme-showcase" role="main">

<br>
<br>
<br>
      <!-- Main jumbotron for a primary marketing message or call to action -->
      <div class="jumbotron">    
        
        <p>Placa de comando.</p>        
<div class="row">
<div class="col-md-6">
 <table class="table table-bordered">
<tbody>
 <tr>
<td><b>Saida 1</b></td>
<td>
<div class="btn-group" role="groupSaida1" aria-label="...">
{% if statusSaida1 %}
 <button type="button" class="btn btn-success" onclick="window.location.href='/saida1/on';">Ligado</button>
 <button type="button" class="btn btn-default" onclick="window.location.href='/saida1/off';">Desligar</button>  
{% else %}  
<button type="button" class="btn btn-default" onclick="window.location.href='/saida1/on';">Ligar</button>
<button type="button" class="btn btn-danger" onclick="window.location.href='/saida1/off';">Desligado</button>  
{% endif %}
</div>
</td>              
 </tr>             
 <tr>
<td><b>Saida 2</b></td>
<td>
<div class="btn-group" role="groupSaida2" aria-label="...">
 {% if statusSaida2 %}
 <button type="button" class="btn btn-success" onclick="window.location.href='/saida2/on';">Ligado</button>
 <button type="button" class="btn btn-default" onclick="window.location.href='/saida2/off';">Desligar</button>  
{% else %}  
<button type="button" class="btn btn-default" onclick="window.location.href='/saida2/on';">Ligar</button>
<button type="button" class="btn btn-danger" onclick="window.location.href='/saida2/off';">Desligado</button>  
{% endif %}
</div>
</td>              
 </tr>             
 <tr>
<td><b>Saida 3</b></td>
<td>
<div class="btn-group" role="groupSaida3" aria-label="...">
 {% if statusSaida3 %}
 <button type="button" class="btn btn-success" onclick="window.location.href='/saida3/on';">Ligado</button>
 <button type="button" class="btn btn-default" onclick="window.location.href='/saida3/off';">Desligar</button>  
{% else %}  
<button type="button" class="btn btn-default" onclick="window.location.href='/saida3/on';">Ligar</button>
<button type="button" class="btn btn-danger" onclick="window.location.href='/saida3/off';">Desligado</button>  
{% endif %}
</div>
</td>              
 </tr>             
 <tr>
<td><b>Saida 4</b></td>
<td>
<div class="btn-group" role="groupSaida4" aria-label="...">
 {% if statusSaida4 %}
 <button type="button" class="btn btn-success" onclick="window.location.href='/saida4/on';">Ligado</button>
 <button type="button" class="btn btn-default" onclick="window.location.href='/saida4/off';">Desligar</button>  
{% else %}  
<button type="button" class="btn btn-default" onclick="window.location.href='/saida4/on';">Ligar</button>
<button type="button" class="btn btn-danger" onclick="window.location.href='/saida4/off';">Desligado</button>  
{% endif %}
</div>
</td>              
 </tr>             
</tbody>
 </table>
</div>
</div>
      </div>
    </div> <!-- /container -->
    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>    
  </body>
</html>

Os fontes pode ser baixados aqui.

Publicando o webserver

Vamos usar um programa de ftp para publicar nosso projeto no Raspberry. Usuários do Windows podem baixar o programa WinSCP e conectar conforme exemplo abaixo:



Será solicitado o username e pasword, após autenticação o programa será aberto com a estrutura de arquivos e diretórios de /home/pi.


  
Faça o upload (arraste) da pasta webraspio para raiz de  /home/pi.

Testando nosso projeto
No terminal SSH execute os comandos abaixo
$ cd webraspio
$ sudo python webraspio.py
O flask é iniciado, mostrando o ip e porta configurados localmente.


  
Acesse o IP do raspberry no Browser para acessar a página da placa de comando.

  Clique nos botões para acionamento das saídas.


 O log de requisições http é apresentado na console

  Vídeo de funcionamento do projeto






Nos próximos posts vamos configurar o webserver para iniciar junto com o linux, monitorar as entradas analógicas, acionamento agendando de saídas, entre outros.




Tagged

10 comentários :

  1. Parabéns, ótimo exemplo/projeto!!!!!

    Como você implementou o monitoramento e agendamento das entradas? POderia informar por favor ?

    []s

    Carlos

    ResponderExcluir
  2. Muito bom, ja havias algumas semanas que procurava um material tão bom qnto esse! Show

    ResponderExcluir
  3. Pra quem quiser colocar o script pra iniciar quando o rasp for ligado, segue abaixo:

    sudo crontab -e

    no final do arquivo, inserir uma linha:

    @reboot python /home/pi/webraspio/webraspio.py &


    Mais informações: http://www.raspberrypi-spy.co.uk/2013/07/running-a-python-script-at-boot-using-cron/

    ResponderExcluir
  4. Sensacional seu trabalho.Queria ter 10% do seu conhecimento.

    ResponderExcluir
  5. Gostaria de uma ajuda!
    Conseguimos conversar por Skype?

    ResponderExcluir
  6. Muito bom, para mim que nunca tinha mexido com raspberry aprendi muito. Amigo teria como fazer algo tipo onde fica aquela aba em cima com as instruções, quando eu clicar lá abrir outra tela da placa de comando tipo, ficar como está no home, com a placa de comando com 4 acionamentos e na outra aba abrir mais 4 acionamentos.

    ResponderExcluir
  7. Bom dia muito bom, mas uma pequena duvida eu li e reli o codigo e não encontrei onde o script faz a integração do codigo Py com o HTML seria no --return render_template('webraspio.html', **statuspin) -- mas e o HTML como se comunica com o Py

    ResponderExcluir