microbiologia
Microscopia animal

housefly legs w.m.

Perna de Mosca domestica

Musca domesticaLinnaeus1758, conhecida pelos nomes comuns de mosca-domésticamosca-de-casa,[1] é uma espécie de dípterobraquícero (moscas) da famíliaMuscidae. É um dos insectos mais comuns e uma presença habitual na maioria dos climas da Terra. A mosca pode pousar em comida, contaminando-a com bactérias, sendo responsável pela propagação de numerosas doenças.

Fonte:https://pt.wikipedia.org/wiki/Musca_domestica

Ampliação 40x

ampliação de 10x

Ampliação 5x

microbiologia
Imagens de Microscopia

Pine Mature Wood x.s.

Madeira Madura de Pinho x.s. – Pinheiro –

Pinheiro é o nome comum das árvores pertencentes à divisão Pinophyta, tradicionalmente incluída no grupo das gimnospérmicas. Este artigo se refere apenas às plantas do género Pinus, da família Pinaceae.[1]

São nativos, na sua maioria, do Hemisfério Norte. Na América do Norte, com diversidade mais alta no México e na Califórnia. Na Eurásia, eles ocorrem desde Portugal e leste da Escócia até ao extremo oriental da RússiaJapãonorte de África, o Himalaia com uma espécie formando a floresta de coníferas subtropical, o (pinheiro-de-sumatra) que já cruzou o Equador em Samatra, na Indonésia. Os pinheiros são também plantados extensivamente em muitas partes do Hemisfério Sul.

No Brasil também são chamados pinheiros, espécies que na verdade não fazem parte da família Pinaceae, como a Araucária (Araucaria angustifolia), mais conhecida como pinheiro-do-paraná. Este pertence a família Araucariaceae, que é pequena e nativa apenas do hemisfério sul. Abrange dois gêneros somente: o Agathis, (natural da Austrália) e o Araucaria que aparece no ChileArgentina e sul-sudeste do Brasil, em regiões de altitude elevada, ou seja, acima de 500 m.

Fonte: https://pt.wikipedia.org/wiki/Pinheiro

Imagens de microscopia

Ampliação de 40x

Microscopia
Fotos de Microscopia

Apresentação de algumas imagens de microscopia

stem of wood dicotyledon x.s.

caule de madeira dicotiledônea x.s.

As dicotiledôneas , também conhecidas como dicotiledôneas (ou, mais raramente, dicotiledôneas ), [2] são um dos dois grupos em que todas as plantas com flores (angiospermas) foram anteriormente divididas. O nome refere-se a uma das características típicas do grupo: a saber, que a semente possui duas folhas embrionárias ou cotilédones . Existem cerca de 200.000  espécies dentro deste grupo. [3] O outro grupo de plantas com flores foi chamado de monocotiledôneas (ou monocotiledôneas), geralmente cada uma com um cotilédone. Historicamente, esses dois grupos formaram as duas divisões das plantas com flores.

Principalmente a partir da década de 1990, a pesquisa filogenética molecular confirmou o que já se suspeitava: que as dicotiledôneas não são um grupo formado por todos os descendentes de um ancestral comum (ou seja, não são um grupo monofilético ). Em vez disso, várias linhagens, como as magnoliidas e grupos agora conhecidos coletivamente como angiospermas basais , divergiram antes das monocotiledôneas; em outras palavras, as monocotiledôneas evoluíram de dentro das dicotiledôneas, conforme definido tradicionalmente. Os dicotiledôneas tradicionais são, portanto, um grupo parafilético . [4]

As eudicotiledôneas são o maior grupo monofilético dentro das dicotiledôneas. Eles se distinguem de todas as outras plantas com flores pela estrutura de seu pólen . Outras dicotiledôneas e as monocotiledôneas possuem pólen monossulcado (ou formas derivadas): grãos com um único sulco. Em contraste, eudicotiledôneas têm pólen tricolpado (ou formas derivadas): grãos com três ou mais poros dispostos em sulcos chamados colpos.

Imagens de Microscópio

Ampliação 40x

Mathlab Sistemas Biomedicos
Segundo trabalho de Mathlab2

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   

Primeira questão

1. (5,0 pontos) Projeto de Controladores PID.

𝜁 = 0,55   𝑒 𝜔𝑛 = 1,3 𝑟𝑎𝑑/𝑠


num1=[0 0 1.69];
den1=[1 1.43 1.69];
Gp1=tf(num1,den1)

Gp1= 

    1.69
S²+1.43s+1.69


FT1=feedback (Gp1,1)

FT1=   

     1.69
S²+1.43s+3.38 

Step(FT1)

Para o sistema em malha fechada, integre esta planta a controladores do tipo PID de acordo com os TRÊS itens a seguir. 

Item A

a) Controlador Proporcional (P)

𝐶𝑎𝑑𝑎 𝑢𝑚 𝑑𝑜𝑠 𝑔𝑟𝑢𝑝𝑜𝑠 𝑑𝑒𝑣𝑒 𝑇𝐸𝑆𝑇𝐴𝑅 𝑣𝑎𝑙𝑜𝑟𝑒𝑠 𝑑𝑒 𝐾𝑝 que “melhorem” a resposta ao degrau quando comparada à resposta do sistema sem nenhum controlador.

num2=[0 0 42.25];
den2=[1 1.43 1.69];
Gp2=tf(num2,den2)

Gp2 =
 
        42.25
  ------------------
  S²+ 1.43s + 1.69
 
Continuous-time transfer function.

FT2=feedback(Gp2,1)

FT2=
 
         42.25
  -------------------
  S² + 1.43 s + 43.94
 
Continuous-time transfer function.

step(FT2)

O valor de 42,25, é devido a multiplicação do controle (KP) de 25, com 1,69.

Questão B

𝐶𝑎𝑑𝑎 𝑢𝑚 𝑑𝑜𝑠 𝑔𝑟𝑢𝑝𝑜𝑠 𝑑𝑒𝑣𝑒 𝑇𝐸𝑆𝑇𝐴𝑅 𝑣𝑎𝑙𝑜𝑟𝑒𝑠 𝑑𝑒 𝐾𝑝  𝑒  𝐾𝑑     que  “melhorem ainda mais”

Considerando o KP

num1=[1.69];
den1=[1,1.43,1.69];
Gp1= tf(num1,den1);

num2=[59,1,250]
den2=[0,0,1]

Gp2 = tf(num2,den2)

Gp3=series(Gp1,Gp2)

ft1 = feedback(Gp3,1);
step(ft1);

Conclusão: A amplitude ficou muito baixa, porem o tempo de resposta até estabilizar ficou extremamente longo.

Questão C

c)  Controlador Proporcional Integral Derivativo (PID)

𝐶𝑎𝑑𝑎 𝑢𝑚 𝑑𝑜𝑠 𝑔𝑟𝑢𝑝𝑜𝑠 𝑑𝑒𝑣𝑒 𝑇𝐸𝑆𝑇𝐴𝑅 𝑣𝑎𝑙𝑜𝑟𝑒𝑠 𝑑𝑒 𝐾𝑝,   𝐾𝑖  𝑒 𝐾𝑑  para “melhorar DEFINITIVAMENTE” a resposta ao degrau quando comparada à resposta do sistema com o controlador proporcional-derivativo.

Atenção: você terá TRÊS (3) sistemas a modelar!

Para cada UM dos TRÊS SISTEMAS, realize as seguintes tarefas:

  • (1,0 pontos) Apresente o Diagrama de Blocos para cada um dos três sistemas;
  • (3,0 pontos)  Apresente o gráfico da resposta ao degrau unitário para cada um dos três sistemas estabelecendo a comparação de desempenho ao se utilizar este ou aquele controlador.

Questão A

% primeira modificação
kp=2;
ki=40;
vl=ki*1.69;

num1=[0, kp, vl ];
den1=[1, 1.43, 1.69];
Gp1=tf(num1,den1)



ft1 = feedback(Gp1,1);
step(ft1);

Se compararmos

Vemos que houve uma melhora da amplitude , com um tempo de resposta um pouco menor, e melhora na amplitude.

% primeira modificação
kp=2;
ki=10;
vl=ki*1.69;

num1=[0, kp, vl ];
den1=[1, 1.43, 1.69];
Gp1=tf(num1,den1)



ft1 = feedback(Gp1,1);
step(ft1);
% primeira modificação
kp=0;
ki=10;
vl=ki*1.69;

num1=[0, kp, vl ];
den1=[1, 1.43, 1.69];
Gp1=tf(num1,den1)



ft1 = feedback(Gp1,1);
step(ft1);


Questão B


num1=[1.69];
den1=[1,1.43,1.69];
Gp1= tf(num1,den1);

% primeira modificação
kp=28
ki=3

num2=[kp,ki]
den2=[0,1,0]

Gp2 = tf(num2,den2)

Gp3=series(Gp1,Gp2)

ft1 = feedback(Gp3,1);
step(ft1);



num1=[1.69];
den1=[1,1.43,1.69];
Gp1= tf(num1,den1);

% primeira modificação
kp=28
ki=0

num2=[kp,ki]
den2=[0,1,0]

Gp2 = tf(num2,den2)

Gp3=series(Gp1,Gp2)

ft1 = feedback(Gp3,1);
step(ft1);

num1=[1.69];
den1=[1,1.43,1.69];
Gp1= tf(num1,den1);

% primeira modificação
kp=18
ki=2

num2=[kp,ki]
den2=[0,1,0]

Gp2 = tf(num2,den2)

Gp3=series(Gp1,Gp2)

ft1 = feedback(Gp3,1);
step(ft1);

Projeto de Controle de um Sistema Biomédico

2. (5,0 pontos) Considere um sistema simples de controle de temperatura cujo diagrama de blocos é dado a seguir;

  1. (1,0 ponto) Encontre a função de transferência da planta sem o controlador;
num1=[ 0, 0, 1];
den1=[1, 4, 0];
G1=tf(num1,den1);


num2=[0, 0.00003];
den2=[10, 2570];
H=tf(num2,den2);

FT=feedback(G1,H,-1)
step(FT);

B) (3,0 pontos) Especifique ao menos dois valores para o ganho no intervalo dado por

 20 < 𝐾𝑝 < 40    e  90 < 𝐾𝑝 < 120 .Encontre graficamente as respostas à rampa para os dois sistemas gerados; 

Kp= 25

num1=[ 0, 0, 25];
den1=[1, 4, 0];
G1=tf(num1,den1);


num2=[0, 0.00003];
den2=[10, 2570];
H=tf(num2,den2);

FT=feedback(G1,H,-1)

step(FT)

AGORA PEGANDO kp= 110


num1=[ 0, 0, 110];
den1=[1, 4, 0];
G1=tf(num1,den1);


num2=[0, 0.00003];
den2=[10, 2570];
H=tf(num2,den2);

FT=feedback(G1,H,-1)

step(FT)

Questão C

(1,0 pontos) Compare as curvas encontradas para os dois valores distintos de 𝐾𝑝 referentes à resposta do sistema à rampa unitária.

Kp=25

O gráfico da direita é o representado por KP=110, enquanto o grafico da esquerda o kp=25.

Podemos ver uma diferença grande no tempo de resposta, enquanto o de 25, demora 8 segundos para obter amplitude, o de 110, consegue resultados muito próximos em apenas 2, tendo uma melhora considerável do tempo.

Outro ponto importante é que a curva do kp de 110, é mais suave que a dede 25, apresendo um “cotovelo” menos promeniente.

MNote2
Melhorias no MNote2

As melhorias não param no MNote2, agora duas funcionalidades novas.

O Python permite muita coisa, agora o MNote, permite rodar o python e mostrar seu resultado no prompt.

MNote com Python

Integração com ChatGPT

Agora é possível perguntar coisas ao chatgpt diretamente no mnote2.

Mathlab octave
Alternativa para o Mathlab


O Octave é uma linguagem de programação de alto nível e um ambiente de desenvolvimento numérico, projetado para realizar cálculos numéricos e análises científicas. Ele fornece uma interface fácil de usar para a manipulação de matrizes, implementação de algoritmos, criação de gráficos e solução de problemas numéricos em geral.

Características do Octave:

  1. Linguagem de programação: O Octave possui uma linguagem de programação própria, que permite aos usuários escreverem scripts e programas para realizar operações numéricas, implementar algoritmos e automatizar tarefas.
  2. Matrizes e cálculos numéricos: O Octave é especialmente adequado para trabalhar com matrizes e realizar cálculos numéricos. Ele suporta operações matriciais, álgebra linear, manipulação de vetores e escalares, além de funções matemáticas avançadas.
  3. Funções embutidas: O Octave possui uma ampla gama de funções embutidas para realizar cálculos matemáticos, estatísticos, trigonométricos, lógicos e outros tipos de operações numéricas.
  4. Gráficos e visualização: O Octave oferece recursos para criar gráficos e visualizar dados. Ele suporta plotagem de gráficos 2D e 3D, diagramas de dispersão, histogramas e outras formas de visualização de dados.
  5. Extensibilidade: O Octave permite estender sua funcionalidade por meio de pacotes e bibliotecas. Existem diversos pacotes disponíveis que fornecem recursos adicionais para áreas específicas, como processamento de sinais, controle, otimização, entre outros.
  6. Código aberto: O Octave é um software de código aberto, o que significa que o seu código-fonte está disponível gratuitamente para acesso, modificação e distribuição. Isso permite que a comunidade contribua para o seu desenvolvimento e oferece flexibilidade aos usuários.

Em resumo, o Octave é uma ferramenta poderosa para realizar cálculos numéricos, análises científicas e implementação de algoritmos, sendo uma alternativa popular ao MATLAB.

Ele é amplamente utilizado por pesquisadores, engenheiros, cientistas e estudantes em diversas áreas acadêmicas e industriais.

Baixando o Octave

O Octave pode ser baixado através do seguinte site:

https://octave.org/download

Baixe o arquivo octave para a plataforma alvo, conforme figura abaixo:

Exemplo comparativo

Octave código

Fonte do projeto:

pkg load control

Kp = 36;
Kd = 0.2 * Kp;

num4 = [0 0 5];
den4 = [1 4.42 4];
Gp4 = tf(num4, den4);

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

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

G_fechado = feedback(G10, 1);

% Traçar o diagrama de Bode do sistema em malha fechada
bode(G_fechado);
grid on;

% Traçar o passo de resposta ao impulso do sistema em malha fechada
step(G_fechado);
grid on;

Mathlab

Neste fragmento, podemos notar as semelhanças entre os códigos:

Agora iremos ver o código do projeto.

num1=[005];
den1=[1,4.42,4];
Gp= tf(num1,den1);
ft1 = feedback(Gp,1);
step(ft1);


num2=[36];
den2=[1];
Gp2= tf(num2,den2);

Gp3 = series(Gp,Gp2);
ft2 = feedback(Gp3,1);
step(ft2);

%% 
%% 

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


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


%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);

% G_fechado = feedback(Gp5,1);

step(G_fechado)
%% 
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);
 %%
 %%
 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);

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.

en_USEnglish