Automatická mierka v sviečkových grafoch

Zápisník experimentátora

Hierarchy: Sviečkové grafy

V dnešnom dieli budeme nastavovať automatickú mierku na sviečkové grafy. Vďaka tomu sa zobrazený graf vždy nejako zmestí na použitý displej. Opäť budeme používať displej Nokia 5110. Plynule nadviažeme na predchádzajúci kód.

Schéma zapojenia sa v ničom neodlišuje od predchádzajúcich zapojení s displejom Nokia 5110, kde sme nepotrebovali pripojiť žiadne ďalšie súčiastky. Iba Arduino, level shifter a displej Nokia 5110.

Program

V hlavnom programe som upravil rozpätie, v ktorom sa generujú hodnoty. Teraz môžu dosahovať hodnoty 0-100, čo je viac ako sú možnosti displeja Nokia 5110, ktorý má len 48 bodov na výšku. Generované hodnoty sa teraz aj rýchlejšie menia, pretože naraz môžu poskočiť až o 3 body. To je zmenené len preto, aby sa dali odfotografovať dynamickejšie sviečkové grafy.

A z grafiky bolo odstránené kreslenie mriežky, ktorá by nám pri automatickom prispôsobení veľkosti už nijako nepomohla. Zmenilo sa aj vykreslenie bodu s aktuálnou hodnotou. Aj tú musíme prepočítať a dobre nám na to poslúži funkcia map. Aby sme ju ale mohli použiť, potrebujeme maximum a minimum. Tieto dve hodnoty sú preto prístupné ako public v triede OHLCChart.

int value=50;

void setup() {
  ...
}

void loop() {
  // update random data
  int valuemove=random(7)-3;
  value+=valuemove;
  value=constrain(value,0,100);
  //Serial.println(value);
  ohlc.addValue(value);

  // render chart
  display.clearDisplay();
  // draw candlesticks
  ohlc.draw();
  // draw last value
  display.fillRect(5,5,15,11,BLACK);
  display.fillRect(6,6,13,9,WHITE);
  display.setCursor(7,7);
  display.print(value);
  // draw indicator
  int _v=map(value,ohlc.minimum,ohlc.maximum,0,47);
  display.drawLine(82,47-_v,83,47-_v,BLACK);
  display.drawLine(83,47-_v+1,83,47-_v-1,BLACK);
  display.display();
  
  delay(200); // 5 values/second
}

candlestick.h

Najprv sa pozrieme na úpravy triedy OHLCChart. Vidíme, že premenné minimum a maximum sa presunuli medzi public, aby boli prístupné aj zvonku. Vďaka tomu si môžeme v hlavnom programe pomocou nich prepočítať aktuálnu pozícu kurzora.

Bolo upravené aj kreslenie grafu. Teraz vypočítame hodnoty vždy a dáme ich ako parameter do každej funkcie renederera, ktorý ich opäť použije na vypočítanie polohy jednotlivých objektov.

template<typename ohlcvalue, const int len, const int stp, typename renderclass>
class OHLCChart {
  ...
  
public:
  ohlcvalue minimum;
  ohlcvalue maximum;

  OHLCChart() : ohlc_step(stp), ohlc_first(len), ohlc_next_step(0) {
    last = data + (len - 1);
  }

  renderclass& getRender() {
    return render;
  }

  ...

  void draw() {
    render.drawHeader();
    if(ohlc_first!=len) {
      minimum = data[ohlc_first].l;
      maximum = data[ohlc_first].h;
      for(int i=ohlc_first+1;i<len;i++) {
        if(data[i].l<minimum)
          minimum = data[i].l;
        if(data[i].h>maximum)
          maximum = data[i].h;
      }
      if(grid_step>0 && minimum!=maximum)
        render.drawGrid(minimum,maximum,grid_step);
    }
    for(int i=ohlc_first;i<len;i++)
      render.drawBar(i,data+i,minimum,maximum);
    render.drawFooter();
  }
};

V nasledovnom kóde si renderer vypočíta správne polohy pomocnej mriežky a popisov každej osi mriežky. V prípade, že by bola mriežka príliš nahusto, nebudú sa vykresľovať popisky.

template<class ohlcvalue>
class OHLCNokia5110BaseRender {
protected:
  Adafruit_PCD8544 *display;
public:
  OHLCNokia5110BaseRender() : display(NULL) {}

  void drawHeader() {}
  void drawFooter() {}
  void setDisplay(Adafruit_PCD8544 *d) {display=d;}
  void drawGrid(ohlcvalue minimum, ohlcvalue maximum, ohlcvalue grid_step) {
    const int HEIGHT = 47;
    int dv = minimum / grid_step;
    int _gs = map(minimum+grid_step,minimum,maximum,0,HEIGHT);
    ohlcvalue bg = (dv + 0) * grid_step;
    for(ohlcvalue i = bg;i<maximum+grid_step;i+=grid_step) {
      for(int j=0;j<=84;j+=5)
        display->drawPixel(j,HEIGHT-(int)map(i,minimum,maximum,0,HEIGHT),BLACK);
      if(_gs>7) { // not too dense
        display->setCursor(0,HEIGHT-(int)map(i,minimum,maximum,0,HEIGHT)-7);
        display->print(i);
        }
      }
    }
};

A keď sa kreslia jednotlivé sviečky, opäť sa použíjú premenné minimum a maximum na vypočítanie polohy jednotlivých objektov sviečkového grafu.

template<class ohlcvalue>
class OHLCNokia5110BarRender : public OHLCNokia5110BaseRender<ohlcvalue> {
public:
  void drawBar(int pos, OHLCData<ohlcvalue> *bar, ohlcvalue minimum, ohlcvalue maximum) {
    if(this->display==NULL)
      return;
    if(minimum==maximum)
      return;

    OHLCData<ohlcvalue> _bar=*bar;
      
    int START = 5 + pos * 6;
    const int BW = 2;
    const int HEIGHT = 47;
    int top;
    int bottom;

    // transformation
    _bar.o = map(_bar.o,minimum,maximum,0,HEIGHT);
    _bar.h = map(_bar.h,minimum,maximum,0,HEIGHT);
    _bar.l = map(_bar.l,minimum,maximum,0,HEIGHT);
    _bar.c = map(_bar.c,minimum,maximum,0,HEIGHT);

    if(_bar.o < _bar.c) {
      top = _bar.c;
      bottom = _bar.o;
    } else {
      top = _bar.o;
      bottom = _bar.c;
    }

    // high
    this->display->drawLine(START+1*BW,HEIGHT-_bar.h,START+1*BW,HEIGHT-top,BLACK);

    // low
    this->display->drawLine(START+1*BW,HEIGHT-_bar.l,START+1*BW,HEIGHT-bottom,BLACK);

    // bar
    if(_bar.o < _bar.c) {
      this->display->fillRect(START,HEIGHT-bottom,2*BW+1,bottom-top+1,WHITE);
      this->display->drawRect(START,HEIGHT-bottom,2*BW+1,bottom-top+1,BLACK);
      }
    else
      this->display->fillRect(START,HEIGHT-bottom,2*BW+1,bottom-top+1,BLACK);
    }
};

Vďaka týmto úpravám bude výsledok vyzerať tak ako na obrázku a pre nás je vývoj kódu knižnice prakticky hotový. Už v ňom žiadne zásadné zmeny nebudú.

Zdrojový kód

Zdrojový kód sa nachádza na GitHub.

Video

Video s ukažkou automatického prispôsobovania nájdete na YouTube.

Pokračovanie

Nabudúce si odmeriame reálnu teplotu v intervale 12 hodín a zobrazíme ju pomocou tohoto kódu.


25.09.2016


Menu