Laboratório Maker 05: Serial Studio – Monitor serial de tempo real

Garimpando pela Internet, acabei me deparando com uma ferramenta espetacular, que faz uso de diversos recursos do Qt. O Serial Studio é uma ferramenta que serve para diversos tipos de diagnósticos. Pois é, um laboratório não é feito só de hardware. Mas de que modo ele pode ser usado como uma ferramenta? – Vamos conhecê-lo primeiro.

Serial Studio – Monitor serial de tempo real para MCUs

A primeira coisa que dá vontade de fazer é utilizá-lo como dashboard permanente. Não que a ideia seja ruim, mas na verdade ele é uma ferramenta para aferir o funcionamento de seus periféricos conectados a uma MCU. E como a comunicação é serial, serve para qualquer MCU: Raspberry, Banana Pi, Orange Pi ou qualquer outro hardware que ofereça uma interface UART.

Os widgets do Serial Studio

Ele possui uma série de widgets para visualização, que são adaptáveis a diversas saídas, como dados únicos ou arrays. Descrevo-os adiante.

Acelerômetro

O widget acelerômetro do Serial Studio mede a força G em cada axis e mostra em um gauge similar aos encontrados em aeronaves.

Tanque / barra

Esse widget é usado para indicar o valor de medições em relação a carga mínima e máxima. Serve para medição de baterias, nível d’água, medição de recursos de software etc.

Horizonte Artificial

Esse é apaixonante. Esse widget é utilizado para aferição de giroscópio.

Mapa

E a última imagem de Ctrl+Chups do projeto: o mapa, para utilizar com GPS. Daqui em diante as imagens são geradas aqui mesmo, com os testes que fiz para comprovar a usabilidade.

Protocolo de Comunicação

O protocolo é baseado em json, que pode ser implementado de duas maneiras: ou na MCU, ou no computador. Fui direto para o computador, pois assim simplifica a implementação do recurso em qualquer projeto pronto, sem precisar mais desenvolvimento. Mas temos que conhecer a estrutura para montar nosso json localmente. É fácil, confira.

Formato da Mensagem

Para motivá-lo, fiz uma leitura analógica em um ESP32 para mostrar a simplicidade. No ESP32 fiz apenas isso:

void loop() {
    String msg = "/*Analog 4,"+String(analogRead(4)) +"*/";
    Serial.println(msg);
     delay(100);
}
O formato é composto por:
Início de mensagem: /*
Título do projeto: Usei “Analog 4“, mas pode ser qualquer coisa, com ou sem espaço.
Mensagem: No caso, fiz uma leitura analógica.
Fim de mensagem: */
 
Esse é o formato mínimo, dispensando o uso de json na MCU. Do lado do Serial Studio, criamos o arquivo json a importar:
 
{
  "t":"%1",
  "g":[
     {
        "t":"Sensor Readings",
        "d":[
           {
              "t":"Analog test",
              "v":"%2",
              "g":true,
              "u":"A4"
           }
           ]
     }
     ]
}
 
Vamos aos detalhes.
Criamos a estrutura dentro de chaves, definimos o título (t) e então criamos um grupo (g). Dentro do grupo temos título, widget e dataset.

Título

O título é obrigatório e pode ser qualquer coisa que desejar.

Widget

É opcional esse parâmetro. Temos 4 possíveis escolhas:

  • map – para utilizar com o GPS
  • bar – para medição de níveis, com valor máximo e mínimo
  • gyro – para utilizar com giroscópio
  • accelerometer – para utilizar com acelerômetro

Dataset

O dataset é um array, contendo 1 item obrigatório e 3 opcionais:

  • t – Título opcional
  • v – valor do dataset (variável, obrigatório)
  • u – Unidade do dataset (mm, cm, C etc)
  • g – Gráfico do dataset (também opcional, mas se quiser ver graficamente, tem que ser true)
  • w – Tipo do widget. Veja a descrição de w a seguir.
w

w do dataset depende do tipo de widget do grupo. Para giroscópio e acelerômetro temos:

  • x – valor de x
  • y – valor de y
  • z – valor de z

Para map temos:

  • lat – para latitude
  • lon – para longitude

Para tanque temos:

  • max – valor máximo
  • min – valor mínimo

O Serial Studio também pode criar um arquivo CSV com todas as leituras recebidas através do dispositivo pela serial, e o arquivo pode ser usado para análise e processamento de dados com MATLAB.

Os widgets podem ser repetidos através de diferentes grupos.

Modos de comunicação

Como citei lá no começo, tem duas maneiras de fazer essa comunicação. Uma delas, formatando o json na MCU; a outra, formatando uma string a enviar pela serial.

Por que eu prefiro fortemente formatar uma string e enviar pela serial? – Simples: pego qualquer projeto pronto que precise ser depurado e só incluo uma string formatando os dados ao final do processamento. Não preciso mexer no código e depois de concluído o debug, removo a string! Magnífico!

Não é por utilizar o formato string que não seja necessário conhecer a estrutura, já que para gerar os gráficos será necessário compor os widgets localmente. O que fiz para criar o arquivo localmente foi utilizar um trecho do formato que seria composto na MCU. O exemplo do projeto dispõe essa referência, da qual podemos extrair as partes desejadas:

{
   "t":"%1",
   "g":[
      {
         "t":"Mission Status",
         "d":[
            {
               "t":"Runtime",
               "v":"%2",
               "u":"ms"
            },
            {
               "t":"Packet count",
               "v":"%3"
            },
            {
               "t":"Battery voltage",
               "v":"%4",
               "g":true,
               "u":"V"
            }
         ]
      },
      {
         "t":"Sensor Readings",
         "d":[
            {
               "t":"Temperature",
               "v":"%5",
               "g":true,
               "u":"°C"
            },
            {
               "t":"Altitude",
               "v":"%6",
               "u":"m"
            },
            {
               "t":"Pressure",
               "v":"%7",
               "u":"KPa",
               "g":true
            },
            {
               "t":"External Temperature",
               "v":"%8",
               "g":true,
               "u":"°C"
            },
            {
               "t":"Humidity",
               "v":"%9",
               "g":true,
               "u":"°C"
            }
         ]
      },
      {
         "t":"GPS",
         "w":"map",
         "d":[
            {
               "t":"GPS Time",
               "v":"%10"
            },
            {
               "t":"Longitude",
               "v":"%11",
               "u":"°N",
               "w":"lon"
            },
            {
               "t":"Latitude",
               "v":"%12",
               "u":"°E",
               "w":"lat"
            },
            {
               "t":"Altitude",
               "v":"%13",
               "u":"m"
            },
            {
               "t":"No. Sats",
               "v":"%14"
            }
         ]
      },
      {
         "t":"Accelerometer",
         "w":"accelerometer",
         "d":[
            {
               "t":"X",
               "v":"%15",
               "u":"m/s^2",
               "g":true,
               "w":"x"
            },
            {
               "t":"Y",
               "v":"%16",
               "u":"m/s^2",
               "g":true,
               "w":"y"
            },
            {
               "t":"Z",
               "v":"%17",
               "u":"m/s^2",
               "g":true,
               "w":"z"
            }
         ]
      },
      {
         "t":"Gyroscope",
         "w":"gyro",
         "d":[
            {
               "t":"X",
               "v":"%18",
               "u":"rad/s",
               "g":true,
               "w":"x"
            },
            {
               "t":"Y",
               "v":"%19",
               "u":"rad/s",
               "g":true,
               "w":"y"
            },
            {
               "t":"Z",
               "v":"%20",
               "u":"rad/s",
               "g":true,
               "w":"z"
            }
         ]
      }
   ]
}

O Serial Studio substitui os parâmetros %n com os valores correspondentes separados por vírgula, como na mensagem que compus no ESP32. Para essa “linguiça” do exemplo, ficaria desse jeito:

/*KAANSATQRO,%s,%s,%s,%s,%s,%s,%,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s*/

 

Exemplos de estrutura para os Widgets do Serial Studio

Fácil ou não? Agora veja a sintaxe para cada um dos widgets das imagens mais acima:

Acelerômetro

{
   "t":"Accelerometer",                # Group title
   "w":"accelerometer",                # Widget type for this group
   "d":[                               # Dataset array
      {
         "t":"X-Acceleration",         # Dataset title
         "v":"%current_x_value%",      # Current value of dataset
         "w":"x"                       # Corresponding widget option for this dataset
      },
      {
         "t":"Y-Acceleration",         # Dataset title
         "v":"%current_y_value%",      # Current value of dataset
         "w":"y"                       # Corresponding widget option for this dataset
      },
      {
         "t":"Z-Acceleration"          # Dataset title
         "v":"%current_z_value%",      # Current value of dataset
         "w":"z"                       # Corresponding widget option for this dataset
      }
   ]
}

Barra

{
    "t":"Battery voltage",       # Dataset title
    "v":"%current_value%",      # Dataset value
    "w":"bar",                  # Widget type
    "min":0,                    # Minimum value in range
    "max":5                     # Maximum value in range
}

Horizonte Artificial

{
   "t":"Gyroscope",             # Group title
   "w":"gyro",                  # Widget type
   "d":[
      {
         "t":"X",               # Dataset title
         "v":"%current_value%,  # Dataset value
         "w":"yaw"              # Corresponding widget option (yaw)
      },
      {
         "t":"Y",               # Dataset title
         "v":"%current_value%", # Dataset value
         "w":"roll"             # Corresponding widget option (roll)
      },
      {
         "t":"Z",               # Dataset title    
         "v":"%current_value%", # Dataset value
         "w":"pitch"            # Corresponding widget option (pitch)
      }
   ]
}

Mapa

{
   "t":"GPS",                 # Group title
   "w":"map",                 # Widget type
   "d":[
      {
         "t":"Longitude",     # Dataset title
         "v":"%longitude%",   # Dataset value
         "w":"lon"            # Corresponding widget option (longitude)
      },
      {
         "t":"Latitude",      # Dataset title
         "v":"%latitude%",    # Dataset value
         "w":"lat"            # Corresponding widget option (latitude)
      }
   ]
}

Download

Eu fiz a compilação, já que o programa é feito em Qt e eu tenho o ambiente pronto para execução do setup, mas em Releases você encontra binários prontos para Windows, Mac e Linux.

Vídeo

Claro, não dá pra passar sem vídeo. Fiz uma apresentação de uma leitura do gerador de sinal PWM e uma leitura de um microfone (que utilizei em outro artigo para mostrar como fazer streaming de áudio do ESP32 para o Raspberry por WiFi) aqui nesse vídeo, em nosso canal Dobitaobytebrasil no Youtube.

O arquivo json utilizado para o vídeo é esse a seguir, caso deseje reproduzir ou ter um ponto de partida:

{
   "t":"%1",
   "g":[
      {
         "t":"Sound test",
         "d":[
            {
               "t":"Microphone",
               "v":"%2",
               "g":true,
               "u":"signal"
            }
            ]
      },
      {
         "t":"%3",
         "d":[
            {
               "t":"Readings",
               "v":"%4",
               "g":true,
               "u":"PWM"
            }
            ]
      }
      ]
}

Até a próxima!

Revisão: Ricardo Amaral de Andrade