[5] JAVA i STM32 – ekspresowy kurs programowania z MicroEJ – stacja pogodowa


Uwaga! Wszystkie opublikowane odcinki kursu są dostępne pod adresem.
W ostatnim artykule serii poświęconej środowisku MicroEJ (JAVA dla mikrokontrolerów, w artykułach skupiamy się na wersji tego języka dla STM32) zostanie przedstawiony sposób na tworzenie własnych widgetów i wykorzystanie ich w interfejsie graficznym. Jako przykład posłuży odczytywanie w czasie rzeczywistym danych z czterech czujników: temperatury (MCP9801), ciśnienia (LPS25HB), wilgotności (HTS221) i natężenia światła (TSL2561) oraz prezentacja wyników na wykresach. Wszystkie czujniki zostały podłączone do zestawu STM32F429I-DISCO za pośrednictwem wspólnej magistrali I2C. Pokazany przykład może zostać wykorzystany jako prosta stacja pogodowa.
Interfejs graficzny
Pierwszym krokiem jest jak zwykle przygotowanie platformy (JPF). Tak samo jak w poprzednim przykładzie, można wykorzystać przygotowaną wcześniej platformę Full. Przechodząc do tworzenia projektu należy pamiętać, aby wybrać odpowiednią listę dołączanych modułów:
- EDC
- EJ.MOTION
- MICROUI
- MWT
- SNI
Ostatnim krokiem konfiguracji projektu jest dodanie biblioteki Widgets. Sposób dołączania tej biblioteki wraz z dokumentacją do projektu zostało przedstawione w przykładzie ilustrującym wykonanie zegara z budzikiem z obsługą za pomocą touch-panela.
Interfejs graficzny obejmuje cztery przyciski klasy com.is2t.mwt.widgets.button.Button oraz cztery wykresy klasy ChartWidget, która zostanie opisana w kolejnym rozdziale. Z uwagi na mały rozmiar wyświetlacza, w danej chwili będzie wyświetlany tylko jeden wykres, a do przełączania wykresów posłużą wspomniane przyciski. Opisy przycisków zostały wykorzystane również do prezentacji ostatniej wartości odczytanej z czujnika. Wymienione obiekty zostały rozmieszczone na ekranie za pomocą widgetu typu BorderComposite. Umieszcza on obiekty na ekranie w pięciu lokalizacjach: NORTH, SOUTH, EAST, WEST i CENTER. Centralna część została wykorzystana do wyświetlenia wykresu, przyciski zaś umieszczono w części NORTH w siatce o rozmiarze 2×2 utworzonej za pomocą trzech obiektów typu GridComposite.
List. 1. Kod klasy MyLook
public class MyLook implements Look {
private static final int FOREGROUND_COLOR_CONTENT = 0x000000;
private static final int FOREGROUND_COLOR_DEFAULT = 0x000000;
private static final int FOREGROUND_COLOR_FOCUSED = 0x000000;
private static final int BACKGROUND_COLOR_CONTENT = 0xB7A9A9;
private static final int BACKGROUND_COLOR_DEFAULT = 0xB7A9A9;
private static final int BACKGROUND_COLOR_FOCUSED = 0xD1DDDD;
private static final int BORDER_COLOR_CONTENT = 0x969aa2;
private static final int BORDER_COLOR_DEFAULT = 0x969aa2;
private static final int BORDER_COLOR_FOCUSED = 0x969aa2;
private static final int SMALL_FONT_INDEX = 0;
private static final int MEDIUM_FONT_INDEX = 1;
private static final DisplayFont SMALL_FONT = DisplayFont.getFont(DisplayFont.LATIN, 10, DisplayFont.STYLE_PLAIN);
private static final DisplayFont MEDIUM_FONT = DisplayFont.getFont(DisplayFont.LATIN, 30, DisplayFont.STYLE_PLAIN);
private static final DisplayFont[] FONTS = {SMALL_FONT, MEDIUM_FONT};
@Override
public int getProperty(int resource) {
switch (resource) {
case LookExtension.GET_SMALL_FONT_INDEX:
return SMALL_FONT_INDEX;
case LookExtension.GET_MEDIUM_FONT_INDEX:
return MEDIUM_FONT_INDEX;
case Look.GET_FOREGROUND_COLOR_CONTENT:
return FOREGROUND_COLOR_CONTENT;
case Look.GET_BACKGROUND_COLOR_DEFAULT:
return BACKGROUND_COLOR_DEFAULT;
case Look.GET_BORDER_COLOR_CONTENT:
return BORDER_COLOR_CONTENT;
case Look.GET_BORDER_COLOR_DEFAULT:
return BORDER_COLOR_DEFAULT;
case Look.GET_BACKGROUND_COLOR_CONTENT:
return BACKGROUND_COLOR_CONTENT;
case Look.GET_FONT_INDEX_DEFAULT:
return MEDIUM_FONT_INDEX;
case Look.GET_FOREGROUND_COLOR_DEFAULT:
return FOREGROUND_COLOR_DEFAULT;
case Look.GET_BACKGROUND_COLOR_FOCUSED:
return BACKGROUND_COLOR_FOCUSED;
case Look.GET_BORDER_COLOR_FOCUSED:
return BORDER_COLOR_FOCUSED;
case Look.GET_FOREGROUND_COLOR_FOCUSED:
return FOREGROUND_COLOR_FOCUSED;
case Look.GET_FONT_INDEX_FOCUSED:
return MEDIUM_FONT_INDEX;
default:
System.out.println("Need resource: "+resource);
}
return 0;
}
@Override
public DisplayFont[] getFonts() {
return FONTS;
}
}
Do projektu należy także dodać obiekty odpowiedzialne za wygląd aplikacji. W przykładzie wykorzystano klasy MyLook i MyTheme z poprzedniego projektu. Zmianie uległy jednak użyte czcionki: większa o rozmiarze 30 px do opisu przycisków oraz mniejsza o rozmiarze 10 px do opisu osi wykresów. Obie czcionki, tak jak poprzednio, należy dodać do projektu nie zapominając o pliku z ich listą i lokalizacją. Przed uruchomieniem symulacji należy jeszcze dodać z projektu zegara klasę DesktopRenderer pozwalającą na dowolną zmianę koloru tła. Podczas konfiguracji symulacji należy pamiętać o dodaniu pliku z listą czcionek w zakładce Configuration?MicroUI?Font.
Kod źródłowy klas MyLook oraz MyTheme przedstawiono na listingach 1 i 2, natomiast kod klasy Main – na listingu 3.
List. 2. Kod klasy MyTheme
public class MyTheme extends Theme {
@Override
public String getName() {
return null;
}
@Override
protected void populate() {
Drawer drawer = new PlainDrawer();
add(new TitleLabelRenderer());
add(new DesktopRenderer());
add(new ButtonRenderer(drawer));
}
@Override
public Look getDefaultLook() {
return new MyLook();
}
@Override
public boolean isStandard() {
return false;
}
}
List. 3. Kod klasy Main
public class Main {
private static Button temperatureButton;
private static Button humidityButton;
private static Button lightButton;
private static Button pressureButton;
private static BorderComposite bc;
public static void main(String[] args) {
MicroUI.errorLog(true);
MWT.RenderingContext.add(new MyTheme());
Desktop desktop = new Desktop();
Panel panel = new Panel();
GridComposite valuesGcH = new GridComposite(MWT.HORIZONTAL, 2);
GridComposite valuesGcV1 = new GridComposite(MWT.VERTICAL, 2);
GridComposite valuesGcV2 = new GridComposite(MWT.VERTICAL, 2);
temperatureButton = new Button("T: ");
valuesGcV1.add(temperatureButton);
pressureButton = new Button("P: ");
valuesGcV1.add(pressureButton);
humidityButton = new Button("H: ");
valuesGcV2.add(humidityButton);
lightButton = new Button("L: ");
valuesGcV2.add(lightButton);
valuesGcH.add(valuesGcV1);
valuesGcH.add(valuesGcV2);
bc =new BorderComposite(MWT.HORIZONTAL);
bc.addAt(valuesGcH, MWT.NORTH);
panel.setWidget(bc);
panel.show(desktop, true);
desktop.show();
}
}
Tworzenie nowego widgetu
Aby móc przedstawić dane na wykresach należy przygotować dwie klasy: ChartWidget oraz ChartRenderer. Pierwsza z nich przechowuje wszystkie potrzebne dane, w tym listę punktów do wyświetlenia. Druga z klas wykorzystuje te dane aby narysować wykres na przydzielonym obszarze ekranu.
List. 4. Kod klasy ChartWidget
public class ChartWidget extends Widget {
private float maxY;
private float minY;
private int maxPoints;
private ArrayList points;
private int color;
public ChartWidget(int maxPoints, int color) {
this.maxPoints = maxPoints;
this.points = new ArrayList(maxPoints);
this.color = color;
}
public float getMaxY() {
return maxY;
}
public float getMinY() {
return minY;
}
public int getColor() {
return color;
}
public void addPoint(float point) {
if(this.points.size() == this.maxPoints)
this.points.remove(0);
this.points.add(point);
this.maxY = this.points.get(0);
this.minY = this.points.get(0);
for(int i=1; i<this.points.size(); i++)="" {="" if(this.points.get(i)=""> this.maxY)
this.maxY = this.points.get(i);
if(this.points.get(i) < this.minY)
this.minY = this.points.get(i);
}
this.repaint();
}
public ArrayList getPoints() {
return this.points;
}
}

Technologie End of Life i bezpieczeństwo sieci – wyzwania Europy związane z tzw. długiem technologicznym
Najczęstsze błędy firm przy wyborze dostawcy energii i jak ich uniknąć
Fotorezystor, czyli czujnik światła dwojakiego działania. Przykład innowacji w automatyce i elektronice możliwej dzięki technologii fotooporników 



