Buscar

Aula 7 - CONTROLE SERIAL do LED RGB

Prévia do material em texto

Aula 5 – Lâmpada de humor controle serial
 universidade são caetano do sul
Sistemas embarcados
Prof.: ismael mouraparede
Objetivo – Controle serial do led rgb 
Para este projeto , você revisará o circuito do projeto Lâmpada de humor, mas agora mergulhará no mundo das comunicações seriais. 
Você controlará sua lâmpada enviando comandos do PC para o Arduino utilizando o Serial Monitor, do IDE do Arduino. 
A comunicação serial é o processo de envio de dados, um bit de cada vez, por um link de comunicação.
 Este projeto também apresenta como manipular strings de texto. Portanto, prepare seu hardware da mesma forma que no projeto 8, e digite o novo código.
Código para o projeto
// Projeto 10 – Mood lamp com controle serial
char buffer[18];
int red, green, blue;
int RedPin = 11;
int GreenPin = 10;
int BluePin = 9;
Código para o projeto
void setup() {
Serial.begin(9600);
Serial.flush();
pinMode(RedPin, OUTPUT);
pinMode(GreenPin, OUTPUT);
pinMode(BluePin, OUTPUT);
}
Código para o projeto
void loop() {
if (Serial.available() > 0) {
int index=0;
delay(100); // deixe o buffer encher
int numChar = Serial.available();
if (numChar>15) {
numChar=15;
}
Código para o projeto
while (numChar--) {
buffer[index++] = Serial.read();
}
splitString(buffer);
}
}
Código para o projeto
void splitString(char* data) {
Serial.print("Data entered: ");
Serial.println(data);
char* parameter;
parameter = strtok (data, " ,");
while (parameter != NULL) {
setLED(parameter);
parameter = strtok (NULL, " ,");
}
Código para o projeto
// Limpa o texto e os buffers seriais
for (int x=0; x<16; x++) {
buffer[x]='\0';
}
Serial.flush();
}
Código para o projeto
void setLED(char* data) {
if ((data[0] == 'r') || (data[0] == 'R')) {
int Ans = strtol(data+1, NULL, 10);
Ans = constrain(Ans,0,255);
analogWrite(RedPin, Ans);
Serial.print("Red is set to: ");
Serial.println(Ans);
}
Código para o projeto
if ((data[0] == 'g') || (data[0] == 'G')) {
int Ans = strtol(data+1, NULL, 10);
Ans = constrain(Ans,0,255);
analogWrite(GreenPin, Ans);
Serial.print("Green is set to: ");
Serial.println(Ans);
}
Código para o projeto
if ((data[0] == 'b') || (data[0] == 'B')) {
int Ans = strtol(data+1, NULL, 10);
Ans = constrain(Ans,0,255);
analogWrite(BluePin, Ans);
Serial.print("Blue is set to: ");
Serial.println(Ans);
}
}
funcionamento
Assim que você tiver verificado o código, faca seu upload para o Arduino.
Note que, quando você faz o upload do programa, nada parece acontecer. Isso ocorre porque o programa esta aguardando por uma entrada sua. Inicie o Serial Monitor, clicando em seu ícone na barra de tarefas do IDE do Arduino.
funcionamento
Na janela de texto do monitor serial, digite manualmente os valores R, G e B para cada um dos três LEDs. Os LEDs mudarão para as cores que você determinou.
Se você digitar R255, o LED vermelho acendera com brilho máximo. 
Se você digitar R255, G255, tanto o LED vermelho quanto o verde acenderão com brilho máximo.
Agora, digite R127, G100, B255, e você terá uma agradável cor purpura. Se digitar r0, g0, b0, você apagara todos os LEDs.
funcionamento
O texto de entrada e projetado para aceitar as letras R, G e B em maiúsculas ou minúsculas, seguidas por um valor de 0 a 255. 
Qualquer valor acima de 255 será limitado a 255 por padrão. Você pode digitar uma virgula ou espaço entre os parâmetros, e pode digitar um, dois ou tres valores de LED a qualquer momento; por exemplo:
r255 b100
r127 b127 g127
G255, B0--
B127, R0, G255
Análise do código
Este projeto apresenta vários novos conceitos, incluindo comunicação serial, ponteiros e manipulação de strings.
Primeiramente, você define um array de 18 caracteres (char) para armazenar sua string de texto, que é maior do que o máximo de 16 permitidos, para garantir que você não tenha erros de estouro de buffer.
	char buffer[18];
Em seguida, você define os inteiros para armazenar os valores red, green e blue, assim como os valores para os pinos digitais:
int red, green, blue;
int RedPin = 11;
int GreenPin = 10;
int BluePin = 9;
Análise do código
Em sua função de inicialização, voce define os tres pinos digitais como saidas. Antes disso, porem, temos o comando Serial.begin:
void setup() {
Serial.begin(9600);
Serial.flush();
pinMode(RedPin, OUTPUT);
pinMode(GreenPin, OUTPUT);
pinMode(BluePin, OUTPUT);
}
Análise do código
Serial.begin diz ao Arduino para iniciar comunicações seriais; o numero dentro dos parênteses (nesse caso, 9600) define a taxa de transmissão (de símbolos ou pulsos por segundo) na qual a linha serial se comunicara.
O comando Serial.flush liberara caracteres que estejam na linha serial, deixando-a vazia e pronta para entradas/saídas.
A linha de comunicações seriais e simplesmente uma forma do Arduino se comunicar com o mundo externo, que, nesse caso, ocorre de, e para, o PC e o Serial Monitor do IDE do Arduino.
Análise do código
No loop principal você tem uma instrução if: 
if (Serial.available() > 0) {
Essa instrução está utilizando o comando Serial.available para verificar se algum caractere foi enviado pela linha serial. Se caracteres já tiverem sido recebidos, a condição é atendida e o código dentro do bloco de instruções if é executado:
if (Serial.available() > 0) {
int index=0;
delay(100); // deixe o buffer encher
int numChar = Serial.available();
if (numChar>15) {
numChar=15;
}
while (numChar--) {
buffer[index++] = Serial.read();
}
splitString(buffer);
}
Análise do código
Um inteiro, index, e declarado e inicializado como zero. Esse inteiro armazenara a posição de um ponteiro para os caracteres do array char.
Você então define uma espera de 100, para garantir que o buffer serial (local na memoria em que os dados recebidos são armazenados, antes do processamento) esteja cheio antes de processar os dados. 
Caso você não faça isso, e possível que a função seja executada e inicie o processamento da string de texto antes de você receber todos os dados. A linha de comunicações seriais e muito lenta quando comparada a velocidade de execução do restante do código. 
Quando você envia uma string de caracteres, a função Serial.available terá imediatamente um valor maior que zero, e a função if iniciara sua execução.
Se você não utilizasse a instrução delay(100), ela poderia iniciar a execução do código da instrução if antes que toda a string tivesse sido recebida, e os dados seriais poderiam ser apenas os primeiros caracteres da string de texto. 
Depois de esperar 100ms para que o buffer serial se encha com os dados enviados, você declara e inicializa o inteiro numChar com o numero de caracteres da string de texto. 
Análise do código
Assim, se enviássemos este texto no Serial Monitor
		R255, G255, B255
O valor de numChar seria 17. Ele seria 17 e não 16, pois ao final de cada linha de texto há um caractere invisível, o caractere NULL, que informa ao Arduino quando atingimos o final da linha de texto.
A próxima instrução if verifica se o valor de numChar e maior que 15; se for, ela o define como 15. Isso garante que você não estoure o array char buffer[18].
Em seguida, temos um comando while, algo que você ainda não viu, por isso deixe-me explica-lo.
Análise do código
Sua sintaxe e a seguinte:
while(expression) {
// instrução(ões)
}
Em seu código, o loop while e:
while (numChar--) {
buffer[index++] = Serial.read();
}
Análise do código
A condição que ele esta verificando e numChar. Em outras palavras, o loop verifica seo valor armazenado no inteiro numChar não e zero. Note que numChar tem -- depois dele.
Isso e um pos-decremento: o valor e diminuído depois de utilizado. Se você tivesse utilizado --numChar, o valor em numChar seria diminuído (subtraído em 1) antes de ser avaliado. 
Em nosso caso, o loop while verifica o valor de numChar e, apenas depois, subtrai 1 dele. Se o valor de numChar for diferente de zero antes do decremento, o código do bloco será executado.
numChar esta definida como o comprimento da string de texto que voce digitou na janela do Serial Monitor. Assim, o codigo do loop while executara varias vezes.
Análise do códigoO código do loop while e:
		buffer[index++] = Serial.read();
Isso define cada elemento do array buffer como os caracteres lidos a partir da linhaserial. Em outras palavras, preenche o array buffer com as letras que voce digitou na janela de texto do Serial Monitor.
O comando Serial.read() lê dados seriais, um bit por vez. Dessa forma, agora que seu array de caracteres foi preenchido com os caracteres que você digitou no monitor serial, o loop while terminara assim que numChar atingir zero (por exemplo, o comprimento da string).
Análise do código
Depois do loop while, temos
		splitString(buffer);
Uma chamada a uma das duas funcoes que voce criou com o nome de splitString(). A funcao se parece com isso:
void splitString(char* data) {
Serial.print("Data entered: ");
Serial.println(data);
char* parameter;
parameter = strtok (data, " ,");
while (parameter != NULL) {
setLED(parameter);
parameter = strtok (NULL, " ,");
}
// Limpa o texto e os buffers seriais
for (int x=0; x<16; x++) {
buffer[x]='\0';
}
Serial.flush();
}
Análise do código
Como ela nao retorna nenhum dado, seu tipo de dados foi definido como void. Você passa a ela um parâmetro: uma variável do tipo char, que você chama de data. Entretanto, nas linguagens de programação C e C++, nao e permitido que você envie um array de caracteres a uma função. 
Você contorna essa limitação utilizando um ponteiro. Você sabe que esta utilizando um ponteiro, pois um asterisco foi adicionado ao nome da variável *data.
Ponteiros representam um tópico avançado em C, por isso nao entrarei em muitos detalhes sobre eles. Caso você queira saber mais, consulte um livro sobre programação em C. 
Tudo que você tem de saber por ora e que, declarando data como um ponteiro, ela se torna uma variável que aponta para outra variável.
Análise do código
Você pode apontar para o endereço no qual a variável esta armazenada na memoria utilizando o símbolo &, ou, em nosso caso, apontar para o valor armazenado nesse endereço na memoria utilizando o símbolo *. 
Você utiliza esse recurso para contornar uma limitação, uma vez que, como mencionamos, nao e permitido que você envie um array de caracteres a uma função. Entretanto, você pode enviar a sua função um ponteiro que aponte para um array de caracteres. Assim, você declarou uma variável de tipo de dado char, e chamou-a de data, mas o símbolo * antes dela significa que ela esta apontando para o valor armazenado na variável buffer.
Ao chamar splitString(), você envia o conteúdo de buffer (mais propriamente, um ponteiro para ele, como acabamos de ver):
splitString(buffer);
Análise do código
Assim, você chamou a função e passou a ela todo o conteúdo do array de caracteres buffer.
O primeiro comando e
		Serial.print("Data entered: ");
Essa e sua forma de enviar dados do Arduino para o PC. Nesse caso, o comando print envia o que estiver dentro dos parênteses para o PC, por meio do cabo USB, e você pode ler o resultado na janela do Serial Monitor. Aqui, você enviou as palavras "Data entered: ". Note que o texto deve estar entre aspas. A próxima linha e semelhante:
Serial.println(data);
Novamente, você enviou dados de volta ao PC. Dessa vez, você enviou a variável data, que e uma copia do conteúdo do array de caracteres buffer que você passou a função.
Assim, se a string de texto digitada for
		R255 G127 B56
Então o comando
Serial.println(data);
Análise do código
Enviara essa string de texto de volta ao PC, exibindo-a na janela do Serial Monitor.
(Certifique-se de que você primeiro habilitou a janela do Serial Monitor.) Dessa vez, o comando print tem ln ao seu final: println. Isso significa simplesmente
“imprima com um avanço de linha”.
Quando voce imprime utilizando o comando print, o cursor (o ponto em que o próximo simbolo de texto surgira) permanece ao final do que você imprimiu. Quando voce utiliza o comando println, um comando de avanço de linha e emitido: imprime-se o texto e depois o cursor desce para a linha seguinte.
Serial.print("Data entered: ");
Serial.println(data);
Análise do código
Dessa forma, se você analisar os dois comandos print, verá que o primeiro imprime "Data entered: ", deixando o cursor ao final do texto, enquanto o comando de impressão seguinte imprime data (o conteúdo do array buffer) e emite um avanço de linha, descendo o cursor para a linha seguinte. Se, depois disso, você utilizar mais uma instrução print ou println, o texto no monitor serial surgira na linha seguinte.
Agora, você cria uma nova variável de tipo char, parameter 
	char* parameter;
Como você esta utilizando essa variável para acessar elementos do array data, ela deve ser do mesmo tipo, dai o símbolo *. Você nao pode passar dados de uma variável de determinado tipo de dados para uma de outro tipo; os dados devem ser primeiramente convertidos. Essa variável e outro exemplo de variável que tem escopo local e que pode ser vista apenas pelo código dentro dessa função. Assim, se você tentar acessar a variável parameter fora da funcao splitString(), recebera um erro.
Análise do código
Depois, você utiliza um comando strtok, muito útil para manipulação de strings de texto. strtok recebe esse nome por causa das palavras String e Token, pois tem como proposito dividir uma string utilizando tokens. Em seu caso, o token que estamos procurando e um espaco ou virgula; o comando esta sendo utilizado para dividir strings de texto em strings menores.
Voce passa o array data ao comando strtok como primeiro argumento e os tokens (entre aspas) como segundo argumento. Portanto:
parameter = strtok (data, " ,");
Com isso, strtok divide a string quando encontra um espaço ou uma virgula. Se sua string de texto for
R127 G56 B98
Então, depois dessa instrucao, o valor de parameter sera
R127
Análise do código
Pois o comando strtok divide a string na primeira ocorrencia de uma virgula. Depois que voce tiver definido a variavel parameter como a secao da string de texto que deseja extrair (por exemplo, o trecho ate o primeiro espaco ou virgula), voce entra em um loop while com a condicao de que parameter nao esteja vazia (por exemplo, se voce tiver atingido o final da string):
while (parameter != NULL) {
Dentro do loop chamamos nossa segunda funcao:
setLED(parameter);
(Veremos essa funcao mais detalhadamente no futuro.) Depois, voce define a variavel
parameter como a proxima secao da string, ate o proximo espaco ou virgula. Isso e feito
passando para strtok um parametro NULL, da seguinte maneira:
parameter = strtok (NULL, " ,");
Isso diz ao comando strtok para continuar de onde tinha parado.
Análise do código
Assim, todo este trecho da função:
char* parameter;
parameter = strtok (data, " ,");
while (parameter != NULL) {
setLED(parameter);
parameter = strtok (NULL, " ,");
}
Esta simplesmente removendo cada parte da string de texto, separadas por espaços ou virgulas, e enviando essas partes a próxima função, setLED().
O trecho final dessa função simplesmente preenche o array buffer com caracteres NULL, o que e feito com o símbolo \0, e então libera os dados do buffer serial, deixando-o pronto para a entrada do próximo conjunto de dados:
// Limpe o texto e os buffers seriais
for (int x=0; x<16; x++) {
buffer[x]='\0';
}
Serial.flush();
Análise do código
A função setLED() tomara cada parte da string de texto e definira o LED correspondente com a cor que voce escolheu. Assim, se a string de texto digitada por você for
		G125 B55
A função splitString() dividirá essa linha em dois componentes separados
G125
B55
E enviará essas strings de texto abreviadas para a função setLED(), que fara a leitura dos dados e decidira qual LED você escolheu, definindo para ele o valor de brilho correspondente.
Análise do código
Vamos retornar a segunda funcao, setLED():
void setLED(char* data) {
if ((data[0] == 'r') || (data[0] == 'R')) {
int Ans = strtol(data+1, NULL, 10);
Ans = constrain(Ans,0,255);
analogWrite(RedPin, Ans);
Serial.print("Red is set to: ");
Serial.println(Ans);
}
if ((data[0] == 'g') || (data[0] == 'G')) {
int Ans = strtol(data+1, NULL, 10);
Ans = constrain(Ans,0,255);analogWrite(GreenPin, Ans);
Serial.print("Green is set to: ");
Serial.println(Ans);
}
Análise do código
Essa função contem três instruções if semelhantes, por isso vamos escolher uma e analisa-la:
if ((data[0] == 'r') || (data[0] == 'R')) {
int Ans = strtol(data+1, NULL, 10);
Ans = constrain(Ans,0,255);
analogWrite(RedPin, Ans);
Serial.print("Red is set to: ");
Serial.println(Ans);
}
Essa instrução if verifica se o primeiro caractere na string data[0] e a letra r ou R (caracteres maiúsculos ou minúsculos são totalmente diferentes para a linguagem C). Você utiliza o comando logico OU (o símbolo ||) para verificar se a letra e um r ou um R, uma vez que as duas opcoes sao validas.
Análise do código
Se ela e um r ou um R, a instrução if sabe que você deseja alterar o brilho do LED vermelho (red), e seu código e executado. Primeiramente, você declara uma variável inteira Ans (que tem escopo local, apenas para a função setLED) e utiliza o comando strtol (string para inteiro longo), para converter os caracteres depois da letra R em um inteiro. O comando strtol aceita três parâmetros: a string que voce esta passando, um ponteiro para o caractere depois do inteiro (que voce nao utilizara, pois já limpou a string utilizando o comando strtok e, portanto, passa um caractere NULL), e a base (nesse caso, base 10, pois você esta utilizando números decimais normais, em vez de números binários, octais ou hexadecimais, que seriam base 2, 8 e 16, respectivamente).
Em resumo, você declara um inteiro e o define como o valor da string de texto depois da letra R (o trecho com o numero). Depois, você utiliza o comando constrain para garantir que Ans vá apenas de 0 a 255 e nao passe disso. Então, você utiliza um comando analogWrite para o pino vermelho, enviando a ele o valor de Ans. O código emite "Red is set to: ", seguido pelo valor de Ans, no Serial Monitor. As outras duas instruções if fazem exatamente o mesmo procedimento, só que para os LEDs verde e azul.
Análise do código
Voce encontrou muitos novos tópicos e conceitos neste projeto. Para garantir que compreendeu exatamente o que fizemos, coloquei o código do projeto (que esta em C, lembre-se) lado a lado com o pseudocódigo correspondente (essencialmente, a linguagem do computador descrita com mais detalhes, utilizando palavras e pensamentos). Consulte a tabela 3.2 para a comparação.
Tabela 3.2 – Explicação para o codigo do projeto utilizando pseudocódigo
Linguagem de programação C
// Projeto 10 – Lâmpada de humor com controle serial
char buffer[18];
int red, green, blue;
int RedPin = 11;
int GreenPin = 10;
int BluePin = 9;
void setup()
{
Serial.begin(9600);
Serial.flush();
pinMode(RedPin, OUTPUT);
pinMode(GreenPin, OUTPUT);
pinMode(BluePin, OUTPUT);
}
de saida
O mesmo para o verdPseudocódigo
Comentario com o numero e nome do projeto
Declare um array de caracteres de 18 letras
Declare 3 inteiros: red, green e blue
Um inteiro atribuindo determinado pino ao LED vermelho
Um inteiro atribuindo determinado pino ao LED verde
Um inteiro atribuindo determinado pino ao LED azul
Funcao setup
Defina as comunicacoes seriais em 9.600 caracteres por segundo
Libere a linha serial
Defina o pino vermelho como pino e
Tabela 3.2 – Explicação para o codigo do projeto utilizando pseudocódigo
void loop()
{
if (Serial.available() > 0) {
int index=0;
delay(100); // Deixe o buffer encher
int numChar = Serial.available();
if (numChar>15) {
numChar=15;
}
while (numChar--) {
buffer[index++] = Serial.read();
}
splitString(buffer);
}
}
loop principal do programa
Se dados estão sendo enviados pela linha serial...
Declare um inteiro, index, e o defina como 0
Espere 100 milissegundos
Defina numChar como os dados entrantes vindos do serial
Se numChar for maior que 15 caracteres...
Faca com que tenha no máximo 15 caracteres
Enquanto numChar nao for 0 (subtraia 1 dele)
Defina element[index] como o valor lido (adicione 1)
Chame a função splitString e envie a ela dados do buffer
Tabela 3.2 – Explicação para o codigo do projeto utilizando pseudocódigo
void splitString(char* data) {
Serial.print("Data entered: ");
Serial.println(data);
char* parameter;
parameter = strtok (data, " ,");
while (parameter != NULL) {
setLED(parameter);
parameter = strtok (NULL, " ,");
}
// Limpe o texto e os buffers seriais
for (int x=0; x<16; x++) {
buffer[x]='\0';
}
Serial.flush();
}
funcao splitString faz referencia aos dados do buffer
Imprima “Data entered: “
Imprima o valor dos dados e depois desca uma linha
Declare parameter como o tipo de dado char
Defina-o como o texto ate o primeiro espaco ou virgula
Enquanto o conteudo de parameter nao estiver vazio...
Chame a funcao SetLED
Defina parameter como a proxima parte da string de texto
Outro comentario
Repita a linha seguinte 16 vezes
Defina cada elemento de buffer como NULL (vazio)
Libere as comunicacoes seriais
Uma funcao setLED recebe o buffer
Se a primeira letra for r ou R...
Defina o inteiro Ans como o numero na proxima parte do texto
Certifique-se de que ele esteja entre 0 e 255
Escreva esse valor no pino vermelho
Imprima “Red is set to: “
E entao o valor de Ans
Análise do código
Fim!

Continue navegando