MNote2
MNote2.26

Com varias correções implementadas nesta versão, estamos modificando drasticamente a ferramenta MQuery. Para isso desativamos a funcionalidade que será totalmente reescrita.

A ultima versão funcional do MQuery esta na versão 2.24

Geiser
Armazenando dados na nuvem com WORDPRESS

O projeto IoT exige que os dados sejam armazenados em nuvem.

Apresentarei uma solução baseada em WordPress, que atende esta demanda.

O WordPress é um software de gestão de conteúdo online, com ele é possivel criar Sites e gerir artigos, como este que estou fazendo.

Plugins são componentes adicionados ao wordpress que permite incluir recursos que se deseja controlar no wordpress.

O Fonte abaixo, é um plugin no wordpress, que está contido no projeto Geiser.

GIT do projeto:

https://github.com/marcelomaurin/GIESER

Para explicar irei apresentar o fonte do plugin e comentar o mesmo.

Plugin do wordpress

Fonte do Plugin index.php:

<?php
/**
 * Plugin Name: Geiser
 * Plugin URI: http://maurinsoft.com.br/geiser
 * Description: Gestão de Contador Geiser
 * Version: 1.0
 * Author: Marcelo Maurin Martins
 * Author URI: https://meusite.com
 */

// Cria as tabelas quando o plugin for ativado
register_activation_hook( __FILE__, 'criar_tabelas' );
function criar_tabelas() {
    global $wpdb;

    // Cria a tabela geiser_leitores
    $table_name1 = $wpdb->prefix . 'geiser_leitores';
    $charset_collate = $wpdb->get_charset_collate();
    $sql1 = "CREATE TABLE $table_name1 (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        nome varchar(50) NOT NULL,
        token varchar(50) NOT NULL,
        status TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
        PRIMARY KEY (id)
    ) $charset_collate;";
    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql1 );

    // Cria a tabela geiser_logs
    $table_name2 = $wpdb->prefix . 'geiser_logs';
    $sql2 = "CREATE TABLE $table_name2 (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        token varchar(50) NOT NULL,
        lastdt datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
	usvh float NOT NULL,
	temp float NOT NULL,
	hum  float NOT NULL,
        PRIMARY KEY (id)
    ) $charset_collate;";
    dbDelta( $sql2 );
}

function geiser_registra_log_endpoint( WP_REST_Request $request ) {
    $file_path = trailingslashit(ABSPATH) . 'ws/registra_log.php';
    $response = wp_remote_post( get_site_url() . '/' . $file_path, array( 'timeout' => 120 ) );
    return $response['body'];
}

// Adiciona a rota do web service para chamar o arquivo ./ws/registra_log.php
add_action( 'rest_api_init', 'geiser_register_api_routes' );
add_action( 'rest_api_init', 'registrar_rota2_personalizada');


function geiser_register_api_routes() {
    //...
    register_rest_route( 'geiser/v1', '/registra_log', array(
        'methods' => 'GET',
        'callback' => 'geiser_registra_log_endpoint',
        'permission_callback' => function () {
            return current_user_can( 'manage_options' );
        }
    ) );
}

// Endpoint do web service para chamar o arquivo ./ws/registra_log.php
/*
function geiser_registra_log_endpoint( WP_REST_Request $request ) {
    // Inclui o arquivo ./ws/registra_log.php
    require_once( dirname( __FILE__ ) . '/ws/registra_log.php' );
}
*/



// Adiciona o menu no painel de administração
add_action( 'admin_menu', 'geiser_admin_menu' );
function geiser_admin_menu() {
    add_menu_page( 'Geiser Admin', 'Geiser Admin', 'manage_options', 'geiser-admin', 'geiser_admin_page', 'geiser_admin_page' );
    add_submenu_page( 'geiser-admin', 'Dispositivos', 'Dispositivos', 'manage_options', 'geiser_dispositivos_page', 'geiser_dispositivos_page' );
    add_submenu_page( 'geiser-admin', 'Logs', 'Logs', 'manage_options', 'geiser_logs_page', 'geiser_logs_page' );
    add_submenu_page( 'geiser-admin', 'Análise', 'Análise', 'manage_options', 'geiser_analise_page', 'geiser_analise_page' );
	 // Adiciona Chart.js ao painel de administração
    add_action('admin_enqueue_scripts', function () {
        wp_enqueue_script('chartjs', 'https://cdn.jsdelivr.net/npm/chart.js');
    });
}

add_action('rest_api_init', 'registrar_rota_personalizada');

function registrar_rota_personalizada() {
  register_rest_route('Geiser/v1', '/registro.php', [
    'methods' => 'GET',
    'callback' => 'geiser_lst_callback',
  ]);
}

function geiser_lst_callback(WP_REST_Request $request) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geiser_logs';

    // Obtenha os parâmetros do corpo da solicitação
    $data_inicio = $request->get_param('data_inicio');
    $data_fim = $request->get_param('data_fim');
    $ip = $request->get_param('ip');

    // Construa a consulta SQL
    $sql = "SELECT * FROM {$table_name}";

    $where_clauses = array();
    if (!empty($data_inicio)) {
        $where_clauses[] = $wpdb->prepare('lastdt >= %s', $data_inicio);
    }
    if (!empty($data_fim)) {
        $where_clauses[] = $wpdb->prepare('lastdt <= %s', $data_fim);
    }
    if (!empty($ip)) {
        $where_clauses[] = $wpdb->prepare('ip = %s', $ip);
    }

    if (!empty($where_clauses)) {
        $sql .= ' WHERE ' . implode(' AND ', $where_clauses);
    }

    $sql .= ' ORDER BY lastdt DESC';

    // Execute a consulta e obtenha os resultados
    $logs = $wpdb->get_results($sql);

    // Retorne os resultados como uma resposta JSON
    return new WP_REST_Response($logs, 200);
}



function registrar_rota2_personalizada() {
  register_rest_route('Geiser/v1', '/registro.php', [
    'methods' => 'POST',
    'callback' => 'geiser_reg_callback',
  ]);
}

function geiser_reg_callback(WP_REST_Request $request) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geiser_logs';
	
    $usvh = $request->get_param('usvh');
    $temp = $request->get_param('temp');
    $hum = $request->get_param('hum');
	
    
    // Obtenha o IP do cliente
    $ip = $_SERVER['REMOTE_ADDR'];
    //echo $ip;
    //echo "<br/>";

    $sql = "SELECT * FROM {$wpdb->prefix}geiser_leitores WHERE token = '$ip'";
    //echo $sql;
    //echo "<br/>";

    // Verificar se o dispositivo está cadastrado na tabela geiser_leitores
    $device = $wpdb->get_row($sql);

    //echo $device;
    //echo "<br/>";

    if ($usvh === null)
    {
        return new WP_REST_Response(array('error' => 'usvh nao definido'), 404);
    }
    if ($device === null) {
        return new WP_REST_Response(array('error' => 'Device nao cadastrado'), 404);
    }

    // Obtenha a data e hora atual do sistema
    $lastdt = date('Y-m-d H:i:s');

    // O campo status deve ser sempre 1
    $status = 1;

    // Insira os dados na tabela
    $wpdb->insert(
        $table_name,
        array(
            'usvh' => $usvh,
            'temp' => $temp,
            'hum' => $hum,
            'token' => $ip
        ),
        array('%s', '%s', '%s', '%s', '%d', '%d', '%s')
    );

    // Retorne uma resposta JSON com uma mensagem de sucesso
    return new WP_REST_Response(array('message' => 'Registro inserido com sucesso!'), 200);
}


function geiser_admin_page() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geiser_leitores';

    // Processa a ação de adicionar um novo registro
    if (isset($_POST['action']) && $_POST['action'] == 'add') {
        $nome = sanitize_text_field($_POST['nome']);
        $token = sanitize_text_field($_POST['token']);
        $status = intval($_POST['status']);
        $wpdb->insert($table_name, compact('nome', 'token', 'status'));
    }

    // Iniciar a saída do HTML
    ob_start();
    ?>
    <div class="wrap">
        <h1>Administração de Geiser</h1>
        <h2>Cadastrar Leitor</h2>
        <form method="post">
            <input type="hidden" name="action" value="add">
            <table>
                <tr>
                    <td><label for="nome">Nome:</label></td>
                    <td><input type="text" name="nome" required></td>
                </tr>
                <tr>
                    <td><label for="token">Token:</label></td>
                    <td><input type="text" name="token" required></td>
                </tr>
                <tr>
                    <td><label for="status">Status:</label></td>
                    <td><input type="checkbox" name="status" value="1"></td>
                </tr>
                <tr>
                    <td colspan="2"><input type="submit" value="Adicionar"></td>
                </tr>
            </table>
        </form>
    </div>
    <?php
    // Encerrar a saída do HTML
    echo ob_get_clean();
}



function geiser_dispositivos_page() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geiser_leitores';
    $leitores = $wpdb->get_results("SELECT * FROM $table_name", ARRAY_A);

    // Iniciar a saída do HTML
    ob_start();
    ?>
    <div class="wrap">
        <h1>Dispositivos Geiser</h1>
        <div class="geiser-dispositivos-container" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); grid-gap: 1em;">
            <?php foreach ($leitores as $leitor): ?>
                <div class="geiser-dispositivo-card" style="padding: 1em; border: 1px solid #ccc; border-radius: 4px; background-color: #f9f9f9;">
                    <h2 style="margin-top: 0;"><?php echo esc_html($leitor['nome']); ?></h2>
                    <p><strong>ID:</strong> <?php echo intval($leitor['id']); ?></p>
                    <p><strong>Token:</strong> <?php echo esc_html($leitor['token']); ?></p>
                    <p><strong>Status:</strong> <?php echo intval($leitor['status']) ? 'Ativo' : 'Inativo'; ?></p>
                </div>
            <?php endforeach; ?>
        </div>
    </div>
    <?php
    // Imprimir a saída do HTML
    echo ob_get_clean();
}



function geiser_logs_page() {
    global $wpdb;

    $table_name = $wpdb->prefix . "geiser_logs";
    $logs = $wpdb->get_results("SELECT * FROM $table_name  order by lastdt desc");

    echo '<h1>Demonstrativo de Armazenamento na Nuvem</h1>';

    // Adicionar estilos CSS
    echo '<style>
        table.geiser-logs-table {
            width: 100%;
            border-collapse: collapse;
        }
        table.geiser-logs-table th,
        table.geiser-logs-table td {
            padding: 8px;
            text-align: left;
            border: 1px solid #ccc;
        }
        table.geiser-logs-table thead {
            background-color: #f5f5f5;
            font-weight: bold;
        }
        table.geiser-logs-table tbody tr:nth-child(even) {
            background-color: #f2f2f2;
        }
    </style>';

    echo '<table class="geiser-logs-table">';
    echo '<thead><tr><th>ID</th><th>Token</th><th>Data/Hora</th><th>USV/h</th><th>Temperatura</th><th>Umidade</th></tr></thead>';
    echo '<tbody>';

    foreach ($logs as $log) {
        echo '<tr>';
        echo '<td>' . $log->id . '</td>';
        echo '<td>' . $log->token . '</td>';
        echo '<td>' . $log->lastdt . '</td>';
        echo '<td>' . $log->usvh . '</td>';
        echo '<td>' . $log->temp . '</td>';
        echo '<td>' . $log->hum . '</td>';
        echo '</tr>';
    }

    echo '</tbody>';
    echo '</table>';
}



function geiser_analise_page() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geiser_logs';

    // Obtenha os dados do banco de dados
    $logs = $wpdb->get_results("SELECT token, TIMESTAMP(lastdt) as datetime, AVG(usvh) as avg_usvh, AVG(temp) as avg_temp, AVG(hum) as avg_hum FROM $table_name GROUP BY token, UNIX_TIMESTAMP(lastdt) DIV (15 * 60) ORDER BY token, datetime", ARRAY_A);

    // Iniciar a saída do HTML
    ob_start();
    ?>
    <div class="wrap">
        <h1>Geiser Análise</h1>
        <div>
            <canvas id="geiserChart"></canvas>
        </div>

        <script>
            document.addEventListener('DOMContentLoaded', function () {
                // Organiza os dados para o gráfico
                var logs = <?php echo json_encode($logs); ?>;
                var tokens = [];
                var data = {
                    usvh: {},
                    temp: {},
                    hum: {}
                };

                logs.forEach(function (log) {
                    if (!data.usvh.hasOwnProperty(log.token)) {
                        data.usvh[log.token] = [];
                        data.temp[log.token] = [];
                        data.hum[log.token] = [];
                    }
                    data.usvh[log.token].push({x: log.datetime, y: parseFloat(log.avg_usvh)});
                    data.temp[log.token].push({x: log.datetime, y: parseFloat(log.avg_temp)});
                    data.hum[log.token].push({x: log.datetime, y: parseFloat(log.avg_hum)});
                });

                Object.keys(data.usvh).forEach(function (key) {
                    tokens.push({
                        label: 'Token ' + key + ' - usvh',
                        data: data.usvh[key],
                        fill: false,
                        borderColor: 'rgb(75, 192, 192)',
                        tension: 0.1
                    });
                    tokens.push({
                        label: 'Token ' + key + ' - temp',
                        data: data.temp[key],
                        fill: false,
                        borderColor: 'rgb(255, 99, 132)',
                        tension: 0.1
                    });
                    tokens.push({
                        label: 'Token ' + key + ' - hum',
                        data: data.hum[key],
                        fill: false,
                        borderColor: 'rgb(255, 205, 86)',
                        tension: 0.1
                    });
                });

                // Cria o gráfico
                var ctx = document.getElementById('geiserChart').getContext('2d');
                new Chart(ctx, {
                    type: 'line',
                    data: {
                        datasets: tokens
                    },
                    options: {
                        scales: {
                            x: {
                                type: 'time',
                                time: {
                                    unit: 'minute',
                                    displayFormats: {
                                        minute: 'HH:mm'
                                    }
                                }
                            }
                        }
                    }
                });
            });
        </script>
    </div>
    <?php
    // Encerrar a saída do HTML
    echo ob_get_clean();
}



// Endpoint do web service para inserir dados na tabela geiser_logs
function geiser_logs_endpoint( WP_REST_Request $request ) {
    global $wpdb;

    $json = $request->get_json_params();

    // Insere os dados na tabela geiser_logs
    $table_name = $wpdb->prefix . 'geiser_logs';
    $data = array(
        'id_leitor' => $json['id_leitor'],
        'nome' => $json['nome'],
        'ip' => $json['ip'],
        'lastdt' => $json['lastdt'],
        'status' => $json['status']
    );
    $wpdb->insert( $table_name, $data );

    return 'Dados inseridos na tabela com sucesso.';
}

Informações do plugin

No fragmento abaixo, criamos o cabeçalho do plugin, onde será utilizado, para identificar o plugin.

<?php
/**
 * Plugin Name: Geiser
 * Plugin URI: http://maurinsoft.com.br/geiser
 * Description: Gestão de Contador Geiser
 * Version: 1.0
 * Author: Marcelo Maurin Martins
 * Author URI: https://meusite.com
 */

Registro de Componentes

O plugin possui diversos componentes, e entenda por componentes as partes que formam este, como funções e serviços.

O código abaixo, declara os componentes do plugin.

// Cria as tabelas quando o plugin for ativado
register_activation_hook( __FILE__, 'criar_tabelas' );
function criar_tabelas() {
    global $wpdb;

    // Cria a tabela geiser_leitores
    $table_name1 = $wpdb->prefix . 'geiser_leitores';
    $charset_collate = $wpdb->get_charset_collate();
    $sql1 = "CREATE TABLE $table_name1 (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        nome varchar(50) NOT NULL,
        token varchar(50) NOT NULL,
        status TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
        PRIMARY KEY (id)
    ) $charset_collate;";
    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql1 );

    // Cria a tabela geiser_logs
    $table_name2 = $wpdb->prefix . 'geiser_logs';
    $sql2 = "CREATE TABLE $table_name2 (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        token varchar(50) NOT NULL,
        lastdt datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
	usvh float NOT NULL,
	temp float NOT NULL,
	hum  float NOT NULL,
        PRIMARY KEY (id)
    ) $charset_collate;";
    dbDelta( $sql2 );
}


...

// Adiciona o menu no painel de administração
add_action( 'admin_menu', 'geiser_admin_menu' );
function geiser_admin_menu() {
    add_menu_page( 'Geiser Admin', 'Geiser Admin', 'manage_options', 'geiser-admin', 'geiser_admin_page', 'geiser_admin_page' );
    add_submenu_page( 'geiser-admin', 'Dispositivos', 'Dispositivos', 'manage_options', 'geiser_dispositivos_page', 'geiser_dispositivos_page' );
    add_submenu_page( 'geiser-admin', 'Logs', 'Logs', 'manage_options', 'geiser_logs_page', 'geiser_logs_page' );
    add_submenu_page( 'geiser-admin', 'Análise', 'Análise', 'manage_options', 'geiser_analise_page', 'geiser_analise_page' );
	 // Adiciona Chart.js ao painel de administração
    add_action('admin_enqueue_scripts', function () {
        wp_enqueue_script('chartjs', 'https://cdn.jsdelivr.net/npm/chart.js');
    });
}

add_action('rest_api_init', 'registrar_rota_personalizada');

A função register_activation_hook chama uma função no momento da sua ativação. Que é feito apenas uma vez.

A função criar_tabelas , cria as tabelas no banco de dados MYSQL, que é o banco de dados do wordpress.

A função add_action permite registrar um menu no wordpress, conforme apresentamos abaixo, e a função add_menu_page, registra os itens do menu.

Cada chamada do add_menu_page, chama uma função que é referenciada, e ao clicar no menu é chamado.

 add_submenu_page( 'geiser-admin', 'Análise', 'Análise', 'manage_options', 'geiser_analise_page', 'geiser_analise_page' );

function geiser_analise_page() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geiser_logs';

    // Obtenha os dados do banco de dados
    $logs = $wpdb->get_results("SELECT token, TIMESTAMP(lastdt) as datetime, AVG(usvh) as avg_usvh, AVG(temp) as avg_temp, AVG(hum) as avg_hum FROM $table_name GROUP BY token, UNIX_TIMESTAMP(lastdt) DIV (15 * 60) ORDER BY token, datetime", ARRAY_A);

    // Iniciar a saída do HTML
    ob_start();
    ?>
    <div class="wrap">
        <h1>Geiser Análise</h1>
        <div>
            <canvas id="geiserChart"></canvas>
        </div>

        <script>
            document.addEventListener('DOMContentLoaded', function () {
                // Organiza os dados para o gráfico
                var logs = <?php echo json_encode($logs); ?>;
                var tokens = [];
                var data = {
                    usvh: {},
                    temp: {},
                    hum: {}
                };

                logs.forEach(function (log) {
                    if (!data.usvh.hasOwnProperty(log.token)) {
                        data.usvh[log.token] = [];
                        data.temp[log.token] = [];
                        data.hum[log.token] = [];
                    }
                    data.usvh[log.token].push({x: log.datetime, y: parseFloat(log.avg_usvh)});
                    data.temp[log.token].push({x: log.datetime, y: parseFloat(log.avg_temp)});
                    data.hum[log.token].push({x: log.datetime, y: parseFloat(log.avg_hum)});
                });

                Object.keys(data.usvh).forEach(function (key) {
                    tokens.push({
                        label: 'Token ' + key + ' - usvh',
                        data: data.usvh[key],
                        fill: false,
                        borderColor: 'rgb(75, 192, 192)',
                        tension: 0.1
                    });
                    tokens.push({
                        label: 'Token ' + key + ' - temp',
                        data: data.temp[key],
                        fill: false,
                        borderColor: 'rgb(255, 99, 132)',
                        tension: 0.1
                    });
                    tokens.push({
                        label: 'Token ' + key + ' - hum',
                        data: data.hum[key],
                        fill: false,
                        borderColor: 'rgb(255, 205, 86)',
                        tension: 0.1
                    });
                });

                // Cria o gráfico
                var ctx = document.getElementById('geiserChart').getContext('2d');
                new Chart(ctx, {
                    type: 'line',
                    data: {
                        datasets: tokens
                    },
                    options: {
                        scales: {
                            x: {
                                type: 'time',
                                time: {
                                    unit: 'minute',
                                    displayFormats: {
                                        minute: 'HH:mm'
                                    }
                                }
                            }
                        }
                    }
                });
            });
        </script>
    </div>
    <?php
    // Encerrar a saída do HTML
    echo ob_get_clean();
}

Criando um Web Service

O Web Service é um serviço web que permite através dos verbos HTTP, armazenar , visualizar e gerenciar dados.

Incluiremos um web service, através do comando .


// Adiciona a rota do web service para chamar o arquivo ./ws/registra_log.php
add_action( 'rest_api_init', 'geiser_register_api_routes' );
...


function geiser_register_api_routes() {
    //...
    register_rest_route( 'geiser/v1', '/registra_log', array(
        'methods' => 'GET',
        'callback' => 'geiser_registra_log_endpoint',
        'permission_callback' => function () {
            return current_user_can( 'manage_options' );
        }
    ) );
}

// Endpoint do web service para inserir dados na tabela geiser_logs
function geiser_logs_endpoint( WP_REST_Request $request ) {
    global $wpdb;

    $json = $request->get_json_params();

    // Insere os dados na tabela geiser_logs
    $table_name = $wpdb->prefix . 'geiser_logs';
    $data = array(
        'id_leitor' => $json['id_leitor'],
        'nome' => $json['nome'],
        'ip' => $json['ip'],
        'lastdt' => $json['lastdt'],
        'status' => $json['status']
    );
    $wpdb->insert( $table_name, $data );

    return 'Dados inseridos na tabela com sucesso.';
}

Perceba que incluiremos o add_action, depois referenciamos a função geiser_register_api_routes, que registra o rest, através da chamada register_rest_route.

Que ao ser chamado, chama a função geiser_logs_endpoint.

Como chamar no arduino

O Arduino armazena o envia os dados para o web service, através do fonte: GIESER\hardware\MSTemp02

Do mesmo projeto, acima mencionado, para economizarmos tempo, irei apresentar o fragmento do código:

Utilizaremos um ethernet shield, para mostrar os dados, e para isso, incluiremos a biblioteca Ethernet.h, conforme apresentado no site.

Iremos definir a porta PORT e o endereço do URL, do wordpress, conforme apresentado no url.

E por fim chamaremos no loop a função WriteSite, que passa os dados que se deseja para o site, conforme apresentado.

#include <Ethernet.h>   

#define PORT 80
#define url "http://177.26.228.0/wp-json/Geiser/v1/registro.php"  
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };



// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, 0, 177);
IPAddress myDns(192, 168, 0, 1);

// Initialize the Ethernet client library
// with the IP address and port of the URL
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;   

void WriteSite()
{
  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.print("connecting to ");
  Serial.print(url);
  Serial.println("...");

  // if you get a connection, report back via serial:
  if (client.connect(url, PORT)) {
    Serial.print("connected to ");
    Serial.println(client.remoteIP());

    // Make a HTTP POST request:
    client.println("POST /your_endpoint_here HTTP/1.1");
    client.print("Host: ");
    client.println(url);
    client.println("Content-Type: application/json");
    client.print("usvh: ");
    client.println(usvh);
    client.print("temp: ");
    client.println(temperature);
    client.print("hum: ");
    client.println(humidity);
    client.println("Connection: close");

    // Calculate the content length
    String jsonBody = String("{\"usvh\":") + String(usvh) + String(",\"temp\":") + String(temperature) + String(",\"hum\":") + String(humidity) + String("}");
    client.print("Content-Length: ");
    client.println(jsonBody.length());

    client.println(); // Required empty line before the body
    client.print(jsonBody);
    client.println();
  } else {
    // if you didn't get a connection to the server:
    Serial.println("connection failed");
  }
  beginMicros = micros();
}              

o client é uma variavel de objeto, que armazena a conexão realizada com o servidor, nele chamaremos um cabeçalho do protocolo http, incluindo no verbo http POST as informações que desejamos incluir.

Desta forma simples conseguimos enviar dados para um servidor web.

Eletricidade
Trabalho de Eletricidade

Reposição de aula.

Marcelo Maurin Martins

Questão 1

Primeiro R235 = 1+ 3 + 6 => 10ohms

Segundo

R4235 = 1\req = 1 \2,5 + 1\ 10 => 4\10 + 1\10 => 5\10 -> 0,5ohms

Por fim a ultima parte

R123456 -> 0,5 + 0,5 + 1 => 2ohms

Resposta Req 2ohms.

Questão 2

R12 = 1/20 + 1/30 => 3\60 +2\60 => 5\60 ohms = req = 12ohms

R123 => 12 +50 =>  62ohms

Questão 3

R123 => 10 + 10 + 10 => 30 ohms
R4586 => 40ohms
1\R1234568 => 1\30 + 1\40 => 4\120 + 3\120 => 7\120 =
R97 = 20ohms
1\R123456789 =120\7 + 1\20 => 2400\140 + 7\140 => 2407\140=> 0,058ohms

4)  questão

R123 = 20+ 30 + 50 = 100ohms

Resposta A 100 ohms

V = R * i => 200 = 100 * i => i = 200\100 => 2A

Resposta B 2ª pois em um circuito em serie a corrente é a mesma.

C)

V1 = 20 * 2 = 40V
V2 = 30* 2 = 60V
V3= 50*2 = 100V

Questão 5

Calcular a intensidade de campo induto

Nro espiras 200

Corrente 1ª

Vamos considerar que o circuito 1 tenha um comprimento médio de 20 cm (0,2 m) e o circuito 2 tenha um comprimento médio de 10 cm (0,1 m).

Ambos os circuitos têm 200 espiras e são percorridos por uma corrente de 1 A.

Para um solenoide retangular, a intensidade do campo indutor pode ser aproximada usando a fórmula de Ampère para um solenoide ideal:

B = μ₀ * n * I

onde B é a intensidade do campo indutor, μ₀ é a permeabilidade magnética do vácuo (aproximadamente 4π × 10⁻⁷ Tm/A), n é o número de espiras por unidade de comprimento e I é a corrente que passa pelas espiras.

Primeiro, vamos calcular o número de espiras por unidade de comprimento para cada circuito:

Circuito 1:

n1 = N / L1 n1 = 200 espiras / 0,2 m n1 = 1000 espiras/m

Circuito 2:

n2 = N / L2 n2 = 200 espiras / 0,1 m n2 = 2000 espiras/m

Agora podemos calcular a intensidade do campo indutor para cada circuito usando a fórmula de Ampère:

Circuito 1:

B1 = μ₀ * n1 * I B1 = (4π × 10⁻⁷ Tm/A) * (1000 espiras/m) * (1 A) B1 ≈ 1,26 T

Circuito 2:

B2 = μ₀ * n2 * I B2 = (4π × 10⁻⁷ Tm/A) * (2000 espiras/m) * (1 A) B2 ≈ 2,52 T

Portanto, a intensidade do campo indutor para o circuito 1 é aproximadamente 1,26 T e para o circuito 2 é aproximadamente 2,52 T.

Biologia Celular microbiologia Sistemas Biomedicos
Amostras de tecido vindos da china

Neste post, vou mostrar a captura de amostras vindas da china.

Figura 01 – Sangue humano – capturada

Referência:

Vídeo de referência mostrando o mesmo tipo de células.

Esofago

Referência:

https://myloview.com.br/fotomural-submucosa-do-esofago-tecido-conjuntivo-no-7379C17

Pele

Tecido do estomago

Íleo

Olho

Referência:

https://commons.wikimedia.org/wiki/File:Large_tileable_plane_of_compound_eyes_under_electron_microscope.jpg

MNote2
MNote 2.26 – Correções

O MNote2 esta passando por uma reformulação no formulário MQuery, na qual esta sendo modificado para atender múltiplas conexões.

No dia de hoje, finalizei a primeira parte da correção onde gravo a nova conexão.

Ainda há pontos que precisam ser identificados, como Salvamento das conexões e Edição das conexões.

As modificações são longas e trabalhosas, não tendo prazo para sua finalização.

As correções foram lançadas no projeto até o ponto atual.

MOSFET Sistemas Analogicos I Sistemas Biomedicos
Continuando com estudo de MOSFET

Atenção

Este artigo esta sendo escrito, e algumas informações podem estar erradas.

No artigo anterior, vimos as características do MOSFET, onde iniciamos o estudo do projeto, analisando a questão do dimensionamento com relação a Potencia fornecida.

Artigo referencia ao MOSFET de Potência.

Um ponto importante que foi visto é que o componente apresenta uma variação grande de potencia em relação a temperatura.

Iremos precisar das seguintes informações neste artigo:

Thermal Characteristics
Symbol Parameter Typical Unit

  • Rθjc Thermal Resistance-Junction to Case 0.68 ℃/W
  • Rθja Thermal Resistance-Junction to Ambient 62

Especificação técnica

https://datasheet.lcsc.com/szlcsc/1811141141_HL-Haolin-Elec-HA210N06_C237262.pdf

Conforme o gráfico acima, pudemos verificar que a partir de 25 C, a potencia cai drasticamente.

Calculo da potencia dissipada

Agora iremos apresentar o calculo da potencia dissipada, ou seja perdida.

Para isso usamos a seguinte formula.

PD  (Id x Vds) x D.
  • PD -> Potencia dissipada (é o que queremos achar)
  • Id -> Corrente do Dreno. Que pode variar de 210A (25C) até 130A (100C). Porem o valor que iremos utilizar é a corrente real consumida.

No nosso ultimo artigo, verificamos que a Potencia consumida é 120W, sendo assim, iremos calcular a corrente.

P = V . I -> 120 = 12 .I -> I 120/12 ; I=10A

Agora iremos determinar o ciclo de trabalho (D), como se trata de corrente continua, e um equipamento ligado 100% do tempo, atribuímos D=1.

Ficando então

Pc =10 x 12 x 1 -> 120W potencia consumida.

Agora precisamos achar a potencia dissipada.

Nesse calculo, iremos considerar outra especificação do datasheet.

RDS(on) Drain-Source On-Resistance VGS=10V, ID=75A — 3.2 4 mΩ

Nessa entendemos que para uma potencia de 10V a 75A, temos uma resistência de 3.2mOhms. o que iremos calcular é a corrente e projetar a potencia baseada nessa resistência.

Onde a Vds = 12V;

Para calcular a potência de aquecimento real, precisamos primeiro encontrar a corrente de dreno (ID) e as perdas de condução no MOSFET. Vamos usar a equação de potência:

P = V x I

Onde P é a potência total (120 W), V é a tensão (12 V) e I é a corrente de dreno (ID). Rearranjando a equação para encontrar ID:

ID = P / V = 120 W / 12 V = 10 A

Agora que conhecemos a corrente de dreno (ID), podemos calcular as perdas de condução usando a fórmula:

P_condução = ID^2 x RDS(on)

Utilizando o valor de RDS(on) de 3.2 mΩ:

P_condução = (10 A)^2 x 3.2 mΩ = 100 A² x 0.0032 Ω ≈ 0.32 W

Lembrando que a Potencia de condução é a potencia transformada em calor, durante o processo da passagem de corrente.

Porem existe uma outra perda que é gerada pela comutação, porem como usamos esse equipamento sem comutação ou com comutação extremamente baixa. O valor da perda passa ser desprezível.

Desta forma consideraremos o PD como Condução.

PD = 0.32 W

Temperatura máxima presumida

Agora iremos aplicar as contas tentando achar a temperatura final durante a carga.

Tj = Ta + (Pd x Rth_total)

Onde:

  • Tj é a temperatura da junção do MOSFET em graus Celsius (°C) (queremos saber)
  • Ta é a temperatura ambiente em graus Celsius (°C) (40°C)
  • Pd é a potência dissipada pelo MOSFET em watts (W) (0.32 W)
  • Rth_total é a resistência térmica total em graus Celsius por watt (°C/W), que inclui tanto a resistência térmica do dissipador de calor (Rth_dissipador) quanto a resistência térmica da junção para o dissipador (Rth_jc) e outros elementos térmicos, como a resistência térmica de interface (Rth_interface) entre o MOSFET e o dissipador, se aplicável.

Recapitulando os valores do Rth_total calculados no seu texto:

Rth_total = Rth_jc + Rth_interface + Rth_dissipador Rth_total = 0,68 + 0 + 1,166 °C/W -> 1,846 °C/W

Lembrando que a Rth_interface é zero, pois não usamos pasta térmica.

Agora, usando a fórmula da temperatura da junção:

Tj = Ta + (Pd x Rθja ) Tj = 40 + (0.32 x 1,846) -> 40 + 0,59072 -> 40,59072 °C

A temperatura final da junção do MOSFET é de aproximadamente 40,6°C. Isso é bem menor do que os resultados anteriores e está dentro dos limites seguros de operação do MOSFET.

Importânte

Como mencionado anteriormente, Rθjc (Resistência Térmica da Junção ao Case) é de 0,68°C/W. No texto original, você forneceu o valor de Rth_total como a soma de Rth_jc, Rth_interface e Rth_dissipador. Nesse caso, Rth_jc deve ser substituído por Rθjc:
Rth_total = Rθjc + Rth_interface + Rth_dissipador
Em seguida, você pode usar o valor de Rth_total para calcular a temperatura da junção do MOSFET (Tj) usando a fórmula:
Tj = Ta + (Pd x Rth_total)
O valor de Rθja (Resistência Térmica da Junção ao Ambiente) de 62°C/W é útil quando você deseja calcular a temperatura da junção do MOSFET sem a ajuda de sistemas de refrigeração adicionais, como dissipadores de calor. Nesse caso, a fórmula seria:
Tj = Ta + (Pd x Rθja)
Tj = Ta + (Pd x Rθja ) Tj = 40 + (0.32 x 1,846) -> 40 + 115,375 -> 155,37 °C

O Calculo de transferência de calor, é uma disciplina de termodinâmica.

Estou fugindo da minha área de ação, porem é de minha opnião que pelo menos uma base, para operar e saber esses calculos é fundamental para o perfeito planejamento de placas de circuito eletrônicos.

Conclusão:

Próximo passo:

Experimentação

Iremos simular o modelo, tentando chegar no padrão de temperatura calculado.

Houve dúvidas quanto ao modelo estar ou não correto, pois inicialmente utilizamos a potencia total (120W), posteriormente, identificamos que a potencia que era convertida em energia térmica era uma fração (ohms) da potencia total.

Porem algumas duvidas ainda persistem, e a experimentação provará a exatidão do calculo.

Peço que aguardem até la.

Eletricidade MOSFET Sistemas Analogicos I
Teste com MOSFET de Potência

Atenção:

Este artigo, é um estudo técnico, e não deve ser referência para aquisição que quaisquer itens.

Vamos a analise.

Neste projeto, visa aprender um pouco mais sobre MOSFET, criando um estudo prático.

Para isso adquiri uma placa pronta.

https://produto.mercadolivre.com.br/MLB-1960854309-mosfet-modulo-de-potncia-impressora-3d-25a-_JM

Onde irei estudar seu funcionamento.

O objetivo deste estudo é entender o funcionamento do MOSFET, não construir uma placa.

Componentes da placa

Iremos analisar os componentes desta placa. Para isso iremos tirar fotos dos componentes, a fim de identificar.

  • Transistor de Potencia. HL DEA Q44 HA210N06
  • Foto Acoplador – CWM5 PC817 Sharp
  • Ponte retificadora – MB6S

Por este motivo mesmo usamos uma placa pronta.

Fazendo o processo de analise e entendendo seu funcionamento.

Ao buscar o data sheet do CI, encontramos o mesmo no seguinte link:

https://pdf1.alldatasheet.com/datasheet-pdf/view/43368/SHARP/PC817.html

https://datasheet.lcsc.com/szlcsc/1811141141_HL-Haolin-Elec-HA210N06_C237262.pdf

Segue dados obtidos no pdf

  • Vdss (Drain to source) 60V (maximo)
  • Vgss (Gate to source) +- 25V (maximo)
  • Id (Continuous Drain Current) Temperatura 25C 210A, Tc 100 130A
  • PD (Power Dissipation) 220W (25C) / 110W (100C)

Inicio da analise

Baseado nessa especificação podemos presumir que a 100C a potencia máxima aplicada seria de 110W.

Sendo assim, supondo trabalharmos com tensão de 5V.

Podemos supor que a corrente fornecida será:

P = V. I

ou seja a 5V:

110W =  5 i -> i = 110 /5 -> 22A de corrente máxima entre a fonte e o dreno.

Ou a 12V:

110w = 12* i -> i = 110 /12 -> 9,11A (máximo)

Este produto é destinado a construção de impressora 3D, mantendo e controlando a corrente da cama aquecida. Observando e pesquisando um exemplo de cama aquecida. Podemos buscar pela seguinte referencia.

Cama aquecida

Olhando a tensão do mesmo.

https://produto.mercadolivre.com.br/MLB-3388393322-plataforma-de-placa-de-aquecimento-com-base-de-cama-quente-d-_JM#position=13&search_layout=stack&type=item&tracking_id=060e55c0-2178-4d01-816b-aeb11799b428

Temos a seguinte especificação:

  • Marca: Anet
  • Material principal: Liga de alumínio
  • Apropriado: Para Anet A8/A6/A2
  • Comprimento do cabo: 87.5cm/34.4in
  • Tamanho da placa: 220* 220* 3mm
  • Tensão do produto: 12V
  • Poder do produto: 120W
  • Tamanho do pacote: 34,5 * 33* 4,5 cm/13,6 * 13* 1.8in
  • Peso do pacote: 662g/23.4oz

Conclusão

Desta forma podemos verificar que a potência requerida esta no limite da fornecida, presumindo que o produto irá funcionar como esperado, porem esta no limite do esforço.

Sendo recomendado uma ventilação auxiliar para o CI, pois este fornece potência maiores quando frio, e um sistema de ventilação iria auxiliar na dissipação térmica ainda mais em países tropicais cuja temperatura máxima pode chegar a 40 graus.

No próximo post irei realizar alguns testes.

Entendendo seu funcionamento.

Mathlab Sistemas Biomedicos
Trabalho de MathLab

Projetos de Sistemas de Controle com o Matlab

DISCIPLINA /AVALIAÇÃO: Tecnologia de Automação I / P1  
Grupo: 1
 Nome: Marcelo , Robson  
Professor: Marcelo Duarte   

(4,0 pontos) Projeto de Controladores PID.

Considere um sistema de controle em malha fechada com realimentação unitária em que a planta é de segunda ordem com a seguinte função de transferência

𝐺𝑝(𝑆) =

(1,0 ponto) Encontre a função de transferência em malha fechada e verifique graficamente a resposta ao degrau com o MATLAB.

Integre a esta planta do sistema em malha fechada a controladores do tipo PID de acordo com as DUAS alternativas a seguir. 

a) Controlador Proporcional (P)

𝐺𝑟𝑢𝑝𝑜 01

𝐾𝑝 = 36

Onde fica num1 o 5

a função ficaria 1, do S^2-> 1S^2 -> 1;

o 4.42 S -> ficaria como segundo nro.

e o ultimo o nro 4.

Ficando [1,4.42,4]

>> num1=[005];
>> den1=[1,4.42,4];
>> Gp= tf(num1,den1)

Gp =
 
         5
  ----------------
  s^2 + 4.42 s + 4
 
Continuous-time transfer function.Model Properties
>> 

Agora vamos montar o grafico.

ft1 = feedback(Gp,1);

step(ft1);

a) Controlador Proporcional (P)

𝐺𝑟𝑢𝑝𝑜 01

𝐾𝑝 = 36

Parte 2

|Gc(s) = Kp

Gc(s) = Kp+kdS

TEMOS A função:

Criamos a segunda função, pela multiplicação do Gc * Gp:

Onde obtemos a função G10:

Kp = 36;
Kd= 0,2 * Kp;


num4=[005];
den4=[1,4.42,4];
Gp4= tf(num1,den1);


%PD = Kp+ Kd * Gp4b ;
%Gp5 = PD * Gp4;

num10=[0,3.6,80];
den10=[1,4.42,4];
G10= tf(num10,den10);
G_fechado= feedback(G10,1);

De posse destas informações

Geramos o gráfico abaixo:

b) Controlador Proporcional Derivativo (PD)

Conforme apresentado em:

http://www.ece.ufrgs.br/~jmgomes/pid/Apostila/apostila/node29.html

O Controlador Proporcional-Derivativo (PD)

A ação derivativa quando combinada com a ação proporcional tem justamente a função de “antecipar” a ação de controle a fim de que o processo reaja mais rápido. Neste caso, o sinal de controle a ser aplicado é proporcional a uma predição da saída do processo.


𝐺𝑟𝑢𝑝𝑜 01
𝐾𝑝 = 36
𝐾𝑑 = 2% 𝑑𝑒 𝐾p

Kp = 36;
Kd= 0,02 * Kp;


num4=[005];
den4=[1,4.42,4];
Gp4= tf(num1,den1);

PD = Kp+ Kd * Gp4;
Gp5 = PD * Gp4;
G_fechado = feedback(Gp5,1);

st5 = step(G_fechado)

2) Projeto de controle de aplicação de insulina

Ficando no Mathlab , conforme apresentado:

s=tf('s');
num6=[1];
den6=[1,2];
uGp6=tf(num6,den6);

num7=[1];
den7=[1,2];
mGp7=tf(num7,den7);

G8=series(uGp6,mGp7);
ft9=feedback(G8,1);
impulse(ft9);

Considerando o Kp ; 2< Kp < 10

e 200 < a 450

Considerando kp=2, e a =200

s=tf('s');

num6=[1];

a=200;

den6=[1,a];

uGp6=tf(num6,den6);

num7=[1];

den7=[1,2];

mGp7=tf(num7,den7);

G8=series(uGp6,mGp7);

G9= Kp*G8;

ft9=feedback(G9,1);

impulse(ft9);

3 ) Sistema de controle em malha lenta

Uma das formas clássicas de se fazer o controle de potência de uma cavidade laser e mostrada na figura 47. O Diodo laser de bombeio, o cristal laser e o cristal SHG formam a cavidade laser. Através da injeção corrente no diodo de bombeio é possível obter um feixe de laser amarelo. O controle da cavidade é efetuado por dois controladores PI ( Proporcional integrative) (OGOTA,2003), PI 1 e P3, ligados numa configuração do tipo cascata ( SEDRA et a.,2007). A malha de controle formada pelo sensor HALL (sensor corrente), PI e 3 MOSFET  ( field-effect transitor) (SEDRA.,2007) garante que o diodo bombeie o cristal com uma potência óptica proporcional ao sinal de referência aplicado na entrada da malha de controle. Para evitar flutuações de potência na saída da cavidade, existe uma malha de controle externa que capta uma percentagem da potência atraves do fotodiodo e realimenta o controlador PI 3.O resultado dessa topologia é a relação linearmente proporcional entre a referência de tensão gerada pelo microcontrolador e a potência óptica na saída da cavidade.

Essa arquitetura de controle possui algumas limitações quando se necessita geral pulso da ordem microssegundos. A primeira causa é o atraso gerado pelo PI 3 e PI 1, visto que a saída de um controlador é usada como entrada para outro. A segunda causa é a fonte de alimentação que possui um tempo de resposta a variação de uma carga muito lenta que pode chegar 1ms, quando utilizada a potência máxima da fonte. A terceira causa é a indutância dos cabos de alimentação do diodo laser que responde a variação da corrente com uma variação de tensão parasita. Cargas sensíveis tais como diodo laser são susceptíveis  a essas variações e podem ter junção PN danificada. Essa topologia é adotada no controle do laser no modo  normal de aplicação laser (regime continuo), no qual os pulso possuem duração longa, entre 50ms e 1000ms.

MNote2
MNote 2.26

Estamos trabalhando em muitas mudanças na versão 2.26.

  • Melhoria no projeto do MQuery, permitindo multiplas conexões e multiplas instancias.
  • Correções na tela do Folder, incluindo verificação de posição baseada em area de trabalho dinâmica. Não permitindo posicionar tela fora da área de trabalho real.

pt_BRPortuguese