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