001package com.pi4j.system.impl;
002
003/*
004 * #%L
005 * **********************************************************************
006 * ORGANIZATION  :  Pi4J
007 * PROJECT       :  Pi4J :: Java Library (Core)
008 * FILENAME      :  RaspiSystemInfoProvider.java
009 *
010 * This file is part of the Pi4J project. More information about
011 * this project can be found here:  http://www.pi4j.com/
012 * **********************************************************************
013 * %%
014 * Copyright (C) 2012 - 2016 Pi4J
015 * %%
016 * This program is free software: you can redistribute it and/or modify
017 * it under the terms of the GNU Lesser General Public License as
018 * published by the Free Software Foundation, either version 3 of the
019 * License, or (at your option) any later version.
020 *
021 * This program is distributed in the hope that it will be useful,
022 * but WITHOUT ANY WARRANTY; without even the implied warranty of
023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
024 * GNU General Lesser Public License for more details.
025 *
026 * You should have received a copy of the GNU General Lesser Public
027 * License along with this program.  If not, see
028 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
029 * #L%
030 */
031
032import com.pi4j.system.SystemInfo;
033import com.pi4j.system.SystemInfoProvider;
034import com.pi4j.util.ExecUtil;
035
036import java.io.IOException;
037import java.text.ParseException;
038
039/**
040 * Raspberry Pi platform specific implementation of the SystemInfoProvider interface.
041 */
042public class RaspiSystemInfoProvider extends DefaultSystemInfoProvider implements SystemInfoProvider {
043
044    private long getClockFrequency(String target) throws IOException, InterruptedException {
045        String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd measure_clock " + target.trim());
046        if(result != null && result.length > 0){
047            for(String line : result) {
048                String parts[] = line.split("=", 2);
049                return Long.parseLong(parts[1].trim());
050            }
051        }
052        throw new UnsupportedOperationException("Invalid command or response.");
053    }
054
055    private boolean getCodecEnabled(String codec) throws IOException, InterruptedException {
056        String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd codec_enabled " + codec);
057        if(result != null && result.length > 0){
058            for(String line : result) {
059                String parts[] = line.split("=", 2);
060                return parts[1].trim().equalsIgnoreCase("enabled");
061            }
062        }
063        throw new RuntimeException("Invalid command or response.");
064    }
065
066    private float getVoltage(String id) throws IOException, InterruptedException, NumberFormatException {
067        String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd measure_volts " + id);
068        if(result != null && result.length > 0){
069            for(String line : result) {
070                String parts[] = line.split("[=V]", 3);
071                return Float.parseFloat(parts[1]);
072            }
073        }
074        throw new UnsupportedOperationException("Invalid command or response.");
075    }
076
077    @Override
078    public String getModelName() throws IOException, InterruptedException, UnsupportedOperationException {
079        return getCpuInfo("model name");
080    }
081
082    @Override
083    public String getOsFirmwareBuild() throws IOException, InterruptedException, UnsupportedOperationException {
084        String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd version");
085        if(result != null){
086            for(String line : result) {
087                if(line.startsWith("version ")){
088                    return line.substring(8);
089                }
090            }
091        }
092        throw new UnsupportedOperationException("Invalid command or response.");
093    }
094
095    @Override
096    public String getOsFirmwareDate() throws IOException, InterruptedException, ParseException, UnsupportedOperationException {
097        String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd version");
098        if(result != null && result.length > 0){
099            for(String line : result) {
100                return line; // return 1st line
101            }
102        }
103        throw new UnsupportedOperationException("Invalid command or response.");
104    }
105
106    // Raspberry Pi Revision :: Model
107    public static final short RPI_MODEL_A       = 0;
108    public static final short RPI_MODEL_B       = 1;
109    public static final short RPI_MODEL_A_PLUS  = 2;
110    public static final short RPI_MODEL_B_PLUS  = 3;
111    public static final short RPI_MODEL_2B      = 4;
112    public static final short RPI_MODEL_ALPHA   = 5;
113    public static final short RPI_MODEL_CM      = 6;
114    public static final short RPI_MODEL_UNKNOWN = 7;
115    public static final short RPI_MODEL_3B      = 8;
116    public static final short RPI_MODEL_ZERO    = 9;
117
118    // Raspberry Pi Revision :: Memory
119    public static final short RPI_RAM_256       = 0;
120    public static final short RPI_RAM_512       = 1;
121    public static final short RPI_RAM_1024      = 2;
122
123    // Raspberry Pi Revision :: Manufacture
124    public static final short RPI_MFG_SONY      = 0;
125    public static final short RPI_MFG_EGOMAN    = 1;
126    public static final short RPI_MFG_EMBEST    = 2;
127    public static final short RPI_MFG_UNKNOWN   = 3;
128    public static final short RPI_MFG_EMBEST2   = 4;
129
130    // Raspberry Pi Revision :: Processor
131    public static final short RPI_PROC_BCM2835  = 0;
132    public static final short RPI_PROC_BCM2836  = 1;
133    public static final short RPI_PROC_BCM2837  = 2;
134
135    @Override
136    public SystemInfo.BoardType getBoardType() throws IOException, InterruptedException, UnsupportedOperationException {
137
138        //-------------------------------------------------------------------------
139        // SEE: https://github.com/AndrewFromMelbourne/raspberry_pi_revision
140        //-------------------------------------------------------------------------
141        //
142        // The file /proc/cpuinfo contains a line such as:-
143        //
144        // Revision    : 0003
145        //
146        // that holds the revision number of the Raspberry Pi.
147        // Known revisions (prior to the Raspberry Pi 2) are:
148        //
149        //     +----------+---------+---------+--------+-------------+
150        //     | Revision |  Model  | PCB Rev | Memory | Manufacture |
151        //     +----------+---------+---------+--------+-------------+
152        //     |   0000   |         |         |        |             |
153        //     |   0001   |         |         |        |             |
154        //     |   0002   |    B    |    1    | 256 MB |             |
155        //     |   0003   |    B    |    1    | 256 MB |             |
156        //     |   0004   |    B    |    2    | 256 MB |   Sony      |
157        //     |   0005   |    B    |    2    | 256 MB |   Qisda     |
158        //     |   0006   |    B    |    2    | 256 MB |   Egoman    |
159        //     |   0007   |    A    |    2    | 256 MB |   Egoman    |
160        //     |   0008   |    A    |    2    | 256 MB |   Sony      |
161        //     |   0009   |    A    |    2    | 256 MB |   Qisda     |
162        //     |   000a   |         |         |        |             |
163        //     |   000b   |         |         |        |             |
164        //     |   000c   |         |         |        |             |
165        //     |   000d   |    B    |    2    | 512 MB |   Egoman    |
166        //     |   000e   |    B    |    2    | 512 MB |   Sony      |
167        //     |   000f   |    B    |    2    | 512 MB |   Qisda     |
168        //     |   0010   |    B+   |    1    | 512 MB |   Sony      |
169        //     |   0011   | compute |    1    | 512 MB |   Sony      |
170        //     |   0012   |    A+   |    1    | 256 MB |   Sony      |
171        //     |   0013   |    B+   |    1    | 512 MB |   Embest    |
172        //     |   0014   | compute |    1    | 512 MB |   Sony      |
173        //     |   0015   |    A+   |    1    | 256 MB |   Sony      |
174        //     +----------+---------+---------+--------+-------------+
175        //
176        // If the Raspberry Pi has been over-volted (voiding the warranty) the
177        // revision number will have 100 at the front. e.g. 1000002.
178        //
179        //-------------------------------------------------------------------------
180        //
181        // With the release of the Raspberry Pi 2, there is a new encoding of the
182        // Revision field in /proc/cpuinfo. The bit fields are as follows
183        //
184        //     +----+----+----+----+----+----+----+----+
185        //     |FEDC|BA98|7654|3210|FEDC|BA98|7654|3210|
186        //     +----+----+----+----+----+----+----+----+
187        //     |    |    |    |    |    |    |    |AAAA|
188        //     |    |    |    |    |    |BBBB|BBBB|    |
189        //     |    |    |    |    |CCCC|    |    |    |
190        //     |    |    |    |DDDD|    |    |    |    |
191        //     |    |    | EEE|    |    |    |    |    |
192        //     |    |    |F   |    |    |    |    |    |
193        //     |    |   G|    |    |    |    |    |    |
194        //     |    |  H |    |    |    |    |    |    |
195        //     +----+----+----+----+----+----+----+----+
196        //     |1098|7654|3210|9876|5432|1098|7654|3210|
197        //     +----+----+----+----+----+----+----+----+
198        //
199        // +---+-------+--------------+--------------------------------------------+
200        // | # | bits  |   contains   | values                                     |
201        // +---+-------+--------------+--------------------------------------------+
202        // | A | 00-03 | PCB Revision | (the pcb revision number)                  |
203        // | B | 04-11 | Model name   | A, B, A+, B+, B Pi2, Alpha, Compute Module |
204        // |   |       |              | unknown, B Pi3, Zero                       |
205        // | C | 12-15 | Processor    | BCM2835, BCM2836, BCM2837                  |
206        // | D | 16-19 | Manufacturer | Sony, Egoman, Embest, unknown, Embest      |
207        // | E | 20-22 | Memory size  | 256 MB, 512 MB, 1024 MB                    |
208        // | F | 23-23 | encoded flag | (if set, revision is a bit field)          |
209        // | G | 24-24 | waranty bit  | (if set, warranty void - Pre Pi2)          |
210        // | H | 25-25 | waranty bit  | (if set, warranty void - Post Pi2)         |
211        // +---+-------+--------------+--------------------------------------------+
212        //
213        // Also, due to some early issues the warranty bit has been move from bit
214        // 24 to bit 25 of the revision number (i.e. 0x2000000).
215
216        // get revision number from /proc/cpuinfo
217        String revision = getRevision();
218
219        // determine the board info by deciphering the revision number
220        long irevision = Long.parseLong(revision, 16);
221        long scheme = (irevision >> 23) & 0x1;
222        long ram = (irevision >> 20) & 0x7;
223        long manufacturer = (irevision >> 16) & 0xF;
224        long processor = (irevision >> 12) & 0xF;
225        long model = (irevision >> 4) & 0xFF;
226        long pcbrev = irevision & 0xF;
227
228//        System.out.println(" SCHEME       : " + scheme);
229//        System.out.println(" MEMSIZE      : " + ram);
230//        System.out.println(" MANUFACTURER : " + manufacturer);
231//        System.out.println(" PROCESSOR    : " + processor);
232//        System.out.println(" MODEL        : " + model);
233//        System.out.println(" PCB REVISION : " + pcbrev);
234
235        // determine board type based on revision scheme
236        if (scheme > 0) {
237            // a new revision scheme was provided with the release of Raspberry Pi 2
238            // if the scheme bit is enabled, then use the new revision numbering scheme
239            switch((int)model) {
240                case RPI_MODEL_A:       return SystemInfo.BoardType.RaspberryPi_A;
241                case RPI_MODEL_A_PLUS:  return SystemInfo.BoardType.RaspberryPi_A_Plus;
242                case RPI_MODEL_B_PLUS:  return SystemInfo.BoardType.RaspberryPi_B_Plus;
243                case RPI_MODEL_2B:      return SystemInfo.BoardType.RaspberryPi_2B;
244                case RPI_MODEL_ALPHA:   return SystemInfo.BoardType.RaspberryPi_Alpha;
245                case RPI_MODEL_CM:      return SystemInfo.BoardType.RaspberryPi_ComputeModule;
246                case RPI_MODEL_UNKNOWN: return SystemInfo.BoardType.RaspberryPi_Unknown;
247                case RPI_MODEL_3B:      return SystemInfo.BoardType.RaspberryPi_3B;
248                case RPI_MODEL_ZERO:    return SystemInfo.BoardType.RaspberryPi_Zero;
249                case RPI_MODEL_B: {
250                    // for model B, also take into consideration the revision
251                    if(pcbrev <= 1)
252                        return SystemInfo.BoardType.RaspberryPi_B_Rev1;
253                    else
254                        return SystemInfo.BoardType.RaspberryPi_B_Rev2;
255                }
256            }
257        }
258
259        // prior to the Raspberry Pi 2, the original revision scheme
260        // was simply a fixed identifier number
261        else if (scheme == 0) {
262
263            // The following info obtained from:
264            // http://elinux.org/RPi_HardwareHistory
265            // -and-
266            // https://github.com/Pi4J/wiringPi/blob/master/wiringPi/wiringPi.c#L808
267
268            // -------------------------------------------------------------------
269            // Revision Release Date    Model   PCB Revision    Memory  Notes
270            // -------------------------------------------------------------------
271            // Beta       Q1 2012       B (Beta) ?.?    256 MB  Beta Board
272            // 0002       Q1 2012       B        1.0    256 MB
273            // 0003       Q3 2012       B        1.0    256 MB  (ECN0001) Fuses mod and D14 removed
274            // 0004       Q3 2012       B        2.0    256 MB  (Mfg by Sony)
275            // 0005       Q4 2012       B        2.0    256 MB  (Mfg by Qisda)
276            // 0006       Q4 2012       B        2.0    256 MB  (Mfg by Egoman)
277            // 0007       Q1 2013       A        2.0    256 MB  (Mfg by Egoman)
278            // 0008       Q1 2013       A        2.0    256 MB  (Mfg by Sony)
279            // 0009       Q1 2013       A        2.0    256 MB  (Mfg by Qisda)
280            // 000d       Q4 2012       B        2.0    512 MB  (Mfg by Egoman)
281            // 000e       Q4 2012       B        2.0    512 MB  (Mfg by Sony)
282            // 000f       Q4 2012       B        2.0    512 MB  (Mfg by Qisda)
283            // 0010       Q3 2014       B+       1.0    512 MB  (Mfg by Sony)
284            // 0011       Q2 2014       CM           1.0        512 MB  (Mfg by Sony)
285            // 0012       Q4 2014       A+           1.0        256 MB  (Mfg by Sony)
286            // 0013       Q1 2015       B+           1.2        512 MB   ?
287            // 0014   ?? ????   CM       1.0    512 MB  (Mfg by Sony)
288            // 0015   ?? ????   A+       1.1    256 MB  (Mfg by Sony)|
289            switch (revision.trim().toLowerCase()) {
290                case "Beta":  // Model B Beta
291                case "0002":  // Model B Revision 1
292                case "0003":  // Model B Revision 1 (Egoman) + Fuses mod and D14 removed
293                    return SystemInfo.BoardType.RaspberryPi_B_Rev1;
294
295                case "0004":  // Model B Revision 2 256MB (Sony)
296                case "0005":  // Model B Revision 2 256MB (Qisda)
297                case "0006":  // Model B Revision 2 256MB (Egoman)
298                    return SystemInfo.BoardType.RaspberryPi_B_Rev2;
299
300                case "0007":  // Model A 256MB (Egoman)
301                case "0008":  // Model A 256MB (Sony)
302                case "0009":  // Model A 256MB (Qisda)
303                    return SystemInfo.BoardType.RaspberryPi_A;
304
305                case "000d":  // Model B Revision 2 512MB (Egoman)
306                case "000e":  // Model B Revision 2 512MB (Sony)
307                case "000f":  // Model B Revision 2 512MB (Egoman)
308                    return SystemInfo.BoardType.RaspberryPi_B_Rev2;
309
310                case "0010":  // Model B Plus 512MB (Sony)
311                    return SystemInfo.BoardType.RaspberryPi_B_Plus;
312
313                case "0011":  // Compute Module 512MB (Sony)
314                    return SystemInfo.BoardType.RaspberryPi_ComputeModule;
315
316                case "0012":  // Model A Plus 512MB (Sony)
317                    return SystemInfo.BoardType.RaspberryPi_A_Plus;
318
319                case "0013":  // Model B Plus 512MB (Egoman)
320                    return SystemInfo.BoardType.RaspberryPi_B_Plus;
321
322                /* UNDOCUMENTED */
323                case "0014":  // Compute Module Rev 1.2, 512MB, (Sony)
324                    return SystemInfo.BoardType.RaspberryPi_ComputeModule;
325
326                /* UNDOCUMENTED */
327                case "0015":  // Model A Plus 256MB (Sony)
328                    return SystemInfo.BoardType.RaspberryPi_A_Plus;
329
330                // unknown
331                default:
332                    return SystemInfo.BoardType.RaspberryPi_Unknown;
333            }
334        }
335
336        // unknown board
337        return SystemInfo.BoardType.UNKNOWN;
338    }
339
340    @Override
341    public float getCpuTemperature() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
342        // CPU temperature is in the form
343        // pi@mypi$ /opt/vc/bin/vcgencmd measure_temp
344        // temp=42.3'C
345        // Support for this was added around firmware version 3357xx per info
346        // at http://www.raspberrypi.org/phpBB3/viewtopic.php?p=169909#p169909
347        String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd measure_temp");
348        if(result != null && result.length > 0){
349            for(String line : result) {
350                String parts[] = line.split("[=']", 3);
351                return Float.parseFloat(parts[1]);
352            }
353        }
354        throw new UnsupportedOperationException();
355    }
356
357    @Override
358    public float getCpuVoltage() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
359        return getVoltage("core");
360    }
361
362    @Override
363    public float getMemoryVoltageSDRam_C() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
364        return getVoltage("sdram_c");
365    }
366
367    @Override
368    public float getMemoryVoltageSDRam_I() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
369        return getVoltage("sdram_i");
370    }
371
372    @Override
373    public float getMemoryVoltageSDRam_P() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
374        return getVoltage("sdram_p");
375    }
376
377    @Override
378    public boolean getCodecH264Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
379        return getCodecEnabled("H264");
380    }
381
382    @Override
383    public boolean getCodecMPG2Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
384        return getCodecEnabled("MPG2");
385    }
386
387    @Override
388    public boolean getCodecWVC1Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
389        return getCodecEnabled("WVC1");
390    }
391
392    @Override
393    public long getClockFrequencyArm() throws IOException, InterruptedException, UnsupportedOperationException {
394        return getClockFrequency("arm");
395    }
396
397    @Override
398    public long getClockFrequencyCore() throws IOException, InterruptedException, UnsupportedOperationException {
399        return getClockFrequency("core");
400    }
401
402    @Override
403    public long getClockFrequencyH264() throws IOException, InterruptedException, UnsupportedOperationException {
404        return getClockFrequency("h264");
405    }
406
407    @Override
408    public long getClockFrequencyISP() throws IOException, InterruptedException, UnsupportedOperationException {
409        return getClockFrequency("isp");
410    }
411
412    @Override
413    public long getClockFrequencyV3D() throws IOException, InterruptedException, UnsupportedOperationException {
414        return getClockFrequency("v3d");
415    }
416
417    @Override
418    public long getClockFrequencyUART() throws IOException, InterruptedException, UnsupportedOperationException {
419        return getClockFrequency("uart");
420    }
421
422    @Override
423    public long getClockFrequencyPWM() throws IOException, InterruptedException, UnsupportedOperationException {
424        return getClockFrequency("pwm");
425    }
426
427    @Override
428    public long getClockFrequencyEMMC() throws IOException, InterruptedException, UnsupportedOperationException {
429        return getClockFrequency("emmc");
430    }
431
432    @Override
433    public long getClockFrequencyPixel() throws IOException, InterruptedException, UnsupportedOperationException {
434        return getClockFrequency("pixel");
435    }
436
437    @Override
438    public long getClockFrequencyVEC() throws IOException, InterruptedException, UnsupportedOperationException {
439        return getClockFrequency("vec");
440    }
441
442    @Override
443    public long getClockFrequencyHDMI() throws IOException, InterruptedException, UnsupportedOperationException {
444        return getClockFrequency("hdmi");
445    }
446
447    @Override
448    public long getClockFrequencyDPI() throws IOException, InterruptedException, UnsupportedOperationException {
449        return getClockFrequency("dpi");
450    }
451}