Prosty logger

Założenia

Tworzymy proste urządzenie, które będzie wyświetlać i zapisywać na karcie SD pomiary napięć dostarczanych na 4 porty.

Lista komponentów

Do tego celu wykorzystamy Arduino UNO i kilka innych elementów, poniżej lista potrzebnych gratów:

  • Arduino UNO
  • ekran OLED ze SSD1306 128x64
  • czytnik kart SD
  • konwerter napięć 3.3V <--> 5V posiadający 8 portów
  • rezystory 2,2kΩ
  • rezystory 1kΩ

Połączenia

Połączenia wykonujemy według poniższego diagramu. Biblioteki Arduino których używamy w dużej części wymuszają użycie kontkretnych pinów. Z rezystorów budujemy dzielniki napięć które pozwolą nam podłączyć do Arduino bezpiecznie źródła napięć 12V (realny margines przy dzielniku 2,2kΩ + 1kΩ to 16V co pozwoli monitorować np.: ładowanie akumulatorów kwasowo-ołowiowych). Takie źródła wpinamy pomiędzy rezystory, pamiętać należy by przyłączyć masę badanych układów do masy naszego loggera.

Źródła o napięciu 5V możemy podłączyć bezpośrednio tak jak załączony na obrazku sensor natężenia prądu ACS712, który podaje napięcia 0V-2.5V dla ujemnych prądów i 2.5-5V dla dodatnich prądów.

prosty_logger

Kod

Biblioteki który będą potrzebne:

#include <SPI.h>
#include <SD.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

Do sterownia wyświetlaczem używamy biblioteki SSD1306Ascii zamiast bardzo popularnej Adafruit ze względu na wymagania dotyczące pamięci RAM. W przypadku równoczesnego używania karty SD i wyświetlacza z biblioteką Adafruit pamięci zabraknie, mimo tego, że kompilator twierdzi, że pamięci wystarcza. Problem rodzi się w trakcie pracy - dynamicznie tworzony jest bufor ekranu i bufor do zapisu na karcie SD co całkowicie wysyca pamięć RAM. W zamian za mniejsze możliwości i migotanie przy czyszczeniu ekranu bibliotek SSD1306Ascii pozwoli na Arduino UNO używać kart SD z wyświetlaczem.

Zmienne globalne:

// zmienne używane przez bibliotekę obsługującą czytnik kart SD:
Sd2Card card;
SdVolume volume;
SdFile root;
File myFile;

// Piny do komunikacji z czytnikiem kart SD
const int chipSelect = 10; //Pin używany jako chipSelect w komunikacji SPI z czytnikiem kart
// Poniższe piny są zdefiniowane w bibliotece i tak musimy przyłączyć właściwe nóżki czytnika kart
// MOSI: 11
// MISO: 12
// SCK: 13

// Ekrany mają różne identyfikatory, najlepiej przed użyciem użyć przykładowego szkicu i2c_scanner
// 0X3C+SA0 - 0x3C or 0x3D
#define I2C_ADDRESS 0x3C

// Define proper RST_PIN if required.
#define RST_PIN -1
SSD1306AsciiAvrI2c oled;

// Dane dotyczące ekranu - piny do komunikacji zdefiniowane w bibliotece Wire.h
// Arduino UNO:       A4(SDA), A5(SCL)
// Arduino MEGA 2560: 20(SDA), 21(SCL)
// Arduino LEONARDO:   2(SDA),  3(SCL)

//Przygotowujemy zmienne wykorzystywane bezpośrednio w naszym programie:
int a0int = 0;
int a1int = 0;
int a2int = 0;
int a3int = 0;

int analogPin0 = A0;
int analogPin1 = A1;
int analogPin2 = A2;
int analogPin3 = A3;

Funkcja setup():

void setup() {
  screenInit();
  cardInit();
}

Główna pętla programu:

void loop() {
  a0int = analogRead(analogPin0);
  a1int = analogRead(analogPin1);
  a2int = analogRead(analogPin2);
  a3int = analogRead(analogPin3);
  drawData();
  delay(1000);
  writeData();
}

Ze względu na prowizoryczny charakter programu nie będziemy się bawić w żadne bardziej zaawansowane metody i do odmierzania czasu między pomiarami używamy funkcji delay().

Definicje funkcji używanych w programie:

void drawData() {
  oled.clear();
  oled.print("A0: "); oled.println(a0int);
  oled.print("A1: "); oled.println(a1int);
  oled.print("A2: "); oled.println(a2int);
  oled.print("A3: "); oled.println(a3int);
}

void screenInit() {
#if RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // RST_PIN >= 0
  // Call oled.setI2cClock(frequency) to change from the default frequency.
  oled.setFont(X11fixed7x14B);
  oled.clear();
  oled.print("Hello");
  delay(2000);
}

void writeData() {
  myFile = SD.open("text.txt", FILE_WRITE);
  myFile.print(a0int); myFile.print(";");
  myFile.print(a1int); myFile.print(";");
  myFile.print(a2int); myFile.print(";");
  myFile.print(a3int); myFile.println(";");
  myFile.close();
}

void cardInit() {
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    // Informujemy, że brakuje karty
    oled.setFont(X11fixed7x14B);
    oled.clear();
    oled.print("Brak karty");
    while (1);
  }

  oled.clear();
  Serial.print("Card type:         ");
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      oled.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      oled.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      oled.println("SDHC");
      break;
    default:
      oled.println("nieznana karta");
  }

  // Próbujemy zaczytać kartę by sprawdzić czy jest na niej właściwy system plików
  if (!volume.init(card)) {
    //Jeżeli na karcie nie ma systemu plików FAT to program zapętla się w tym miejscu
    oled.println("Brak FAT16/FAT32");    
    while (1);
  }
  delay(2000);

  SD.begin();
}

Poprzedni wpis Następny wpis