Event-Based Library - Biblioteca de eventos para Arduino

Depois de feito meus primeiros programas para o Arduino, talvez por ter trabalhado durante um tempo em linguagens como Java e C#, tenho achado a sua programação um pouco antiquada. Tarefas como gerenciar apertos de botões ou fazer um LED acender suavimente atrávés de PWM, apesar de serem simples de se compreender, requer um volume razoável de código para ficarem consistentes, ainda mais quando é necessário que vários componentes funcionem simultâneamente.

Por sofrer com essa dificuldade eu resolvi transformar todas as minhas dificuldades de programação com o Arduino em uma coleção de classes que facilitam a vida do programador. Atualmente essa coleção conta com os seguintes componentes:

AnalogEvent

Gerenciador de eventos para portas analogicas que pode ser usado para ler potenciômetros ou outros sensores. Implementa o evento onChange.

Nesse exemplo nós configuramos a porta analógica do pino 1 para ler valores de um potenciometro com uma histerese de 3:

#include <AnalogEvent.h>

void setup() {
  AnalogEvent.addAnalogPort(1,        //potentiometer pin
                            onChange, //onChange event function
                            3);       //hysteresis
  
  Serial.begin(9600);
}

void loop() {
  AnalogEvent.loop();
}

void onChange(AnalogPortInformation* Sender) {
  Serial.print("Analog (pin:");
  Serial.print(Sender->pin);
  Serial.print(") changed to: ");
  Serial.print(Sender->value);
  Serial.println("!");
}

ButtonEvent

Gerenciador de eventos para botões conectados em portas digitais. Implementa os eventos onUp, onDown, onHold e onDouble.

Nesse exemplo nós configuramos um botão no pino 12 com todos os seus eventos ativados:

#include <ButtonEvent.h>

void setup() {
  ButtonEvent.addButton(12,       //button pin
                        onDown,   //onDown event function
                        onUp,     //onUp event function
                        onHold,   //onHold event function
                        1000,     //hold time in milliseconds
                        onDouble, //double event function
                        200);     //double time interval

  Serial.begin(9600);
}

void loop() {
  ButtonEvent.loop();
}

void onDown(ButtonInformation* Sender) {
  Serial.print("Button (pin:");
  Serial.print(Sender->pin);
  Serial.println(") down!");
}

void onUp(ButtonInformation* Sender) {
  Serial.print("Button (pin:");
  Serial.print(Sender->pin);
  Serial.println(") up!");
}

void onHold(ButtonInformation* Sender) {
  Serial.print("Button (pin:");
  Serial.print(Sender->pin);
  Serial.print(") hold for ");
  Serial.print(Sender->holdMillis);
  Serial.println("ms!");
}

void onDouble(ButtonInformation* Sender) {
  Serial.print("Button (pin:");
  Serial.print(Sender->pin);
  Serial.print(") double click in ");
  Serial.print(Sender->doubleMillis);
  Serial.println("ms!");
}

LedControl

Classe que implementa funções simples para controlar LEDs com alguns recursos assíncronos.

Nesse exemplo dois LEDs piscam juntos de duas maneiras diferentes. O LED no pino 9 está configurado um efeito fade in/out de duração de 500ms e com intervalo de 1s, enquanto o LED do pino 8 é um pisca simples de intervalo 200ms:

#include <LedControl.h>

void setup() {
  LedControl.startBlink(9,1000,500);
  LedControl.startBlink(8,200);
}

void loop() {
  LedControl.loop();
}

Esse projeto possui um repositório com maiores informações sobre as classes, instruções de uso e opções para download. O nome do projeto é ebl-arduino, que significa Event-Based Library for Arduino sua página pode ser acessada através da URL GitHub - renatoferreirarenatoferreira/ebl-arduino: Automatically exported from code.google.com/p/ebl-arduino.

Fonte: Event-Based Library - Biblioteca de eventos para Arduino

Cara, parabéns. Bem legal.

Uma pergunta, tem dependência das libs do arduino ou é pure C/standalone?

PS.: Experimente Ruby e/ou CoffeeScript. Deixa Java e C# antiquadas :wink:

Valeu, nofxx!

Tem algumas dependencias sim, mas é tipo digitalWrite, analogWrite, pinMode, tudo coisa simples de substituir se fosse o caso. Você acha que isso pode ser um problema?

Ruby > C#??? TRUCO! :stuck_out_tongue:

Complicado dizer... na verdade é pessoal.
Depois q descobri como é fácil montar um AVR e usar apenas C, ñ usei nenhuma lib do arduino mais.
Acho mais tranquilo, nao depender daquela porrada de code... Incluo apenas o q preciso.
A diff é q o avrfreaks.com fica mais util q arduino.cc hehe....

Agora pro nosso pequeno offtopic (hehe):

Quanto às linguagens, e falando apenas de linguagem, Ruby owna totalmente.
Coisas como essa são válidas: "5.times puts "rocknroll" if nofx.is_playing?"
Repare q dá pra LER..hauehae...
Repare q até o '5' é objeto (chamando método #times nele)...

Tudo ser objeto possibilita umas mágicas (leia metaprogramação, classes dinamicas) interessantes:

class Integer
def par?
self % 2 == 0
end
end

5.par? # false

class Toy
end

bla = Toy
instance = bla.new

Ateh classes são objetos :wink: oh um singleton:

def instance.novo_meth
..code..
end

Quanto a implementação, ruby ñ eh pra windouze tanto quanto C# nao eh p/ sistemas operacionais de vdd (Mono).
Mas as opções p/ ruby no windouze são jRuby (ruby na jvm), e IronRuby (ruby na vm do c# , sei lah.. nunca usei).
E o ruby installer q coloca a YARV mesmo (VM q usamos no linux).

Cara, o sucesso do Arduino está na facilidade de manipular a plaquinha e a sua facil integração com a IDE, ele é muito acessível para todo mundo. Eu até acho que outras opções podem ser melhores (dependendo do aspecto), mas nem creio que seja essa a pretensão do Arduino. De qualquer forma ele atraiu muita gente para essa área.

Enfim, não me importaria em colocar uns ifdefs e deixar as classes compatíveis com AVR não. Por onde começo? Preciso comprar gravadores ou coisas assim? :roll_eyes:

Parabéns muito boa a iniciativa!

Só devemos ter cuidado para não ter uma lib com muitas funções e fazer import de 1000 linhas para acender um LED em algum de seus projetos!!!

Alou Rferreira,

Sim, digitalRead e amigos trazem iniciantes ao arduino todos os dias, vc tem toda a razão.
Agora a userbase de AVR não é negligenciável... uma comparação inocente de foruns:
avrfreaks.net tem ~20x mais users e posts q aqui.

Vc nao precisa comprar nada, todavia o barato (literalmente) é comprar.
Um "arduino" sai pra mim por 20 reais (1 avr, 1 cristal, 2 caps, lm7805).

Com seu arduino mesmo, é soh usar um makefile.

Mude o chip p/ atmel328p e o gravador p/ 'arduino' (onde tá usbasp)

PS.: Seguindo a linha de pensamento do Danilo, coloque no Readme qts kb ficou sua lib compilada.

Cada proj aqui agora são apenas .c files, esse Makefile.
No emacs eu soh uso um atalho ele compila e envia p/ meu gravador.

Olá Rferreira,

Você fez uma analise de quanta RAM sua lib esta utilizando?
isso é bem legal avaliar, a eficiência da lib para ver ser não esta sendo overhead

Pessoal, excelentes colocações. A idéia dessas classes é de justamente facilitar a vida dos novos usuários como em qualquer linguagem ou framework de alto nível. O ponto focal é eliminar a complicação da sintaxe do C, os laços complicados, o uso de delay() em funções que envolvem tempo, etc, além de oferecer mais possibilidades para pessoas que não são avançados em programação. Claro que para isso existe sobrecarga de recursos, mas não pretendo ficar paranóico com isso, pelo menos por agora. Até porque a medida que uma tecnologia fica mais acessível, a demanda por hardware que comporte as novas "facilidades" é natural. O proprio hardware do Arduino passou por um processo evolutivo (atmega8/168/328) antes mesmo de eu chegar com minhas classes "gordas" :grin:.

Olha, quanto a questão do tamanho da classe eu só sei aquele número que aparece embaixo na IDE quando termina de compilar e, como a biblioteca é distribuida através do código fonte, eu não tenho a menor idéia de como compilá-la separado para ver o tamanho do resultado. Quanto a ocupação da SRAM, nunca tentei medir isso antes, até gugluei aqui e achei algumas técnicas meio cabeludas, mas ainda não tentei. Assim que tiver mais informações eu atualizo a documentação. Minhas habilidades de C/C++ são bem maiores que meus conhecimentos da plataforma AVR/Arduino, então qualquer sugestão ou ajuda nessa área é muito bem vinda.

Por hora vou colocar mais duas classes nos próximos dias, na verdade tenho até uma terceira em mente, mas acho que vou levar algum tempo pra fazer então nem vou prometer.

Novas classes foram adicionadas à Event-Based Library for Arduino na sua revisão 45.

Dois novos componentes estão disponíveis nessa revisão:

Properties

Manipulador de variáveis persistentes. Pode ser usado para salvar dados de configuração na area persistente de memória EEPROM do Arduino.

Nesse exemplo duas variáveis para contar boots são salvas na EEPROM, uma do tipo numerico e outra do tipo string:

#include <Properties.h>

#define PROPERTY_BOOTS 0
#define PROPERTY_TEXT 1

void setup() {
  Serial.begin(9600);
  
  if (Properties.load()) {
    if (Properties.getInt(PROPERTY_BOOTS)<=15) {
      //print data
      Serial.print("PROPERTY_BOOTS: ");
      Serial.println(Properties.getInt(PROPERTY_BOOTS));
      Serial.print("PROPERTY_TEXT (");
      Serial.print(Properties.get(PROPERTY_TEXT)->valueSize);
      Serial.print("): ");
      Serial.write((byte*)Properties.get(PROPERTY_TEXT)->value,Properties.get(PROPERTY_TEXT)->valueSize);
      Serial.println();
    
      //sum PROPERTY_BOOTS and update text
      Properties.set(PROPERTY_BOOTS,Properties.getInt(PROPERTY_BOOTS)+1);
      char* text = (char*)malloc(32);
      sprintf(text,"Boot times: %d...", Properties.getInt(PROPERTY_BOOTS));
      Properties.set(PROPERTY_TEXT,(void*)text,strlen(text));
      Properties.save();
    } else {
      Serial.println("Flushing!");
      
      //flush data
      Properties.flush();
      Properties.save();
    }
  } else {
    Serial.println("Starting!");
    
    //create data
    Properties.set(PROPERTY_BOOTS,1);
    char* text = (char*)malloc(32);
    sprintf(text,"Boot times: %d...", Properties.getInt(PROPERTY_BOOTS));
    Properties.set(PROPERTY_TEXT,(void*)text,strlen(text));
    Properties.save();
  }
}

void loop() {
  
}

TimedEvent

Simples temporizador de tarefas.

Nesse exemplo um evento (event1) é utilizado para controlar quando um outro evento (event2) é iniciado e parado em intervalos regulares enquanto eles enviam dados pelo Serial Monitor:

#include <TimedEvent.h>

#define CONTROLLED_TIMER 1

bool flag = true;

void setup() {
  Serial.begin(9600);

  //event 1
  TimedEvent.addTimer(5000, event1);
  //event 2
  TimedEvent.addTimer(CONTROLLED_TIMER, 500, event2);
  TimedEvent.start(CONTROLLED_TIMER);
}

void loop() {
  TimedEvent.loop();
}

void event1(TimerInformation* Sender) {
  Serial.print("event1: ");
  if (flag=!flag) {
    TimedEvent.start(CONTROLLED_TIMER);
    Serial.println("START!");
  } else {
    TimedEvent.stop(CONTROLLED_TIMER);
    Serial.println("STOP!");
  }
}

void event2(TimerInformation* Sender) {
    Serial.println("event2!!");  
}

Esse projeto possui um repositório com maiores informações sobre as classes, instruções de uso e opções para download. Ele pode ser acessado pela URL GitHub - renatoferreirarenatoferreira/ebl-arduino: Automatically exported from code.google.com/p/ebl-arduino.

Fonte: Event-Based Library for Arduino atualizada para R45!

@Rferreira: cara, muito legal sua iniciativa. Já pôs um link para o projeto no Arduino Playground? Acho que vale a pena. []s

Opa, neuron! Brigadão!! :slight_smile: Mas tem algumas coisas que eu estou pretendendo fazer antes de pensar em playgrounds e afins, até porque a ultima vez que pensei em colocar algo lá não entendi bem como funciona (foi na época da migração do forum)...

A última versão da ebl-arduino (r52) trás para os usuários do Arduino uma forma fácil de trocar comandos e dados entre PC e Arduino usando .Net, sendo também fácil de ser portado para outras linguagens e transportes (TCP e assim por diante).

AdvancedSerial

Uma nova classe que implementa um protocolo serial que torna mais fácil a comunicação entre aplicativos do PC e os programas do Arduino.

Este é um exemplo de programa que recebe comandos do PC para controlar um display LCD:

#include <AdvancedSerial.h>
#include <LiquidCrystal.h>

#define BACKLIGHT_ON_MESSAGE 0
#define BACKLIGHT_OFF_MESSAGE 1
#define TEXT_MESSAGE 2

//pins
#define PIN_BACKLIGHT 7

//configure lcd
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

void setup() {
  //configure backlight pin
  pinMode(PIN_BACKLIGHT, OUTPUT);
  //configure LCD
  lcd.begin(16, 2);
  //begin serial port with a desirable speed
  Serial.begin(115200);
  //configure receiver callback
  AdvancedSerial.setReceiver(onMessage);
}

void loop() {
  //AdvancedSerial job
  AdvancedSerial.loop();
}

void onMessage(AdvancedSerialMessage* message) {
  switch (message->id) {
    case BACKLIGHT_ON_MESSAGE:
      digitalWrite(PIN_BACKLIGHT, HIGH);
      break;
      
    case BACKLIGHT_OFF_MESSAGE:
      digitalWrite(PIN_BACKLIGHT, LOW);
      break;
      
    case TEXT_MESSAGE:
      lcd.clear();
      for (int i=0; i<message->size; i++) {
        if (i==16) lcd.setCursor(0, 1);
        lcd.write((char)message->payload[i]);
      }
      break;
  }
}

Client API - AdvancedSerialClient

AdvancedSerialClient é o ponto de entrada para comunicação com programas baseados na AdvancedSerial. Ele permite garantir que o dispositivo está conectado, enviar mensagens para ele e receber eventos assíncronos.

Esse pedaço de código C# é responsável por enviar mensagens de controle para o programa LCDWriter do último exemplo:

try
{
    //create object
    AdvancedSerialClient ArduinoClient = new AdvancedSerialClient();
    //connect to device
    ArduinoClient.Open(Arguments[PARAMETER_PORT].ToString(), 115200);

    //command for LED
    if (Arguments.ContainsKey(PARAMETER_BACKLIGHT))
    {
        SerialProtocol.AdvancedSerialMessage BacklightMessage = new SerialProtocol.AdvancedSerialMessage();

        if ((bool)Arguments[PARAMETER_BACKLIGHT])
            BacklightMessage.ID = BACKLIGHT_ON_MESSAGE;
        else
            BacklightMessage.ID = BACKLIGHT_OFF_MESSAGE;

        ArduinoClient.Send(BacklightMessage);
    }

    //command for text
    if (Arguments.ContainsKey(PARAMETER_TEXT))
    {
        SerialProtocol.AdvancedSerialMessage TextMessage = new SerialProtocol.AdvancedSerialMessage();
        TextMessage.ID = TEXT_MESSAGE;
        TextMessage.Payload = new System.Text.ASCIIEncoding().GetBytes(Arguments[PARAMETER_TEXT].ToString());
        TextMessage.Size = (byte)TextMessage.Payload.Length;
        ArduinoClient.Send(TextMessage);
    }
}
catch (Exception ex)
{
    Console.Write("Error: " + ex.Message);
}

O completo, e pronto para usar, código desse exemplo está disponível na árvore de código da biblioteca.

Rodando o exemplo

São necessários para rodar adequadamente esse exemplo:

  • 1 Arduino;
  • 1 Protoboard (não esquecer o jogo de fios para as conexões);
  • 1 Display 1602A (16x2 com luz de fundo);
  • 1 Potenciômetro de 10k.

Os componentes deverão ser montados na protoboard da seguinte forma:

Clique aqui para baixar o arquivo do Fritzing de fonte dessa imagem.

Uma vez que voce tiver gravado o programa LCDWriter dos exemplos da AdvancedSerial (menu da IDE: File/Examples/AdvancedSerial/LCDWriter) para sua placa Arduino o exemplo da API cliente LCDWrite.exe (diretório da IDE: libraries\AdvancedSerial\clientapi\dotnet\Release) será capaz de controlar a luz de fundo e o texto mostrado no display LCD.

Para encerrar, uma pequena demonstração do exemplo funcionando:

Esse projeto possui um repositório com maiores informações sobre as classes, instruções de uso e opções para download. Ele pode ser acessado pela URL GitHub - renatoferreirarenatoferreira/ebl-arduino: Automatically exported from code.google.com/p/ebl-arduino.

Fonte: Controlando o Arduino com .Net