Manual
do
Maker
.
com
Existe uma razão muito justa para esse artigo existir, mas não posso revelar ainda. Então, por mais que pareça estranho, ou por mais que ache simples o tema, acompanhe o artigo... tenho certeza que no próximo artigo relacionado fará todo o sentido. Para não parecer algo extremamente simplório, vamos fazer esse color picker com ESP32 enviar as cores para um programa em Python, assim fica mais divertido.
A maioria (se não todos os) dos programas gráficos tem um color picker. Essa ferramenta é fundamental para quando queremos uma amostra de cores de uma imagem. A materialização de um color picker tem um propósito maior, que é trazer uma cor que está no mundo real para dentro da tela do computador, por exemplo. Essa pode ser uma ótima ferramenta para designers, decoradores, arquitetos etc.
Nesse artigo, o processo será feito com a Heltec Stick, disponível na curto circuito por esse link. Para capturar as cores, utilizaremos o sensor TCS34725, também disponível na CurtoCircuito.
Essa placa Heltec Stick tem LoRa, portanto serve para montes de aplicações, mas por uma razão específica, a comunicação entre o computador e a placa será por WiFi.
A leitura da cor é RGB, que deverá ser convertida para CMYK antes de ser enviada. Também há uma razão para isso, mas não é aplicável para esse artigo.
Para que seja um dispositivo móvel, será necessário alimentá-lo por bateria. Nada melhor do que esse adaptador da Saravati.
Posteriormente farei um case em acrílico 100% cast da Sinteglas, mas o objetivo agora é exclusivamente provar o conceito.
O modelo que tenho em mãos é LoRa 868 certificado, com chip sx1276/sx1278. Processador ESP32, que nos dá toda a flexibilidade do FreeRTOS (exceto o TCP+).
A interface ainda é micro USB (algumas placas de outros fabricantes já estão saindo com USB-C), com um regulador de tensão completo, proteção ESD e contra curto, blindagem de RF e outras medidas de proteção. Essa placa é caprichada!
Ela possui uma interface de bateria, da qual farei uso nesse projeto. E foi por essa razão que essa placa foi escolhida; tamanho diminuto e mobilidade. A interface de bateria tem um sistema de gerenciamento para carga e descarga, sobrecarga, detecção de alimentação por bateria (fazendo a troca automática da alimentação entre USB e bateria). Encantadora!
Como todo ESP32, tem bluetooth e WiFi. Com o LoRa, são 3 redes disponíveis.
Poderia ter parado por aí, mas essa placa oferece mais; um display integrado de 0.49' 64x32 OLED, sendo útil para fins de debug, informações de bateria e/ou outros.
O controlador USB é um CP2102, reconhecido imediatamente pelo Linux (não saberei informar sobre o Windows, mas acredito que seja tão direto quanto).
Essa placa é totalmente suportada pela IDE do Arduino, bastando incluir a URL json em File > Preferences.
https://resource.heltec.cn/download/package_heltec_esp32_index.json
Depois, Em Tools > Board Manager pesquise por Heltec ESP32.
A placa estará pronta para uso!
Com exclusividade, a Heltec oferece suporte a um protocolo LoRaWAN que permite se comunicar com qualquer gateway/estação base. Como requerimento, o número de ativação serial será necessário. Para isso, após estar com a placa em mãos acesse essa página.
Abra o sketch de exemplo em Arquivo > Exemplos > ESP32 > ChipID. Suba-o e abra o monitor serial. A informação será exibida periodicamente. Eu já estou com minha licença!
A biblioteca LoRaWAN estará disponível para download no github.
Recursos e documentação da placa podem ser pegos diretamente no site da Heltec.
No gerenciador de bibliotecas da IDE do Arduino, podemos encontrar a biblioteca para ESP32, contendo alguns bons exemplos.
Recomendo o cadastro da placa para obter a licença, pois principalmente para quem está iniciando em LoRaWAN, é muito auxiliador para entender um pouco da prática. Ao abrir um dos sketches de LoRaWAN, bastará adicionar sua licença para poder usar o sketch. A placa que tenho em mãos está marcada como 868, mas tem em outras frequências. Tentei ingressar em EU868 e EU915, mas pelo visto não tenho gateways na região.
Para concluir a apresentação da placa, aqui está o pinout:
Feitas as apresentações, bora começar o projeto!
Não foi à toa que escolhi o TCS34725 para fazer o color picker com ESP32. Esse sensor tem uma precisão bastante satisfatória. Porém, sempre deve-se atentar à influência de luz externa. Por isso que devemos isolar ao máximo a região a ser lida. A conexão é simples e o sensor opera em 3v3 ou 5v, basta escolher no pinout. Para o ESP32, utilize 3v3.
Utilizei a biblioteca TCS34725 disponível no repositório de bibliotecas do Arduino, mas para não dizer que foi tudo perfeito - eu tinha soldado do lado errado uma barra de conexão fêmea. Como precisava ser do outro lado e meus jumpers são todos macho-fêmea agora, tive que remover. O problema é que fritei a placa toda pra conseguir remover o slot e não sei se de alguma forma influenciei a leitura, mas a amostragem RGB parece incrivelmente satisfatória. Já a conversão para CMYK na IDE do Arduino está estranha, não sei se pode ser algo relacionado ao compilador, mas usei a mesma função que no código do projeto que apresentarei no próximo artigo. Enfim, o problema está exclusivamente relacionado à quantidade de preto, mas não será um problema.
A conversão CMYK deve ser enviada para um dispositivo remoto - para esse artigo, o programa feito em Qt. Antes de fazer o programa, fiz um socket server com Python:
import socket
HOST = '192.168.1.200' # Endereco IP do Servidor
PORT = 1234 # Porta que o Servidor esta
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
orig = (HOST, PORT)
tcp.bind(orig)
tcp.listen(1)
while True:
con, client = tcp.accept()
print( 'Client: ', client)
while True:
msg = con.recv(7)
if not msg: break
#print( client, msg)
for i in range(len(msg)):
print(msg[i])
print( 'Closing connection to: ', client)
con.close()
Basta deixá-lo rodando e pegar amostras no sensor de cores. O wiring do sensor de cores é simples: 3v3, GND, 23 e 22. Só se guiar pelo pinout disposto mais acima.
O código para a captura é esse:
#include <WiFi.h>
#include <Wire.h>
#include "heltec.h"
#include "TCS34725.h"
#define OLED_UPDATE_INTERVAL 500
const char* ssid = "SuhankoFamily";
const char* password = "fsjmr112";
const uint16_t port = 1234;
const char * host = "192.168.1.209"; // TODO: passar para o modo AP
TCS34725 tcs;
unsigned long interval = millis();
int timeout = 3000;
int values[4] = {0,0,0,0};
unsigned char RGBsample[3] = {0,0,0};
void rgb2cmyk(uint8_t R, uint8_t G, uint8_t B){
float Rfrac = (float)R/255.0;
float Gfrac = (float)G/255.0;
float Bfrac = (float)B/255.0;
float K = 1-max({Rfrac,Gfrac,Bfrac});
float C = (1-Rfrac-K)/(1-K);
float M = (1-Gfrac-K)/(1-K);
float Y = (1-Bfrac-K)/(1-K);
values[0] = C*100;
values[1] = M*100;
values[2] = Y*100;
values[3] = K*100/2; //GAMBIARRA PARA ACERTAR O NIVEL DE PRETO. PRECISA DE CORREÇÃO.
}
void setupWIFI()
{
Heltec.display->clear();
Heltec.display->drawString(0, 0, "Connecting...");
Heltec.display->drawString(0, 10, String(ssid));
Heltec.display->display();
WiFi.disconnect(true);
delay(1000);
WiFi.mode(WIFI_STA);
//WiFi.onEvent(WiFiEvent);
WiFi.setAutoConnect(true);
WiFi.setAutoReconnect(true);
//WiFi.setHostname(HOSTNAME);
WiFi.begin(ssid, password);
byte count = 0;
while(WiFi.status() != WL_CONNECTED && count < 10)
{
count ++;
delay(500);
Serial.print(".");
}
Heltec.display->clear();
if(WiFi.status() == WL_CONNECTED){
Heltec.display->drawString(0, 0, "Conectado");
Heltec.display->drawString(0, 10, "Ate logo");
Heltec.display->display();
delay(5000);
Heltec.display->clear();
Heltec.display->display();
}
else{
Heltec.display->drawString(0, 0, "Connect False");
Heltec.display->display();
}
Heltec.display->clear();
}
void setup()
{
pinMode(0,INPUT_PULLDOWN);
Serial.begin(115200);
Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);
pinMode(25, OUTPUT);
while (!Serial) {
;
}
setupWIFI();
Wire.begin(23,22);
if (!tcs.attach(Wire)) Serial.println("ERROR: TCS34725 NOT FOUND !!!");
tcs.integrationTime(33); // ms
tcs.gain(TCS34725::Gain::X01);
}
void loop(){
if (!digitalRead(0)){
WiFiClient client;
Serial.println("INTERRUPCAO");
digitalWrite(25,HIGH);
delay(1500);
digitalWrite(25,LOW);
Heltec.display->clear();
Heltec.display->setFont(ArialMT_Plain_10);
Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
Heltec.display->drawString(0, 0, String("Address"));
Heltec.display->display();
if (tcs.available()){
TCS34725::Color color = tcs.color();
if (millis()-interval > timeout){
RGBsample[0] = color.r;
RGBsample[1] = color.g;
RGBsample[2] = color.b;
rgb2cmyk(RGBsample[0],RGBsample[1],RGBsample[2]);
Serial.print("R : "); Serial.println(RGBsample[0]);
Serial.print("G : "); Serial.println(RGBsample[1]);
Serial.print("B : "); Serial.println(RGBsample[2]);
interval = millis();
if (!client.connect(host, port)){
Serial.println("Connection to host failed");
}
unsigned char msg[6];
memset(msg,0,sizeof(msg));
msg[0] = 0x5e;
msg[1] = values[0];
msg[2] = values[1];
msg[3] = values[2];
msg[4] = values[3];
msg[5] = '$';
Serial.print("msg: ");
for (uint8_t i=0;i<6;i++){
Serial.write(msg[i]);
}
Serial.println(" ");
Serial.print("CMYK: ");
for (uint8_t j=0;j<4;j++){
Serial.print(values[j]);
Serial.print(" ");
}
Serial.println(" ");
client.write(&msg[0],7);
client.stop();
}
}
}
}
void printRGB(){
Heltec.display->clear();
Heltec.display->setFont(ArialMT_Plain_10);
Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
Heltec.display->drawString(0, 0, "RGB");
Heltec.display->drawString(0, 10, "CMYK");
Heltec.display->display();
}
void WiFiEvent(WiFiEvent_t event)
{
Serial.printf("[WiFi-event] event: %d\n", event);
switch(event)
{
case SYSTEM_EVENT_WIFI_READY: /**< ESP32 WiFi ready */
break;
case SYSTEM_EVENT_SCAN_DONE: /**< ESP32 finish scanning AP */
break;
case SYSTEM_EVENT_STA_START: /**< ESP32 station start */
break;
case SYSTEM_EVENT_STA_STOP: /**< ESP32 station stop */
break;
case SYSTEM_EVENT_STA_CONNECTED: /**< ESP32 station connected to AP */
break;
case SYSTEM_EVENT_STA_DISCONNECTED: /**< ESP32 station disconnected from AP */
break;
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: /**< the auth mode of AP connected by ESP32 station changed */
break;
case SYSTEM_EVENT_STA_GOT_IP: /**< ESP32 station got IP from connected AP */
case SYSTEM_EVENT_STA_LOST_IP: /**< ESP32 station lost IP and the IP is reset to 0 */
break;
case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: /**< ESP32 station wps succeeds in enrollee mode */
case SYSTEM_EVENT_STA_WPS_ER_FAILED: /**< ESP32 station wps fails in enrollee mode */
case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: /**< ESP32 station wps timeout in enrollee mode */
case SYSTEM_EVENT_STA_WPS_ER_PIN: /**< ESP32 station wps pin code in enrollee mode */
break;
case SYSTEM_EVENT_AP_START: /**< ESP32 soft-AP start */
case SYSTEM_EVENT_AP_STOP: /**< ESP32 soft-AP stop */
case SYSTEM_EVENT_AP_STACONNECTED: /**< a station connected to ESP32 soft-AP */
case SYSTEM_EVENT_AP_STADISCONNECTED: /**< a station disconnected from ESP32 soft-AP */
case SYSTEM_EVENT_AP_PROBEREQRECVED: /**< Receive probe request packet in soft-AP interface */
case SYSTEM_EVENT_AP_STA_GOT_IP6: /**< ESP32 station or ap interface v6IP addr is preferred */
case SYSTEM_EVENT_AP_STAIPASSIGNED:
break;
case SYSTEM_EVENT_ETH_START: /**< ESP32 ethernet start */
case SYSTEM_EVENT_ETH_STOP: /**< ESP32 ethernet stop */
case SYSTEM_EVENT_ETH_CONNECTED: /**< ESP32 ethernet phy link up */
case SYSTEM_EVENT_ETH_DISCONNECTED: /**< ESP32 ethernet phy link down */
case SYSTEM_EVENT_ETH_GOT_IP: /**< ESP32 ethernet got IP from connected AP */
case SYSTEM_EVENT_MAX:
break;
}
}
Ainda tem modificações a fazer para a relação com o próximo artigo (que já está 99% pronto, mas esse deveria precedê-lo). De qualquer modo, já é um bom exemplo. Nesse código do color picker com ESP32 temos:
Espero que acompanhe a série, porque a surpresa para o próximo artigo relacionado é incrível! Como dica do que está por vir, dê uma olhada no artigo do ILI9341 e tente imaginar qual será o projeto.
Revisão: Ricardo Amaral de Andrade
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.