AVR-IplaTV-Box - zamieniamy zestaw Atnel ATB na Arduino

Posted on 26 October, 2019 at 15:43

Tags: Java Linux AVR Arduino


AVR-IplaTV-Box - zamieniamy zestaw Atnel ATB na Arduino

Ten post jest kontynuacją poprzedniego wątku, w którym przedstawiałem koncepcję urządzenia AVR-IplaTV-Box. Analizując komentarze jakie pojawiły się pod tamtym postem postanowiłem zamienić zastosowany wcześniej zestaw ATB na zewnętrzny moduł Arduino z wyświetlaczem LCD.

Migracja z zestawu ATB do Arduino

Proces migracji z ATB do Arduino był stosunkowo prosty, ponieważ głównym zadaniem tego zewnętrznego modułu jest odczytywanie komend IR z pilota i przesyłanie ich do komputera. Biorąc pod uwagę fakt, że każde Arduino jest wyposażone w układ pozwalający na komunikację UART<->USB, proces developmentu sprowadził się w głównej mierze do doboru bibliotek, kórych w sieci jest bardzo dużo.

Dodatkowa rozbudowa - LCD

Podczas tworzenia zewnętrznego modułu IR postanowiłem również zaimplementować wyświetlacz LCD 2x16 ze sterownikiem HD44780 w celu wyświetlania dodatkowych informacji o aktualnie oglądanym programie TV. Niestety nie posiadałem konwertera HD44780<->I2C, który pozwala na sterowanie tym wyświetlaczem przy użyciu 4 przewodów (w tym 2 zasilających). W rezultacie, w środku urządzenia znajduje się dużo przewodów połączeniowych.

avr-ipla-tv-box-arduino--front
Zewnętrzny moduł IR - przód
avr-ipla-tv-box-arduino--back
Zewnętrzny moduł IR - tył
avr-ipla-tv-box-arduino--inside
Zewnętrzny moduł IR - wnętrze

Hardware

Jeśli chodzi o część sprzętową modułu, to składa się ona z Arduino Nano, wyświetlacza LCD HD44780, odbiornika IR TSOP31236 oraz kilku biernych elementów elektronicznych. Do budowy korpusu modułu wykorzystałem niepotrzebne etui na okulary (eco-friendly xD). Poniżej zamieszczam schemat elektryczny oraz połączeniowy modułu, który został stworzony przy użyciu darmowego programu Fritzing.

avr-ipla-tv-box-arduino--bb
Schemat połączeniowy modułu
avr-ipla-tv-box-arduino--schem
Schemat elektryczny modułu
arduino-nano-pinout
Arduino Nano - pinout
Źródło: https://core-electronics.com.au/tutorials/compare-arduino-boards.html#nano

Dobór bibliotek Arduino

W projekcie zastosowałem Arduino Nano, które jest bardzo popularne wśród społeczności Arduino, więc znalezienie biblioteki do obsługi odbiornika podczerwieni TSOP31236 oraz wyświetlacza HD44780 było trywialnie proste.

Finalnie w projekcie wykorzystałem dwie poniższe biblioteki:

  • LiquidCrystal by Arduino, Adafruit version 1.0.7
  • IRremote by shirriff version 2.2.3

Dodatkowo postanowiłem napisać prostą funkcję do scrollowania tekstu w jednej linii, która domyślnie nie jest implementowana w bibliotekach do obsługi LCD ze sterownikiem HD44780.

Software

Sam program na Arduino jest stosunkowo prosty i łatwy do analizy. W głównej pętli programu wywoływane są funkcje do odczytu/aktualizacji stanu całej aplikacji oraz jednego przerwania, które nakręca softwarowy timer odpowiedzialny za scrollowanie tekstu na wyświetlaczu.

W oprogramowanie modułu można wyróżnić cztery główne stany:

  • Odczyt komend z IR i wysłanie ich do komputera.
  • Odczyt danych z komputera dot. aktualnie oglądanego programu TV.
  • Aktualizacja danych do wyświetlenia na wyświetlaczu LCD - aktualizacja bufora.
  • Wyświetlenie informacji na LCD - scrollowanie.

Scrollowanie tekstu odbywa się w jednym z dwóch trybów. Pierwszy (no force) wyświetla aktualny tekst z bufora, a sam bufor jest aktualizowany dopiero, gdy cała zawartość zostanie wyświetlona (płynne przejście). Drugi tryb (force) wymusza natychmiastową aktualizację bufora, a proces scrollowania zaczyna się od początku. Tryb ‘force’ jest wykorzystywany w momencie zmiany aktualnie oglądanego kanału, natomiast tryb ‘no force’ podczas aktualizacji informacji o aktualnie oglądanym programie (w głównej mierze chodzi o aktualizację postępu aktualnie oglądanego programu wyrażonego w procentach)

/*
   AVR Ipla TV Box.

   version: v1.1.0
   author: DevTomek.pl
*/
#include <LiquidCrystal.h> // LiquidCrystal by Arduino, Adafruit version 1.0.7
#include <IRremote.h> // IRremote by shirriff version 2.2.3

// LED
const int INFO_LED = 13;

// IR
const int IR_SENSOR = 2;
IRrecv irrecv(IR_SENSOR);
decode_results results;

// LCD
const int SCREEN_WIDTH = 16;
const int SCREEN_HEIGHT = 2;
const int RS = 7, EN = 8, D4 = 9, D5 = 10, D6 = 11, D7 = 12;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);

// Serial Read
const char READ_DATA_SEPARATOR = '|';
const uint8_t READ_BUFFER_SIZE = 255;
char readBuffer[READ_BUFFER_SIZE];
boolean hasNewData = false;

// Global Variables
String line1 = "AVR-Ipla-TV-Box";
String line1Buffer = line1;
unsigned int line1Length = 16;
String line2 = "    DevTomek    ";
String line2Buffer = line2;
int stringStart, stringStop = 0;
int scrollCursor = SCREEN_WIDTH;
volatile uint8_t timer1 = 0;


void setup() {

  /* Based on: Arduino Timer Interrupts Calculator */
  // TIMER 1 for interrupt frequency 100 Hz:
  cli(); // stop interrupts
  TCCR1A = 0; // set entire TCCR1A register to 0
  TCCR1B = 0; // same for TCCR1B
  TCNT1  = 0; // initialize counter value to 0
  // set compare match register for 100 Hz increments
  OCR1A = 19999; // = 16000000 / (8 * 100) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12, CS11 and CS10 bits for 8 prescaler
  TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei(); // allow interrupts

  Serial.begin(9600);
  Serial.println("Starting initialization...");
  pinMode(INFO_LED , OUTPUT);
  lcd.begin(SCREEN_WIDTH, SCREEN_HEIGHT);
  irrecv.enableIRIn();
  Serial.println("Initialization done!");
}

void loop() {
  readIRData();
  readSerialData();
  updateData();

  if (timer1 > 25) {
    scroll();
    timer1 = 0;
  }

}

ISR(TIMER1_COMPA_vect) {
  timer1++;
}

void readIRData() {
  if (irrecv.decode(&results)) {
    if (results.value != 0xFFFFFFFF) {
      digitalWrite(INFO_LED , HIGH);
      Serial.println(results.value, HEX);
      digitalWrite(INFO_LED , LOW);
    }
    irrecv.resume();
  }
}

void readSerialData() {
  while (hasNewData == false && Serial.available() > 0) {
    static uint8_t ndx = 0;
    char rc = Serial.read();
    if (rc != '\n') {
      readBuffer[ndx] = rc;
      ndx++;
      if (ndx >= READ_BUFFER_SIZE) {
        ndx = READ_BUFFER_SIZE - 1;
      }
    } else {
      readBuffer[ndx] = '\0';
      ndx = 0;
      hasNewData = true;
    }
  }
}

void updateData() {
  if (hasNewData == true) {
    // RX frame structure: [LINE_1_DATA|LINE_2_DATA|IS_FORCE_MODE]
    line1Buffer =  getValue(readBuffer, READ_DATA_SEPARATOR, 0);
    line2Buffer =  getValue(readBuffer, READ_DATA_SEPARATOR, 1);
    boolean isForceMode = getValue(readBuffer, READ_DATA_SEPARATOR, 2) == "true";
    if (isForceMode) {
      updateLcdData();
    }
    hasNewData = false;
  }
}

String getValue(String data, char separator, int index) {
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length() - 1;

  for (int i = 0; i <= maxIndex && found <= index; i++) {
    if (data.charAt(i) == separator || i == maxIndex) {
      found++;
      strIndex[0] = strIndex[1] + 1;
      strIndex[1] = (i == maxIndex) ? i + 1 : i;
    }
  }

  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

void scroll() {
  lcd.clear();
  lcd.setCursor(scrollCursor, 0);
  lcd.print(line1.substring(stringStart, stringStop));
  lcd.setCursor(0, 1);
  lcd.print(line2);

  if (stringStart == 0 && scrollCursor > 0) {
    scrollCursor--;
    stringStop++;
  } else if (stringStart == stringStop) {
    updateLcdData(); // update the text as the previous one will be displayed
  } else if (stringStop == line1Length  ) {
    stringStart++;
  } else if (line1Length < SCREEN_WIDTH && stringStop == (SCREEN_WIDTH + line1Length) ) {
    stringStart++;
    stringStop = stringStart;
  } else {
    stringStart++;
    stringStop++;
  }

}

void updateLcdData() {
  stringStart = stringStop = 0;
  scrollCursor = SCREEN_WIDTH;
  line1 = line1Buffer;
  line2 = line2Buffer;
  line1Length =  line1.length();
}

Aktualny kod można również znaleźć na moim GitHub’ie w repozytorium AVR-Ipla-TV-Box i katalogu Arduino.

Podsumowanie

Na koniec chciałbym tylko zaznaczyć, że zastosowany na początku projektu zestaw Atnel ATB, był celowym wyborem, ponieważ projekt był wtedy w fazie rozwoju, więc zastosowanie zestawu uruchomieniowego miało jak najbardziej sens. Po długotrwałych testach okazało się, że cały projekt zakończył się sukcesem i wszystko działa zgodnie z moimi oczekiwaniami, co oznaczało że trzeba przejść do kolejnego etapu, czyli stworzenie finalnej wersji urządzenia i zastąpienie zestawu uruchomieniowego, czymś w rodzaju zewnętrznego modułu IR.


Please provide a valid nick.
Please provide a valid content.

* These fields are required.