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:  https://www.pi4j.com/
012 * **********************************************************************
013 * %%
014 * Copyright (C) 2012 - 2019 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    public static final short RPI_MODEL_CM3     = 10;
118    public static final short RPI_MODEL_ZERO_W  = 12;
119    public static final short RPI_MODEL_3B_PLUS = 13;
120
121    // Raspberry Pi Revision :: Memory
122    public static final short RPI_RAM_256       = 0;
123    public static final short RPI_RAM_512       = 1;
124    public static final short RPI_RAM_1024      = 2;
125
126    // Raspberry Pi Revision :: Manufacture
127    public static final short RPI_MFG_SONY      = 0;
128    public static final short RPI_MFG_EGOMAN    = 1;
129    public static final short RPI_MFG_EMBEST    = 2;
130    public static final short RPI_MFG_UNKNOWN   = 3;
131    public static final short RPI_MFG_EMBEST2   = 4;
132
133    // Raspberry Pi Revision :: Processor
134    public static final short RPI_PROC_BCM2835  = 0;
135    public static final short RPI_PROC_BCM2836  = 1;
136    public static final short RPI_PROC_BCM2837  = 2;
137
138    @Override
139    public SystemInfo.BoardType getBoardType() throws IOException, InterruptedException, UnsupportedOperationException {
140
141        //-------------------------------------------------------------------------
142        // SEE: https://github.com/AndrewFromMelbourne/raspberry_pi_revision
143        //-------------------------------------------------------------------------
144        //
145        // The file /proc/cpuinfo contains a line such as:-
146        //
147        // Revision    : 0003
148        //
149        // that holds the revision number of the Raspberry Pi.
150        // Known revisions (prior to the Raspberry Pi 2) are:
151        //
152        //     +----------+---------+---------+--------+-------------+
153        //     | Revision |  Model  | PCB Rev | Memory | Manufacture |
154        //     +----------+---------+---------+--------+-------------+
155        //     |   0000   |         |         |        |             |
156        //     |   0001   |         |         |        |             |
157        //     |   0002   |    B    |    1    | 256 MB |             |
158        //     |   0003   |    B    |    1    | 256 MB |             |
159        //     |   0004   |    B    |    2    | 256 MB |   Sony      |
160        //     |   0005   |    B    |    2    | 256 MB |   Qisda     |
161        //     |   0006   |    B    |    2    | 256 MB |   Egoman    |
162        //     |   0007   |    A    |    2    | 256 MB |   Egoman    |
163        //     |   0008   |    A    |    2    | 256 MB |   Sony      |
164        //     |   0009   |    A    |    2    | 256 MB |   Qisda     |
165        //     |   000a   |         |         |        |             |
166        //     |   000b   |         |         |        |             |
167        //     |   000c   |         |         |        |             |
168        //     |   000d   |    B    |    2    | 512 MB |   Egoman    |
169        //     |   000e   |    B    |    2    | 512 MB |   Sony      |
170        //     |   000f   |    B    |    2    | 512 MB |   Qisda     |
171        //     |   0010   |    B+   |    1    | 512 MB |   Sony      |
172        //     |   0011   | compute |    1    | 512 MB |   Sony      |
173        //     |   0012   |    A+   |    1    | 256 MB |   Sony      |
174        //     |   0013   |    B+   |    1    | 512 MB |   Embest    |
175        //     |   0014   | compute |    1    | 512 MB |   Sony      |
176        //     |   0015   |    A+   |    1    | 256 MB |   Sony      |
177        //     +----------+---------+---------+--------+-------------+
178        //
179        // If the Raspberry Pi has been over-volted (voiding the warranty) the
180        // revision number will have 100 at the front. e.g. 1000002.
181        //
182        //-------------------------------------------------------------------------
183        //
184        // With the release of the Raspberry Pi 2, there is a new encoding of the
185        // Revision field in /proc/cpuinfo. The bit fields are as follows
186        //
187        //     +----+----+----+----+----+----+----+----+
188        //     |FEDC|BA98|7654|3210|FEDC|BA98|7654|3210|
189        //     +----+----+----+----+----+----+----+----+
190        //     |    |    |    |    |    |    |    |AAAA|
191        //     |    |    |    |    |    |BBBB|BBBB|    |
192        //     |    |    |    |    |CCCC|    |    |    |
193        //     |    |    |    |DDDD|    |    |    |    |
194        //     |    |    | EEE|    |    |    |    |    |
195        //     |    |    |F   |    |    |    |    |    |
196        //     |    |   G|    |    |    |    |    |    |
197        //     |    |  H |    |    |    |    |    |    |
198        //     +----+----+----+----+----+----+----+----+
199        //     |1098|7654|3210|9876|5432|1098|7654|3210|
200        //     +----+----+----+----+----+----+----+----+
201        //
202        // +---+-------+--------------+--------------------------------------------+
203        // | # | bits  |   contains   | values                                     |
204        // +---+-------+--------------+--------------------------------------------+
205        // | A | 00-03 | PCB Revision | (the pcb revision number)                  |
206        // | B | 04-11 | Model name   | A, B, A+, B+, B Pi2, Alpha, Compute Module |
207        // |   |       |              | unknown, B Pi3, Zero, CM3, ZeroW, Pi3+     |
208        // | C | 12-15 | Processor    | BCM2835, BCM2836, BCM2837                  |
209        // | D | 16-19 | Manufacturer | Sony, Egoman, Embest, unknown, Embest      |
210        // | E | 20-22 | Memory size  | 256 MB, 512 MB, 1024 MB                    |
211        // | F | 23-23 | encoded flag | (if set, revision is a bit field)          |
212        // | G | 24-24 | waranty bit  | (if set, warranty void - Pre Pi2)          |
213        // | H | 25-25 | waranty bit  | (if set, warranty void - Post Pi2)         |
214        // +---+-------+--------------+--------------------------------------------+
215        //
216        // Also, due to some early issues the warranty bit has been move from bit
217        // 24 to bit 25 of the revision number (i.e. 0x2000000).
218
219        // get revision number from /proc/cpuinfo
220        String revision = getRevision();
221
222        // determine the board info by deciphering the revision number
223        long irevision = Long.parseLong(revision, 16);
224        long scheme = (irevision >> 23) & 0x1;
225        @SuppressWarnings("unused")
226                long ram = (irevision >> 20) & 0x7;
227        @SuppressWarnings("unused")
228        long manufacturer = (irevision >> 16) & 0xF;
229        @SuppressWarnings("unused")
230        long processor = (irevision >> 12) & 0xF;
231        long model = (irevision >> 4) & 0xFF;
232        long pcbrev = irevision & 0xF;
233
234//        System.out.println(" SCHEME       : " + scheme);
235//        System.out.println(" MEMSIZE      : " + ram);
236//        System.out.println(" MANUFACTURER : " + manufacturer);
237//        System.out.println(" PROCESSOR    : " + processor);
238//        System.out.println(" MODEL        : " + model);
239//        System.out.println(" PCB REVISION : " + pcbrev);
240
241        // determine board type based on revision scheme
242        if (scheme > 0) {
243            // a new revision scheme was provided with the release of Raspberry Pi 2
244            // if the scheme bit is enabled, then use the new revision numbering scheme
245            switch((int)model) {
246                case RPI_MODEL_A:       return SystemInfo.BoardType.RaspberryPi_A;
247                case RPI_MODEL_A_PLUS:  return SystemInfo.BoardType.RaspberryPi_A_Plus;
248                case RPI_MODEL_B_PLUS:  return SystemInfo.BoardType.RaspberryPi_B_Plus;
249                case RPI_MODEL_2B:      return SystemInfo.BoardType.RaspberryPi_2B;
250                case RPI_MODEL_ALPHA:   return SystemInfo.BoardType.RaspberryPi_Alpha;
251                case RPI_MODEL_CM:      return SystemInfo.BoardType.RaspberryPi_ComputeModule;
252                case RPI_MODEL_UNKNOWN: return SystemInfo.BoardType.RaspberryPi_Unknown;
253                case RPI_MODEL_3B:      return SystemInfo.BoardType.RaspberryPi_3B;
254                case RPI_MODEL_ZERO:    return SystemInfo.BoardType.RaspberryPi_Zero;
255                case RPI_MODEL_CM3:     return SystemInfo.BoardType.RaspberryPi_ComputeModule3;
256                case RPI_MODEL_ZERO_W:  return SystemInfo.BoardType.RaspberryPi_ZeroW;
257                case RPI_MODEL_3B_PLUS: return SystemInfo.BoardType.RaspberryPi_3B_Plus;
258                case RPI_MODEL_B: {
259                    // for model B, also take into consideration the revision
260                    if(pcbrev <= 1)
261                        return SystemInfo.BoardType.RaspberryPi_B_Rev1;
262                    else
263                        return SystemInfo.BoardType.RaspberryPi_B_Rev2;
264                }
265            }
266        }
267
268        // prior to the Raspberry Pi 2, the original revision scheme
269        // was simply a fixed identifier number
270        else if (scheme == 0) {
271
272            // The following info obtained from:
273            // http://elinux.org/RPi_HardwareHistory
274            // -and-
275            // https://github.com/Pi4J/wiringPi/blob/master/wiringPi/wiringPi.c#L808
276
277            // -------------------------------------------------------------------
278            // Revision Release Date    Model   PCB Revision    Memory  Notes
279            // -------------------------------------------------------------------
280            // Beta       Q1 2012       B (Beta) ?.?    256 MB  Beta Board
281            // 0002       Q1 2012       B        1.0    256 MB
282            // 0003       Q3 2012       B        1.0    256 MB  (ECN0001) Fuses mod and D14 removed
283            // 0004       Q3 2012       B        2.0    256 MB  (Mfg by Sony)
284            // 0005       Q4 2012       B        2.0    256 MB  (Mfg by Qisda)
285            // 0006       Q4 2012       B        2.0    256 MB  (Mfg by Egoman)
286            // 0007       Q1 2013       A        2.0    256 MB  (Mfg by Egoman)
287            // 0008       Q1 2013       A        2.0    256 MB  (Mfg by Sony)
288            // 0009       Q1 2013       A        2.0    256 MB  (Mfg by Qisda)
289            // 000d       Q4 2012       B        2.0    512 MB  (Mfg by Egoman)
290            // 000e       Q4 2012       B        2.0    512 MB  (Mfg by Sony)
291            // 000f       Q4 2012       B        2.0    512 MB  (Mfg by Qisda)
292            // 0010       Q3 2014       B+       1.0    512 MB  (Mfg by Sony)
293            // 0011       Q2 2014       CM           1.0        512 MB  (Mfg by Sony)
294            // 0012       Q4 2014       A+           1.0        256 MB  (Mfg by Sony)
295            // 0013       Q1 2015       B+           1.2        512 MB   ?
296            // 0014   ?? ????   CM       1.0    512 MB  (Mfg by Sony)
297            // 0015   ?? ????   A+       1.1    256 MB  (Mfg by Sony)|
298            switch (revision.trim().toLowerCase()) {
299                case "Beta":  // Model B Beta
300                case "0002":  // Model B Revision 1
301                case "0003":  // Model B Revision 1 (Egoman) + Fuses mod and D14 removed
302                    return SystemInfo.BoardType.RaspberryPi_B_Rev1;
303
304                case "0004":  // Model B Revision 2 256MB (Sony)
305                case "0005":  // Model B Revision 2 256MB (Qisda)
306                case "0006":  // Model B Revision 2 256MB (Egoman)
307                    return SystemInfo.BoardType.RaspberryPi_B_Rev2;
308
309                case "0007":  // Model A 256MB (Egoman)
310                case "0008":  // Model A 256MB (Sony)
311                case "0009":  // Model A 256MB (Qisda)
312                    return SystemInfo.BoardType.RaspberryPi_A;
313
314                case "000d":  // Model B Revision 2 512MB (Egoman)
315                case "000e":  // Model B Revision 2 512MB (Sony)
316                case "000f":  // Model B Revision 2 512MB (Egoman)
317                    return SystemInfo.BoardType.RaspberryPi_B_Rev2;
318
319                case "0010":  // Model B Plus 512MB (Sony)
320                    return SystemInfo.BoardType.RaspberryPi_B_Plus;
321
322                case "0011":  // Compute Module 512MB (Sony)
323                    return SystemInfo.BoardType.RaspberryPi_ComputeModule;
324
325                case "0012":  // Model A Plus 512MB (Sony)
326                    return SystemInfo.BoardType.RaspberryPi_A_Plus;
327
328                case "0013":  // Model B Plus 512MB (Egoman)
329                    return SystemInfo.BoardType.RaspberryPi_B_Plus;
330
331                /* UNDOCUMENTED */
332                case "0014":  // Compute Module Rev 1.2, 512MB, (Sony)
333                    return SystemInfo.BoardType.RaspberryPi_ComputeModule;
334
335                /* UNDOCUMENTED */
336                case "0015":  // Model A Plus 256MB (Sony)
337                    return SystemInfo.BoardType.RaspberryPi_A_Plus;
338
339                // unknown
340                default:
341                    return SystemInfo.BoardType.RaspberryPi_Unknown;
342            }
343        }
344
345        // unknown board
346        return SystemInfo.BoardType.UNKNOWN;
347    }
348
349    @Override
350    public float getCpuTemperature() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
351        // CPU temperature is in the form
352        // pi@mypi$ /opt/vc/bin/vcgencmd measure_temp
353        // temp=42.3'C
354        // Support for this was added around firmware version 3357xx per info
355        // at http://www.raspberrypi.org/phpBB3/viewtopic.php?p=169909#p169909
356        String result[] = ExecUtil.execute("/opt/vc/bin/vcgencmd measure_temp");
357        if(result != null && result.length > 0){
358            for(String line : result) {
359                String parts[] = line.split("[=']", 3);
360                return Float.parseFloat(parts[1]);
361            }
362        }
363        throw new UnsupportedOperationException();
364    }
365
366    @Override
367    public float getCpuVoltage() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
368        return getVoltage("core");
369    }
370
371    @Override
372    public float getMemoryVoltageSDRam_C() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
373        return getVoltage("sdram_c");
374    }
375
376    @Override
377    public float getMemoryVoltageSDRam_I() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
378        return getVoltage("sdram_i");
379    }
380
381    @Override
382    public float getMemoryVoltageSDRam_P() throws IOException, InterruptedException, NumberFormatException, UnsupportedOperationException {
383        return getVoltage("sdram_p");
384    }
385
386    @Override
387    public boolean getCodecH264Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
388        return getCodecEnabled("H264");
389    }
390
391    @Override
392    public boolean getCodecMPG2Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
393        return getCodecEnabled("MPG2");
394    }
395
396    @Override
397    public boolean getCodecWVC1Enabled() throws IOException, InterruptedException, UnsupportedOperationException {
398        return getCodecEnabled("WVC1");
399    }
400
401    @Override
402    public long getClockFrequencyArm() throws IOException, InterruptedException, UnsupportedOperationException {
403        return getClockFrequency("arm");
404    }
405
406    @Override
407    public long getClockFrequencyCore() throws IOException, InterruptedException, UnsupportedOperationException {
408        return getClockFrequency("core");
409    }
410
411    @Override
412    public long getClockFrequencyH264() throws IOException, InterruptedException, UnsupportedOperationException {
413        return getClockFrequency("h264");
414    }
415
416    @Override
417    public long getClockFrequencyISP() throws IOException, InterruptedException, UnsupportedOperationException {
418        return getClockFrequency("isp");
419    }
420
421    @Override
422    public long getClockFrequencyV3D() throws IOException, InterruptedException, UnsupportedOperationException {
423        return getClockFrequency("v3d");
424    }
425
426    @Override
427    public long getClockFrequencyUART() throws IOException, InterruptedException, UnsupportedOperationException {
428        return getClockFrequency("uart");
429    }
430
431    @Override
432    public long getClockFrequencyPWM() throws IOException, InterruptedException, UnsupportedOperationException {
433        return getClockFrequency("pwm");
434    }
435
436    @Override
437    public long getClockFrequencyEMMC() throws IOException, InterruptedException, UnsupportedOperationException {
438        return getClockFrequency("emmc");
439    }
440
441    @Override
442    public long getClockFrequencyPixel() throws IOException, InterruptedException, UnsupportedOperationException {
443        return getClockFrequency("pixel");
444    }
445
446    @Override
447    public long getClockFrequencyVEC() throws IOException, InterruptedException, UnsupportedOperationException {
448        return getClockFrequency("vec");
449    }
450
451    @Override
452    public long getClockFrequencyHDMI() throws IOException, InterruptedException, UnsupportedOperationException {
453        return getClockFrequency("hdmi");
454    }
455
456    @Override
457    public long getClockFrequencyDPI() throws IOException, InterruptedException, UnsupportedOperationException {
458        return getClockFrequency("dpi");
459    }
460}