//

IoT with Java 8 and TinkerForge – 005

31.7.2014 | 8 minutes of reading time

Is it winter or not rather fall right now ? We do not know for sure right now . Weather is always an important topic for conversation. So we are going to be a professional weatherman – with Java, JavaFX and Tinkerforge.

To be able to make a statement about the weather we do not need a poor frog that is confined in a glass and climbs up and down a ladder every once in a while. We can also build a frog-friendly weather station.

The components
==============
For our weather station we need the following ingredients today: a temperature-, a light- and a barometer sensor. This way we are able to measure the air pressure, the barometric height and the luminosity (in Lux).
The temperature-(Link) and barometer sensor(Link) was already part of the previous articles. New for us today is the light sensor. Additionally we are using an 20×4-LCD-display.

The Light Sensor
========================
The light sensor (Ambient Light Sensor) is able to measure the brightness of the environment and provides the value in Lux. The resolution (0 Lux-900 Lux) is 0,1 which equals 12 bit. With approximately 2 g the light sensor is one of the lightest and uses only 1 mAt.

The Weatherstation
==================
As always (see previous articles) all elements are plugged into the master and shortly after they appear on the display of the BrickViewer. Note: Do not forget to update your sensor software if a newer version is available.
All sensors can be used with the Java-Interface which provides the usual ActionListener concept. Additionally every sensor can be requested directly. But keep in mind that this method leads to an unnecessary high bandwidth. The sensorsin this example are programmed without JavaFX because this time no visualization with LineChart is shown. As an example in listing 1: the barometer. Instead of the JavaFX-Elements this time we write directly into the 20×4-LCD display. So far nothing special.

1public class Light implements Runnable {
2 
3    private String UID;
4    private int callbackPeriod;
5    private LCD20x4 lcd20x4 = new LCD20x4("jvX");
6 
7    public Light(final String UID, int callbackPeriod) {
8        this.UID = UID;
9        this.callbackPeriod = callbackPeriod;
10    }
11 
12    @Override
13    public void run() {
14        IPConnection ipcon = new IPConnection();
15        BrickletAmbientLight bricklet 
16            = new BrickletAmbientLight(UID, ipcon);
17        try {
18            ipcon.connect(Localhost.HOST, Localhost.PORT);
19 
20            bricklet.setIlluminanceCallbackPeriod(callbackPeriod);
21            bricklet.addIlluminanceListener(illuminance -> {
22                final double lux = illuminance / 10.0;
23                final String text = "Lux   : "
24                    + lux + " Lux";
25                lcd20x4.printLine(3, text);
26            });
27        } catch (IOException
28                | AlreadyConnectedException
29                | TimeoutException | NotConnectedException e) {
30            e.printStackTrace();
31        }
32    }
33}
34

The 20×4-LDC-Bricklet
=========================

The LCD brick let contains 20×4 dot matrix inclusively a blue background lightning. Additionally there are four buttons at the lower edge of the board. The API enables us to directly write individual characters or whole rows up to a length of 20 characters. The background lightning can be switched on and off and also the state of the buttons can be read. With this LCD bricklet the data of the connected sensors are displayed. Every sensor value is assigned an own row. In our example the following rows are assigned as follows:

  • Temperature in degree Celsius
  • Air pressure in Millibar
  • Barometric height in meters
  • Luminosity in Lux

The advantage with the implementation of the 20×4-LCD display is that it is Thread-safe (italic). The brick let can be provided with data competitively by different threads without having to expect problems. The dispribution of the rows simplifies the programming a lot since only the particular sensor is responsible for the clear display of its own row. Since here for reasons of simplification per sensor unit only one instance of the LCD brick let is generated also the initialization is scrolled through multiple times. For our example this is sufficient for now.

In order to show the requested characters on the LCD-bricklet they first have to be converted in a bit pattern. For this purpose the method String utf16ToKS0066U(String utf16) (also contained in the exemplary source code) can be extracted from the TinkerForge documentation. After the coding the text of max. 20 characters per row can be displayed with the method writeLine() (Listing 2)

1public void printLine(short lineNr, final String text) {
2        try {
3            lcd.writeLine(lineNr, (short)0, clearLine);
4            lcd.writeLine(lineNr, (short)0, utf16ToKS0066U(text));
5        } catch (TimeoutException | NotConnectedException e) {
6            e.printStackTrace();
7        }
8}
9

Listing 3 shows the complete implementation of the class LCD20x4 (italic).

1public class LCD20x4 {
2 
3    private BrickletLCD20x4 lcd;
4 
5    public LCD20x4(final String UID) {
6        IPConnection ipcon = new IPConnection();
7        lcd = new BrickletLCD20x4(UID, ipcon);
8        try {
9            ipcon.connect(Localhost.HOST, Localhost.PORT);
10            lcd.backlightOn();
11            lcd.clearDisplay();
12            lcd.setDefaultTextCounter(-1);
13        } catch (IOException 
14                    | AlreadyConnectedException 
15                    | TimeoutException | NotConnectedException e) {
16            e.printStackTrace();
17        }
18    }
19 
20 
21    private final String clearLine 
22        = utf16ToKS0066U("                    ");
23 
24 
25    public void printLine(short lineNr, final String text) {
26        try {
27            lcd.writeLine(lineNr, (short)0, clearLine);
28            lcd.writeLine(lineNr, (short)0, utf16ToKS0066U(text));
29        } catch (TimeoutException | NotConnectedException e) {
30            e.printStackTrace();
31        }
32    }
33 
34    public void printLine(int lineNr, final String text) {
35        printLine((short) lineNr, text);
36    }
37 
38    static String utf16ToKS0066U(String utf16)
39    {
40        String ks0066u = "";
41        char c;
42 
43        for (int i = 0; i < utf16.length(); i++) {
44            int codePoint = utf16.codePointAt(i);
45 
46            if (Character.isHighSurrogate(utf16.charAt(i))) {
47                // Skip low surrogate
48                i++;
49            }
50 
51            // ASCII subset from JIS X 0201
52            if (codePoint >= 0x0020 && codePoint <= 0x007e) {
53                // The LCD charset doesn't include '\' 
54                // and '~', use similar characters instead
55                switch (codePoint) {
56                    case 0x005c: c = (char)0xa4; break; 
57                        // REVERSE SOLIDUS maps to IDEOGRAPHIC COMMA
58                    case 0x007e: c = (char)0x2d; break; 
59                        // TILDE maps to HYPHEN-MINUS
60                    default: c = (char)codePoint; break;
61                }
62            }
63            // Katakana subset from JIS X 0201
64            else if (codePoint >= 0xff61
65                        && codePoint <= 0xff9f) {
66                c = (char)(codePoint - 0xfec0);
67            }
68            // Special characters
69            else {
70                switch (codePoint) {
71                    case 0x00a5: c = (char)0x5c; break; 
72                        // YEN SIGN
73                    case 0x2192: c = (char)0x7e; break; 
74                        // RIGHTWARDS ARROW
75                    case 0x2190: c = (char)0x7f; break; 
76                        // LEFTWARDS ARROW
77                    case 0x00b0: c = (char)0xdf; break;
78                        // DEGREE SIGN maps 
79                        // to KATAKANA SEMI-VOICED SOUND MARK
80                    case 0x03b1: c = (char)0xe0; break; 
81                        // GREEK SMALL LETTER ALPHA
82                    case 0x00c4: c = (char)0xe1; break; 
83                        // LATIN CAPITAL LETTER A WITH DIAERESIS
84                    case 0x00e4: c = (char)0xe1; break; 
85                        // LATIN SMALL LETTER A WITH DIAERESIS
86                    case 0x00df: c = (char)0xe2; break; 
87                        // LATIN SMALL LETTER SHARP S
88                    case 0x03b5: c = (char)0xe3; break;
89                        // GREEK SMALL LETTER EPSILON
90                    case 0x00b5: c = (char)0xe4; break; 
91                        // MICRO SIGN
92                    case 0x03bc: c = (char)0xe4; break; 
93                        // GREEK SMALL LETTER MU
94                    case 0x03c2: c = (char)0xe5; break; 
95                        // GREEK SMALL LETTER FINAL SIGMA
96                    case 0x03c1: c = (char)0xe6; break; 
97                        // GREEK SMALL LETTER RHO
98                    case 0x221a: c = (char)0xe8; break; 
99                        // SQUARE ROOT
100                    case 0x00b9: c = (char)0xe9; break; 
101                        // SUPERSCRIPT ONE maps to SUPERSCRIPT (minus) ONE
102                    case 0x00a4: c = (char)0xeb; break; 
103                        // CURRENCY SIGN
104                    case 0x00a2: c = (char)0xec; break; 
105                        // CENT SIGN
106                    case 0x2c60: c = (char)0xed; break; 
107                        // LATIN CAPITAL LETTER L WITH DOUBLE BAR
108                    case 0x00f1: c = (char)0xee; break; 
109                        // LATIN SMALL LETTER N WITH TILDE
110                    case 0x00d6: c = (char)0xef; break; 
111                        // LATIN CAPITAL LETTER O WITH DIAERESIS
112                    case 0x00f6: c = (char)0xef; break; 
113                        // LATIN SMALL LETTER O WITH DIAERESIS
114                    case 0x03f4: c = (char)0xf2; break; 
115                        // GREEK CAPITAL THETA SYMBOL
116                    case 0x221e: c = (char)0xf3; break; 
117                        // INFINITY
118                    case 0x03a9: c = (char)0xf4; break; 
119                        // GREEK CAPITAL LETTER OMEGA
120                    case 0x00dc: c = (char)0xf5; break; 
121                        // LATIN CAPITAL LETTER U WITH DIAERESIS
122                    case 0x00fc: c = (char)0xf5; break; 
123                        // LATIN SMALL LETTER U WITH DIAERESIS
124                    case 0x03a3: c = (char)0xf6; break; 
125                        // GREEK CAPITAL LETTER SIGMA
126                    case 0x03c0: c = (char)0xf7; break; 
127                        // GREEK SMALL LETTER PI
128                    case 0x0304: c = (char)0xf8; break; 
129                        // COMBINING MACRON
130                    case 0x00f7: c = (char)0xfd; break; 
131                        // DIVISION SIGN
132 
133                    default:
134                    case 0x25a0: c = (char)0xff; break; // BLACK SQUARE
135                }
136            }
137 
138            // Special handling for 'x' followed by COMBINING MACRON
139            if (c == (char)0xf8) {
140                if (!ks0066u.endsWith("x")) {
141                    c = (char)0xff; // BLACK SQUARE
142                }
143                if (ks0066u.length() > 0) {
144                    ks0066u = ks0066u.substring(0, ks0066u.length() - 1);
145                }
146            }
147            ks0066u += c;
148        }
149        return ks0066u;
150    }
151}
152

The usage
——————————

The implementing happens in the main-method of the class WeatherStation (Listing 4). All sensors are started in a separate thread. In order for the program to not be terminated ahead of time we wait on the command line as long as the user types in an „O“ followed by ENTER.

1public class WeatherStation {
2 
3    private static int callbackPeriod = 10000;
4 
5    public static void main(String args[]) throws Exception {
6        new Thread(
7            new Temperature("dXj", callbackPeriod)).start();
8        new Thread(new Barometer("jY4", callbackPeriod)).start();
9        new Thread(new Light("jy2", callbackPeriod)).start();
10 
11        final  BufferedReader in 
12            = new BufferedReader(new InputStreamReader(System.in));
13 
14        final Thread t = new Thread(() -> {
15            System.out
16                .println("press Q THEN ENTER to terminate");
17            int quit=0;
18            while(true){
19                try {
20                    Thread.sleep(1000);
21                    String msg = null;
22                    while(true){
23                        try{
24                            msg=in.readLine();
25                        }catch(Exception e){}
26                        if(msg != null &&
27                            msg.equals("Q")) { quit = 1; }
28                        if(quit==1) break;
29                    }
30                } catch (InterruptedException e) {
31                    e.printStackTrace();
32                }
33                break;
34            }
35 
36        });
37        t.start();
38    }
39}
40

Summary
————————

In this version the weather station is still connected to the computer via USB. In one of the next articles I am going to show how the weather station can be decoupled from the PC. It then will be possible to run the weather station e.g. outside in a dry place with power supply and still get the evaluation comfortably at the computer in JavaFX. For ideas, suggestions or questions please just contact me on Twitter: @SvenRuppert

Stay tuned. Happy coding!

The source code to this article can be found under
https://bitbucket.org/rapidpm/jaxenter.de-0012-iot-tinkerforge .
For people who are interested in more complex examples I recommend the following
https://bitbucket.org/rapidpm/modules .

share post

Likes

0

//

Gemeinsam bessere Projekte umsetzen

Wir helfen Deinem Unternehmen

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.